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