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

@@ -0,0 +1,77 @@
import { describe, expect, test } from "bun:test";
import {
getSystemPrefersDark,
parseThemePreference,
readThemePreference,
resolveEffectiveTheme,
THEME_MEDIA_QUERY,
THEME_PREFERENCE_STORAGE_KEY,
writeThemePreference,
} from "../../../src/web/hooks/use-theme-preference";
function createStorage(initial?: string): Storage {
const values = new Map<string, string>();
if (initial !== undefined) values.set(THEME_PREFERENCE_STORAGE_KEY, initial);
return {
clear: () => values.clear(),
getItem: (key) => values.get(key) ?? null,
key: (index) => Array.from(values.keys())[index] ?? null,
get length() {
return values.size;
},
removeItem: (key) => values.delete(key),
setItem: (key, value) => values.set(key, value),
};
}
describe("theme preference 纯逻辑", () => {
test("解析合法和非法主题偏好", () => {
expect(parseThemePreference("dark")).toBe("dark");
expect(parseThemePreference("light")).toBe("light");
expect(parseThemePreference("system")).toBe("system");
expect(parseThemePreference("unknown")).toBe("system");
});
test("读取和写入 localStorage", () => {
const storage = createStorage("dark");
expect(readThemePreference(storage)).toBe("dark");
writeThemePreference("light", storage);
expect(storage.getItem(THEME_PREFERENCE_STORAGE_KEY)).toBe("light");
});
test("storage 异常时回退且不抛错", () => {
const brokenStorage = {
getItem: () => {
throw new Error("blocked");
},
setItem: () => {
throw new Error("blocked");
},
} as unknown as Storage;
expect(readThemePreference(brokenStorage)).toBe("system");
expect(() => writeThemePreference("dark", brokenStorage)).not.toThrow();
});
test("解析系统主题和异常回退", () => {
const matchMedia = ((query: string) => {
expect(query).toBe(THEME_MEDIA_QUERY);
return { matches: true };
}) as Window["matchMedia"];
const brokenMatchMedia = (() => {
throw new Error("blocked");
}) as Window["matchMedia"];
expect(getSystemPrefersDark(matchMedia)).toBe(true);
expect(getSystemPrefersDark(brokenMatchMedia)).toBe(false);
});
test("根据系统主题解析实际主题", () => {
expect(resolveEffectiveTheme("dark", false)).toBe("dark");
expect(resolveEffectiveTheme("light", true)).toBe("light");
expect(resolveEffectiveTheme("system", true)).toBe("dark");
expect(resolveEffectiveTheme("system", false)).toBe("light");
});
});