summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoshe Atlow <moshe@atlow.co.il>2023-02-19 21:15:03 +0200
committerMichaƫl Zasso <targos@protonmail.com>2023-03-14 07:54:15 +0100
commit6d32a16319f4b6628b66146517c49931065af11a (patch)
tree7f8e2d0f7cde8befd9ade7b21140620bc4f07e21
parentffa86f7fa92d7156fe2a9586cef86d0207971c8b (diff)
downloadnode-new-6d32a16319f4b6628b66146517c49931065af11a.tar.gz
test_runner: bootstrap reporters before running tests
PR-URL: https://github.com/nodejs/node/pull/46737 Fixes: https://github.com/nodejs/node/issues/46747 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
-rw-r--r--doc/api/test.md3
-rw-r--r--lib/internal/main/test_runner.js5
-rw-r--r--lib/internal/test_runner/harness.js12
-rw-r--r--lib/internal/test_runner/runner.js21
4 files changed, 29 insertions, 12 deletions
diff --git a/doc/api/test.md b/doc/api/test.md
index 57753556a0..da6ba355fe 100644
--- a/doc/api/test.md
+++ b/doc/api/test.md
@@ -705,6 +705,9 @@ added: v18.9.0
**Default:** `false`.
* `files`: {Array} An array containing the list of files to run.
**Default** matching files from [test runner execution model][].
+ * `setup` {Function} A function that accepts the `TestsStream` instance
+ and can be used to setup listeners before any tests are run.
+ **Default:** `undefined`.
* `signal` {AbortSignal} Allows aborting an in-progress test execution.
* `timeout` {number} A number of milliseconds the test execution will
fail after.
diff --git a/lib/internal/main/test_runner.js b/lib/internal/main/test_runner.js
index 658aab0332..9a209eb189 100644
--- a/lib/internal/main/test_runner.js
+++ b/lib/internal/main/test_runner.js
@@ -22,8 +22,7 @@ if (isUsingInspector()) {
inspectPort = process.debugPort;
}
-const testsStream = run({ concurrency, inspectPort, watch: getOptionValue('--watch') });
-testsStream.once('test:fail', () => {
+run({ concurrency, inspectPort, watch: getOptionValue('--watch'), setup: setupTestReporters })
+.once('test:fail', () => {
process.exitCode = kGenericUserError;
});
-setupTestReporters(testsStream);
diff --git a/lib/internal/test_runner/harness.js b/lib/internal/test_runner/harness.js
index 17b1e586ba..84660b541c 100644
--- a/lib/internal/test_runner/harness.js
+++ b/lib/internal/test_runner/harness.js
@@ -170,21 +170,27 @@ function setup(root) {
}
let globalRoot;
+let reportersSetup;
function getGlobalRoot() {
if (!globalRoot) {
globalRoot = createTestTree();
globalRoot.reporter.once('test:fail', () => {
process.exitCode = kGenericUserError;
});
- setupTestReporters(globalRoot.reporter);
+ reportersSetup = setupTestReporters(globalRoot.reporter);
}
return globalRoot;
}
+async function startSubtest(subtest) {
+ await reportersSetup;
+ await subtest.start();
+}
+
function test(name, options, fn) {
const parent = testResources.get(executionAsyncId()) || getGlobalRoot();
const subtest = parent.createSubtest(Test, name, options, fn);
- return subtest.start();
+ return startSubtest(subtest);
}
function runInParentContext(Factory) {
@@ -192,7 +198,7 @@ function runInParentContext(Factory) {
const parent = testResources.get(executionAsyncId()) || getGlobalRoot();
const subtest = parent.createSubtest(Factory, name, options, fn, overrides);
if (parent === getGlobalRoot()) {
- subtest.start();
+ startSubtest(subtest);
}
}
diff --git a/lib/internal/test_runner/runner.js b/lib/internal/test_runner/runner.js
index d73b14868b..2e0c62505f 100644
--- a/lib/internal/test_runner/runner.js
+++ b/lib/internal/test_runner/runner.js
@@ -13,8 +13,10 @@ const {
ObjectAssign,
ObjectKeys,
PromisePrototypeThen,
+ SafePromiseAll,
SafePromiseAllReturnVoid,
SafePromiseAllSettledReturnVoid,
+ PromiseResolve,
SafeMap,
SafeSet,
StringPrototypeIndexOf,
@@ -24,6 +26,7 @@ const {
const { spawn } = require('child_process');
const { readdirSync, statSync } = require('fs');
+const { finished } = require('internal/streams/end-of-stream');
// TODO(aduh95): switch to internal/readline/interface when backporting to Node.js 16.x is no longer a concern.
const { createInterface } = require('readline');
const { FilesWatcher } = require('internal/watch_mode/files_watcher');
@@ -33,7 +36,7 @@ const {
ERR_TEST_FAILURE,
},
} = require('internal/errors');
-const { validateArray, validateBoolean } = require('internal/validators');
+const { validateArray, validateBoolean, validateFunction } = require('internal/validators');
const { getInspectPort, isUsingInspector, isInspectorMessage } = require('internal/util/inspector');
const { kEmptyObject } = require('internal/util');
const { createTestTree } = require('internal/test_runner/harness');
@@ -299,7 +302,10 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
subtest.addToReport(ast);
});
- const { 0: code, 1: signal } = await once(child, 'exit', { signal: t.signal });
+ const { 0: { 0: code, 1: signal } } = await SafePromiseAll([
+ once(child, 'exit', { signal: t.signal }),
+ finished(parser, { signal: t.signal }),
+ ]);
runningProcesses.delete(path);
runningSubtests.delete(path);
@@ -348,7 +354,7 @@ function run(options) {
if (options === null || typeof options !== 'object') {
options = kEmptyObject;
}
- const { concurrency, timeout, signal, files, inspectPort, watch } = options;
+ const { concurrency, timeout, signal, files, inspectPort, watch, setup } = options;
if (files != null) {
validateArray(files, 'options.files');
@@ -356,6 +362,9 @@ function run(options) {
if (watch != null) {
validateBoolean(watch, 'options.watch');
}
+ if (setup != null) {
+ validateFunction(setup, 'options.setup');
+ }
const root = createTestTree({ concurrency, timeout, signal });
const testFiles = files ?? createTestFileList();
@@ -366,13 +375,13 @@ function run(options) {
filesWatcher = watchFiles(testFiles, root, inspectPort);
postRun = undefined;
}
-
- PromisePrototypeThen(SafePromiseAllSettledReturnVoid(testFiles, (path) => {
+ const runFiles = () => SafePromiseAllSettledReturnVoid(testFiles, (path) => {
const subtest = runTestFile(path, root, inspectPort, filesWatcher);
runningSubtests.set(path, subtest);
return subtest;
- }), postRun);
+ });
+ PromisePrototypeThen(PromisePrototypeThen(PromiseResolve(setup?.(root.reporter)), runFiles), postRun);
return root.reporter;
}