@@ -3,6 +3,7 @@ import { promises as fs } from 'fs';
33import path from 'path' ;
44import os from 'os' ;
55import { startFileWatcher } from '../src/core/file-watcher.js' ;
6+ import { rmWithRetries } from './test-helpers.js' ;
67
78describe ( 'FileWatcher' , ( ) => {
89 let tempDir : string ;
@@ -12,22 +13,27 @@ describe('FileWatcher', () => {
1213 } ) ;
1314
1415 afterEach ( async ( ) => {
15- await fs . rm ( tempDir , { recursive : true , force : true } ) ;
16+ await rmWithRetries ( tempDir ) ;
1617 } ) ;
1718
1819 it ( 'triggers onChanged after debounce window' , async ( ) => {
1920 const debounceMs = 400 ;
2021 let callCount = 0 ;
2122
23+ let resolveReady ! : ( ) => void ;
24+ const ready = new Promise < void > ( ( resolve ) => {
25+ resolveReady = resolve ;
26+ } ) ;
27+
2228 const stop = startFileWatcher ( {
2329 rootPath : tempDir ,
2430 debounceMs,
31+ onReady : ( ) => resolveReady ( ) ,
2532 onChanged : ( ) => { callCount ++ ; } ,
2633 } ) ;
2734
2835 try {
29- // Give chokidar a moment to finish initializing before the first write
30- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
36+ await ready ;
3137 await fs . writeFile ( path . join ( tempDir , 'test.ts' ) , 'export const x = 1;' ) ;
3238 // Wait for chokidar to pick up the event (including awaitWriteFinish stabilityThreshold)
3339 // + debounce window + OS scheduling slack
@@ -39,25 +45,31 @@ describe('FileWatcher', () => {
3945 } , 8000 ) ;
4046
4147 it ( 'debounces rapid changes into a single callback' , async ( ) => {
42- const debounceMs = 300 ;
48+ const debounceMs = 800 ;
4349 let callCount = 0 ;
4450
51+ let resolveReady ! : ( ) => void ;
52+ const ready = new Promise < void > ( ( resolve ) => {
53+ resolveReady = resolve ;
54+ } ) ;
55+
4556 const stop = startFileWatcher ( {
4657 rootPath : tempDir ,
4758 debounceMs,
59+ onReady : ( ) => resolveReady ( ) ,
4860 onChanged : ( ) => { callCount ++ ; } ,
4961 } ) ;
5062
5163 try {
5264 // Give chokidar a moment to finish initializing before the first write
53- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
65+ await ready ;
5466 // Write 5 files in quick succession — all within the debounce window
5567 for ( let i = 0 ; i < 5 ; i ++ ) {
5668 await fs . writeFile ( path . join ( tempDir , `file${ i } .ts` ) , `export const x${ i } = ${ i } ;` ) ;
57- await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
69+ await new Promise ( ( resolve ) => setTimeout ( resolve , 20 ) ) ;
5870 }
5971 // Wait for debounce to settle
60- await new Promise ( ( resolve ) => setTimeout ( resolve , debounceMs + 400 ) ) ;
72+ await new Promise ( ( resolve ) => setTimeout ( resolve , debounceMs + 1200 ) ) ;
6173 expect ( callCount ) . toBe ( 1 ) ;
6274 } finally {
6375 stop ( ) ;
@@ -68,14 +80,20 @@ describe('FileWatcher', () => {
6880 const debounceMs = 500 ;
6981 let callCount = 0 ;
7082
83+ let resolveReady ! : ( ) => void ;
84+ const ready = new Promise < void > ( ( resolve ) => {
85+ resolveReady = resolve ;
86+ } ) ;
87+
7188 const stop = startFileWatcher ( {
7289 rootPath : tempDir ,
7390 debounceMs,
91+ onReady : ( ) => resolveReady ( ) ,
7492 onChanged : ( ) => { callCount ++ ; } ,
7593 } ) ;
7694
7795 // Give chokidar a moment to finish initializing before the first write
78- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
96+ await ready ;
7997 await fs . writeFile ( path . join ( tempDir , 'cancel.ts' ) , 'export const y = 99;' ) ;
8098 // Let chokidar detect the event (including awaitWriteFinish stabilityThreshold)
8199 // but stop before the debounce window expires.
@@ -90,16 +108,22 @@ describe('FileWatcher', () => {
90108 const debounceMs = 250 ;
91109 let callCount = 0 ;
92110
111+ let resolveReady ! : ( ) => void ;
112+ const ready = new Promise < void > ( ( resolve ) => {
113+ resolveReady = resolve ;
114+ } ) ;
115+
93116 const stop = startFileWatcher ( {
94117 rootPath : tempDir ,
95118 debounceMs,
119+ onReady : ( ) => resolveReady ( ) ,
96120 onChanged : ( ) => {
97121 callCount ++ ;
98122 }
99123 } ) ;
100124
101125 try {
102- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
126+ await ready ;
103127 await fs . writeFile ( path . join ( tempDir , 'notes.txt' ) , 'this should be ignored' ) ;
104128 await new Promise ( ( resolve ) => setTimeout ( resolve , debounceMs + 700 ) ) ;
105129 expect ( callCount ) . toBe ( 0 ) ;
@@ -112,16 +136,22 @@ describe('FileWatcher', () => {
112136 const debounceMs = 250 ;
113137 let callCount = 0 ;
114138
139+ let resolveReady ! : ( ) => void ;
140+ const ready = new Promise < void > ( ( resolve ) => {
141+ resolveReady = resolve ;
142+ } ) ;
143+
115144 const stop = startFileWatcher ( {
116145 rootPath : tempDir ,
117146 debounceMs,
147+ onReady : ( ) => resolveReady ( ) ,
118148 onChanged : ( ) => {
119149 callCount ++ ;
120150 }
121151 } ) ;
122152
123153 try {
124- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
154+ await ready ;
125155 await fs . writeFile ( path . join ( tempDir , '.gitignore' ) , 'dist/\n' ) ;
126156 await new Promise ( ( resolve ) => setTimeout ( resolve , debounceMs + 700 ) ) ;
127157 expect ( callCount ) . toBe ( 1 ) ;
0 commit comments