Skip to content

Commit 0cd9941

Browse files
committed
doc,lib,src,test: add --test-skip-pattern cli option
1 parent f8e325e commit 0cd9941

File tree

7 files changed

+75
-36
lines changed

7 files changed

+75
-36
lines changed

doc/api/cli.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,9 @@ A regular expression that configures the test runner to only execute tests
19691969
whose name matches the provided pattern. See the documentation on
19701970
[filtering tests by name][] for more details.
19711971

1972+
If both `--test-name-pattern` and `--test-skip-pattern` are supplied,
1973+
`--test-name-pattern` will take precedence.
1974+
19721975
### `--test-only`
19731976

19741977
<!-- YAML
@@ -2037,6 +2040,19 @@ node --test --test-shard=2/3
20372040
node --test --test-shard=3/3
20382041
```
20392042

2043+
### `--test-skip-pattern`
2044+
<!-- YAML
2045+
added:
2046+
- REPLACEME
2047+
-->
2048+
2049+
A regular expression that configures the test runner to only execute tests
2050+
whose name don't match the provided pattern. See the documentation on
2051+
[filtering tests by name][] for more details.
2052+
2053+
If both `--test-name-pattern` and `--test-skip-pattern` are supplied,
2054+
`--test-name-pattern` will take precedence.
2055+
20402056
### `--test-timeout`
20412057

20422058
<!-- YAML

doc/api/test.md

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -298,20 +298,13 @@ describe.only('a suite', () => {
298298

299299
## Filtering tests by name
300300

301-
The [`--test-name-pattern`][] command-line option can be used to only run tests
302-
whose name matches the provided pattern. Test name patterns are interpreted as
303-
JavaScript regular expressions. The `--test-name-pattern` option can be
304-
specified multiple times in order to run nested tests. For each test that is
305-
executed, any corresponding test hooks, such as `beforeEach()`, are also
306-
run. Tests that are not executed are omitted from the test runner output.
307-
308-
Given the following test file, starting Node.js with the
309-
`--test-name-pattern="test [1-3]"` option would cause the test runner to execute
310-
`test 1`, `test 2`, and `test 3`. If `test 1` did not match the test name
311-
pattern, then its subtests would not execute, despite matching the pattern. The
312-
same set of tests could also be executed by passing `--test-name-pattern`
313-
multiple times (e.g. `--test-name-pattern="test 1"`,
314-
`--test-name-pattern="test 2"`, etc.).
301+
The [`--test-name-pattern`][] and [`--test-skip-pattern`][] command-line options provide flexibility in selecting which tests to run and which to skip based on their names.
302+
303+
### Using `--test-name-pattern`
304+
305+
The `--test-name-pattern` option filters tests based on their names by matching them against the provided pattern, which is interpreted as a JavaScript regular expression. This option can be specified multiple times to include nested tests. When a test matches the pattern and is executed, any corresponding test hooks, such as `beforeEach()`, are also run. Tests that do not match the pattern are omitted from the test runner output.
306+
307+
Consider the following test file:
315308

316309
```js
317310
test('test 1', async (t) => {
@@ -325,14 +318,11 @@ test('Test 4', async (t) => {
325318
});
326319
```
327320

328-
Test name patterns can also be specified using regular expression literals. This
329-
allows regular expression flags to be used. In the previous example, starting
330-
Node.js with `--test-name-pattern="/test [4-5]/i"` would match `Test 4` and
331-
`Test 5` because the pattern is case-insensitive.
321+
Using Node.js with the `--test-name-pattern="test [1-3]"` option would execute `test 1`, `test 2`, and `test 3`. If `test 1` did not match the pattern, its subtests would not execute, even if they match the pattern. Alternatively, specifying the pattern multiple times (e.g., `--test-name-pattern="test 1"`, `--test-name-pattern="test 2"`, etc.) achieves the same result.
322+
323+
Regular expression literals can also be used, allowing regular expression flags. For instance, using `--test-name-pattern="/test [4-5]/i"` would match `Test 4` and `Test 5` due to the case-insensitive flag.
332324

333-
To match a single test with a pattern, you can prefix it with all its ancestor
334-
test names separated by space, to ensure it is unique.
335-
For example, given the following test file:
325+
To match a single test uniquely, prefix its name with all its ancestor test names separated by spaces. For example:
336326

337327
```js
338328
describe('test 1', (t) => {
@@ -344,10 +334,16 @@ describe('test 2', (t) => {
344334
});
345335
```
346336

347-
Starting Node.js with `--test-name-pattern="test 1 some test"` would match
348-
only `some test` in `test 1`.
337+
Using `--test-name-pattern="test 1 some test"` would match only `some test` in `test 1`.
338+
339+
### Using `--test-skip-pattern`
340+
341+
Similarly, the `--test-skip-pattern` option allows skipping tests based on their names by matching them against the provided pattern, interpreted as JavaScript regular expressions. If a test's name matches the skip pattern, it will be excluded from execution.
342+
343+
Note that if both `--test-name-pattern` and `--test-skip-pattern` are
344+
supplied, `--test-name-pattern` will take precedence.
349345

350-
Test name patterns do not change the set of files that the test runner executes.
346+
Test name patterns and skip patterns do not alter the set of files executed by the test runner.
351347

352348
## Extraneous asynchronous activity
353349

@@ -3154,6 +3150,7 @@ Can be used to abort test subtasks when the test has been aborted.
31543150
[`--import`]: cli.md#--importmodule
31553151
[`--test-concurrency`]: cli.md#--test-concurrency
31563152
[`--test-name-pattern`]: cli.md#--test-name-pattern
3153+
[`--test-skip-pattern`]: cli.md#--test-skip-pattern
31573154
[`--test-only`]: cli.md#--test-only
31583155
[`--test-reporter-destination`]: cli.md#--test-reporter-destination
31593156
[`--test-reporter`]: cli.md#--test-reporter

lib/internal/test_runner/runner.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ function filterExecArgv(arg, i, arr) {
113113
!ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
114114
}
115115

116-
function getRunArgs(path, { forceExit, inspectPort, testNamePatterns, only }) {
116+
function getRunArgs(path, { forceExit, inspectPort, testNamePatterns, testSkipPatterns, only }) {
117117
const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv);
118118
if (forceExit === true) {
119119
ArrayPrototypePush(argv, '--test-force-exit');
@@ -124,6 +124,9 @@ function getRunArgs(path, { forceExit, inspectPort, testNamePatterns, only }) {
124124
if (testNamePatterns != null) {
125125
ArrayPrototypeForEach(testNamePatterns, (pattern) => ArrayPrototypePush(argv, `--test-name-pattern=${pattern}`));
126126
}
127+
if (testSkipPatterns != null) {
128+
ArrayPrototypeForEach(testSkipPatterns, (pattern) => ArrayPrototypePush(argv, `--test-skip-pattern=${pattern}`));
129+
}
127130
if (only === true) {
128131
ArrayPrototypePush(argv, '--test-only');
129132
}

lib/internal/test_runner/test.js

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ const {
8484
forceExit,
8585
sourceMaps,
8686
testNamePatterns,
87+
testSkipPatterns,
8788
testOnlyFlag,
8889
} = parseCommandLine();
8990
let kResistStopPropagation;
@@ -300,8 +301,7 @@ class Test extends AsyncResource {
300301
ownAfterEachCount: 0,
301302
};
302303

303-
if ((testNamePatterns !== null && !this.matchesTestNamePatterns()) ||
304-
(testOnlyFlag && !this.only)) {
304+
if (!this.matchesTestNamePatterns() || (testOnlyFlag && !this.only)) {
305305
this.filtered = true;
306306
this.parent.filteredSubtestCount++;
307307
}
@@ -409,17 +409,26 @@ class Test extends AsyncResource {
409409
}
410410

411411
matchesTestNamePatterns() {
412-
const matchesByNameOrParent = ArrayPrototypeSome(testNamePatterns, (re) =>
412+
let patterns;
413+
let bool = true;
414+
if (testNamePatterns !== null) {
415+
patterns = testNamePatterns;
416+
} else if (testSkipPatterns !== null) {
417+
patterns = testSkipPatterns;
418+
bool = false;
419+
} else return true; // Nothing is specified, just continue
420+
421+
const matchesByNameOrParent = ArrayPrototypeSome(patterns, (re) =>
413422
RegExpPrototypeExec(re, this.name) !== null,
414423
) ||
415-
this.parent?.matchesTestNamePatterns();
424+
this.parent?.matchesTestNamePatterns();
416425

417-
if (matchesByNameOrParent) return true;
426+
if (matchesByNameOrParent) return bool;
418427

419428
const testNameWithAncestors = StringPrototypeTrim(this.getTestNameWithAncestors());
420-
if (!testNameWithAncestors) return false;
429+
if (!testNameWithAncestors) return !bool;
421430

422-
return ArrayPrototypeSome(testNamePatterns, (re) => RegExpPrototypeExec(re, testNameWithAncestors) !== null);
431+
return ArrayPrototypeSome(patterns, (re) => RegExpPrototypeExec(re, testNameWithAncestors) !== null);
423432
}
424433

425434
/**
@@ -897,7 +906,7 @@ class Test extends AsyncResource {
897906
this.finished = true;
898907

899908
if (this.parent === this.root &&
900-
this.root.waitingOn > this.root.subtests.length) {
909+
this.root.waitingOn > this.root.subtests.length) {
901910
// At this point all of the tests have finished running. However, there
902911
// might be ref'ed handles keeping the event loop alive. This gives the
903912
// global after() hook a chance to clean them up. The user may also
@@ -995,7 +1004,7 @@ class TestHook extends Test {
9951004

9961005
// Report failures in the root test's after() hook.
9971006
if (error && parent !== null &&
998-
parent === parent.root && this.hookType === 'after') {
1007+
parent === parent.root && this.hookType === 'after') {
9991008

10001009
if (isTestFailureError(error)) {
10011010
error.failureType = kHookFailure;
@@ -1016,7 +1025,7 @@ class Suite extends Test {
10161025
constructor(options) {
10171026
super(options);
10181027

1019-
if (testNamePatterns !== null && !options.skip) {
1028+
if ((testNamePatterns !== null || testSkipPatterns !== null) && !options.skip) {
10201029
this.fn = options.fn || this.fn;
10211030
this.skipped = false;
10221031
}
@@ -1050,7 +1059,12 @@ class Suite extends Test {
10501059
// tests that it contains - in case of children matching patterns.
10511060
this.filtered = false;
10521061
this.parent.filteredSubtestCount--;
1053-
} else if (testOnlyFlag && testNamePatterns == null && this.filteredSubtestCount === this.subtests.length) {
1062+
} else if (
1063+
testOnlyFlag &&
1064+
testNamePatterns == null &&
1065+
testSkipPatterns == null &&
1066+
this.filteredSubtestCount === this.subtests.length
1067+
) {
10541068
// If no subtests are marked as "only", run them all
10551069
this.filteredSubtestCount = 0;
10561070
}

lib/internal/test_runner/utils.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ function parseCommandLine() {
200200
let destinations;
201201
let reporters;
202202
let testNamePatterns;
203+
let testSkipPatterns;
203204
let testOnlyFlag;
204205

205206
if (isChildProcessV8) {
@@ -240,6 +241,9 @@ function parseCommandLine() {
240241
testNamePatternFlag,
241242
(re) => convertStringToRegExp(re, '--test-name-pattern'),
242243
) : null;
244+
const testSkipPatternFlag = getOptionValue('--test-skip-pattern');
245+
testSkipPatterns = testSkipPatternFlag?.length > 0 ?
246+
ArrayPrototypeMap(testSkipPatternFlag, (re) => convertStringToRegExp(re, '--test-skip-pattern')) : null;
243247
}
244248

245249
globalTestOptions = {
@@ -250,6 +254,7 @@ function parseCommandLine() {
250254
sourceMaps,
251255
testOnlyFlag,
252256
testNamePatterns,
257+
testSkipPatterns,
253258
reporters,
254259
destinations,
255260
};

src/node_options.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,9 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
661661
"run test at specific shard",
662662
&EnvironmentOptions::test_shard,
663663
kAllowedInEnvvar);
664+
AddOption("--test-skip-pattern",
665+
"run tests whose name don't match this regular expression",
666+
&EnvironmentOptions::test_skip_pattern);
664667
AddOption("--test-udp-no-try-send", "", // For testing only.
665668
&EnvironmentOptions::test_udp_no_try_send);
666669
AddOption("--throw-deprecation",

src/node_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ class EnvironmentOptions : public Options {
176176
bool test_only = false;
177177
bool test_udp_no_try_send = false;
178178
std::string test_shard;
179+
std::vector<std::string> test_skip_pattern;
179180
bool throw_deprecation = false;
180181
bool trace_atomics_wait = false;
181182
bool trace_deprecation = false;

0 commit comments

Comments
 (0)