nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
115 lines (106 loc) • 3.84 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/test_runner/reporter/spec.js
import * as assert from "nstdlib/lib/assert";
import Transform from "nstdlib/lib/internal/streams/transform";
import * as colors from "nstdlib/lib/internal/util/colors";
import { kSubtestsFailed } from "nstdlib/lib/internal/test_runner/test";
import { getCoverageReport } from "nstdlib/lib/internal/test_runner/utils";
import { relative } from "nstdlib/lib/path";
import {
formatTestReport,
indent,
reporterColorMap,
reporterUnicodeSymbolMap,
} from "nstdlib/lib/internal/test_runner/reporter/utils";
class SpecReporter extends Transform {
#stack = [];
#reported = [];
#failedTests = [];
#cwd = process.cwd();
constructor() {
super({ __proto__: null, writableObjectMode: true });
colors.refresh();
}
#handleTestReportEvent(type, data) {
const subtest = Array.prototype.shift.call(this.#stack); // This is the matching `test:start` event
if (subtest) {
assert(subtest.type === "test:start");
assert(subtest.data.nesting === data.nesting);
assert(subtest.data.name === data.name);
}
let prefix = "";
while (this.#stack.length) {
// Report all the parent `test:start` events
const parent = Array.prototype.pop.call(this.#stack);
assert(parent.type === "test:start");
const msg = parent.data;
Array.prototype.unshift.call(this.#reported, msg);
prefix += `${indent(msg.nesting)}${reporterUnicodeSymbolMap["arrow:right"]}${msg.name}\n`;
}
let hasChildren = false;
if (
this.#reported[0] &&
this.#reported[0].nesting === data.nesting &&
this.#reported[0].name === data.name
) {
Array.prototype.shift.call(this.#reported);
hasChildren = true;
}
const indentation = indent(data.nesting);
return `${formatTestReport(type, data, prefix, indentation, hasChildren)}\n`;
}
#handleEvent({ type, data }) {
switch (type) {
case "test:fail":
if (data.details?.error?.failureType !== kSubtestsFailed) {
Array.prototype.push.call(this.#failedTests, data);
}
return this.#handleTestReportEvent(type, data);
case "test:pass":
return this.#handleTestReportEvent(type, data);
case "test:start":
Array.prototype.unshift.call(this.#stack, {
__proto__: null,
data,
type,
});
break;
case "test:stderr":
case "test:stdout":
return data.message;
case "test:diagnostic":
return `${reporterColorMap[type]}${indent(data.nesting)}${reporterUnicodeSymbolMap[type]}${data.message}${colors.white}\n`;
case "test:coverage":
return getCoverageReport(
indent(data.nesting),
data.summary,
reporterUnicodeSymbolMap["test:coverage"],
colors.blue,
true,
);
}
}
_transform({ type, data }, encoding, callback) {
callback(null, this.#handleEvent({ __proto__: null, type, data }));
}
_flush(callback) {
if (this.#failedTests.length === 0) {
callback(null, "");
return;
}
const results = [
`\n${reporterColorMap["test:fail"]}${reporterUnicodeSymbolMap["test:fail"]}failing tests:${colors.white}\n`,
];
for (let i = 0; i < this.#failedTests.length; i++) {
const test = this.#failedTests[i];
const formattedErr = formatTestReport("test:fail", test);
if (test.file) {
const relPath = relative(this.#cwd, test.file);
const location = `test at ${relPath}:${test.line}:${test.column}`;
Array.prototype.push.call(results, location);
}
Array.prototype.push.call(results, formattedErr);
}
callback(null, Array.prototype.join.call(results, "\n"));
}
}
export default SpecReporter;