Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fa18b17

Browse filesBrowse files
atlowChemiMoLow
authored andcommitted
test_runner: add testNamePatterns to run api
Accept a `testNamePatterns` value in the `run` fn, and drill those patterns to the spawned processes. PR-URL: #47648 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent 27a696f commit fa18b17
Copy full SHA for fa18b17

File tree

Expand file treeCollapse file tree

4 files changed

+63
-7
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+63
-7
lines changed
Open diff view settings
Collapse file

‎doc/api/test.md‎

Copy file name to clipboardExpand all lines: doc/api/test.md
+10Lines changed: 10 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,10 @@ unless a destination is explicitly provided.
709709

710710
<!-- YAML
711711
added: v18.9.0
712+
changes:
713+
- version: REPLACEME
714+
pr-url: https://github.com/nodejs/node/pull/47628
715+
description: Add a testNamePatterns option.
712716
-->
713717

714718
* `options` {Object} Configuration options for running tests. The following
@@ -734,6 +738,12 @@ added: v18.9.0
734738
number. If a nullish value is provided, each process gets its own port,
735739
incremented from the primary's `process.debugPort`.
736740
**Default:** `undefined`.
741+
* `testNamePatterns` {string|RegExp|Array} A String, RegExp or a RegExp Array,
742+
that can be used to only run tests whose name matches the provided pattern.
743+
Test name patterns are interpreted as JavaScript regular expressions.
744+
For each test that is executed, any corresponding test hooks, such as
745+
`beforeEach()`, are also run.
746+
**Default:** `undefined`.
737747
* Returns: {TestsStream}
738748

739749
```mjs
Collapse file

‎lib/internal/test_runner/runner.js‎

Copy file name to clipboardExpand all lines: lib/internal/test_runner/runner.js
+32-7Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
'use strict';
22
const {
33
ArrayFrom,
4+
ArrayIsArray,
45
ArrayPrototypeFilter,
56
ArrayPrototypeForEach,
67
ArrayPrototypeIncludes,
78
ArrayPrototypeIndexOf,
9+
ArrayPrototypeMap,
810
ArrayPrototypePush,
911
ArrayPrototypeSlice,
1012
ArrayPrototypeSome,
@@ -33,11 +35,13 @@ const { FilesWatcher } = require('internal/watch_mode/files_watcher');
3335
const console = require('internal/console/global');
3436
const {
3537
codes: {
38+
ERR_INVALID_ARG_TYPE,
3639
ERR_TEST_FAILURE,
3740
},
3841
} = require('internal/errors');
3942
const { validateArray, validateBoolean, validateFunction } = require('internal/validators');
4043
const { getInspectPort, isUsingInspector, isInspectorMessage } = require('internal/util/inspector');
44+
const { isRegExp } = require('internal/util/types');
4145
const { kEmptyObject } = require('internal/util');
4246
const { createTestTree } = require('internal/test_runner/harness');
4347
const {
@@ -53,6 +57,7 @@ const { YAMLToJs } = require('internal/test_runner/yaml_to_js');
5357
const { TokenKind } = require('internal/test_runner/tap_lexer');
5458

5559
const {
60+
convertStringToRegExp,
5661
countCompletedTest,
5762
doesPathMatchFilter,
5863
isSupportedFileType,
@@ -137,11 +142,14 @@ function filterExecArgv(arg, i, arr) {
137142
!ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
138143
}
139144

140-
function getRunArgs({ path, inspectPort }) {
145+
function getRunArgs({ path, inspectPort, testNamePatterns }) {
141146
const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv);
142147
if (isUsingInspector()) {
143148
ArrayPrototypePush(argv, `--inspect-port=${getInspectPort(inspectPort)}`);
144149
}
150+
if (testNamePatterns) {
151+
ArrayPrototypeForEach(testNamePatterns, (pattern) => ArrayPrototypePush(argv, `--test-name-pattern=${pattern}`));
152+
}
145153
ArrayPrototypePush(argv, path);
146154

147155
return argv;
@@ -255,9 +263,9 @@ class FileTest extends Test {
255263
const runningProcesses = new SafeMap();
256264
const runningSubtests = new SafeMap();
257265

258-
function runTestFile(path, root, inspectPort, filesWatcher) {
266+
function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
259267
const subtest = root.createSubtest(FileTest, path, async (t) => {
260-
const args = getRunArgs({ path, inspectPort });
268+
const args = getRunArgs({ path, inspectPort, testNamePatterns });
261269
const stdio = ['pipe', 'pipe', 'pipe'];
262270
const env = { ...process.env, NODE_TEST_CONTEXT: 'child' };
263271
if (filesWatcher) {
@@ -339,7 +347,7 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
339347
return promise;
340348
}
341349

342-
function watchFiles(testFiles, root, inspectPort) {
350+
function watchFiles(testFiles, root, inspectPort, testNamePatterns) {
343351
const filesWatcher = new FilesWatcher({ throttle: 500, mode: 'filter' });
344352
filesWatcher.on('changed', ({ owners }) => {
345353
filesWatcher.unfilterFilesOwnedBy(owners);
@@ -353,7 +361,7 @@ function watchFiles(testFiles, root, inspectPort) {
353361
await once(runningProcess, 'exit');
354362
}
355363
await runningSubtests.get(file);
356-
runningSubtests.set(file, runTestFile(file, root, inspectPort, filesWatcher));
364+
runningSubtests.set(file, runTestFile(file, root, inspectPort, filesWatcher, testNamePatterns));
357365
}, undefined, (error) => {
358366
triggerUncaughtException(error, true /* fromPromise */);
359367
}));
@@ -365,6 +373,7 @@ function run(options) {
365373
if (options === null || typeof options !== 'object') {
366374
options = kEmptyObject;
367375
}
376+
let { testNamePatterns } = options;
368377
const { concurrency, timeout, signal, files, inspectPort, watch, setup } = options;
369378

370379
if (files != null) {
@@ -376,20 +385,36 @@ function run(options) {
376385
if (setup != null) {
377386
validateFunction(setup, 'options.setup');
378387
}
388+
if (testNamePatterns != null) {
389+
if (!ArrayIsArray(testNamePatterns)) {
390+
testNamePatterns = [testNamePatterns];
391+
}
392+
validateArray(testNamePatterns, 'options.testNamePatterns');
393+
testNamePatterns = ArrayPrototypeMap(testNamePatterns, (value, i) => {
394+
if (isRegExp(value)) {
395+
return value;
396+
}
397+
const name = `options.testNamePatterns[${i}]`;
398+
if (typeof value === 'string') {
399+
return convertStringToRegExp(value, name);
400+
}
401+
throw new ERR_INVALID_ARG_TYPE(name, ['string', 'RegExp'], value);
402+
});
403+
}
379404

380405
const root = createTestTree({ concurrency, timeout, signal });
381406
const testFiles = files ?? createTestFileList();
382407

383408
let postRun = () => root.postRun();
384409
let filesWatcher;
385410
if (watch) {
386-
filesWatcher = watchFiles(testFiles, root, inspectPort);
411+
filesWatcher = watchFiles(testFiles, root, inspectPort, testNamePatterns);
387412
postRun = undefined;
388413
}
389414
const runFiles = () => {
390415
root.harness.bootstrapComplete = true;
391416
return SafePromiseAllSettledReturnVoid(testFiles, (path) => {
392-
const subtest = runTestFile(path, root, inspectPort, filesWatcher);
417+
const subtest = runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns);
393418
runningSubtests.set(path, subtest);
394419
return subtest;
395420
});
Collapse file
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
const test = require('node:test');
3+
4+
test('this should be skipped');
5+
test('this should be executed');
Collapse file

‎test/parallel/test-runner-run.mjs‎

Copy file name to clipboardExpand all lines: test/parallel/test-runner-run.mjs
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,20 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
101101
assert.strictEqual(result[11], '# todo 0\n');
102102
assert.match(result[12], /# duration_ms \d+\.?\d*/);
103103
});
104+
105+
it('should skip tests not matching testNamePatterns - RegExp', async () => {
106+
const result = await run({ files: [join(testFixtures, 'test/skip_by_name.cjs')], testNamePatterns: [/executed/] })
107+
.compose(tap)
108+
.toArray();
109+
assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP test name does not match pattern\n');
110+
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
111+
});
112+
113+
it('should skip tests not matching testNamePatterns - string', async () => {
114+
const result = await run({ files: [join(testFixtures, 'test/skip_by_name.cjs')], testNamePatterns: ['executed'] })
115+
.compose(tap)
116+
.toArray();
117+
assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP test name does not match pattern\n');
118+
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
119+
});
104120
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.