@@ -30,23 +30,34 @@ test('Single file change', async () => {
3030
3131 return new Promise ( ( resolve , reject ) => {
3232 const timeout = setTimeout ( ( ) => {
33- watcher . unwatch ( targetPath ) ;
33+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
34+ watcher . watchers . clear ( ) ;
35+ watcher . removeAllListeners ( ) ;
36+ cleanup ( targetPath ) ;
3437 reject ( new Error ( 'Test timeout' ) ) ;
3538 } , TEST_TIMEOUT ) ;
3639
3740 watcher . on ( 'change' , ( fileName ) => {
3841 assert . strictEqual ( fileName . endsWith ( 'file.ext' ) , true ) ;
3942 clearTimeout ( timeout ) ;
40- watcher . unwatch ( targetPath ) ;
41- cleanup ( targetPath ) ;
43+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
44+ watcher . watchers . clear ( ) ;
4245 watcher . removeAllListeners ( ) ;
46+ cleanup ( targetPath ) ;
4347 resolve ( ) ;
4448 } ) ;
4549
4650 setTimeout ( ( ) => {
4751 const filePath = path . join ( targetPath , 'file.ext' ) ;
4852 fs . writeFile ( filePath , 'example' , 'utf8' , ( err ) => {
49- if ( err ) reject ( err ) ;
53+ if ( err ) {
54+ clearTimeout ( timeout ) ;
55+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
56+ watcher . watchers . clear ( ) ;
57+ watcher . removeAllListeners ( ) ;
58+ cleanup ( targetPath ) ;
59+ reject ( err ) ;
60+ }
5061 } ) ;
5162 } , WRITE_TIMEOUT ) ;
5263 } ) ;
@@ -63,7 +74,10 @@ test('Aggregated change', async () => {
6374
6475 return new Promise ( ( resolve , reject ) => {
6576 const timeout = setTimeout ( ( ) => {
66- watcher . unwatch ( targetPath ) ;
77+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
78+ watcher . watchers . clear ( ) ;
79+ watcher . removeAllListeners ( ) ;
80+ cleanup ( targetPath ) ;
6781 reject ( new Error ( 'Test timeout' ) ) ;
6882 } , TEST_TIMEOUT ) ;
6983
@@ -82,19 +96,210 @@ test('Aggregated change', async () => {
8296 assert . strictEqual ( changeCount , 3 ) ;
8397 assert . strictEqual ( changes . length , 3 ) ;
8498 clearTimeout ( timeout ) ;
85- watcher . unwatch ( targetPath ) ;
86- cleanup ( targetPath ) ;
99+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
100+ watcher . watchers . clear ( ) ;
87101 watcher . removeAllListeners ( ) ;
102+ cleanup ( targetPath ) ;
88103 resolve ( ) ;
89104 } ) ;
90105
91106 setTimeout ( ( ) => {
92107 for ( const name of files ) {
93108 const filePath = path . join ( targetPath , name ) ;
94109 fs . writeFile ( filePath , 'example' , 'utf8' , ( err ) => {
95- if ( err ) reject ( err ) ;
110+ if ( err ) {
111+ clearTimeout ( timeout ) ;
112+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
113+ watcher . watchers . clear ( ) ;
114+ watcher . removeAllListeners ( ) ;
115+ cleanup ( targetPath ) ;
116+ reject ( err ) ;
117+ }
96118 } ) ;
97119 }
98120 } , WRITE_TIMEOUT ) ;
99121 } ) ;
100122} ) ;
123+
124+ test ( 'File deletion detection' , async ( ) => {
125+ const targetPath = path . join ( dir , 'test/example3' ) ;
126+ fs . mkdirSync ( targetPath ) ;
127+ const filePath = path . join ( targetPath , 'delete-me.txt' ) ;
128+
129+ // Create file first
130+ fs . writeFileSync ( filePath , 'content' ) ;
131+
132+ const watcher = new metawatch . DirectoryWatcher ( OPTIONS ) ;
133+ watcher . watch ( targetPath ) ;
134+
135+ return new Promise ( ( resolve , reject ) => {
136+ const timeout = setTimeout ( ( ) => {
137+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
138+ watcher . watchers . clear ( ) ;
139+ watcher . removeAllListeners ( ) ;
140+ cleanup ( targetPath ) ;
141+ reject ( new Error ( 'Test timeout' ) ) ;
142+ } , TEST_TIMEOUT ) ;
143+
144+ watcher . on ( 'delete' , ( fileName ) => {
145+ assert . strictEqual ( fileName , filePath ) ;
146+ clearTimeout ( timeout ) ;
147+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
148+ watcher . watchers . clear ( ) ;
149+ watcher . removeAllListeners ( ) ;
150+ cleanup ( targetPath ) ;
151+ resolve ( ) ;
152+ } ) ;
153+
154+ setTimeout ( ( ) => {
155+ fs . unlinkSync ( filePath ) ;
156+ } , WRITE_TIMEOUT ) ;
157+ } ) ;
158+ } ) ;
159+
160+ test ( 'Nested directory watching' , async ( ) => {
161+ const targetPath = path . join ( dir , 'test/example4' ) ;
162+ const nestedPath = path . join ( targetPath , 'nested' ) ;
163+ fs . mkdirSync ( targetPath ) ;
164+ fs . mkdirSync ( nestedPath ) ;
165+
166+ const watcher = new metawatch . DirectoryWatcher ( OPTIONS ) ;
167+ watcher . watch ( targetPath ) ;
168+
169+ return new Promise ( ( resolve , reject ) => {
170+ const timeout = setTimeout ( ( ) => {
171+ // Clean up all watchers
172+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
173+ watcher . watchers . clear ( ) ;
174+ watcher . removeAllListeners ( ) ;
175+ cleanup ( targetPath ) ;
176+ reject ( new Error ( 'Test timeout' ) ) ;
177+ } , TEST_TIMEOUT ) ;
178+
179+ watcher . on ( 'change' , ( fileName ) => {
180+ assert . strictEqual ( fileName . endsWith ( 'nested-file.txt' ) , true ) ;
181+ clearTimeout ( timeout ) ;
182+ // Clean up all watchers
183+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
184+ watcher . watchers . clear ( ) ;
185+ watcher . removeAllListeners ( ) ;
186+ cleanup ( targetPath ) ;
187+ resolve ( ) ;
188+ } ) ;
189+
190+ setTimeout ( ( ) => {
191+ const filePath = path . join ( nestedPath , 'nested-file.txt' ) ;
192+ fs . writeFile ( filePath , 'content' , 'utf8' , ( err ) => {
193+ if ( err ) {
194+ clearTimeout ( timeout ) ;
195+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
196+ watcher . watchers . clear ( ) ;
197+ watcher . removeAllListeners ( ) ;
198+ cleanup ( targetPath ) ;
199+ reject ( err ) ;
200+ }
201+ } ) ;
202+ } , WRITE_TIMEOUT ) ;
203+ } ) ;
204+ } ) ;
205+
206+ test ( 'Constructor with default options' , ( ) => {
207+ const watcher = new metawatch . DirectoryWatcher ( ) ;
208+ assert . strictEqual ( watcher . timeout , 5000 ) ;
209+ assert . strictEqual ( watcher . timer , null ) ;
210+ assert . strictEqual ( watcher . watchers . size , 0 ) ;
211+ assert . strictEqual ( watcher . queue . size , 0 ) ;
212+ } ) ;
213+
214+ test ( 'Constructor with custom timeout' , ( ) => {
215+ const customTimeout = 1000 ;
216+ const watcher = new metawatch . DirectoryWatcher ( { timeout : customTimeout } ) ;
217+ assert . strictEqual ( watcher . timeout , customTimeout ) ;
218+ } ) ;
219+
220+ test ( 'Constructor with zero timeout' , ( ) => {
221+ const watcher = new metawatch . DirectoryWatcher ( { timeout : 0 } ) ;
222+ assert . strictEqual ( watcher . timeout , 0 ) ;
223+ } ) ;
224+
225+ test ( 'Unwatch non-existent directory' , ( ) => {
226+ const watcher = new metawatch . DirectoryWatcher ( ) ;
227+ // Should not throw error
228+ watcher . unwatch ( '/non/existent/path' ) ;
229+ assert . strictEqual ( watcher . watchers . size , 0 ) ;
230+ } ) ;
231+
232+ test ( 'Watch same directory twice' , async ( ) => {
233+ const targetPath = path . join ( dir , 'test/example5' ) ;
234+
235+ // Clean up if exists
236+ if ( fs . existsSync ( targetPath ) ) {
237+ fs . rmSync ( targetPath , { recursive : true , force : true } ) ;
238+ }
239+
240+ fs . mkdirSync ( targetPath ) ;
241+
242+ const watcher = new metawatch . DirectoryWatcher ( OPTIONS ) ;
243+
244+ try {
245+ // Watch first time
246+ watcher . watch ( targetPath ) ;
247+
248+ // Wait a bit for the watcher to be created
249+ await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
250+
251+ const firstWatcher = watcher . watchers . get ( targetPath ) ;
252+ assert . ok ( firstWatcher ) ;
253+
254+ // Watch second time - should not create new watcher
255+ watcher . watch ( targetPath ) ;
256+ const secondWatcher = watcher . watchers . get ( targetPath ) ;
257+ assert . strictEqual ( firstWatcher , secondWatcher ) ;
258+ } finally {
259+ // Always clean up watchers
260+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
261+ watcher . watchers . clear ( ) ;
262+ watcher . removeAllListeners ( ) ;
263+ cleanup ( targetPath ) ;
264+ }
265+ } ) ;
266+
267+ test ( 'Immediate timeout (timeout: 0)' , async ( ) => {
268+ const targetPath = path . join ( dir , 'test/example6' ) ;
269+ fs . mkdirSync ( targetPath ) ;
270+
271+ const watcher = new metawatch . DirectoryWatcher ( { timeout : 0 } ) ;
272+ watcher . watch ( targetPath ) ;
273+
274+ return new Promise ( ( resolve , reject ) => {
275+ const timeout = setTimeout ( ( ) => {
276+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
277+ watcher . watchers . clear ( ) ;
278+ watcher . removeAllListeners ( ) ;
279+ cleanup ( targetPath ) ;
280+ reject ( new Error ( 'Test timeout' ) ) ;
281+ } , TEST_TIMEOUT ) ;
282+
283+ let eventCount = 0 ;
284+
285+ watcher . on ( 'change' , ( ) => {
286+ eventCount ++ ;
287+ if ( eventCount === 2 ) {
288+ clearTimeout ( timeout ) ;
289+ watcher . watchers . forEach ( ( w ) => w . close ( ) ) ;
290+ watcher . watchers . clear ( ) ;
291+ watcher . removeAllListeners ( ) ;
292+ cleanup ( targetPath ) ;
293+ resolve ( ) ;
294+ }
295+ } ) ;
296+
297+ // Create two files quickly
298+ setTimeout ( ( ) => {
299+ const file1 = path . join ( targetPath , 'file1.txt' ) ;
300+ const file2 = path . join ( targetPath , 'file2.txt' ) ;
301+ fs . writeFileSync ( file1 , 'content1' ) ;
302+ fs . writeFileSync ( file2 , 'content2' ) ;
303+ } , WRITE_TIMEOUT ) ;
304+ } ) ;
305+ } ) ;
0 commit comments