Skip to content

Commit e3abb7f

Browse files
committed
test_runner: report changed file on watch restart
The `test:watch:restarted` reporter event now includes a `file` property with the path of the file whose change triggered the restart. The watcher already surfaces this path through the `changed` event since it started printing the changed file name, so this just forwards it to consumers of the reporter stream. Fixes: #63786 Signed-off-by: Paul Bouchon <mail@bitpshr.net>
1 parent 9f1e44c commit e3abb7f

3 files changed

Lines changed: 61 additions & 2 deletions

File tree

doc/api/test.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4076,6 +4076,18 @@ Emitted when no more tests are queued for execution in watch mode.
40764076

40774077
### Event: `'test:watch:restarted'`
40784078

4079+
<!-- YAML
4080+
changes:
4081+
- version: REPLACEME
4082+
pr-url: https://github.com/nodejs/node/pull/64457
4083+
description: Added the `file` property to the event data.
4084+
-->
4085+
4086+
* `data` {Object}
4087+
* `file` {string|undefined} The path of the file whose change triggered the
4088+
restart. This value is `undefined` when the triggering file cannot be
4089+
determined.
4090+
40794091
Emitted when one or more tests are restarted due to a file change in watch mode.
40804092

40814093
## `getTestContext()`

lib/internal/test_runner/runner.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ function watchFiles(testFiles, opts) {
659659
}
660660

661661
// Watch for changes in current filtered files
662-
const onChanged = ({ owners, eventType }) => {
662+
const onChanged = ({ owners, eventType, trigger }) => {
663663
if (!opts.hasFiles && (eventType === 'rename' || eventType === 'change')) {
664664
const updatedTestFiles = createTestFileList(opts.globPatterns, opts.cwd);
665665
const newFileName = ArrayPrototypeFind(updatedTestFiles, (x) => !ArrayPrototypeIncludes(testFiles, x));
@@ -681,7 +681,7 @@ function watchFiles(testFiles, opts) {
681681
// Reset the root start time to recalculate the duration
682682
// of the run
683683
opts.root.clearExecutionTime();
684-
opts.root.reporter[kEmitMessage]('test:watch:restarted');
684+
opts.root.reporter[kEmitMessage]('test:watch:restarted', { __proto__: null, file: trigger });
685685

686686
// Restart test files
687687
if (opts.isolation === 'none') {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Test run({ watch: true }) reports the triggering file path in the
2+
// test:watch:restarted event data.
3+
import * as common from '../common/index.mjs';
4+
import { run } from 'node:test';
5+
import assert from 'node:assert';
6+
import { writeFileSync } from 'node:fs';
7+
import { join } from 'node:path';
8+
import { once } from 'node:events';
9+
import tmpdir from '../common/tmpdir.js';
10+
import { refreshForTestRunnerWatch, skipIfNoWatch, fixtureContent } from '../common/watch.js';
11+
12+
skipIfNoWatch();
13+
refreshForTestRunnerWatch();
14+
15+
const changedFile = join(tmpdir.path, 'test.js');
16+
17+
let alreadyDrained = false;
18+
const restartedFiles = [];
19+
const onRestarted = common.mustCall((file) => {
20+
restartedFiles.push(file);
21+
}, 1);
22+
23+
const controller = new AbortController();
24+
const stream = run({
25+
cwd: tmpdir.path,
26+
watch: true,
27+
signal: controller.signal,
28+
}).on('data', function({ type, data }) {
29+
if (type === 'test:watch:restarted') {
30+
onRestarted(data.file);
31+
}
32+
if (type === 'test:watch:drained') {
33+
if (alreadyDrained) {
34+
controller.abort();
35+
}
36+
alreadyDrained = true;
37+
}
38+
});
39+
40+
await once(stream, 'test:watch:drained');
41+
42+
writeFileSync(changedFile, fixtureContent['test.js']);
43+
44+
// eslint-disable-next-line no-unused-vars
45+
for await (const _ of stream);
46+
47+
assert.deepStrictEqual(restartedFiles, [changedFile]);

0 commit comments

Comments
 (0)