| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a0be95e commit a4bebf8
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -103,8 +103,7 @@ const kCanceledTests = new SafeSet() | |||
| 103 | 103 | ||
| 104 | 104 | let kResistStopPropagation; | |
| 105 | 105 | ||
| 106 | - function createTestFileList(patterns) { | ||
| 107 | - const cwd = process.cwd(); | ||
| 106 | + function createTestFileList(patterns, cwd) { | ||
| 108 | 107 | const hasUserSuppliedPattern = patterns != null; | |
| 109 | 108 | if (!patterns || patterns.length === 0) { | |
| 110 | 109 | patterns = [kDefaultPattern]; | |
@@ -361,7 +360,17 @@ function runTestFile(path, filesWatcher, opts) { | |||
| 361 | 360 | env.FORCE_COLOR = '1'; | |
| 362 | 361 | } | |
| 363 | 362 | ||
| 364 | - const child = spawn(process.execPath, args, { __proto__: null, signal: t.signal, encoding: 'utf8', env, stdio }); | ||
| 363 | + const child = spawn( | ||
| 364 | + process.execPath, args, | ||
| 365 | + { | ||
| 366 | + __proto__: null, | ||
| 367 | + signal: t.signal, | ||
| 368 | + encoding: 'utf8', | ||
| 369 | + env, | ||
| 370 | + stdio, | ||
| 371 | + cwd: opts.cwd, | ||
| 372 | + }, | ||
| 373 | + ); | ||
| 365 | 374 | if (watchMode) { | |
| 366 | 375 | filesWatcher.runningProcesses.set(path, child); | |
| 367 | 376 | filesWatcher.watcher.watchChildProcessModules(child, path); | |
@@ -437,7 +446,11 @@ function runTestFile(path, filesWatcher, opts) { | |||
| 437 | 446 | function watchFiles(testFiles, opts) { | |
| 438 | 447 | const runningProcesses = new SafeMap(); | |
| 439 | 448 | const runningSubtests = new SafeMap(); | |
| 440 | - const watcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: 'filter', signal: opts.signal }); | ||
| 449 | + const watcherMode = opts.hasFiles ? 'filter' : 'all'; | ||
| 450 | + const watcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: watcherMode, signal: opts.signal }); | ||
| 451 | + if (!opts.hasFiles) { | ||
| 452 | + watcher.watchPath(opts.cwd); | ||
| 453 | + } | ||
| 441 | 454 | const filesWatcher = { __proto__: null, watcher, runningProcesses, runningSubtests }; | |
| 442 | 455 | opts.root.harness.watching = true; | |
| 443 | 456 | ||
@@ -455,24 +468,24 @@ function watchFiles(testFiles, opts) { | |||
| 455 | 468 | runningSubtests.set(file, runTestFile(file, filesWatcher, opts)); | |
| 456 | 469 | } | |
| 457 | 470 | ||
| 471 | + // Watch for changes in current filtered files | ||
| 458 | 472 | watcher.on('changed', ({ owners, eventType }) => { | |
| 459 | - if (!opts.hasFiles && eventType === 'rename') { | ||
| 460 | - const updatedTestFiles = createTestFileList(opts.globPatterns); | ||
| 473 | + if (!opts.hasFiles && (eventType === 'rename' || eventType === 'change')) { | ||
| 474 | + const updatedTestFiles = createTestFileList(opts.globPatterns, opts.cwd); | ||
| 461 | 475 | const newFileName = ArrayPrototypeFind(updatedTestFiles, (x) => !ArrayPrototypeIncludes(testFiles, x)); | |
| 462 | 476 | const previousFileName = ArrayPrototypeFind(testFiles, (x) => !ArrayPrototypeIncludes(updatedTestFiles, x)); | |
| 463 | 477 | ||
| 464 | 478 | testFiles = updatedTestFiles; | |
| 465 | 479 | ||
| 466 | - // When file renamed | ||
| 467 | - if (newFileName && previousFileName) { | ||
| 480 | + // When file renamed (created / deleted) we need to update the watcher | ||
| 481 | + if (newFileName) { | ||
| 468 | 482 | owners = new SafeSet().add(newFileName); | |
| 469 | 483 | watcher.filterFile(resolve(newFileName), owners); | |
| 470 | 484 | } | |
| 471 | 485 | ||
| 472 | 486 | if (!newFileName && previousFileName) { | |
| 473 | 487 | return; // Avoid rerunning files when file deleted | |
| 474 | 488 | } | |
| 475 | - | ||
| 476 | 489 | } | |
| 477 | 490 | ||
| 478 | 491 | if (opts.isolation === 'none') { | |
@@ -611,7 +624,11 @@ function run(options = kEmptyObject) { | |||
| 611 | 624 | setup, // This line can be removed when parseCommandLine() is removed here. | |
| 612 | 625 | }; | |
| 613 | 626 | const root = createTestTree(rootTestOptions, globalOptions); | |
| 614 | - let testFiles = files ?? createTestFileList(globPatterns); | ||
| 627 | + | ||
| 628 | + // This const should be replaced by a run option in the future. | ||
| 629 | + const cwd = process.cwd(); | ||
| 630 | + | ||
| 631 | + let testFiles = files ?? createTestFileList(globPatterns, cwd); | ||
| 615 | 632 | ||
| 616 | 633 | if (shard) { | |
| 617 | 634 | testFiles = ArrayPrototypeFilter(testFiles, (_, index) => index % shard.total === shard.index - 1); | |
@@ -632,6 +649,7 @@ function run(options = kEmptyObject) { | |||
| 632 | 649 | globPatterns, | |
| 633 | 650 | only, | |
| 634 | 651 | forceExit, | |
| 652 | + cwd, | ||
| 635 | 653 | isolation, | |
| 636 | 654 | }; | |
| 637 | 655 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -162,9 +162,7 @@ class FilesWatcher extends EventEmitter { | |||
| 162 | 162 | if (this.#passthroughIPC) { | |
| 163 | 163 | this.#setupIPC(child); | |
| 164 | 164 | } | |
| 165 | - if (this.#mode !== 'filter') { | ||
| 166 | - return; | ||
| 167 | - } | ||
| 165 | + | ||
| 168 | 166 | child.on('message', (message) => { | |
| 169 | 167 | try { | |
| 170 | 168 | if (ArrayIsArray(message['watch:require'])) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,4 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const test = require('node:test'); | ||
| 3 | + | ||
| 4 | + test('this should pass'); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -41,7 +41,7 @@ function refresh() { | |||
| 41 | 41 | ||
| 42 | 42 | const runner = join(import.meta.dirname, '..', 'fixtures', 'test-runner-watch.mjs'); | |
| 43 | 43 | ||
| 44 | - async function testWatch({ fileToUpdate, file, action = 'update', cwd = tmpdir.path }) { | ||
| 44 | + async function testWatch({ fileToUpdate, file, action = 'update', cwd = tmpdir.path, fileToCreate }) { | ||
| 45 | 45 | const ran1 = util.createDeferredPromise(); | |
| 46 | 46 | const ran2 = util.createDeferredPromise(); | |
| 47 | 47 | const args = [runner]; | |
@@ -56,7 +56,7 @@ async function testWatch({ fileToUpdate, file, action = 'update', cwd = tmpdir.p | |||
| 56 | 56 | child.stdout.on('data', (data) => { | |
| 57 | 57 | stdout += data.toString(); | |
| 58 | 58 | currentRun += data.toString(); | |
| 59 | - const testRuns = stdout.match(/# duration_ms\s\d+/g); | ||
| 59 | + const testRuns = stdout.match(/duration_ms\s\d+/g); | ||
| 60 | 60 | if (testRuns?.length >= 1) ran1.resolve(); | |
| 61 | 61 | if (testRuns?.length >= 2) ran2.resolve(); | |
| 62 | 62 | }); | |
@@ -78,10 +78,10 @@ async function testWatch({ fileToUpdate, file, action = 'update', cwd = tmpdir.p | |||
| 78 | 78 | ||
| 79 | 79 | for (const run of runs) { | |
| 80 | 80 | assert.doesNotMatch(run, /run\(\) is being called recursively/); | |
| 81 | - assert.match(run, /# tests 1/); | ||
| 82 | - assert.match(run, /# pass 1/); | ||
| 83 | - assert.match(run, /# fail 0/); | ||
| 84 | - assert.match(run, /# cancelled 0/); | ||
| 81 | + assert.match(run, /tests 1/); | ||
| 82 | + assert.match(run, /pass 1/); | ||
| 83 | + assert.match(run, /fail 0/); | ||
| 84 | + assert.match(run, /cancelled 0/); | ||
| 85 | 85 | } | |
| 86 | 86 | }; | |
| 87 | 87 | ||
@@ -101,21 +101,21 @@ async function testWatch({ fileToUpdate, file, action = 'update', cwd = tmpdir.p | |||
| 101 | 101 | assert.strictEqual(runs.length, 2); | |
| 102 | 102 | ||
| 103 | 103 | const [firstRun, secondRun] = runs; | |
| 104 | - assert.match(firstRun, /# tests 1/); | ||
| 105 | - assert.match(firstRun, /# pass 1/); | ||
| 106 | - assert.match(firstRun, /# fail 0/); | ||
| 107 | - assert.match(firstRun, /# cancelled 0/); | ||
| 104 | + assert.match(firstRun, /tests 1/); | ||
| 105 | + assert.match(firstRun, /pass 1/); | ||
| 106 | + assert.match(firstRun, /fail 0/); | ||
| 107 | + assert.match(firstRun, /cancelled 0/); | ||
| 108 | 108 | assert.doesNotMatch(firstRun, /run\(\) is being called recursively/); | |
| 109 | 109 | ||
| 110 | 110 | if (action === 'rename2') { | |
| 111 | 111 | assert.match(secondRun, /MODULE_NOT_FOUND/); | |
| 112 | 112 | return; | |
| 113 | 113 | } | |
| 114 | 114 | ||
| 115 | - assert.match(secondRun, /# tests 1/); | ||
| 116 | - assert.match(secondRun, /# pass 1/); | ||
| 117 | - assert.match(secondRun, /# fail 0/); | ||
| 118 | - assert.match(secondRun, /# cancelled 0/); | ||
| 115 | + assert.match(secondRun, /tests 1/); | ||
| 116 | + assert.match(secondRun, /pass 1/); | ||
| 117 | + assert.match(secondRun, /fail 0/); | ||
| 118 | + assert.match(secondRun, /cancelled 0/); | ||
| 119 | 119 | assert.doesNotMatch(secondRun, /run\(\) is being called recursively/); | |
| 120 | 120 | }; | |
| 121 | 121 | ||
@@ -144,10 +144,37 @@ async function testWatch({ fileToUpdate, file, action = 'update', cwd = tmpdir.p | |||
| 144 | 144 | } | |
| 145 | 145 | }; | |
| 146 | 146 | ||
| 147 | + const testCreate = async () => { | ||
| 148 | + await ran1.promise; | ||
| 149 | + runs.push(currentRun); | ||
| 150 | + currentRun = ''; | ||
| 151 | + const newFilePath = tmpdir.resolve(fileToCreate); | ||
| 152 | + const interval = setInterval( | ||
| 153 | + () => writeFileSync( | ||
| 154 | + newFilePath, | ||
| 155 | + 'module.exports = {};' | ||
| 156 | + ), | ||
| 157 | + common.platformTimeout(1000) | ||
| 158 | + ); | ||
| 159 | + await ran2.promise; | ||
| 160 | + runs.push(currentRun); | ||
| 161 | + clearInterval(interval); | ||
| 162 | + child.kill(); | ||
| 163 | + await once(child, 'exit'); | ||
| 164 | + | ||
| 165 | + for (const run of runs) { | ||
| 166 | + assert.match(run, /tests 1/); | ||
| 167 | + assert.match(run, /pass 1/); | ||
| 168 | + assert.match(run, /fail 0/); | ||
| 169 | + assert.match(run, /cancelled 0/); | ||
| 170 | + } | ||
| 171 | + }; | ||
| 172 | + | ||
| 147 | 173 | action === 'update' && await testUpdate(); | |
| 148 | 174 | action === 'rename' && await testRename(); | |
| 149 | 175 | action === 'rename2' && await testRename(); | |
| 150 | 176 | action === 'delete' && await testDelete(); | |
| 177 | + action === 'create' && await testCreate(); | ||
| 151 | 178 | } | |
| 152 | 179 | ||
| 153 | 180 | describe('test runner watch mode', () => { | |
@@ -193,4 +220,8 @@ describe('test runner watch mode', () => { | |||
| 193 | 220 | action: 'rename2' | |
| 194 | 221 | }); | |
| 195 | 222 | }); | |
| 223 | + | ||
| 224 | + it('should run new tests when a new file is created in the watched directory', async () => { | ||
| 225 | + await testWatch({ action: 'create', fileToCreate: 'new-test-file.test.js' }); | ||
| 226 | + }); | ||
| 196 | 227 | }); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -37,7 +37,12 @@ function refresh() { | |||
| 37 | 37 | .forEach(([file, content]) => writeFileSync(fixturePaths[file], content)); | |
| 38 | 38 | } | |
| 39 | 39 | ||
| 40 | - async function testWatch({ fileToUpdate, file, action = 'update' }) { | ||
| 40 | + async function testWatch({ | ||
| 41 | + fileToUpdate, | ||
| 42 | + file, | ||
| 43 | + action = 'update', | ||
| 44 | + fileToCreate, | ||
| 45 | + }) { | ||
| 41 | 46 | const ran1 = util.createDeferredPromise(); | |
| 42 | 47 | const ran2 = util.createDeferredPromise(); | |
| 43 | 48 | const child = spawn(process.execPath, | |
@@ -127,9 +132,36 @@ async function testWatch({ fileToUpdate, file, action = 'update' }) { | |||
| 127 | 132 | } | |
| 128 | 133 | }; | |
| 129 | 134 | ||
| 135 | + const testCreate = async () => { | ||
| 136 | + await ran1.promise; | ||
| 137 | + runs.push(currentRun); | ||
| 138 | + currentRun = ''; | ||
| 139 | + const newFilePath = tmpdir.resolve(fileToCreate); | ||
| 140 | + const interval = setInterval( | ||
| 141 | + () => writeFileSync( | ||
| 142 | + newFilePath, | ||
| 143 | + 'module.exports = {};' | ||
| 144 | + ), | ||
| 145 | + common.platformTimeout(1000) | ||
| 146 | + ); | ||
| 147 | + await ran2.promise; | ||
| 148 | + runs.push(currentRun); | ||
| 149 | + clearInterval(interval); | ||
| 150 | + child.kill(); | ||
| 151 | + await once(child, 'exit'); | ||
| 152 | + | ||
| 153 | + for (const run of runs) { | ||
| 154 | + assert.match(run, /tests 1/); | ||
| 155 | + assert.match(run, /pass 1/); | ||
| 156 | + assert.match(run, /fail 0/); | ||
| 157 | + assert.match(run, /cancelled 0/); | ||
| 158 | + } | ||
| 159 | + }; | ||
| 160 | + | ||
| 130 | 161 | action === 'update' && await testUpdate(); | |
| 131 | 162 | action === 'rename' && await testRename(); | |
| 132 | 163 | action === 'delete' && await testDelete(); | |
| 164 | + action === 'create' && await testCreate(); | ||
| 133 | 165 | } | |
| 134 | 166 | ||
| 135 | 167 | describe('test runner watch mode', () => { | |
@@ -157,4 +189,8 @@ describe('test runner watch mode', () => { | |||
| 157 | 189 | it('should not throw when delete a watched test file', { skip: common.isAIX }, async () => { | |
| 158 | 190 | await testWatch({ fileToUpdate: 'test.js', action: 'delete' }); | |
| 159 | 191 | }); | |
| 192 | + | ||
| 193 | + it('should run new tests when a new file is created in the watched directory', async () => { | ||
| 194 | + await testWatch({ action: 'create', fileToCreate: 'new-test-file.test.js' }); | ||
| 195 | + }); | ||
| 160 | 196 | }); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments