-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathfile-watcher.test.ts
More file actions
162 lines (139 loc) · 4.53 KB
/
file-watcher.test.ts
File metadata and controls
162 lines (139 loc) · 4.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { startFileWatcher } from '../src/core/file-watcher.js';
import { rmWithRetries } from './test-helpers.js';
describe('FileWatcher', () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'file-watcher-test-'));
});
afterEach(async () => {
await rmWithRetries(tempDir);
});
it('triggers onChanged after debounce window', async () => {
const debounceMs = 400;
let callCount = 0;
let resolveReady!: () => void;
const ready = new Promise<void>((resolve) => {
resolveReady = resolve;
});
const stop = startFileWatcher({
rootPath: tempDir,
debounceMs,
onReady: () => resolveReady(),
onChanged: () => { callCount++; },
});
try {
await ready;
await fs.writeFile(path.join(tempDir, 'test.ts'), 'export const x = 1;');
// Wait for chokidar to pick up the event (including awaitWriteFinish stabilityThreshold)
// + debounce window + OS scheduling slack
await new Promise((resolve) => setTimeout(resolve, debounceMs + 1000));
expect(callCount).toBe(1);
} finally {
stop();
}
}, 8000);
it('debounces rapid changes into a single callback', async () => {
const debounceMs = 800;
let callCount = 0;
let resolveReady!: () => void;
const ready = new Promise<void>((resolve) => {
resolveReady = resolve;
});
const stop = startFileWatcher({
rootPath: tempDir,
debounceMs,
onReady: () => resolveReady(),
onChanged: () => { callCount++; },
});
try {
// Give chokidar a moment to finish initializing before the first write
await ready;
// Write 5 files in quick succession — all within the debounce window
for (let i = 0; i < 5; i++) {
await fs.writeFile(path.join(tempDir, `file${i}.ts`), `export const x${i} = ${i};`);
await new Promise((resolve) => setTimeout(resolve, 20));
}
// Wait for debounce to settle
await new Promise((resolve) => setTimeout(resolve, debounceMs + 1200));
expect(callCount).toBe(1);
} finally {
stop();
}
}, 8000);
it('stop() cancels a pending callback', async () => {
const debounceMs = 500;
let callCount = 0;
let resolveReady!: () => void;
const ready = new Promise<void>((resolve) => {
resolveReady = resolve;
});
const stop = startFileWatcher({
rootPath: tempDir,
debounceMs,
onReady: () => resolveReady(),
onChanged: () => { callCount++; },
});
// Give chokidar a moment to finish initializing before the first write
await ready;
await fs.writeFile(path.join(tempDir, 'cancel.ts'), 'export const y = 99;');
// Let chokidar detect the event (including awaitWriteFinish stabilityThreshold)
// but stop before the debounce window expires.
await new Promise((resolve) => setTimeout(resolve, 350));
stop();
// Wait past where debounce would have fired
await new Promise((resolve) => setTimeout(resolve, debounceMs + 200));
expect(callCount).toBe(0);
}, 5000);
it('ignores changes to non-tracked file extensions', async () => {
const debounceMs = 250;
let callCount = 0;
let resolveReady!: () => void;
const ready = new Promise<void>((resolve) => {
resolveReady = resolve;
});
const stop = startFileWatcher({
rootPath: tempDir,
debounceMs,
onReady: () => resolveReady(),
onChanged: () => {
callCount++;
}
});
try {
await ready;
await fs.writeFile(path.join(tempDir, 'notes.txt'), 'this should be ignored');
await new Promise((resolve) => setTimeout(resolve, debounceMs + 700));
expect(callCount).toBe(0);
} finally {
stop();
}
}, 5000);
it('triggers on .gitignore changes', async () => {
const debounceMs = 250;
let callCount = 0;
let resolveReady!: () => void;
const ready = new Promise<void>((resolve) => {
resolveReady = resolve;
});
const stop = startFileWatcher({
rootPath: tempDir,
debounceMs,
onReady: () => resolveReady(),
onChanged: () => {
callCount++;
}
});
try {
await ready;
await fs.writeFile(path.join(tempDir, '.gitignore'), 'dist/\n');
await new Promise((resolve) => setTimeout(resolve, debounceMs + 700));
expect(callCount).toBe(1);
} finally {
stop();
}
}, 5000);
});