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 { kCancelledByParent, Test, Suite } = require('internal/test_runner/test');
|
||||||
const {
|
const {
|
||||||
parseCommandLine,
|
parseCommandLine,
|
||||||
|
reporterScope,
|
||||||
setupTestReporters,
|
setupTestReporters,
|
||||||
} = require('internal/test_runner/utils');
|
} = require('internal/test_runner/utils');
|
||||||
const { bigint: hrtime } = process.hrtime;
|
const { bigint: hrtime } = process.hrtime;
|
||||||
|
|
||||||
const testResources = new SafeMap();
|
const testResources = new SafeMap();
|
||||||
|
|
||||||
|
testResources.set(reporterScope.asyncId(), reporterScope);
|
||||||
|
|
||||||
function createTestTree(options = kEmptyObject) {
|
function createTestTree(options = kEmptyObject) {
|
||||||
return setup(new Test({ __proto__: null, ...options, name: '<root>' }));
|
return setup(new Test({ __proto__: null, ...options, name: '<root>' }));
|
||||||
}
|
}
|
||||||
@ -40,9 +43,14 @@ function createProcessEventHandler(eventName, rootTest) {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this error is coming from a test. If it is, fail the test.
|
|
||||||
const test = testResources.get(executionAsyncId());
|
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 (!test || test.finished) {
|
||||||
// If the test is already finished or the resource that created the error
|
// 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.
|
// is not mapped to a Test, report this as a top level diagnostic.
|
||||||
|
@ -20,6 +20,7 @@ const {
|
|||||||
StringPrototypeSlice,
|
StringPrototypeSlice,
|
||||||
} = primordials;
|
} = primordials;
|
||||||
|
|
||||||
|
const { AsyncResource } = require('async_hooks');
|
||||||
const { relative } = require('path');
|
const { relative } = require('path');
|
||||||
const { createWriteStream } = require('fs');
|
const { createWriteStream } = require('fs');
|
||||||
const { pathToFileURL } = require('internal/url');
|
const { pathToFileURL } = require('internal/url');
|
||||||
@ -163,15 +164,15 @@ async function getReportersMap(reporters, destinations, rootTest) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reporterScope = new AsyncResource('TestReporterScope');
|
||||||
async function setupTestReporters(rootTest) {
|
const setupTestReporters = reporterScope.bind(async (rootTest) => {
|
||||||
const { reporters, destinations } = parseCommandLine();
|
const { reporters, destinations } = parseCommandLine();
|
||||||
const reportersMap = await getReportersMap(reporters, destinations, rootTest);
|
const reportersMap = await getReportersMap(reporters, destinations, rootTest);
|
||||||
for (let i = 0; i < reportersMap.length; i++) {
|
for (let i = 0; i < reportersMap.length; i++) {
|
||||||
const { reporter, destination } = reportersMap[i];
|
const { reporter, destination } = reportersMap[i];
|
||||||
compose(rootTest.reporter, reporter).pipe(destination);
|
compose(rootTest.reporter, reporter).pipe(destination);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
let globalTestOptions;
|
let globalTestOptions;
|
||||||
|
|
||||||
@ -417,6 +418,7 @@ module.exports = {
|
|||||||
isTestFailureError,
|
isTestFailureError,
|
||||||
kDefaultPattern,
|
kDefaultPattern,
|
||||||
parseCommandLine,
|
parseCommandLine,
|
||||||
|
reporterScope,
|
||||||
setupTestReporters,
|
setupTestReporters,
|
||||||
getCoverageReport,
|
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.strictEqual(child.stdout.toString(), '');
|
||||||
assert.match(child.stderr.toString(), /ERR_INVALID_ARG_TYPE/);
|
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