Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions test/helpers/watcher-flow.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* The handshake a test of the native watcher makes before it writes what it
* means to hear: changesFlow, and the probe files it writes (isProbe).
* The handshake a watcher test makes, on either of chokidar's backends, before
* it writes what it means to hear: changesFlow, and the probe files it writes
* (isProbe).
*/
import assert from 'node:assert/strict';
import { writeFileSync } from 'node:fs';
Expand Down Expand Up @@ -29,10 +30,12 @@ export const isProbe = (path: string): boolean => basename(path).startsWith(PROB
* one is added. So a file written in between isn't reported until a later
* change in its directory, when chokidar reads the directory again, and a probe
* reported in one directory can come from a stream made before another
* directory's watch. Each attempt writes a new probe in each directory still
* unreported and waits up to a second, since the next probe is that later
* change. The probes stay in the batches, and a test's results leave them out
* (isProbe).
* directory's watch. chokidar's polling backend starts a directory's poll just
* before `onReady` too, and the poll's first stat is what each later one is
* compared with, so a file written before that stat is never reported. Each
* attempt writes a new probe in each directory still unreported and waits up
* to a second, since the next probe is that later change. The probes stay in
* the batches, and a test's results leave them out (isProbe).
*/
export async function changesFlow(root: string, batches: FsChange[][], dirs: readonly string[]): Promise<void> {
const where = dirs.length ? dirs : [''];
Expand Down
12 changes: 9 additions & 3 deletions test/workspace-watcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,9 @@ async function waitFor(predicate: () => boolean, message: string, timeoutMs = EV
}

/**
* A polling watcher, once chokidar's first scan is done. A change made before
* then can be part of the files chokidar starts from (ignoreInitial), and is
* A polling watcher, once chokidar's first scan is done and its poll reports
* changes (changesFlow). A change made before then can be part of the files
* chokidar starts from (ignoreInitial), or of the poll's first stat, and is
* then never reported. If the watcher has reported an error and isn't ready
* when the wait checks, the wait fails with that error, and a watcher that
* doesn't get ready is stopped.
Expand All @@ -144,6 +145,7 @@ async function readyWithPolling(): Promise<{ watcher: MountWatcher; batches: FsC
try {
await waitFor(() => ready || failed !== undefined, 'expected the watcher to be ready');
if (!ready) throw failed;
await changesFlow(tmp, collected.batches, []);
} catch (error) {
await collected.watcher.stop();
throw error;
Expand Down Expand Up @@ -330,8 +332,10 @@ describe('MountWatcher lifecycle', () => {
try {
assert.ok(existsSync(nested), 'start() should have created the watched path');
// A file written before chokidar's first scan is done can be part of
// what it starts from (ignoreInitial), and then is never reported.
// what it starts from (ignoreInitial), or of the poll's first stat, just
// after onReady, and then is never reported.
await waitFor(() => ready, 'onReady should have fired');
await changesFlow(nested, batches, []);
writeFileSync(join(nested, 'ticket.md'), 'hi');
await changeFor(batches, 'ticket.md');
} finally {
Expand Down Expand Up @@ -455,6 +459,7 @@ describe('MountWatcher lifecycle', () => {

try {
await waitFor(() => ready, 'expected initial watcher ready');
await changesFlow(tmp, batches, []);

const file = join(tmp, 'normal.md');
writeFileSync(file, 'v1');
Expand Down Expand Up @@ -738,6 +743,7 @@ describe('MountWatcher lifecycle', () => {
await (watcher as unknown as { reattach(): Promise<void> }).reattach();
assert.ok(errors.some((e) => e.message === 'close failed'), "the re-attach reported its close's failure");
await waitFor(() => readyCount === 2, 'expected the watcher to attach again');
await changesFlow(tmp, batches, []);
writeFileSync(join(tmp, 'after.md'), 'x');
await changeFor(batches, 'after.md', 'created');
} finally {
Expand Down
Loading