Skip to content

Commit acfe5ce

Browse files
committed
test_runner: accept testOnly in run
1 parent 55fde47 commit acfe5ce

File tree

4 files changed

+43
-17
lines changed

4 files changed

+43
-17
lines changed

doc/api/test.md

+2
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,8 @@ changes:
887887
number. If a nullish value is provided, each process gets its own port,
888888
incremented from the primary's `process.debugPort`.
889889
**Default:** `undefined`.
890+
* `only`: {boolean} If truthy, the test context will only run tests that
891+
have the `only` option set
890892
* `setup` {Function} A function that accepts the `TestsStream` instance
891893
and can be used to setup listeners before any tests are run.
892894
**Default:** `undefined`.

lib/internal/test_runner/runner.js

+24-17
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,17 @@ function filterExecArgv(arg, i, arr) {
110110
!ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
111111
}
112112

113-
function getRunArgs({ path, inspectPort, testNamePatterns }) {
113+
function getRunArgs(path, { inspectPort, testNamePatterns, testOnly }) {
114114
const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv);
115115
if (isUsingInspector()) {
116116
ArrayPrototypePush(argv, `--inspect-port=${getInspectPort(inspectPort)}`);
117117
}
118-
if (testNamePatterns) {
118+
if (testNamePatterns != null) {
119119
ArrayPrototypeForEach(testNamePatterns, (pattern) => ArrayPrototypePush(argv, `--test-name-pattern=${pattern}`));
120120
}
121+
if (testOnly != null) {
122+
ArrayPrototypePush(argv, '--test-only');
123+
}
121124
ArrayPrototypePush(argv, path);
122125

123126
return argv;
@@ -301,17 +304,17 @@ class FileTest extends Test {
301304
}
302305
}
303306

304-
function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
307+
function runTestFile(path, filesWatcher, opts) {
305308
const watchMode = filesWatcher != null;
306-
const subtest = root.createSubtest(FileTest, path, async (t) => {
307-
const args = getRunArgs({ __proto__: null, path, inspectPort, testNamePatterns });
309+
const subtest = opts.root.createSubtest(FileTest, path, async (t) => {
310+
const args = getRunArgs(path, opts);
308311
const stdio = ['pipe', 'pipe', 'pipe'];
309312
const env = { __proto__: null, ...process.env, NODE_TEST_CONTEXT: 'child-v8' };
310313
if (watchMode) {
311314
stdio.push('ipc');
312315
env.WATCH_REPORT_DEPENDENCIES = '1';
313316
}
314-
if (root.harness.shouldColorizeTestFiles) {
317+
if (opts.root.harness.shouldColorizeTestFiles) {
315318
env.FORCE_COLOR = '1';
316319
}
317320

@@ -358,7 +361,7 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
358361
filesWatcher.runningProcesses.delete(path);
359362
filesWatcher.runningSubtests.delete(path);
360363
if (filesWatcher.runningSubtests.size === 0) {
361-
root.reporter[kEmitMessage]('test:watch:drained');
364+
opts.root.reporter[kEmitMessage]('test:watch:drained');
362365
}
363366
}
364367

@@ -381,10 +384,10 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
381384
return subtest.start();
382385
}
383386

384-
function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
387+
function watchFiles(testFiles, opts) {
385388
const runningProcesses = new SafeMap();
386389
const runningSubtests = new SafeMap();
387-
const watcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: 'filter', signal });
390+
const watcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: 'filter', signal: opts.signal });
388391
const filesWatcher = { __proto__: null, watcher, runningProcesses, runningSubtests };
389392

390393
watcher.on('changed', ({ owners }) => {
@@ -400,19 +403,19 @@ function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
400403
}
401404
if (!runningSubtests.size) {
402405
// Reset the topLevel counter
403-
root.harness.counters.topLevel = 0;
406+
opts.root.harness.counters.topLevel = 0;
404407
}
405408
await runningSubtests.get(file);
406-
runningSubtests.set(file, runTestFile(file, root, inspectPort, filesWatcher, testNamePatterns));
409+
runningSubtests.set(file, runTestFile(file, filesWatcher, opts));
407410
}, undefined, (error) => {
408411
triggerUncaughtException(error, true /* fromPromise */);
409412
}));
410413
});
411-
if (signal) {
414+
if (opts.signal) {
412415
kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
413-
signal.addEventListener(
416+
opts.signal.addEventListener(
414417
'abort',
415-
() => root.postRun(),
418+
() => opts.root.postRun(),
416419
{ __proto__: null, once: true, [kResistStopPropagation]: true },
417420
);
418421
}
@@ -425,14 +428,17 @@ function run(options) {
425428
options = kEmptyObject;
426429
}
427430
let { testNamePatterns, shard } = options;
428-
const { concurrency, timeout, signal, files, inspectPort, watch, setup } = options;
431+
const { concurrency, timeout, signal, files, inspectPort, watch, setup, testOnly } = options;
429432

430433
if (files != null) {
431434
validateArray(files, 'options.files');
432435
}
433436
if (watch != null) {
434437
validateBoolean(watch, 'options.watch');
435438
}
439+
if (testOnly != null) {
440+
validateBoolean(testOnly, 'options.testOnly');
441+
}
436442
if (shard != null) {
437443
validateObject(shard, 'options.shard');
438444
// Avoid re-evaluating the shard object in case it's a getter
@@ -478,14 +484,15 @@ function run(options) {
478484

479485
let postRun = () => root.postRun();
480486
let filesWatcher;
487+
const opts = { __proto__: null, root, signal, inspectPort, testNamePatterns, testOnly };
481488
if (watch) {
482-
filesWatcher = watchFiles(testFiles, root, inspectPort, signal, testNamePatterns);
489+
filesWatcher = watchFiles(testFiles, opts);
483490
postRun = undefined;
484491
}
485492
const runFiles = () => {
486493
root.harness.bootstrapComplete = true;
487494
return SafePromiseAllSettledReturnVoid(testFiles, (path) => {
488-
const subtest = runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns);
495+
const subtest = runTestFile(path, filesWatcher, opts);
489496
filesWatcher?.runningSubtests.set(path, subtest);
490497
return subtest;
491498
});
+5
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.only('this should be executed');

test/parallel/test-runner-run.mjs

+12
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,18 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
148148
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
149149
});
150150

151+
it('should pass testOnly to children', async () => {
152+
const result = await run({
153+
files: [join(testFixtures, 'test_only.js')],
154+
testOnly: true
155+
})
156+
.compose(tap)
157+
.toArray();
158+
159+
assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP \'only\' option not set\n');
160+
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
161+
});
162+
151163
it('should emit "test:watch:drained" event on watch mode', async () => {
152164
const controller = new AbortController();
153165
await run({

0 commit comments

Comments
 (0)