1
0

test: 重构测试体系 — 建立组件测试层、补充后端测试、清理低质量测试

- 新增 jsdom + @testing-library/react 组件测试环境
- 新增 12 个组件测试,覆盖所有前端组件
- 补充后端 middleware 和 helpers 单元测试
- 删除伪测试 use-target-detail-logic.test.ts
- 精简过度枚举的 color-threshold.test.ts
- 新增 bunfig.toml 配置测试 preload
- 更新 DEVELOPMENT.md 测试章节
- 安装 @types/jsdom 修复类型声明
This commit is contained in:
2026-05-15 18:31:33 +08:00
parent 2b08f81a0d
commit 8793fbd786
24 changed files with 1392 additions and 143 deletions

81
tests/setup.ts Normal file
View File

@@ -0,0 +1,81 @@
/**
* 全局测试配置
* 主要为后端测试提供基础环境
* 组件测试使用各自的 test-utils.tsx
*/
/* eslint-disable @typescript-eslint/no-empty-function */
// Set up jsdom for ALL tests (both backend and frontend)
import { JSDOM } from "jsdom";
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
pretendToBeVisual: true,
url: "http://localhost",
});
globalThis.document = dom.window.document;
globalThis.window = dom.window as unknown as typeof globalThis & Window;
globalThis.navigator = dom.window.navigator;
globalThis.HTMLElement = dom.window.HTMLElement;
globalThis.Element = dom.window.Element;
globalThis.getComputedStyle = dom.window.getComputedStyle;
// Ensure document.body exists
if (!globalThis.document.body) {
const body = globalThis.document.createElement("body");
globalThis.document.documentElement.appendChild(body);
}
// CRITICAL: Set up polyfills BEFORE any other imports
// This ensures @testing-library/react sees these when it loads
// IE-style event handling polyfill (React fallback)
const nodeProto = dom.window.Node.prototype;
const elementProto = dom.window.Element.prototype;
const htmlElementProto = dom.window.HTMLElement.prototype;
const attachEventFn = () => {};
const detachEventFn = () => {};
Object.defineProperty(nodeProto, "attachEvent", { configurable: true, value: attachEventFn, writable: true });
Object.defineProperty(nodeProto, "detachEvent", { configurable: true, value: detachEventFn, writable: true });
Object.defineProperty(elementProto, "attachEvent", { configurable: true, value: attachEventFn, writable: true });
Object.defineProperty(elementProto, "detachEvent", { configurable: true, value: detachEventFn, writable: true });
Object.defineProperty(htmlElementProto, "attachEvent", { configurable: true, value: attachEventFn, writable: true });
Object.defineProperty(htmlElementProto, "detachEvent", { configurable: true, value: detachEventFn, writable: true });
// Other polyfills
globalThis.ResizeObserver = class {
disconnect() {}
observe() {}
unobserve() {}
};
globalThis.IntersectionObserver = class {
disconnect() {}
observe() {}
takeRecords() {
return [];
}
unobserve() {}
} as unknown as typeof IntersectionObserver;
globalThis.requestAnimationFrame = (cb: FrameRequestCallback) => setTimeout(cb, 16);
globalThis.cancelAnimationFrame = (id: number) => clearTimeout(id);
Object.defineProperty(dom.window, "matchMedia", {
value: (query: string) => ({
addEventListener: () => {},
addListener: () => {},
dispatchEvent: () => true,
matches: false,
media: query,
onchange: null,
removeEventListener: () => {},
removeListener: () => {},
}),
writable: true,
});
dom.window.Element.prototype.scrollTo = () => {};
dom.window.Element.prototype.scrollIntoView = () => {};