Skip to content

Commit 0c3ae25

Browse files
pmarchinitargos
authored andcommitted
test_runner: print formatted errors on summary
PR-URL: #56911 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Chemi Atlow <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent f4254b8 commit 0c3ae25

File tree

4 files changed

+111
-19
lines changed

4 files changed

+111
-19
lines changed

lib/internal/test_runner/reporter/spec.js

+31-19
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,31 @@ class SpecReporter extends Transform {
3030
colors.refresh();
3131
}
3232

33+
#formatFailedTestResults() {
34+
if (this.#failedTests.length === 0) {
35+
return '';
36+
}
37+
38+
const results = [
39+
`\n${reporterColorMap['test:fail']}${reporterUnicodeSymbolMap['test:fail']}failing tests:${colors.white}\n`,
40+
];
41+
42+
for (let i = 0; i < this.#failedTests.length; i++) {
43+
const test = this.#failedTests[i];
44+
const formattedErr = formatTestReport('test:fail', test);
45+
46+
if (test.file) {
47+
const relPath = relative(this.#cwd, test.file);
48+
const location = `test at ${relPath}:${test.line}:${test.column}`;
49+
ArrayPrototypePush(results, location);
50+
}
51+
52+
ArrayPrototypePush(results, formattedErr);
53+
}
54+
55+
this.#failedTests = []; // Clean up the failed tests
56+
return ArrayPrototypeJoin(results, '\n'); ;
57+
}
3358
#handleTestReportEvent(type, data) {
3459
const subtest = ArrayPrototypeShift(this.#stack); // This is the matching `test:start` event
3560
if (subtest) {
@@ -74,31 +99,18 @@ class SpecReporter extends Transform {
7499
case 'test:coverage':
75100
return getCoverageReport(indent(data.nesting), data.summary,
76101
reporterUnicodeSymbolMap['test:coverage'], colors.blue, true);
102+
case 'test:summary':
103+
// We report only the root test summary
104+
if (data.file === undefined) {
105+
return this.#formatFailedTestResults();
106+
}
77107
}
78108
}
79109
_transform({ type, data }, encoding, callback) {
80110
callback(null, this.#handleEvent({ __proto__: null, type, data }));
81111
}
82112
_flush(callback) {
83-
if (this.#failedTests.length === 0) {
84-
callback(null, '');
85-
return;
86-
}
87-
const results = [`\n${reporterColorMap['test:fail']}${reporterUnicodeSymbolMap['test:fail']}failing tests:${colors.white}\n`];
88-
for (let i = 0; i < this.#failedTests.length; i++) {
89-
const test = this.#failedTests[i];
90-
const formattedErr = formatTestReport('test:fail', test);
91-
92-
if (test.file) {
93-
const relPath = relative(this.#cwd, test.file);
94-
const location = `test at ${relPath}:${test.line}:${test.column}`;
95-
96-
ArrayPrototypePush(results, location);
97-
}
98-
99-
ArrayPrototypePush(results, formattedErr);
100-
}
101-
callback(null, ArrayPrototypeJoin(results, '\n'));
113+
callback(null, this.#formatFailedTestResults());
102114
}
103115
}
104116

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { run } from 'node:test';
2+
import { spec } from 'node:test/reporters';
3+
import tmpdir from '../../../common/tmpdir.js';
4+
import { writeFileSync } from 'node:fs';
5+
6+
7+
const fixtureContent = {
8+
'dependency.js': 'module.exports = {};',
9+
'dependency.mjs': 'export const a = 1;',
10+
'test.js': `
11+
const test = require('node:test');
12+
require('./dependency.js');
13+
import('./dependency.mjs');
14+
import('data:text/javascript,');
15+
test('test has ran');`,
16+
'failing-test.js': `
17+
const test = require('node:test');
18+
test('failing test', () => {
19+
throw new Error('failed');
20+
});`,
21+
};
22+
23+
tmpdir.refresh();
24+
25+
const fixturePaths = Object.keys(fixtureContent)
26+
.reduce((acc, file) => ({ ...acc, [file]: tmpdir.resolve(file) }), {});
27+
Object.entries(fixtureContent)
28+
.forEach(([file, content]) => writeFileSync(fixturePaths[file], content));
29+
30+
const controller = new AbortController();
31+
const { signal } = controller;
32+
33+
const stream = run({
34+
watch: true,
35+
cwd: tmpdir.path,
36+
signal,
37+
});
38+
39+
40+
stream.compose(spec).pipe(process.stdout);
41+
42+
for await (const event of stream) {
43+
if (event.type === 'test:watch:drained') {
44+
controller.abort();
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
✖ failing test (*ms)
2+
✔ test has ran (*ms)
3+
ℹ tests 2
4+
ℹ suites 0
5+
ℹ pass 1
6+
ℹ fail 1
7+
ℹ cancelled 0
8+
ℹ skipped 0
9+
ℹ todo 0
10+
ℹ duration_ms *
11+
12+
✖ failing tests:
13+
14+
*
15+
✖ failing test (*ms)
16+
Error: failed
17+
*
18+
*
19+
*
20+
*
21+
*
22+
*
23+
ℹ tests 0
24+
ℹ suites 0
25+
ℹ pass 0
26+
ℹ fail 0
27+
ℹ cancelled 0
28+
ℹ skipped 0
29+
ℹ todo 0
30+
ℹ duration_ms *

test/parallel/test-runner-output.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ const tests = [
239239
name: 'test-runner/output/test-runner-plan.js',
240240
flags: ['--test-reporter=tap'],
241241
},
242+
{
243+
name: 'test-runner/output/test-runner-watch-spec.mjs',
244+
transform: specTransform,
245+
},
242246
process.features.inspector ? {
243247
name: 'test-runner/output/coverage_failure.js',
244248
flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'],

0 commit comments

Comments
 (0)