/** * Vitest 全局测试 setup * * - 注册 @testing-library/jest-dom 自定义匹配器 * - mock next/navigation(App Router hooks) * - polyfill BroadcastChannel / WebSocket(jsdom 未实现) * - 启动/重置/关闭 MSW server */ import "@testing-library/jest-dom/vitest"; import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest"; import { server } from "./src/mocks/server"; // mock next/navigation(App Router hooks 依赖) vi.mock("next/navigation", () => ({ useParams: () => ({}), useRouter: () => ({ push: vi.fn(), replace: vi.fn(), back: vi.fn(), forward: vi.fn(), refresh: vi.fn(), prefetch: vi.fn(), }), useSearchParams: () => new URLSearchParams(), usePathname: () => "/", redirect: vi.fn(), notFound: vi.fn(), })); // btoa Unicode 兼容 polyfill 已移至 vitest.polyfills.ts(需在 server import 之前生效) // BroadcastChannel polyfill(jsdom 未实现 BroadcastChannel) if (typeof globalThis.BroadcastChannel === "undefined") { class MockBroadcastChannel { name: string; onmessage: ((event: MessageEvent) => void) | null = null; constructor(name: string) { this.name = name; } addEventListener(): void {} removeEventListener(): void {} postMessage(): void {} close(): void {} } // 测试中需要 as 断言将 mock 类挂载到全局 globalThis.BroadcastChannel = MockBroadcastChannel as unknown as typeof BroadcastChannel; } // WebSocket polyfill(jsdom 未实现 WebSocket) if (typeof globalThis.WebSocket === "undefined") { class MockWebSocket { static CONNECTING = 0; static OPEN = 1; static CLOSING = 2; static CLOSED = 3; url: string; onopen: ((event: Event) => void) | null = null; onmessage: ((event: MessageEvent) => void) | null = null; onerror: ((event: Event) => void) | null = null; onclose: ((event: CloseEvent) => void) | null = null; readyState = 0; constructor(url: string) { this.url = url; } send(): void {} close(): void {} } globalThis.WebSocket = MockWebSocket as unknown as typeof WebSocket; } // 启动 MSW server(拦截 HTTP 请求返回 mock 响应) beforeAll(() => server.listen({ onUnhandledRequest: "bypass" })); // 每个测试后恢复初始 MSW handlers(隔离测试间状态) afterEach(() => server.restoreHandlers()); // 全部测试完成后关闭 MSW server afterAll(() => server.close()); // 每个测试前清理 localStorage(防止测试间状态泄漏) beforeEach(() => { localStorage.clear(); });