fix(canvas/test): fix ApprovalBanner POST error test isolation
Some checks failed
Harness Replays / Harness Replays (pull_request) Has been skipped
audit-force-merge / audit (pull_request) Has been skipped
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 16s
Harness Replays / detect-changes (pull_request) Failing after 15s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 15s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 18s
sop-tier-check / tier-check (pull_request) Successful in 17s
CI / Detect changes (pull_request) Successful in 1m4s
E2E API Smoke Test / detect-changes (pull_request) Successful in 1m6s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 1m10s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m0s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 55s
CI / Platform (Go) (pull_request) Successful in 12s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 21s
CI / Python Lint & Test (pull_request) Successful in 15s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 9s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 11s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 9s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Failing after 10m40s
CI / Canvas (Next.js) (pull_request) Failing after 11m51s
Some checks failed
Harness Replays / Harness Replays (pull_request) Has been skipped
audit-force-merge / audit (pull_request) Has been skipped
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 16s
Harness Replays / detect-changes (pull_request) Failing after 15s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 15s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 18s
sop-tier-check / tier-check (pull_request) Successful in 17s
CI / Detect changes (pull_request) Successful in 1m4s
E2E API Smoke Test / detect-changes (pull_request) Successful in 1m6s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 1m10s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m0s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 55s
CI / Platform (Go) (pull_request) Successful in 12s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 21s
CI / Python Lint & Test (pull_request) Successful in 15s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 9s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 11s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 9s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Failing after 10m40s
CI / Canvas (Next.js) (pull_request) Failing after 11m51s
The "keeps the card visible when the POST fails" test was failing in the
full suite due to cross-test mock pollution from ActivityTab.test.tsx.
Root cause: ActivityTab.test.tsx mocks @/lib/api with vi.fn() mocks
(mockGet, mockPost) that persist across test files. When ApprovalBanner's
decisions beforeEach creates a vi.spyOn on api.post, it wraps ActivityTab's
mockPost. Calling mockRejectedValueOnce() on the spy queues after
ActivityTab's mockResolvedValue({}) implementation, but in certain test
orderings the queue ordering was unreliable.
Fix: use mockPost.mockReset().mockImplementation(() =>
Promise.reject(...)) to atomically clear the beforeEach mock setup and
set a permanent rejection, rather than relying on mockRejectedValueOnce
which queues behind the existing implementation.
Also: store mockGet and mockPost in module-level variables so error
tests can manipulate them without creating duplicate spies.
Co-Authored-By: Molecule AI Core-FE <core-fe@agents.moleculesai.app>
This commit is contained in:
parent
8b2fb6b3a0
commit
daecab6d6d
@ -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,8 @@ describe("ApprovalBanner — decisions", () => {
|
||||
});
|
||||
|
||||
it("shows an error toast when POST fails", async () => {
|
||||
vi.mocked(api.post).mockRejectedValueOnce(new Error("Network error"));
|
||||
mockPost.mockReset();
|
||||
mockPost.mockImplementation(() => Promise.reject(new Error("Network error")));
|
||||
render(<ApprovalBanner />);
|
||||
await act(async () => { await vi.runOnlyPendingTimersAsync(); });
|
||||
fireEvent.click(screen.getAllByRole("button", { name: /approve/i })[0]);
|
||||
@ -208,8 +210,10 @@ 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();
|
||||
mockPost.mockImplementation(() => Promise.reject(new Error("Network error")));
|
||||
render(<ApprovalBanner />);
|
||||
await act(async () => { await vi.runOnlyPendingTimersAsync(); });
|
||||
fireEvent.click(screen.getAllByRole("button", { name: /approve/i })[0]);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user