node/test/parallel/test-watch-mode-files_watcher.mjs
Daniel Bayley cff7da7749
src,test: further cleanup references to osx
PR-URL: https://github.com/nodejs/node/pull/53820
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
2024-07-15 20:32:26 +00:00

186 lines
6.5 KiB
JavaScript

// Flags: --expose-internals
import * as common from '../common/index.mjs';
import * as fixtures from '../common/fixtures.mjs';
import tmpdir from '../common/tmpdir.js';
import path from 'node:path';
import assert from 'node:assert';
import process from 'node:process';
import { describe, it, beforeEach, afterEach } from 'node:test';
import { writeFileSync, mkdirSync } from 'node:fs';
import { setTimeout } from 'node:timers/promises';
import { once } from 'node:events';
import { spawn } from 'node:child_process';
import watcher from 'internal/watch_mode/files_watcher';
if (common.isIBMi)
common.skip('IBMi does not support `fs.watch()`');
const supportsRecursiveWatching = common.isMacOS || common.isWindows;
const { FilesWatcher } = watcher;
tmpdir.refresh();
describe('watch mode file watcher', () => {
let watcher;
let changesCount;
beforeEach(() => {
changesCount = 0;
watcher = new FilesWatcher({ debounce: 100 });
watcher.on('changed', () => changesCount++);
});
afterEach(() => watcher.clear());
let counter = 0;
function writeAndWaitForChanges(watcher, file) {
return new Promise((resolve) => {
const interval = setInterval(() => writeFileSync(file, `write ${counter++}`), 100);
watcher.once('changed', () => {
clearInterval(interval);
resolve();
});
});
}
it('should watch changed files', async () => {
const file = tmpdir.resolve('file1');
writeFileSync(file, 'written');
watcher.filterFile(file);
await writeAndWaitForChanges(watcher, file);
assert.strictEqual(changesCount, 1);
});
it('should debounce changes', async () => {
const file = tmpdir.resolve('file2');
writeFileSync(file, 'written');
watcher.filterFile(file);
await writeAndWaitForChanges(watcher, file);
writeFileSync(file, '1');
writeFileSync(file, '2');
writeFileSync(file, '3');
writeFileSync(file, '4');
await setTimeout(200); // debounce * 2
writeFileSync(file, '5');
const changed = once(watcher, 'changed');
writeFileSync(file, 'after');
await changed;
// Unfortunately testing that changesCount === 2 is flaky
assert.ok(changesCount < 5);
});
it('should debounce changes on multiple files', async () => {
const files = [];
for (let i = 0; i < 10; i++) {
const file = tmpdir.resolve(`file-debounced-${i}`);
writeFileSync(file, 'written');
watcher.filterFile(file);
files.push(file);
}
files.forEach((file) => writeFileSync(file, '1'));
files.forEach((file) => writeFileSync(file, '2'));
files.forEach((file) => writeFileSync(file, '3'));
files.forEach((file) => writeFileSync(file, '4'));
await setTimeout(200); // debounce * 2
files.forEach((file) => writeFileSync(file, '5'));
const changed = once(watcher, 'changed');
files.forEach((file) => writeFileSync(file, 'after'));
await changed;
// Unfortunately testing that changesCount === 2 is flaky
assert.ok(changesCount < 5);
});
it('should ignore files in watched directory if they are not filtered',
{ skip: !supportsRecursiveWatching }, async () => {
watcher.on('changed', common.mustNotCall());
watcher.watchPath(tmpdir.path);
writeFileSync(tmpdir.resolve('file3'), '1');
// Wait for this long to make sure changes are not triggered
await setTimeout(1000);
});
it('should allow clearing filters', async () => {
const file = tmpdir.resolve('file4');
writeFileSync(file, 'written');
watcher.filterFile(file);
await writeAndWaitForChanges(watcher, file);
writeFileSync(file, '1');
assert.strictEqual(changesCount, 1);
watcher.clearFileFilters();
writeFileSync(file, '2');
// Wait for this long to make sure changes are triggered only once
await setTimeout(1000);
assert.strictEqual(changesCount, 1);
});
it('should watch all files in watched path when in "all" mode',
{ skip: !supportsRecursiveWatching }, async () => {
watcher = new FilesWatcher({ debounce: 100, mode: 'all' });
watcher.on('changed', () => changesCount++);
const file = tmpdir.resolve('file5');
watcher.watchPath(tmpdir.path);
const changed = once(watcher, 'changed');
await setTimeout(common.platformTimeout(100)); // avoid throttling
writeFileSync(file, 'changed');
await changed;
assert.strictEqual(changesCount, 1);
});
it('should ruse existing watcher if it exists',
{ skip: !supportsRecursiveWatching }, () => {
assert.deepStrictEqual(watcher.watchedPaths, []);
watcher.watchPath(tmpdir.path);
assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]);
watcher.watchPath(tmpdir.path);
assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]);
});
it('should ruse existing watcher of a parent directory',
{ skip: !supportsRecursiveWatching }, () => {
assert.deepStrictEqual(watcher.watchedPaths, []);
watcher.watchPath(tmpdir.path);
assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]);
watcher.watchPath(tmpdir.resolve('subdirectory'));
assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]);
});
it('should remove existing watcher if adding a parent directory watcher',
{ skip: !supportsRecursiveWatching }, () => {
assert.deepStrictEqual(watcher.watchedPaths, []);
const subdirectory = tmpdir.resolve('subdirectory');
mkdirSync(subdirectory);
watcher.watchPath(subdirectory);
assert.deepStrictEqual(watcher.watchedPaths, [subdirectory]);
watcher.watchPath(tmpdir.path);
assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]);
});
it('should clear all watchers when calling clear',
{ skip: !supportsRecursiveWatching }, () => {
assert.deepStrictEqual(watcher.watchedPaths, []);
watcher.watchPath(tmpdir.path);
assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]);
watcher.clear();
assert.deepStrictEqual(watcher.watchedPaths, []);
});
it('should watch files from subprocess IPC events', async () => {
const file = fixtures.path('watch-mode/ipc.js');
const child = spawn(process.execPath, [file], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'], encoding: 'utf8' });
watcher.watchChildProcessModules(child);
await once(child, 'exit');
let expected = [file, tmpdir.resolve('file')];
if (supportsRecursiveWatching) {
expected = expected.map((file) => path.dirname(file));
}
assert.deepStrictEqual(watcher.watchedPaths, expected);
});
});