v1 声称完成的下游工作经核查实际未完成: - api-gateway: /api/admin/graphql 路由未注册,go vet 编译失败 - teacher-bff: resolver 已完成但 schema 未同步(命名空间 vs 扁平) - iam: proto 缺 BatchGetUsers rpc 声明 v2 记录详细核查证据和修复要求
85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
/**
|
||
* 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();
|
||
});
|