@@ -9,10 +9,13 @@ import { spawn } from 'node:child_process';
9
9
import { writeFileSync , readFileSync } from 'node:fs' ;
10
10
import { inspect } from 'node:util' ;
11
11
import { once } from 'node:events' ;
12
+ import { createInterface } from 'node:readline/promises' ;
12
13
13
14
if ( common . isIBMi )
14
15
common . skip ( 'IBMi does not support `fs.watch()`' ) ;
15
16
17
+ const supportsRecursive = common . isOSX || common . isWindows ;
18
+
16
19
function restart ( file ) {
17
20
// To avoid flakiness, we save the file repeatedly until test is done
18
21
writeFileSync ( file , readFileSync ( file ) ) ;
@@ -59,8 +62,8 @@ async function spawnWithRestarts({
59
62
}
60
63
61
64
let tmpFiles = 0 ;
62
- function createTmpFile ( content = 'console.log("running");' ) {
63
- const file = path . join ( tmpdir . path , `${ tmpFiles ++ } .js ` ) ;
65
+ function createTmpFile ( content = 'console.log("running");' , ext = '.js' ) {
66
+ const file = path . join ( tmpdir . path , `${ tmpFiles ++ } ${ ext } ` ) ;
64
67
writeFileSync ( file , content ) ;
65
68
return file ;
66
69
}
@@ -74,11 +77,29 @@ function assertRestartedCorrectly({ stdout, messages: { inner, completed, restar
74
77
assert . deepStrictEqual ( lines . slice ( - end . length ) , end ) ;
75
78
}
76
79
80
+ async function failWriteSucceed ( { file, watchedFile } ) {
81
+ const child = spawn ( execPath , [ '--watch' , '--no-warnings' , file ] , { encoding : 'utf8' } ) ;
82
+
83
+ try {
84
+ // Break the chunks into lines
85
+ for await ( const data of createInterface ( { input : child . stdout } ) ) {
86
+ if ( data . startsWith ( 'Completed running' ) ) {
87
+ break ;
88
+ }
89
+ if ( data . startsWith ( 'Failed running' ) ) {
90
+ writeFileSync ( watchedFile , 'console.log("test has ran");' ) ;
91
+ }
92
+ }
93
+ } finally {
94
+ child . kill ( ) ;
95
+ }
96
+ }
97
+
77
98
tmpdir . refresh ( ) ;
78
99
79
100
// Warning: this suite can run safely with concurrency: true
80
101
// only if tests do not watch/depend on the same files
81
- describe ( 'watch mode' , { concurrency : true , timeout : 60_0000 } , ( ) => {
102
+ describe ( 'watch mode' , { concurrency : true , timeout : 60_000 } , ( ) => {
82
103
it ( 'should watch changes to a file - event loop ended' , async ( ) => {
83
104
const file = createTmpFile ( ) ;
84
105
const { stderr, stdout } = await spawnWithRestarts ( { file } ) ;
@@ -104,16 +125,8 @@ describe('watch mode', { concurrency: true, timeout: 60_0000 }, () => {
104
125
} ) ;
105
126
} ) ;
106
127
107
- it ( 'should not watch when running an non-existing file' , async ( ) => {
108
- const file = fixtures . path ( 'watch-mode/non-existing.js' ) ;
109
- const { stderr, stdout } = await spawnWithRestarts ( { file, restarts : 0 } ) ;
110
-
111
- assert . match ( stderr , / c o d e : ' M O D U L E _ N O T _ F O U N D ' / ) ;
112
- assert . strictEqual ( stdout , `Failed running ${ inspect ( file ) } \n` ) ;
113
- } ) ;
114
-
115
128
it ( 'should watch when running an non-existing file - when specified under --watch-path' , {
116
- skip : ! common . isOSX && ! common . isWindows
129
+ skip : ! supportsRecursive
117
130
} , async ( ) => {
118
131
const file = fixtures . path ( 'watch-mode/subdir/non-existing.js' ) ;
119
132
const watchedFile = fixtures . path ( 'watch-mode/subdir/file.js' ) ;
@@ -220,4 +233,39 @@ describe('watch mode', { concurrency: true, timeout: 60_0000 }, () => {
220
233
messages : { restarted : `Restarting ${ inspect ( file ) } ` , completed : `Completed running ${ inspect ( file ) } ` } ,
221
234
} ) ;
222
235
} ) ;
236
+
237
+ // TODO: Remove skip after https://github.com/nodejs/node/pull/45271 lands
238
+ it ( 'should not watch when running an missing file' , {
239
+ skip : ! supportsRecursive
240
+ } , async ( ) => {
241
+ const nonExistingfile = path . join ( tmpdir . path , `${ tmpFiles ++ } .js` ) ;
242
+ await failWriteSucceed ( { file : nonExistingfile , watchedFile : nonExistingfile } ) ;
243
+ } ) ;
244
+
245
+ it ( 'should not watch when running an missing mjs file' , {
246
+ skip : ! supportsRecursive
247
+ } , async ( ) => {
248
+ const nonExistingfile = path . join ( tmpdir . path , `${ tmpFiles ++ } .mjs` ) ;
249
+ await failWriteSucceed ( { file : nonExistingfile , watchedFile : nonExistingfile } ) ;
250
+ } ) ;
251
+
252
+ it ( 'should watch changes to previously missing dependency' , {
253
+ skip : ! supportsRecursive
254
+ } , async ( ) => {
255
+ const dependency = path . join ( tmpdir . path , `${ tmpFiles ++ } .js` ) ;
256
+ const relativeDependencyPath = `./${ path . basename ( dependency ) } ` ;
257
+ const dependant = createTmpFile ( `console.log(require('${ relativeDependencyPath } '))` ) ;
258
+
259
+ await failWriteSucceed ( { file : dependant , watchedFile : dependency } ) ;
260
+ } ) ;
261
+
262
+ it ( 'should watch changes to previously missing ESM dependency' , {
263
+ skip : ! supportsRecursive
264
+ } , async ( ) => {
265
+ const dependency = path . join ( tmpdir . path , `${ tmpFiles ++ } .mjs` ) ;
266
+ const relativeDependencyPath = `./${ path . basename ( dependency ) } ` ;
267
+ const dependant = createTmpFile ( `import '${ relativeDependencyPath } '` , '.mjs' ) ;
268
+
269
+ await failWriteSucceed ( { file : dependant , watchedFile : dependency } ) ;
270
+ } ) ;
223
271
} ) ;
0 commit comments