Skip to content

Commit 618a9e1

Browse files
committed
test_runner: stop watch mode when abortSignal aborted
PR-URL: #48259 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent 27f195f commit 618a9e1

File tree

3 files changed

+25
-5
lines changed

3 files changed

+25
-5
lines changed

lib/internal/test_runner/runner.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,8 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
412412
return subtest.start();
413413
}
414414

415-
function watchFiles(testFiles, root, inspectPort, testNamePatterns) {
416-
const filesWatcher = new FilesWatcher({ throttle: 500, mode: 'filter' });
415+
function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
416+
const filesWatcher = new FilesWatcher({ throttle: 500, mode: 'filter', signal });
417417
filesWatcher.on('changed', ({ owners }) => {
418418
filesWatcher.unfilterFilesOwnedBy(owners);
419419
PromisePrototypeThen(SafePromiseAllReturnVoid(testFiles, async (file) => {
@@ -435,6 +435,7 @@ function watchFiles(testFiles, root, inspectPort, testNamePatterns) {
435435
triggerUncaughtException(error, true /* fromPromise */);
436436
}));
437437
});
438+
signal?.addEventListener('abort', () => root.postRun(), { __proto__: null, once: true });
438439
return filesWatcher;
439440
}
440441

@@ -477,7 +478,7 @@ function run(options) {
477478
let postRun = () => root.postRun();
478479
let filesWatcher;
479480
if (watch) {
480-
filesWatcher = watchFiles(testFiles, root, inspectPort, testNamePatterns);
481+
filesWatcher = watchFiles(testFiles, root, inspectPort, signal, testNamePatterns);
481482
postRun = undefined;
482483
}
483484
const runFiles = () => {

lib/internal/watch_mode/files_watcher.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,18 @@ class FilesWatcher extends EventEmitter {
3030
#ownerDependencies = new SafeMap();
3131
#throttle;
3232
#mode;
33+
#signal;
3334

34-
constructor({ throttle = 500, mode = 'filter' } = kEmptyObject) {
35+
constructor({ throttle = 500, mode = 'filter', signal } = kEmptyObject) {
3536
super();
3637

3738
validateNumber(throttle, 'options.throttle', 0, TIMEOUT_MAX);
3839
validateOneOf(mode, 'options.mode', ['filter', 'all']);
3940
this.#throttle = throttle;
4041
this.#mode = mode;
42+
this.#signal = signal;
43+
44+
signal?.addEventListener('abort', () => this.clear(), { __proto__: null, once: true });
4145
}
4246

4347
#isPathWatched(path) {
@@ -89,7 +93,7 @@ class FilesWatcher extends EventEmitter {
8993
if (this.#isPathWatched(path)) {
9094
return;
9195
}
92-
const watcher = watch(path, { recursive });
96+
const watcher = watch(path, { recursive, signal: this.#signal });
9397
watcher.on('change', (eventType, fileName) => this
9498
.#onChange(recursive ? resolve(path, fileName) : path));
9599
this.#watchers.set(path, { handle: watcher, recursive });

test/parallel/test-runner-run.mjs

+15
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,19 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
117117
assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP test name does not match pattern\n');
118118
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
119119
});
120+
121+
it('should stop watch mode when abortSignal aborts', async () => {
122+
const controller = new AbortController();
123+
const result = await run({ files: [join(testFixtures, 'test/random.cjs')], watch: true, signal: controller.signal })
124+
.compose(async function* (source) {
125+
for await (const chunk of source) {
126+
if (chunk.type === 'test:pass') {
127+
controller.abort();
128+
yield chunk.data.name;
129+
}
130+
}
131+
})
132+
.toArray();
133+
assert.deepStrictEqual(result, ['this should pass']);
134+
});
120135
});

0 commit comments

Comments
 (0)