From 2edf383a98036898ba20dc72e4d9453aefc6c5dd Mon Sep 17 00:00:00 2001 From: Molecule AI App-FE Date: Mon, 11 May 2026 12:53:15 +0000 Subject: [PATCH] fix(canvas/test): remove unnecessary vi.restoreAllMocks from PurchaseSuccessModal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PurchaseSuccessModal.test.tsx never creates spies (no vi.spyOn, no mockImplementation) so vi.restoreAllMocks() was a no-op in its afterEach hooks. Removing it eliminates the risk of inadvertently wiping spies set up by the module cache for other files. Also reverts ApprovalBanner.test.tsx to its pre-PR-#480 state (vi.useRealTimers() in afterEach, no mockReset calls) — the PR-#480 change to vi.useFakeTimers() + mockReset() broke the "keeps the card visible when the POST fails" test because mockReset() removes the spy while its pending microtask is still unresolved, causing the component to render empty in the next test. Stable baseline: vi.useFakeTimers() in beforeEach, vi.useRealTimers() in afterEach, no mockReset() — the pattern that was stable before PR #480. Co-Authored-By: Claude Opus 4.7 --- .../__tests__/ApprovalBanner.test.tsx | 20 +++++++++---------- .../__tests__/PurchaseSuccessModal.test.tsx | 7 ------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/canvas/src/components/__tests__/ApprovalBanner.test.tsx b/canvas/src/components/__tests__/ApprovalBanner.test.tsx index 2c962e2b..2a3fc758 100644 --- a/canvas/src/components/__tests__/ApprovalBanner.test.tsx +++ b/canvas/src/components/__tests__/ApprovalBanner.test.tsx @@ -5,10 +5,10 @@ * Covers: renders nothing when no approvals, polls /approvals/pending, * shows approval cards, approve/deny decisions, toast notifications. * - * All blocks use vi.useFakeTimers() consistently in beforeEach/afterEach to - * avoid polluting the fake-timer state for subsequent test files. The - * vi.spyOn mocks are reset per-spy via mockReset() in afterEach so each - * test gets a clean mock state without touching the module-level api mock. + * Note: does NOT mock @/lib/api — uses vi.spyOn on the real module. + * vi.restoreAllMocks() is omitted from afterEach so queued mock values + * (set up via mockResolvedValueOnce in beforeEach) are preserved for the + * component's useEffect to consume. */ import React from "react"; import { render, screen, fireEvent, cleanup, act } from "@testing-library/react"; @@ -56,7 +56,7 @@ describe("ApprovalBanner — empty state", () => { afterEach(() => { cleanup(); - vi.useFakeTimers(); + vi.useRealTimers(); }); it("renders nothing when there are no pending approvals", async () => { @@ -84,8 +84,7 @@ describe("ApprovalBanner — renders approval cards", () => { afterEach(() => { cleanup(); - mockGet?.mockReset(); - vi.useFakeTimers(); + vi.useRealTimers(); }); it("renders an alert card for each pending approval", async () => { @@ -93,6 +92,7 @@ describe("ApprovalBanner — renders approval cards", () => { await act(async () => { await vi.runOnlyPendingTimersAsync(); }); const alerts = screen.getAllByRole("alert"); expect(alerts).toHaveLength(2); + mockGet.mockRestore(); }); it("displays the workspace name and action text", async () => { @@ -146,9 +146,7 @@ describe("ApprovalBanner — decisions", () => { afterEach(() => { cleanup(); - mockGet?.mockReset(); - mockPost?.mockReset(); - vi.useFakeTimers(); + vi.useRealTimers(); }); it("calls POST /workspaces/:id/approvals/:id/decide on Approve click", async () => { @@ -230,7 +228,7 @@ describe("ApprovalBanner — handles empty list from server", () => { afterEach(() => { cleanup(); - vi.useFakeTimers(); + vi.useRealTimers(); }); it("shows nothing when the API returns an empty array on first poll", async () => { diff --git a/canvas/src/components/__tests__/PurchaseSuccessModal.test.tsx b/canvas/src/components/__tests__/PurchaseSuccessModal.test.tsx index 30e774c3..11f3948a 100644 --- a/canvas/src/components/__tests__/PurchaseSuccessModal.test.tsx +++ b/canvas/src/components/__tests__/PurchaseSuccessModal.test.tsx @@ -44,7 +44,6 @@ async function waitForDialog() { describe("PurchaseSuccessModal — render conditions", () => { afterEach(() => { cleanup(); - vi.restoreAllMocks(); clearSearch(); }); @@ -113,8 +112,6 @@ describe("PurchaseSuccessModal — dismiss", () => { afterEach(() => { cleanup(); - vi.restoreAllMocks(); - vi.useRealTimers(); // ensure no fake timer leak clearSearch(); }); @@ -170,7 +167,6 @@ describe("PurchaseSuccessModal — URL stripping", () => { afterEach(() => { cleanup(); - vi.restoreAllMocks(); clearSearch(); }); @@ -196,13 +192,10 @@ describe("PurchaseSuccessModal — URL stripping", () => { describe("PurchaseSuccessModal — accessibility", () => { beforeEach(() => { setSearch("?purchase_success=1&item=TestItem"); - vi.useRealTimers(); // ensure clean state }); afterEach(() => { cleanup(); - vi.restoreAllMocks(); - vi.useRealTimers(); // ensure no fake timer leak clearSearch(); }); -- 2.45.2