Skip to content

Commit 5e954c3

Browse files
authored
test_runner: centralize CLI option handling
The test runner relies on a few CLI options. That code was spread across a few locations. This commit centralizes that logic. PR-URL: #46707 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent f17a642 commit 5e954c3

File tree

3 files changed

+65
-27
lines changed

3 files changed

+65
-27
lines changed

lib/internal/test_runner/harness.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ const {
1616
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
1717

1818
const { kEmptyObject } = require('internal/util');
19-
const { getOptionValue } = require('internal/options');
2019
const { kCancelledByParent, Test, ItTest, Suite } = require('internal/test_runner/test');
21-
const { setupTestReporters } = require('internal/test_runner/utils');
20+
const {
21+
parseCommandLine,
22+
setupTestReporters,
23+
} = require('internal/test_runner/utils');
2224
const { bigint: hrtime } = process.hrtime;
2325

24-
const isTestRunnerCli = getOptionValue('--test');
2526
const testResources = new SafeMap();
2627
const wasRootSetup = new SafeWeakSet();
2728

@@ -56,8 +57,8 @@ function createProcessEventHandler(eventName, rootTest) {
5657
};
5758
}
5859

59-
function configureCoverage(rootTest) {
60-
if (!getOptionValue('--experimental-test-coverage')) {
60+
function configureCoverage(rootTest, globalOptions) {
61+
if (!globalOptions.coverage) {
6162
return null;
6263
}
6364

@@ -98,6 +99,11 @@ function setup(root) {
9899
if (wasRootSetup.has(root)) {
99100
return root;
100101
}
102+
103+
// Parse the command line options before the hook is enabled. We don't want
104+
// global input validation errors to end up in the uncaughtException handler.
105+
const globalOptions = parseCommandLine();
106+
101107
const hook = createHook({
102108
init(asyncId, type, triggerAsyncId, resource) {
103109
if (resource instanceof Test) {
@@ -122,7 +128,7 @@ function setup(root) {
122128
createProcessEventHandler('uncaughtException', root);
123129
const rejectionHandler =
124130
createProcessEventHandler('unhandledRejection', root);
125-
const coverage = configureCoverage(root);
131+
const coverage = configureCoverage(root, globalOptions);
126132
const exitHandler = () => {
127133
root.coverage = collectCoverage(root, coverage);
128134
root.postRun(new ERR_TEST_FAILURE(
@@ -142,8 +148,8 @@ function setup(root) {
142148
process.on('uncaughtException', exceptionHandler);
143149
process.on('unhandledRejection', rejectionHandler);
144150
process.on('beforeExit', exitHandler);
145-
// TODO(MoLow): Make it configurable to hook when isTestRunnerCli === false.
146-
if (isTestRunnerCli) {
151+
// TODO(MoLow): Make it configurable to hook when isTestRunner === false.
152+
if (globalOptions.isTestRunner) {
147153
process.on('SIGINT', terminationHandler);
148154
process.on('SIGTERM', terminationHandler);
149155
}

lib/internal/test_runner/test.js

+2-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use strict';
22
const {
3-
ArrayPrototypeMap,
43
ArrayPrototypePush,
54
ArrayPrototypeReduce,
65
ArrayPrototypeShift,
@@ -31,13 +30,12 @@ const {
3130
},
3231
AbortError,
3332
} = require('internal/errors');
34-
const { getOptionValue } = require('internal/options');
3533
const { MockTracker } = require('internal/test_runner/mock');
3634
const { TestsStream } = require('internal/test_runner/tests_stream');
3735
const {
38-
convertStringToRegExp,
3936
createDeferredCallback,
4037
isTestFailureError,
38+
parseCommandLine,
4139
} = require('internal/test_runner/utils');
4240
const {
4341
createDeferredPromise,
@@ -65,22 +63,13 @@ const kTestTimeoutFailure = 'testTimeoutFailure';
6563
const kHookFailure = 'hookFailed';
6664
const kDefaultTimeout = null;
6765
const noop = FunctionPrototype;
68-
const isTestRunner = getOptionValue('--test');
69-
const testOnlyFlag = !isTestRunner && getOptionValue('--test-only');
70-
const testNamePatternFlag = isTestRunner ? null :
71-
getOptionValue('--test-name-pattern');
72-
const testNamePatterns = testNamePatternFlag?.length > 0 ?
73-
ArrayPrototypeMap(
74-
testNamePatternFlag,
75-
(re) => convertStringToRegExp(re, '--test-name-pattern'),
76-
) : null;
7766
const kShouldAbort = Symbol('kShouldAbort');
7867
const kFilename = process.argv?.[1];
7968
const kHookNames = ObjectSeal(['before', 'after', 'beforeEach', 'afterEach']);
8069
const kUnwrapErrors = new SafeSet()
8170
.add(kTestCodeFailure).add(kHookFailure)
8271
.add('uncaughtException').add('unhandledRejection');
83-
72+
const { testNamePatterns, testOnlyFlag } = parseCommandLine();
8473

8574
function stopTest(timeout, signal) {
8675
if (timeout === kDefaultTimeout) {

lib/internal/test_runner/utils.js

+49-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22
const {
3+
ArrayPrototypeMap,
34
ArrayPrototypePush,
45
ObjectGetOwnPropertyDescriptor,
56
SafePromiseAllReturnArrayLike,
@@ -148,8 +149,27 @@ async function getReportersMap(reporters, destinations) {
148149

149150

150151
async function setupTestReporters(testsStream) {
152+
const { reporters, destinations } = parseCommandLine();
153+
const reportersMap = await getReportersMap(reporters, destinations);
154+
for (let i = 0; i < reportersMap.length; i++) {
155+
const { reporter, destination } = reportersMap[i];
156+
compose(testsStream, reporter).pipe(destination);
157+
}
158+
}
159+
160+
let globalTestOptions;
161+
162+
function parseCommandLine() {
163+
if (globalTestOptions) {
164+
return globalTestOptions;
165+
}
166+
167+
const isTestRunner = getOptionValue('--test');
168+
const coverage = getOptionValue('--experimental-test-coverage');
151169
const destinations = getOptionValue('--test-reporter-destination');
152170
const reporters = getOptionValue('--test-reporter');
171+
let testNamePatterns;
172+
let testOnlyFlag;
153173

154174
if (reporters.length === 0 && destinations.length === 0) {
155175
ArrayPrototypePush(reporters, kDefaultReporter);
@@ -160,15 +180,37 @@ async function setupTestReporters(testsStream) {
160180
}
161181

162182
if (destinations.length !== reporters.length) {
163-
throw new ERR_INVALID_ARG_VALUE('--test-reporter', reporters,
164-
'must match the number of specified \'--test-reporter-destination\'');
183+
throw new ERR_INVALID_ARG_VALUE(
184+
'--test-reporter',
185+
reporters,
186+
'must match the number of specified \'--test-reporter-destination\'',
187+
);
165188
}
166189

167-
const reportersMap = await getReportersMap(reporters, destinations);
168-
for (let i = 0; i < reportersMap.length; i++) {
169-
const { reporter, destination } = reportersMap[i];
170-
compose(testsStream, reporter).pipe(destination);
190+
if (isTestRunner) {
191+
testOnlyFlag = false;
192+
testNamePatterns = null;
193+
} else {
194+
const testNamePatternFlag = getOptionValue('--test-name-pattern');
195+
testOnlyFlag = getOptionValue('--test-only');
196+
testNamePatterns = testNamePatternFlag?.length > 0 ?
197+
ArrayPrototypeMap(
198+
testNamePatternFlag,
199+
(re) => convertStringToRegExp(re, '--test-name-pattern'),
200+
) : null;
171201
}
202+
203+
globalTestOptions = {
204+
__proto__: null,
205+
isTestRunner,
206+
coverage,
207+
testOnlyFlag,
208+
testNamePatterns,
209+
reporters,
210+
destinations,
211+
};
212+
213+
return globalTestOptions;
172214
}
173215

174216
module.exports = {
@@ -177,5 +219,6 @@ module.exports = {
177219
doesPathMatchFilter,
178220
isSupportedFileType,
179221
isTestFailureError,
222+
parseCommandLine,
180223
setupTestReporters,
181224
};

0 commit comments

Comments
 (0)