Skip to content

Commit c19fa45

Browse files
cjihrigjuanarbol
authored andcommitted
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 Backport-PR-URL: #46839 Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Moshe Atlow <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 0898145 commit c19fa45

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
@@ -14,12 +14,13 @@ const {
1414
},
1515
} = require('internal/errors');
1616
const { kEmptyObject } = require('internal/util');
17-
const { getOptionValue } = require('internal/options');
1817
const { kCancelledByParent, Test, ItTest, Suite } = require('internal/test_runner/test');
19-
const { setupTestReporters } = require('internal/test_runner/utils');
18+
const {
19+
parseCommandLine,
20+
setupTestReporters,
21+
} = require('internal/test_runner/utils');
2022
const { bigint: hrtime } = process.hrtime;
2123

22-
const isTestRunnerCli = getOptionValue('--test');
2324
const testResources = new SafeMap();
2425
const wasRootSetup = new SafeWeakSet();
2526

@@ -54,8 +55,8 @@ function createProcessEventHandler(eventName, rootTest) {
5455
};
5556
}
5657

57-
function configureCoverage(rootTest) {
58-
if (!getOptionValue('--experimental-test-coverage')) {
58+
function configureCoverage(rootTest, globalOptions) {
59+
if (!globalOptions.coverage) {
5960
return null;
6061
}
6162

@@ -96,6 +97,11 @@ function setup(root) {
9697
if (wasRootSetup.has(root)) {
9798
return root;
9899
}
100+
101+
// Parse the command line options before the hook is enabled. We don't want
102+
// global input validation errors to end up in the uncaughtException handler.
103+
const globalOptions = parseCommandLine();
104+
99105
const hook = createHook({
100106
init(asyncId, type, triggerAsyncId, resource) {
101107
if (resource instanceof Test) {
@@ -120,7 +126,7 @@ function setup(root) {
120126
createProcessEventHandler('uncaughtException', root);
121127
const rejectionHandler =
122128
createProcessEventHandler('unhandledRejection', root);
123-
const coverage = configureCoverage(root);
129+
const coverage = configureCoverage(root, globalOptions);
124130
const exitHandler = () => {
125131
root.coverage = collectCoverage(root, coverage);
126132
root.postRun(new ERR_TEST_FAILURE(
@@ -140,8 +146,8 @@ function setup(root) {
140146
process.on('uncaughtException', exceptionHandler);
141147
process.on('unhandledRejection', rejectionHandler);
142148
process.on('beforeExit', exitHandler);
143-
// TODO(MoLow): Make it configurable to hook when isTestRunnerCli === false.
144-
if (isTestRunnerCli) {
149+
// TODO(MoLow): Make it configurable to hook when isTestRunner === false.
150+
if (globalOptions.isTestRunner) {
145151
process.on('SIGINT', terminationHandler);
146152
process.on('SIGTERM', terminationHandler);
147153
}

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
ObjectCreate,
56
ObjectGetOwnPropertyDescriptor,
@@ -149,8 +150,27 @@ async function getReportersMap(reporters, destinations) {
149150

150151

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

155175
if (reporters.length === 0 && destinations.length === 0) {
156176
ArrayPrototypePush(reporters, kDefaultReporter);
@@ -161,15 +181,37 @@ async function setupTestReporters(testsStream) {
161181
}
162182

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

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

175217
module.exports = {
@@ -178,5 +220,6 @@ module.exports = {
178220
doesPathMatchFilter,
179221
isSupportedFileType,
180222
isTestFailureError,
223+
parseCommandLine,
181224
setupTestReporters,
182225
};

0 commit comments

Comments
 (0)