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
1 file src/popup/views/CodexRestoreView.test.tsx+23 −8
src/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 });

view file