test: 测试体系全面优化,修复 Windows SQLite EBUSY 和前端产品缺陷

测试基础设施
- 统一 SQLite 测试 DB/临时目录 helper(tests/helpers.ts),支持 Windows EBUSY 重试清理
- 测试库使用 PRAGMA journal_mode=DELETE 避免 WAL 句柄延迟
- 路由 handler 测试改用 createMigratedMemoryTestDatabase 避免 File DB 锁
- SQLite 聚焦 --rerun-each=20 全部通过(720 pass)

后端测试补强
- 新增 tests/server/app.test.ts 真实 startServer 集成测试
- 覆盖 /api/meta、项目 CRUD、错误路径、静态 fallback、安全 header
- bootstrap/logger 测试捕获预期输出,消除测试噪音

前端测试补强
- 移除 .ant-* 内部类名依赖,改为角色/文本/导航/请求契约断言
- 项目页补充搜索、Tab 切换、表单、表格操作、错误反馈行为测试
- 新增 hooks(use-theme-preference、use-sidebar-collapsed、use-projects)纯逻辑测试
- 新增 ErrorBoundary 错误展示和刷新按钮测试
- 新增搜索清空行为测试
- 测试 setup 过滤 antd/rc-trigger NaN height warning

产品修复(测试暴露)
- 修复 ProjectToolbar 搜索框无法输入(新增 draftKeyword 状态)
- 加固 ProjectFormModal 表单字段同步(useEffect 替代不可靠的 afterOpenChange)
- 清理 ProjectFormModal 冗余 afterOpenChange 同步逻辑

重构与合规
- ProjectContext 拆分为三文件满足 React Fast Refresh 规则
- use-projects.ts 导出内部 helper 函数供测试验证
- scripts/build.ts 提取纯生成函数供测试使用,修复构建步骤日志编号
- 修复 build 测试覆盖真实生成逻辑

文档同步
- 更新后端/前端/开发文档测试规范、质量门禁和 helper 使用说明
This commit is contained in:
2026-05-29 00:45:21 +08:00
parent 6cb378d7cb
commit 2ea4bd4410
31 changed files with 1417 additions and 723 deletions

View File

@@ -17,8 +17,13 @@ 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.HTMLBodyElement = dom.window.HTMLBodyElement;
globalThis.HTMLHtmlElement = dom.window.HTMLHtmlElement;
globalThis.Element = dom.window.Element;
globalThis.getComputedStyle = dom.window.getComputedStyle;
globalThis.getComputedStyle = (element: Element, pseudoElt?: null | string) => {
// jsdom 不支持伪元素计算样式antd/rc-trigger 会传入伪元素参数,测试中退回普通样式即可。
return dom.window.getComputedStyle(element, pseudoElt ? undefined : pseudoElt);
};
// Ensure document.body exists
if (!globalThis.document.body) {
@@ -44,6 +49,32 @@ Object.defineProperty(elementProto, "detachEvent", { configurable: true, value:
Object.defineProperty(htmlElementProto, "attachEvent", { configurable: true, value: attachEventFn, writable: true });
Object.defineProperty(htmlElementProto, "detachEvent", { configurable: true, value: detachEventFn, writable: true });
// 抑制 antd/rc-trigger 在 jsdom 中产生的 NaN height warning
const originalStderrWrite = process.stderr.write.bind(process.stderr);
process.stderr.write = (chunk: string | Uint8Array, encodingOrCb?: unknown, cb?: unknown) => {
const str = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString();
if (str.includes("NaN") && str.includes("height") && str.includes("css style property")) return true;
return originalStderrWrite(
chunk,
encodingOrCb as Parameters<typeof process.stderr.write>[1],
cb as Parameters<typeof process.stderr.write>[2],
);
};
const originalConsoleError = console.error;
console.error = (...args: unknown[]) => {
const message = args.map(String).join(" ");
if (message.includes("NaN") && message.includes("height") && message.includes("css style property")) return;
originalConsoleError(...args);
};
const originalConsoleWarn = console.warn;
console.warn = (...args: unknown[]) => {
const message = args.map(String).join(" ");
if (message.includes("NaN") && message.includes("height") && message.includes("css style property")) return;
originalConsoleWarn(...args);
};
// Other polyfills
globalThis.ResizeObserver = class {
disconnect() {}