11cb0ef41Sopenharmony_ci// Flags: --expose-internals 21cb0ef41Sopenharmony_ciimport * as common from '../common/index.mjs'; 31cb0ef41Sopenharmony_ciimport * as fixtures from '../common/fixtures.mjs'; 41cb0ef41Sopenharmony_ciimport tmpdir from '../common/tmpdir.js'; 51cb0ef41Sopenharmony_ciimport path from 'node:path'; 61cb0ef41Sopenharmony_ciimport assert from 'node:assert'; 71cb0ef41Sopenharmony_ciimport process from 'node:process'; 81cb0ef41Sopenharmony_ciimport { describe, it, beforeEach, afterEach } from 'node:test'; 91cb0ef41Sopenharmony_ciimport { writeFileSync, mkdirSync } from 'node:fs'; 101cb0ef41Sopenharmony_ciimport { setTimeout } from 'node:timers/promises'; 111cb0ef41Sopenharmony_ciimport { once } from 'node:events'; 121cb0ef41Sopenharmony_ciimport { spawn } from 'node:child_process'; 131cb0ef41Sopenharmony_ciimport watcher from 'internal/watch_mode/files_watcher'; 141cb0ef41Sopenharmony_ci 151cb0ef41Sopenharmony_ciif (common.isIBMi) 161cb0ef41Sopenharmony_ci common.skip('IBMi does not support `fs.watch()`'); 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ciconst supportsRecursiveWatching = common.isOSX || common.isWindows; 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_ciconst { FilesWatcher } = watcher; 211cb0ef41Sopenharmony_citmpdir.refresh(); 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_cidescribe('watch mode file watcher', () => { 241cb0ef41Sopenharmony_ci let watcher; 251cb0ef41Sopenharmony_ci let changesCount; 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_ci beforeEach(() => { 281cb0ef41Sopenharmony_ci changesCount = 0; 291cb0ef41Sopenharmony_ci watcher = new FilesWatcher({ debounce: 100 }); 301cb0ef41Sopenharmony_ci watcher.on('changed', () => changesCount++); 311cb0ef41Sopenharmony_ci }); 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ci afterEach(() => watcher.clear()); 341cb0ef41Sopenharmony_ci 351cb0ef41Sopenharmony_ci let counter = 0; 361cb0ef41Sopenharmony_ci function writeAndWaitForChanges(watcher, file) { 371cb0ef41Sopenharmony_ci return new Promise((resolve) => { 381cb0ef41Sopenharmony_ci const interval = setInterval(() => writeFileSync(file, `write ${counter++}`), 100); 391cb0ef41Sopenharmony_ci watcher.once('changed', () => { 401cb0ef41Sopenharmony_ci clearInterval(interval); 411cb0ef41Sopenharmony_ci resolve(); 421cb0ef41Sopenharmony_ci }); 431cb0ef41Sopenharmony_ci }); 441cb0ef41Sopenharmony_ci } 451cb0ef41Sopenharmony_ci 461cb0ef41Sopenharmony_ci it('should watch changed files', async () => { 471cb0ef41Sopenharmony_ci const file = path.join(tmpdir.path, 'file1'); 481cb0ef41Sopenharmony_ci writeFileSync(file, 'written'); 491cb0ef41Sopenharmony_ci watcher.filterFile(file); 501cb0ef41Sopenharmony_ci await writeAndWaitForChanges(watcher, file); 511cb0ef41Sopenharmony_ci assert.strictEqual(changesCount, 1); 521cb0ef41Sopenharmony_ci }); 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ci it('should debounce changes', async () => { 551cb0ef41Sopenharmony_ci const file = path.join(tmpdir.path, 'file2'); 561cb0ef41Sopenharmony_ci writeFileSync(file, 'written'); 571cb0ef41Sopenharmony_ci watcher.filterFile(file); 581cb0ef41Sopenharmony_ci await writeAndWaitForChanges(watcher, file); 591cb0ef41Sopenharmony_ci 601cb0ef41Sopenharmony_ci writeFileSync(file, '1'); 611cb0ef41Sopenharmony_ci writeFileSync(file, '2'); 621cb0ef41Sopenharmony_ci writeFileSync(file, '3'); 631cb0ef41Sopenharmony_ci writeFileSync(file, '4'); 641cb0ef41Sopenharmony_ci await setTimeout(200); // debounce * 2 651cb0ef41Sopenharmony_ci writeFileSync(file, '5'); 661cb0ef41Sopenharmony_ci const changed = once(watcher, 'changed'); 671cb0ef41Sopenharmony_ci writeFileSync(file, 'after'); 681cb0ef41Sopenharmony_ci await changed; 691cb0ef41Sopenharmony_ci // Unfortunately testing that changesCount === 2 is flaky 701cb0ef41Sopenharmony_ci assert.ok(changesCount < 5); 711cb0ef41Sopenharmony_ci }); 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci it('should ignore files in watched directory if they are not filtered', 741cb0ef41Sopenharmony_ci { skip: !supportsRecursiveWatching }, async () => { 751cb0ef41Sopenharmony_ci watcher.on('changed', common.mustNotCall()); 761cb0ef41Sopenharmony_ci watcher.watchPath(tmpdir.path); 771cb0ef41Sopenharmony_ci writeFileSync(path.join(tmpdir.path, 'file3'), '1'); 781cb0ef41Sopenharmony_ci // Wait for this long to make sure changes are not triggered 791cb0ef41Sopenharmony_ci await setTimeout(1000); 801cb0ef41Sopenharmony_ci }); 811cb0ef41Sopenharmony_ci 821cb0ef41Sopenharmony_ci it('should allow clearing filters', async () => { 831cb0ef41Sopenharmony_ci const file = path.join(tmpdir.path, 'file4'); 841cb0ef41Sopenharmony_ci writeFileSync(file, 'written'); 851cb0ef41Sopenharmony_ci watcher.filterFile(file); 861cb0ef41Sopenharmony_ci await writeAndWaitForChanges(watcher, file); 871cb0ef41Sopenharmony_ci 881cb0ef41Sopenharmony_ci writeFileSync(file, '1'); 891cb0ef41Sopenharmony_ci assert.strictEqual(changesCount, 1); 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_ci watcher.clearFileFilters(); 921cb0ef41Sopenharmony_ci writeFileSync(file, '2'); 931cb0ef41Sopenharmony_ci // Wait for this long to make sure changes are triggered only once 941cb0ef41Sopenharmony_ci await setTimeout(1000); 951cb0ef41Sopenharmony_ci assert.strictEqual(changesCount, 1); 961cb0ef41Sopenharmony_ci }); 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ci it('should watch all files in watched path when in "all" mode', 991cb0ef41Sopenharmony_ci { skip: !supportsRecursiveWatching }, async () => { 1001cb0ef41Sopenharmony_ci watcher = new FilesWatcher({ debounce: 100, mode: 'all' }); 1011cb0ef41Sopenharmony_ci watcher.on('changed', () => changesCount++); 1021cb0ef41Sopenharmony_ci 1031cb0ef41Sopenharmony_ci const file = path.join(tmpdir.path, 'file5'); 1041cb0ef41Sopenharmony_ci watcher.watchPath(tmpdir.path); 1051cb0ef41Sopenharmony_ci 1061cb0ef41Sopenharmony_ci const changed = once(watcher, 'changed'); 1071cb0ef41Sopenharmony_ci await setTimeout(common.platformTimeout(100)); // avoid throttling 1081cb0ef41Sopenharmony_ci writeFileSync(file, 'changed'); 1091cb0ef41Sopenharmony_ci await changed; 1101cb0ef41Sopenharmony_ci assert.strictEqual(changesCount, 1); 1111cb0ef41Sopenharmony_ci }); 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_ci it('should ruse existing watcher if it exists', 1141cb0ef41Sopenharmony_ci { skip: !supportsRecursiveWatching }, () => { 1151cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, []); 1161cb0ef41Sopenharmony_ci watcher.watchPath(tmpdir.path); 1171cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); 1181cb0ef41Sopenharmony_ci watcher.watchPath(tmpdir.path); 1191cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); 1201cb0ef41Sopenharmony_ci }); 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci it('should ruse existing watcher of a parent directory', 1231cb0ef41Sopenharmony_ci { skip: !supportsRecursiveWatching }, () => { 1241cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, []); 1251cb0ef41Sopenharmony_ci watcher.watchPath(tmpdir.path); 1261cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); 1271cb0ef41Sopenharmony_ci watcher.watchPath(path.join(tmpdir.path, 'subdirectory')); 1281cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); 1291cb0ef41Sopenharmony_ci }); 1301cb0ef41Sopenharmony_ci 1311cb0ef41Sopenharmony_ci it('should remove existing watcher if adding a parent directory watcher', 1321cb0ef41Sopenharmony_ci { skip: !supportsRecursiveWatching }, () => { 1331cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, []); 1341cb0ef41Sopenharmony_ci const subdirectory = path.join(tmpdir.path, 'subdirectory'); 1351cb0ef41Sopenharmony_ci mkdirSync(subdirectory); 1361cb0ef41Sopenharmony_ci watcher.watchPath(subdirectory); 1371cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, [subdirectory]); 1381cb0ef41Sopenharmony_ci watcher.watchPath(tmpdir.path); 1391cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); 1401cb0ef41Sopenharmony_ci }); 1411cb0ef41Sopenharmony_ci 1421cb0ef41Sopenharmony_ci it('should clear all watchers when calling clear', 1431cb0ef41Sopenharmony_ci { skip: !supportsRecursiveWatching }, () => { 1441cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, []); 1451cb0ef41Sopenharmony_ci watcher.watchPath(tmpdir.path); 1461cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); 1471cb0ef41Sopenharmony_ci watcher.clear(); 1481cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, []); 1491cb0ef41Sopenharmony_ci }); 1501cb0ef41Sopenharmony_ci 1511cb0ef41Sopenharmony_ci it('should watch files from subprocess IPC events', async () => { 1521cb0ef41Sopenharmony_ci const file = fixtures.path('watch-mode/ipc.js'); 1531cb0ef41Sopenharmony_ci const child = spawn(process.execPath, [file], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'], encoding: 'utf8' }); 1541cb0ef41Sopenharmony_ci watcher.watchChildProcessModules(child); 1551cb0ef41Sopenharmony_ci await once(child, 'exit'); 1561cb0ef41Sopenharmony_ci let expected = [file, path.join(tmpdir.path, 'file')]; 1571cb0ef41Sopenharmony_ci if (supportsRecursiveWatching) { 1581cb0ef41Sopenharmony_ci expected = expected.map((file) => path.dirname(file)); 1591cb0ef41Sopenharmony_ci } 1601cb0ef41Sopenharmony_ci assert.deepStrictEqual(watcher.watchedPaths, expected); 1611cb0ef41Sopenharmony_ci }); 1621cb0ef41Sopenharmony_ci}); 163