test_runner: catch reporter errors
PR-URL: https://github.com/nodejs/node/pull/49646 Fixes: https://github.com/nodejs/node/issues/48937 Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
parent
2e4e1e1a76
commit
a4c7f81241
@ -21,12 +21,15 @@ const { kEmptyObject } = require('internal/util');
|
||||
const { kCancelledByParent, Test, Suite } = require('internal/test_runner/test');
|
||||
const {
|
||||
parseCommandLine,
|
||||
reporterScope,
|
||||
setupTestReporters,
|
||||
} = require('internal/test_runner/utils');
|
||||
const { bigint: hrtime } = process.hrtime;
|
||||
|
||||
const testResources = new SafeMap();
|
||||
|
||||
testResources.set(reporterScope.asyncId(), reporterScope);
|
||||
|
||||
function createTestTree(options = kEmptyObject) {
|
||||
return setup(new Test({ __proto__: null, ...options, name: '<root>' }));
|
||||
}
|
||||
@ -40,9 +43,14 @@ function createProcessEventHandler(eventName, rootTest) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Check if this error is coming from a test. If it is, fail the test.
|
||||
const test = testResources.get(executionAsyncId());
|
||||
|
||||
// Check if this error is coming from a reporter. If it is, throw it.
|
||||
if (test === reporterScope) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Check if this error is coming from a test. If it is, fail the test.
|
||||
if (!test || test.finished) {
|
||||
// If the test is already finished or the resource that created the error
|
||||
// is not mapped to a Test, report this as a top level diagnostic.
|
||||
|
@ -20,6 +20,7 @@ const {
|
||||
StringPrototypeSlice,
|
||||
} = primordials;
|
||||
|
||||
const { AsyncResource } = require('async_hooks');
|
||||
const { relative } = require('path');
|
||||
const { createWriteStream } = require('fs');
|
||||
const { pathToFileURL } = require('internal/url');
|
||||
@ -163,15 +164,15 @@ async function getReportersMap(reporters, destinations, rootTest) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function setupTestReporters(rootTest) {
|
||||
const reporterScope = new AsyncResource('TestReporterScope');
|
||||
const setupTestReporters = reporterScope.bind(async (rootTest) => {
|
||||
const { reporters, destinations } = parseCommandLine();
|
||||
const reportersMap = await getReportersMap(reporters, destinations, rootTest);
|
||||
for (let i = 0; i < reportersMap.length; i++) {
|
||||
const { reporter, destination } = reportersMap[i];
|
||||
compose(rootTest.reporter, reporter).pipe(destination);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let globalTestOptions;
|
||||
|
||||
@ -417,6 +418,7 @@ module.exports = {
|
||||
isTestFailureError,
|
||||
kDefaultPattern,
|
||||
parseCommandLine,
|
||||
reporterScope,
|
||||
setupTestReporters,
|
||||
getCoverageReport,
|
||||
};
|
||||
|
8
test/fixtures/test-runner/custom_reporters/throwing-async.js
vendored
Normal file
8
test/fixtures/test-runner/custom_reporters/throwing-async.js
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = async function * customReporter() {
|
||||
yield 'Going to throw an error\n';
|
||||
setImmediate(() => {
|
||||
throw new Error('Reporting error');
|
||||
});
|
||||
};
|
6
test/fixtures/test-runner/custom_reporters/throwing.js
vendored
Normal file
6
test/fixtures/test-runner/custom_reporters/throwing.js
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = async function * customReporter() {
|
||||
yield 'Going to throw an error\n';
|
||||
throw new Error('Reporting error');
|
||||
};
|
@ -134,4 +134,25 @@ describe('node:test reporters', { concurrency: true }, () => {
|
||||
assert.strictEqual(child.stdout.toString(), '');
|
||||
assert.match(child.stderr.toString(), /ERR_INVALID_ARG_TYPE/);
|
||||
});
|
||||
|
||||
it('should throw when reporter errors', async () => {
|
||||
const child = spawnSync(process.execPath,
|
||||
['--test', '--test-reporter', fixtures.fileURL('test-runner/custom_reporters/throwing.js'),
|
||||
fixtures.path('test-runner/default-behavior/index.test.js')]);
|
||||
assert.strictEqual(child.status, 7);
|
||||
assert.strictEqual(child.signal, null);
|
||||
assert.strictEqual(child.stdout.toString(), 'Going to throw an error\n');
|
||||
assert.match(child.stderr.toString(), /Error: Reporting error\r?\n\s+at customReporter/);
|
||||
});
|
||||
|
||||
it('should throw when reporter errors asynchronously', async () => {
|
||||
const child = spawnSync(process.execPath,
|
||||
['--test', '--test-reporter',
|
||||
fixtures.fileURL('test-runner/custom_reporters/throwing-async.js'),
|
||||
fixtures.path('test-runner/default-behavior/index.test.js')]);
|
||||
assert.strictEqual(child.status, 7);
|
||||
assert.strictEqual(child.signal, null);
|
||||
assert.strictEqual(child.stdout.toString(), 'Going to throw an error\n');
|
||||
assert.match(child.stderr.toString(), /Emitted 'error' event on Duplex instance/);
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user