Merge pull request 'fix(canvas/test): ApprovalBanner mockReset to prevent queue stacking' (#467) from fix/approvalbanner-mockreset-452 into main
Some checks failed
CI / Platform (Go) (push) Blocked by required conditions
CI / Canvas Deploy Reminder (push) Blocked by required conditions
Block internal-flavored paths / Block forbidden paths (push) Successful in 9s
Harness Replays / detect-changes (push) Successful in 16s
publish-workspace-server-image / build-and-push (push) Failing after 15s
E2E API Smoke Test / detect-changes (push) Successful in 35s
Handlers Postgres Integration / detect-changes (push) Successful in 43s
CI / Detect changes (push) Successful in 48s
E2E Staging Canvas (Playwright) / detect-changes (push) Successful in 47s
Secret scan / Scan diff for credential-shaped strings (push) Successful in 17s
Runtime PR-Built Compatibility / detect-changes (push) Successful in 37s
Harness Replays / Harness Replays (push) Successful in 8s
E2E API Smoke Test / E2E API Smoke Test (push) Successful in 8s
publish-canvas-image / Build & push canvas image (push) Failing after 1m20s
Sweep stale e2e-* orgs (staging) / Sweep e2e orgs (push) Successful in 19s
CI / Shellcheck (E2E scripts) (push) Successful in 5s
CI / Python Lint & Test (push) Successful in 6s
Handlers Postgres Integration / Handlers Postgres Integration (push) Successful in 6s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (push) Successful in 7s
CI / Canvas (Next.js) (push) Has been cancelled
E2E Staging Canvas (Playwright) / Canvas tabs E2E (push) Has been cancelled
Staging SaaS smoke (every 30 min) / Staging SaaS smoke (push) Failing after 4m52s
main-red-watchdog / watchdog (push) Successful in 56s
Continuous synthetic E2E (staging) / Synthetic E2E against staging (push) Failing after 5m0s

This commit is contained in:
Molecule AI · core-be 2026-05-11 11:58:53 +00:00
commit 71cfb70a6f

View File

@ -41,9 +41,10 @@ const pendingApproval = (id = "a1", workspaceId = "ws-1"): {
created_at: "2026-05-10T10:00:00Z",
});
// Shared spy reference so individual tests can call mockGet.mockRestore()
// without needing to pass it through beforeEach → it scope chain.
// Shared spy references so individual tests can reset or reject the POST mock
// without needing to call spyOn again (which would create a duplicate spy).
let mockGet: ReturnType<typeof vi.spyOn>;
let mockPost: ReturnType<typeof vi.spyOn>;
// ─── Tests ────────────────────────────────────────────────────────────────────
@ -139,8 +140,8 @@ describe("ApprovalBanner — renders approval cards", () => {
describe("ApprovalBanner — decisions", () => {
beforeEach(() => {
vi.useFakeTimers();
vi.spyOn(api, "get").mockResolvedValueOnce([pendingApproval("a1")]);
vi.spyOn(api, "post").mockResolvedValue({});
mockGet = vi.spyOn(api, "get").mockResolvedValueOnce([pendingApproval("a1")]);
mockPost = vi.spyOn(api, "post").mockResolvedValue({});
});
afterEach(() => {
@ -196,7 +197,7 @@ describe("ApprovalBanner — decisions", () => {
});
it("shows an error toast when POST fails", async () => {
vi.mocked(api.post).mockRejectedValueOnce(new Error("Network error"));
mockPost.mockReset().mockRejectedValue(new Error("Network error"));
render(<ApprovalBanner />);
await act(async () => { await vi.runOnlyPendingTimersAsync(); });
fireEvent.click(screen.getAllByRole("button", { name: /approve/i })[0]);
@ -208,8 +209,9 @@ describe("ApprovalBanner — decisions", () => {
});
it("keeps the card visible when the POST fails", async () => {
// Use mockRejectedValueOnce on the same spy as beforeEach (don't call spyOn again)
vi.mocked(api.post).mockRejectedValueOnce(new Error("Network error"));
// Reset the post mock before rejecting so the beforeEach's resolved value
// is gone and we get a clean rejection instead of a resolved→rejected queue.
mockPost.mockReset().mockRejectedValue(new Error("Network error"));
render(<ApprovalBanner />);
await act(async () => { await vi.runOnlyPendingTimersAsync(); });
fireEvent.click(screen.getAllByRole("button", { name: /approve/i })[0]);