|
1 | | -import * as common from '../common/index.mjs'; |
| 1 | +import '../common/index.mjs'; |
| 2 | +import { skipIfNoWatch, refreshForTestRunnerWatch, testRunnerWatch } from '../common/watch.js'; |
2 | 3 | import { describe, it, beforeEach } from 'node:test'; |
3 | | -import { once } from 'node:events'; |
4 | | -import assert from 'node:assert'; |
5 | | -import { spawn } from 'node:child_process'; |
6 | | -import { writeFileSync, renameSync, unlinkSync } from 'node:fs'; |
7 | | -import { setTimeout } from 'node:timers/promises'; |
8 | | -import tmpdir from '../common/tmpdir.js'; |
9 | 4 |
|
10 | | -if (common.isIBMi) |
11 | | - common.skip('IBMi does not support `fs.watch()`'); |
12 | | - |
13 | | -if (common.isAIX) |
14 | | - common.skip('folder watch capability is limited in AIX.'); |
15 | | - |
16 | | -let fixturePaths; |
17 | | - |
18 | | -// This test updates these files repeatedly, |
19 | | -// Reading them from disk is unreliable due to race conditions. |
20 | | -const fixtureContent = { |
21 | | - 'dependency.js': 'module.exports = {};', |
22 | | - 'dependency.mjs': 'export const a = 1;', |
23 | | - 'test.js': ` |
24 | | -const test = require('node:test'); |
25 | | -require('./dependency.js'); |
26 | | -import('./dependency.mjs'); |
27 | | -import('data:text/javascript,'); |
28 | | -test('test has ran');`, |
29 | | -}; |
30 | | - |
31 | | -function refresh() { |
32 | | - tmpdir.refresh(); |
33 | | - fixturePaths = Object.keys(fixtureContent) |
34 | | - .reduce((acc, file) => ({ ...acc, [file]: tmpdir.resolve(file) }), {}); |
35 | | - Object.entries(fixtureContent) |
36 | | - .forEach(([file, content]) => writeFileSync(fixturePaths[file], content)); |
37 | | -} |
38 | | - |
39 | | -async function testWatch({ |
40 | | - fileToUpdate, |
41 | | - file, |
42 | | - action = 'update', |
43 | | - fileToCreate, |
44 | | - isolation, |
45 | | -}) { |
46 | | - const ran1 = Promise.withResolvers(); |
47 | | - const ran2 = Promise.withResolvers(); |
48 | | - const child = spawn(process.execPath, |
49 | | - ['--watch', '--test', '--test-reporter=spec', |
50 | | - isolation ? `--test-isolation=${isolation}` : '', |
51 | | - file ? fixturePaths[file] : undefined].filter(Boolean), |
52 | | - { encoding: 'utf8', stdio: 'pipe', cwd: tmpdir.path }); |
53 | | - let stdout = ''; |
54 | | - let currentRun = ''; |
55 | | - const runs = []; |
56 | | - |
57 | | - child.stdout.on('data', (data) => { |
58 | | - stdout += data.toString(); |
59 | | - currentRun += data.toString(); |
60 | | - const testRuns = stdout.match(/duration_ms\s\d+/g); |
61 | | - if (testRuns?.length >= 1) ran1.resolve(); |
62 | | - if (testRuns?.length >= 2) ran2.resolve(); |
63 | | - }); |
64 | | - |
65 | | - const testUpdate = async () => { |
66 | | - await ran1.promise; |
67 | | - runs.push(currentRun); |
68 | | - currentRun = ''; |
69 | | - const content = fixtureContent[fileToUpdate]; |
70 | | - const path = fixturePaths[fileToUpdate]; |
71 | | - writeFileSync(path, content); |
72 | | - await setTimeout(common.platformTimeout(1000)); |
73 | | - await ran2.promise; |
74 | | - runs.push(currentRun); |
75 | | - child.kill(); |
76 | | - await once(child, 'exit'); |
77 | | - |
78 | | - assert.strictEqual(runs.length, 2); |
79 | | - |
80 | | - for (const run of runs) { |
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 | | - } |
86 | | - }; |
87 | | - |
88 | | - const testRename = async () => { |
89 | | - await ran1.promise; |
90 | | - runs.push(currentRun); |
91 | | - currentRun = ''; |
92 | | - const fileToRenamePath = tmpdir.resolve(fileToUpdate); |
93 | | - const newFileNamePath = tmpdir.resolve(`test-renamed-${fileToUpdate}`); |
94 | | - renameSync(fileToRenamePath, newFileNamePath); |
95 | | - await setTimeout(common.platformTimeout(1000)); |
96 | | - await ran2.promise; |
97 | | - runs.push(currentRun); |
98 | | - child.kill(); |
99 | | - await once(child, 'exit'); |
100 | | - |
101 | | - assert.strictEqual(runs.length, 2); |
102 | | - |
103 | | - for (const run of runs) { |
104 | | - assert.match(run, /tests 1/); |
105 | | - assert.match(run, /pass 1/); |
106 | | - assert.match(run, /fail 0/); |
107 | | - assert.match(run, /cancelled 0/); |
108 | | - } |
109 | | - }; |
110 | | - |
111 | | - const testDelete = async () => { |
112 | | - await ran1.promise; |
113 | | - runs.push(currentRun); |
114 | | - currentRun = ''; |
115 | | - const fileToDeletePath = tmpdir.resolve(fileToUpdate); |
116 | | - unlinkSync(fileToDeletePath); |
117 | | - await setTimeout(common.platformTimeout(2000)); |
118 | | - ran2.resolve(); |
119 | | - runs.push(currentRun); |
120 | | - child.kill(); |
121 | | - await once(child, 'exit'); |
122 | | - |
123 | | - assert.strictEqual(runs.length, 2); |
124 | | - |
125 | | - for (const run of runs) { |
126 | | - assert.doesNotMatch(run, /MODULE_NOT_FOUND/); |
127 | | - } |
128 | | - }; |
129 | | - |
130 | | - const testCreate = async () => { |
131 | | - await ran1.promise; |
132 | | - runs.push(currentRun); |
133 | | - currentRun = ''; |
134 | | - const newFilePath = tmpdir.resolve(fileToCreate); |
135 | | - writeFileSync(newFilePath, 'module.exports = {};'); |
136 | | - await setTimeout(common.platformTimeout(1000)); |
137 | | - await ran2.promise; |
138 | | - runs.push(currentRun); |
139 | | - child.kill(); |
140 | | - await once(child, 'exit'); |
141 | | - |
142 | | - for (const run of runs) { |
143 | | - assert.match(run, /tests 1/); |
144 | | - assert.match(run, /pass 1/); |
145 | | - assert.match(run, /fail 0/); |
146 | | - assert.match(run, /cancelled 0/); |
147 | | - } |
148 | | - }; |
149 | | - |
150 | | - action === 'update' && await testUpdate(); |
151 | | - action === 'rename' && await testRename(); |
152 | | - action === 'delete' && await testDelete(); |
153 | | - action === 'create' && await testCreate(); |
154 | | -} |
| 5 | +skipIfNoWatch(); |
155 | 6 |
|
156 | 7 | describe('test runner watch mode', () => { |
157 | | - beforeEach(refresh); |
| 8 | + beforeEach(refreshForTestRunnerWatch); |
158 | 9 | for (const isolation of ['none', 'process']) { |
159 | 10 | describe(`isolation: ${isolation}`, () => { |
160 | 11 | it('should run tests repeatedly', async () => { |
161 | | - await testWatch({ file: 'test.js', fileToUpdate: 'test.js', isolation }); |
| 12 | + await testRunnerWatch({ file: 'test.js', fileToUpdate: 'test.js', isolation }); |
162 | 13 | }); |
163 | 14 |
|
164 | 15 | it('should run tests with dependency repeatedly', async () => { |
165 | | - await testWatch({ file: 'test.js', fileToUpdate: 'dependency.js', isolation }); |
| 16 | + await testRunnerWatch({ file: 'test.js', fileToUpdate: 'dependency.js', isolation }); |
166 | 17 | }); |
167 | 18 |
|
168 | 19 | it('should run tests with ESM dependency', async () => { |
169 | | - await testWatch({ file: 'test.js', fileToUpdate: 'dependency.mjs', isolation }); |
| 20 | + await testRunnerWatch({ file: 'test.js', fileToUpdate: 'dependency.mjs', isolation }); |
170 | 21 | }); |
171 | 22 |
|
172 | 23 | it('should support running tests without a file', async () => { |
173 | | - await testWatch({ fileToUpdate: 'test.js', isolation }); |
| 24 | + await testRunnerWatch({ fileToUpdate: 'test.js', isolation }); |
174 | 25 | }); |
175 | 26 |
|
176 | 27 | it('should support a watched test file rename', async () => { |
177 | | - await testWatch({ fileToUpdate: 'test.js', action: 'rename', isolation }); |
| 28 | + await testRunnerWatch({ fileToUpdate: 'test.js', action: 'rename', isolation }); |
178 | 29 | }); |
179 | 30 |
|
180 | 31 | it('should not throw when delete a watched test file', async () => { |
181 | | - await testWatch({ fileToUpdate: 'test.js', action: 'delete', isolation }); |
| 32 | + await testRunnerWatch({ fileToUpdate: 'test.js', action: 'delete', isolation }); |
182 | 33 | }); |
183 | 34 |
|
184 | 35 | it('should run new tests when a new file is created in the watched directory', { |
185 | 36 | todo: isolation === 'none' ? |
186 | 37 | 'This test is failing when isolation is set to none and must be fixed' : |
187 | 38 | undefined, |
188 | 39 | }, async () => { |
189 | | - await testWatch({ action: 'create', fileToCreate: 'new-test-file.test.js', isolation }); |
| 40 | + await testRunnerWatch({ action: 'create', fileToCreate: 'new-test-file.test.js', isolation }); |
190 | 41 | }); |
191 | 42 | }); |
192 | 43 | } |
|
0 commit comments