mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
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: https://github.com/nodejs/node/pull/47648 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
@@ -726,6 +726,10 @@ unless a destination is explicitly provided.
|
||||
added:
|
||||
- v18.9.0
|
||||
- v16.19.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/47628
|
||||
description: Add a testNamePatterns option.
|
||||
-->
|
||||
|
||||
* `options` {Object} Configuration options for running tests. The following
|
||||
@@ -751,6 +755,12 @@ added:
|
||||
number. If a nullish value is provided, each process gets its own port,
|
||||
incremented from the primary's `process.debugPort`.
|
||||
**Default:** `undefined`.
|
||||
* `testNamePatterns` {string|RegExp|Array} A String, RegExp or a RegExp Array,
|
||||
that can be used to only run tests whose name matches the provided pattern.
|
||||
Test name patterns are interpreted as JavaScript regular expressions.
|
||||
For each test that is executed, any corresponding test hooks, such as
|
||||
`beforeEach()`, are also run.
|
||||
**Default:** `undefined`.
|
||||
* Returns: {TestsStream}
|
||||
|
||||
```mjs
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
'use strict';
|
||||
const {
|
||||
ArrayFrom,
|
||||
ArrayIsArray,
|
||||
ArrayPrototypeFilter,
|
||||
ArrayPrototypeForEach,
|
||||
ArrayPrototypeIncludes,
|
||||
ArrayPrototypeIndexOf,
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeSlice,
|
||||
ArrayPrototypeSome,
|
||||
@@ -33,11 +35,13 @@ const { FilesWatcher } = require('internal/watch_mode/files_watcher');
|
||||
const console = require('internal/console/global');
|
||||
const {
|
||||
codes: {
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_TEST_FAILURE,
|
||||
},
|
||||
} = require('internal/errors');
|
||||
const { validateArray, validateBoolean, validateFunction } = require('internal/validators');
|
||||
const { getInspectPort, isUsingInspector, isInspectorMessage } = require('internal/util/inspector');
|
||||
const { isRegExp } = require('internal/util/types');
|
||||
const { kEmptyObject } = require('internal/util');
|
||||
const { createTestTree } = require('internal/test_runner/harness');
|
||||
const {
|
||||
@@ -53,6 +57,7 @@ const { YAMLToJs } = require('internal/test_runner/yaml_to_js');
|
||||
const { TokenKind } = require('internal/test_runner/tap_lexer');
|
||||
|
||||
const {
|
||||
convertStringToRegExp,
|
||||
countCompletedTest,
|
||||
doesPathMatchFilter,
|
||||
isSupportedFileType,
|
||||
@@ -138,11 +143,14 @@ function filterExecArgv(arg, i, arr) {
|
||||
!ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
|
||||
}
|
||||
|
||||
function getRunArgs({ path, inspectPort }) {
|
||||
function getRunArgs({ path, inspectPort, testNamePatterns }) {
|
||||
const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv);
|
||||
if (isUsingInspector()) {
|
||||
ArrayPrototypePush(argv, `--inspect-port=${getInspectPort(inspectPort)}`);
|
||||
}
|
||||
if (testNamePatterns) {
|
||||
ArrayPrototypeForEach(testNamePatterns, (pattern) => ArrayPrototypePush(argv, `--test-name-pattern=${pattern}`));
|
||||
}
|
||||
ArrayPrototypePush(argv, path);
|
||||
|
||||
return argv;
|
||||
@@ -256,9 +264,9 @@ class FileTest extends Test {
|
||||
const runningProcesses = new SafeMap();
|
||||
const runningSubtests = new SafeMap();
|
||||
|
||||
function runTestFile(path, root, inspectPort, filesWatcher) {
|
||||
function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
|
||||
const subtest = root.createSubtest(FileTest, path, async (t) => {
|
||||
const args = getRunArgs({ path, inspectPort });
|
||||
const args = getRunArgs({ path, inspectPort, testNamePatterns });
|
||||
const stdio = ['pipe', 'pipe', 'pipe'];
|
||||
const env = { ...process.env, NODE_TEST_CONTEXT: 'child' };
|
||||
if (filesWatcher) {
|
||||
@@ -340,7 +348,7 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
|
||||
return promise;
|
||||
}
|
||||
|
||||
function watchFiles(testFiles, root, inspectPort) {
|
||||
function watchFiles(testFiles, root, inspectPort, testNamePatterns) {
|
||||
const filesWatcher = new FilesWatcher({ throttle: 500, mode: 'filter' });
|
||||
filesWatcher.on('changed', ({ owners }) => {
|
||||
filesWatcher.unfilterFilesOwnedBy(owners);
|
||||
@@ -354,7 +362,7 @@ function watchFiles(testFiles, root, inspectPort) {
|
||||
await once(runningProcess, 'exit');
|
||||
}
|
||||
await runningSubtests.get(file);
|
||||
runningSubtests.set(file, runTestFile(file, root, inspectPort, filesWatcher));
|
||||
runningSubtests.set(file, runTestFile(file, root, inspectPort, filesWatcher, testNamePatterns));
|
||||
}, undefined, (error) => {
|
||||
triggerUncaughtException(error, true /* fromPromise */);
|
||||
}));
|
||||
@@ -366,6 +374,7 @@ function run(options) {
|
||||
if (options === null || typeof options !== 'object') {
|
||||
options = kEmptyObject;
|
||||
}
|
||||
let { testNamePatterns } = options;
|
||||
const { concurrency, timeout, signal, files, inspectPort, watch, setup } = options;
|
||||
|
||||
if (files != null) {
|
||||
@@ -377,6 +386,22 @@ function run(options) {
|
||||
if (setup != null) {
|
||||
validateFunction(setup, 'options.setup');
|
||||
}
|
||||
if (testNamePatterns != null) {
|
||||
if (!ArrayIsArray(testNamePatterns)) {
|
||||
testNamePatterns = [testNamePatterns];
|
||||
}
|
||||
validateArray(testNamePatterns, 'options.testNamePatterns');
|
||||
testNamePatterns = ArrayPrototypeMap(testNamePatterns, (value, i) => {
|
||||
if (isRegExp(value)) {
|
||||
return value;
|
||||
}
|
||||
const name = `options.testNamePatterns[${i}]`;
|
||||
if (typeof value === 'string') {
|
||||
return convertStringToRegExp(value, name);
|
||||
}
|
||||
throw new ERR_INVALID_ARG_TYPE(name, ['string', 'RegExp'], value);
|
||||
});
|
||||
}
|
||||
|
||||
const root = createTestTree({ concurrency, timeout, signal });
|
||||
const testFiles = files ?? createTestFileList();
|
||||
@@ -384,13 +409,13 @@ function run(options) {
|
||||
let postRun = () => root.postRun();
|
||||
let filesWatcher;
|
||||
if (watch) {
|
||||
filesWatcher = watchFiles(testFiles, root, inspectPort);
|
||||
filesWatcher = watchFiles(testFiles, root, inspectPort, testNamePatterns);
|
||||
postRun = undefined;
|
||||
}
|
||||
const runFiles = () => {
|
||||
root.harness.bootstrapComplete = true;
|
||||
return SafePromiseAllSettledReturnVoid(testFiles, (path) => {
|
||||
const subtest = runTestFile(path, root, inspectPort, filesWatcher);
|
||||
const subtest = runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns);
|
||||
runningSubtests.set(path, subtest);
|
||||
return subtest;
|
||||
});
|
||||
|
||||
5
test/fixtures/test-runner/test/skip_by_name.cjs
vendored
Normal file
5
test/fixtures/test-runner/test/skip_by_name.cjs
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
const test = require('node:test');
|
||||
|
||||
test('this should be skipped');
|
||||
test('this should be executed');
|
||||
@@ -101,4 +101,20 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
|
||||
assert.strictEqual(result[11], '# todo 0\n');
|
||||
assert.match(result[12], /# duration_ms \d+\.?\d*/);
|
||||
});
|
||||
|
||||
it('should skip tests not matching testNamePatterns - RegExp', async () => {
|
||||
const result = await run({ files: [join(testFixtures, 'test/skip_by_name.cjs')], testNamePatterns: [/executed/] })
|
||||
.compose(tap)
|
||||
.toArray();
|
||||
assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP test name does not match pattern\n');
|
||||
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
|
||||
});
|
||||
|
||||
it('should skip tests not matching testNamePatterns - string', async () => {
|
||||
const result = await run({ files: [join(testFixtures, 'test/skip_by_name.cjs')], testNamePatterns: ['executed'] })
|
||||
.compose(tap)
|
||||
.toArray();
|
||||
assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP test name does not match pattern\n');
|
||||
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user