fix(canvas/test): ApprovalBanner mockReset to prevent queue stacking
Some checks failed
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 19s
Harness Replays / detect-changes (pull_request) Successful in 20s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 18s
CI / Detect changes (pull_request) Successful in 1m19s
E2E API Smoke Test / detect-changes (pull_request) Successful in 1m18s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 1m15s
sop-tier-check / tier-check (pull_request) Successful in 18s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m14s
CI / Platform (Go) (pull_request) Successful in 7s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 5s
CI / Python Lint & Test (pull_request) Successful in 6s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 8s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 6s
audit-force-merge / audit (pull_request) Successful in 10s
Harness Replays / Harness Replays (pull_request) Failing after 1m16s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 7m56s
CI / Canvas (Next.js) (pull_request) Failing after 9m10s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
Some checks failed
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 19s
Harness Replays / detect-changes (pull_request) Successful in 20s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 18s
CI / Detect changes (pull_request) Successful in 1m19s
E2E API Smoke Test / detect-changes (pull_request) Successful in 1m18s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 1m15s
sop-tier-check / tier-check (pull_request) Successful in 18s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m14s
CI / Platform (Go) (pull_request) Successful in 7s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 5s
CI / Python Lint & Test (pull_request) Successful in 6s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 8s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 6s
audit-force-merge / audit (pull_request) Successful in 10s
Harness Replays / Harness Replays (pull_request) Failing after 1m16s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 7m56s
CI / Canvas (Next.js) (pull_request) Failing after 9m10s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
Cherry-picked from PR #452 (fix/canvas-test-and-design-fixes) which was closed without merge during the PR #443 cascade. The fix adds a mockPost reference so individual tests can reset the POST mock cleanly instead of queueing multiple resolved/rejected values. Without this, the "shows an error toast when POST fails" and "keeps the card visible when POST fails" tests queue two responses from beforeEach's mockResolvedValue({}) and the second mockRejectedValueOnce() call, causing non-deterministic test outcomes. Fixes test failures in ApprovalBanner suite.
This commit is contained in:
parent
ce06b8cd59
commit
c2d27d2b3f
@ -41,9 +41,10 @@ const pendingApproval = (id = "a1", workspaceId = "ws-1"): {
|
|||||||
created_at: "2026-05-10T10:00:00Z",
|
created_at: "2026-05-10T10:00:00Z",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Shared spy reference so individual tests can call mockGet.mockRestore()
|
// Shared spy references so individual tests can reset or reject the POST mock
|
||||||
// without needing to pass it through beforeEach → it scope chain.
|
// without needing to call spyOn again (which would create a duplicate spy).
|
||||||
let mockGet: ReturnType<typeof vi.spyOn>;
|
let mockGet: ReturnType<typeof vi.spyOn>;
|
||||||
|
let mockPost: ReturnType<typeof vi.spyOn>;
|
||||||
|
|
||||||
// ─── Tests ────────────────────────────────────────────────────────────────────
|
// ─── Tests ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@ -139,8 +140,8 @@ describe("ApprovalBanner — renders approval cards", () => {
|
|||||||
describe("ApprovalBanner — decisions", () => {
|
describe("ApprovalBanner — decisions", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
vi.spyOn(api, "get").mockResolvedValueOnce([pendingApproval("a1")]);
|
mockGet = vi.spyOn(api, "get").mockResolvedValueOnce([pendingApproval("a1")]);
|
||||||
vi.spyOn(api, "post").mockResolvedValue({});
|
mockPost = vi.spyOn(api, "post").mockResolvedValue({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -196,7 +197,7 @@ describe("ApprovalBanner — decisions", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("shows an error toast when POST fails", async () => {
|
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 />);
|
render(<ApprovalBanner />);
|
||||||
await act(async () => { await vi.runOnlyPendingTimersAsync(); });
|
await act(async () => { await vi.runOnlyPendingTimersAsync(); });
|
||||||
fireEvent.click(screen.getAllByRole("button", { name: /approve/i })[0]);
|
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 () => {
|
it("keeps the card visible when the POST fails", async () => {
|
||||||
// Use mockRejectedValueOnce on the same spy as beforeEach (don't call spyOn again)
|
// Reset the post mock before rejecting so the beforeEach's resolved value
|
||||||
vi.mocked(api.post).mockRejectedValueOnce(new Error("Network error"));
|
// is gone and we get a clean rejection instead of a resolved→rejected queue.
|
||||||
|
mockPost.mockReset().mockRejectedValue(new Error("Network error"));
|
||||||
render(<ApprovalBanner />);
|
render(<ApprovalBanner />);
|
||||||
await act(async () => { await vi.runOnlyPendingTimersAsync(); });
|
await act(async () => { await vi.runOnlyPendingTimersAsync(); });
|
||||||
fireEvent.click(screen.getAllByRole("button", { name: /approve/i })[0]);
|
fireEvent.click(screen.getAllByRole("button", { name: /approve/i })[0]);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user