Skip to content

Commit 5efd995

Browse files
committed
test_runner: ensure test watcher picks up new test files
1 parent b95eac5 commit 5efd995

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

lib/internal/test_runner/runner.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ function createTestFileList(patterns) {
109109

110110
function filterExecArgv(arg, i, arr) {
111111
return !ArrayPrototypeIncludes(kFilterArgs, arg) &&
112-
!ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
112+
!ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
113113
}
114114

115115
function getRunArgs(path, { forceExit, inspectPort, testNamePatterns, testSkipPatterns, only }) {
@@ -166,7 +166,7 @@ class FileTest extends Test {
166166
if (firstSpaceIndex === -1) return false;
167167
const secondSpaceIndex = StringPrototypeIndexOf(comment, ' ', firstSpaceIndex + 1);
168168
return secondSpaceIndex === -1 &&
169-
ArrayPrototypeIncludes(kDiagnosticsFilterArgs, StringPrototypeSlice(comment, 0, firstSpaceIndex));
169+
ArrayPrototypeIncludes(kDiagnosticsFilterArgs, StringPrototypeSlice(comment, 0, firstSpaceIndex));
170170
}
171171
#handleReportItem(item) {
172172
const isTopLevel = item.data.nesting === 0;
@@ -416,7 +416,22 @@ function watchFiles(testFiles, opts) {
416416
const watcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: 'filter', signal: opts.signal });
417417
const filesWatcher = { __proto__: null, watcher, runningProcesses, runningSubtests };
418418
opts.root.harness.watching = true;
419+
// Watch for created/deleted files in the current directory
420+
const allFilesWatcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: 'all' });
421+
allFilesWatcher.watchPath(process.cwd());
422+
allFilesWatcher.on('changed', ({ owners, eventType }) => {
423+
if (eventType === 'rename') {
424+
const updatedTestFiles = createTestFileList(opts.globPatterns);
425+
426+
const newFiles = ArrayPrototypeFilter(updatedTestFiles, (x) => !ArrayPrototypeIncludes(testFiles, x));
419427

428+
testFiles = updatedTestFiles;
429+
newFiles.forEach((newFile) => {
430+
watcher.emit('changed', { __proto__: null, owners: new SafeSet().add(newFile), eventType: 'rename' });
431+
});
432+
}
433+
});
434+
// Watch for changes in current filtered files
420435
watcher.on('changed', ({ owners, eventType }) => {
421436
if (!opts.hasFiles && eventType === 'rename') {
422437
const updatedTestFiles = createTestFileList(opts.globPatterns);

test/parallel/test-runner-watch-mode.mjs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,18 @@ function refresh() {
3434
.forEach(([file, content]) => writeFileSync(fixturePaths[file], content));
3535
}
3636

37-
async function testWatch({ fileToUpdate, file, action = 'update' }) {
37+
async function testWatch({
38+
fileToUpdate,
39+
file,
40+
action = 'update',
41+
fileToCreate
42+
}) {
3843
const ran1 = util.createDeferredPromise();
3944
const ran2 = util.createDeferredPromise();
4045
const child = spawn(process.execPath,
4146
['--watch', '--test', file ? fixturePaths[file] : undefined].filter(Boolean),
42-
{ encoding: 'utf8', stdio: 'pipe', cwd: tmpdir.path });
47+
{ encoding: 'utf8', stdio: 'pipe', cwd: tmpdir.path }
48+
);
4349
let stdout = '';
4450
let currentRun = '';
4551
const runs = [];
@@ -111,9 +117,34 @@ async function testWatch({ fileToUpdate, file, action = 'update' }) {
111117
}
112118
};
113119

120+
const testCreate = async () => {
121+
await ran1.promise;
122+
const newFilePath = tmpdir.resolve(fileToCreate);
123+
const interval = setInterval(
124+
() => writeFileSync(
125+
newFilePath,
126+
'module.exports = {};'
127+
),
128+
common.platformTimeout(1000)
129+
);
130+
await ran2.promise;
131+
runs.push(currentRun);
132+
clearInterval(interval);
133+
child.kill();
134+
await once(child, 'exit');
135+
136+
for (const run of runs) {
137+
assert.match(run, /# tests 1/);
138+
assert.match(run, /# pass 1/);
139+
assert.match(run, /# fail 0/);
140+
assert.match(run, /# cancelled 0/);
141+
}
142+
};
143+
114144
action === 'update' && await testUpdate();
115145
action === 'rename' && await testRename();
116146
action === 'delete' && await testDelete();
147+
action === 'create' && await testCreate();
117148
}
118149

119150
describe('test runner watch mode', () => {
@@ -141,4 +172,8 @@ describe('test runner watch mode', () => {
141172
it('should not throw when delete a watched test file', { skip: common.isAIX }, async () => {
142173
await testWatch({ fileToUpdate: 'test.js', action: 'delete' });
143174
});
175+
176+
it('should run new tests when a new file is created in the watched directory', async () => {
177+
await testWatch({ action: 'create', fileToCreate: 'new-test-file.test.js' });
178+
});
144179
});

0 commit comments

Comments
 (0)