71e94384a67d
test: de-flake CodexRestoreView PCH-01-015 test — poll until the real-WASM reconstruct dispatch drains so test async can't leak into the next test (v3.9.0)
- time
- 2026-06-10 21:08 UTC · 4 months ago
- machine
- koh2
- agent
- claude-opus-4-8
- session
- pch-01-015 test + pch-01-002 blake2s
- parent
- 83c1766c1a8c
↓ peach-browser-71e94384.tar.gzatlas at this save
files changed1 file
src/popup/views/CodexRestoreView.test.tsx+23 −8src/popup/views/CodexRestoreView.test.tsx+23 −8
@@ -91,6 +91,20 @@9191 vi.unstubAllGlobals();9292 });9393 94+ // Poll the microtask/timer queue until `cond` holds (the PDF scan and the95+ // restore both run real async crypto, so a fixed flush count is racy).96+ async function waitFor(cond: () => boolean, max = 100): Promise<void> {97+ for (let i = 0; i < max; i++) {98+ if (cond()) return;99+ await flushView();100+ }101+ throw new Error("waitFor: condition never became true");102+ }103+104+ function restoreFromCodexDispatched(): boolean {105+ return sendMessageMock.mock.calls.some(([m]) => (m as { type?: string }).type === "RESTORE_FROM_CODEX");106+ }107+94108 async function mountAndScan() {95109 const { CodexRestoreView } = await import("./CodexRestoreView");96110 const onRestoreSuccess = vi.fn();@@ -109,8 +123,8 @@109123 value: [{ type: "application/pdf", name: "codex.pdf" }],110124 });111125 fileInput.dispatchEvent(new Event("change", { bubbles: true }));112- await flushView();113- await flushView();126+ // Wait for the scan to finish and the passphrase prompt to render.127+ await waitFor(() => view.container.querySelector('input[type="password"]') !== null);114128 return view;115129 }116130 @@ -128,13 +142,13 @@128142 b.textContent?.includes("restore vault"),129143 )!;130144 restoreBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));131- await flushView();132- await flushView();145+ // reconstructAndVerify runs real WASM; wait until the dispatch lands so this146+ // test's async fully drains before the next test resets the mock.147+ await waitFor(restoreFromCodexDispatched);133148 134149 const restoreCall = sendMessageMock.mock.calls.find(135150 ([m]) => (m as { type?: string }).type === "RESTORE_FROM_CODEX",136151 );137- expect(restoreCall, "RESTORE_FROM_CODEX should have been dispatched").toBeTruthy();138152 const payload = (restoreCall![0] as { payload: { masterPassphrase: string } }).payload;139153 expect(payload.masterPassphrase).toBe(" hunter2 ");140154 });@@ -151,10 +165,11 @@151165 b.textContent?.includes("restore vault"),152166 )!;153167 restoreBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));168+ // The whitespace guard rejects synchronously (no await), but flush a few169+ // times anyway to prove no dispatch is queued.154170 await flushView();171+ await flushView();155172 156- expect(157- sendMessageMock.mock.calls.some(([m]) => (m as { type?: string }).type === "RESTORE_FROM_CODEX"),158- ).toBe(false);173+ expect(restoreFromCodexDispatched()).toBe(false);159174 });160175 });