authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-05 12:33:27-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-05 12:33:27-07:00
logf8b4c464b4760d73e0ece7e68011c738803d8b69
treef602b7de646088943fe02db386ed4ab1f2b39fe5
parentd92dca74eb7b7c6068619752f5b9a55c0e36f352
signature Signed by PGP key B5690EEEBB952194

test(web): cancel pending highlight fixture frames during cleanup (#10188)


1 files changed, 41 insertions(+), 6 deletions(-)

apps/web/src/components/files/fileEditorHighlight.test.ts+41-6
......@@ -64,6 +64,7 @@ let renderer: FileRenderer;
6464let tokenizer: Tokenizer;
6565let file: FileContents;
6666let document: TextDocument<unknown>;
67const animationFrames = new Set<ReturnType<typeof setImmediate>>();
6768
6869function nextResponse(): Promise<HeldResponse> {
6970 const response = responses.shift();
......@@ -147,10 +148,18 @@ beforeEach(async () => {
147148 responses = [];
148149 responseWaiters = [];
149150 terminationPromises = [];
150 vi.stubGlobal("requestAnimationFrame", (callback: FrameRequestCallback) =>
151 setImmediate(() => callback(0)),
152 );
153 vi.stubGlobal("cancelAnimationFrame", clearImmediate);
151 vi.stubGlobal("requestAnimationFrame", (callback: FrameRequestCallback) => {
152 const frame = setImmediate(() => {
153 animationFrames.delete(frame);
154 callback(0);
155 });
156 animationFrames.add(frame);
157 return frame;
158 });
159 vi.stubGlobal("cancelAnimationFrame", (frame: ReturnType<typeof setImmediate>) => {
160 animationFrames.delete(frame);
161 clearImmediate(frame);
162 });
154163 vi.stubGlobal("window", { matchMedia: () => ({ matches: true }) });
155164 pool = new WorkerPoolManager(
156165 // Adapt browser transport only; Pierre's real worker produces each response.
......@@ -176,15 +185,41 @@ beforeEach(async () => {
176185 renderContents();
177186});
178187
179afterEach(async () => {
188async function cleanUpFixture() {
180189 tokenizer?.cleanUp();
181190 renderer?.cleanUp();
182191 pool?.terminate();
183192 await Promise.all(terminationPromises);
193 // Pool termination can queue a final broadcast after its workers have exited.
194 for (const frame of animationFrames) clearImmediate(frame);
195 animationFrames.clear();
184196 vi.unstubAllGlobals();
185});
197}
198
199afterEach(cleanUpFixture);
186200
187201describe("editable file highlighting", () => {
202 it("cleans up an already terminated worker pool", async () => {
203 (await nextResponse()).deliver();
204 expect(pool.getStats().totalWorkers).toBe(1);
205 pool.terminate();
206 await Promise.all(terminationPromises);
207 expect(pool.getStats().totalWorkers).toBe(0);
208
209 const animationFrame = globalThis.requestAnimationFrame;
210 const cancelFrame = globalThis.cancelAnimationFrame;
211 const window = globalThis.window;
212 try {
213 await cleanUpFixture();
214 // Deliver the real Immediate queue after cleanup has removed the browser globals.
215 await new Promise<void>((resolve) => setImmediate(resolve));
216 } finally {
217 vi.stubGlobal("requestAnimationFrame", animationFrame);
218 vi.stubGlobal("cancelAnimationFrame", cancelFrame);
219 vi.stubGlobal("window", window);
220 }
221 });
222
188223 it("still accepts an asynchronous highlight when the file has not changed", async () => {
189224 expect(renderContents()).not.toContain('style="color:');
190225 (await nextResponse()).deliver();