Files
huskies/frontend/src/setupTests.ts
T
dave 2f6a221f09 fix(test): stub WebSocket in setupTests so rpcCall fails fast
770's HTTP→read-RPC migration replaced fetch-based agent calls with
rpcCall over WebSocket. setupTests.ts only mocks fetch though, so the
real jsdom WebSocket runs — and jsdom's WebSocket implementation
asynchronously fires its connection-failure error after ~9 seconds
(via internal Timeout._onTimeout). Tests that await component-mount
state (like App.test.tsx > calls getCurrentProject() on mount) hang
on that pending promise and time out at 10s.

This silently broke 1 test on master (App.test.tsx getCurrentProject
on mount) and cascaded into 16 failures in any worktree where the
test file count changed and timing shifted (804, 806).

Fix: replace the global WebSocket constructor with a stub that
immediately fires onerror + onclose on the next microtask. rpcCall
sees the error and rejects synchronously; components catch and continue
rendering with empty state. Tests pass without flake.

Verified locally: vitest run → 349/349 pass.
2026-04-28 17:05:53 +00:00

46 lines
1.7 KiB
TypeScript

import "@testing-library/jest-dom";
import { beforeEach, vi } from "vitest";
// Default WebSocket stub: every `new WebSocket(...)` immediately fires
// `onerror` + `onclose` on the next microtask. Without this, `rpcCall` from
// `./api/rpc` (added by 770's HTTP→read-RPC migration) opens a real jsdom
// WebSocket that hangs ~9s before firing its connection-failure error,
// making any test that mounts a component calling `listAgents()` time out.
// Tests that need real WS responses should override per-test with
// `vi.stubGlobal("WebSocket", ...)`.
class FailingWebSocket {
onopen: ((ev: Event) => void) | null = null;
onmessage: ((ev: MessageEvent) => void) | null = null;
onerror: ((ev: Event) => void) | null = null;
onclose: ((ev: CloseEvent) => void) | null = null;
readyState = 0;
constructor(_url: string) {
queueMicrotask(() => {
this.readyState = 3;
this.onerror?.(new Event("error"));
this.onclose?.(new CloseEvent("close"));
});
}
send(_data: string) {}
close() {}
}
vi.stubGlobal("WebSocket", FailingWebSocket);
// Provide a default fetch mock so components that call API endpoints on mount
// don't throw URL-parse errors in the jsdom test environment. Tests that need
// specific responses should mock the relevant `api.*` method as usual.
beforeEach(() => {
vi.stubGlobal(
"fetch",
vi.fn((input: string | URL | Request) => {
const url = typeof input === "string" ? input : input.toString();
// Endpoints that return arrays need [] not {} to avoid "not iterable" errors.
const arrayEndpoints = ["/agents/config"];
const body = arrayEndpoints.some((ep) => url.endsWith(ep))
? JSON.stringify([])
: JSON.stringify({});
return Promise.resolve(new Response(body, { status: 200 }));
}),
);
});