项目:html5-examples | 版本:v1.1 | 生成日期:2026-10-06 | 依据:源码分析
在罗列测试资产之前,需要先如实概括这个项目的质量保障哲学,否则测试现状会显得"单薄"而误导读判。项目把正确性责任分配给了四道防线:第一道是类型系统——tsconfig.json 开启 strict + noUnusedLocals + noUnusedParameters,配合 vue-tsc 做全量模板检查,大量"传错 prop、拼错字段名"类缺陷在编译期就被拦截,这是项目敢于让 750 个示例数据、上百个组件彼此引用而不用测试兜底的底气;第二道是机制内建——防抖、竞态保护、401 联动、错误边界这些易错逻辑没有散落在页面里,而是沉淀进 useListFilter/useResourceFetch/client.ts/authStore 等少数底座模块,测一处等于保护全站;第三道才是单测——集中打在底座模块的边界计算与状态机上;第四道是人工与半自动目检——Playwright 截图脚本与 gui-test-screenshots/ 记录了各页面的视觉基准。理解了这个"类型兜大头、单测打底座、目检保外观"的分工,就不会用"测试覆盖率低"一句话否定它,也不会误以为可以不加测试就随意改动底座。
同时也要坦率指出:这个质量体系有明显的不对称——前端有 CI 三门禁,后端 server/ 目录没有一行测试、也没有独立 CI(详见 §3)。这是当前质量保障体系最大的结构性缺口。
测试框架 Vitest(vitest.config.ts):环境 happy-dom、globals: true(用例内直接用 describe/it/expect,不引断言库)、用例匹配 src/**/*.{test,spec}.{ts,tsx}、别名与 vite.config.ts 一致。实测全项目用例文件共 10 个:
| 用例文件 | 被测对象 | 覆盖要点(据模块职责推断) |
|---|---|---|
src/api/client.test.ts | HTTP 客户端 | 重试编排(仅 GET + 0/5xx)、指数退避、错误归一(AbortError→408、网络错误→0)、getApiErrorMessage 的 message 数组拼接 |
src/stores/authStore.test.ts | 认证状态机 | 会话持久化/恢复、提前刷新调度、401 处理、clearFavorites 触发路径 |
src/composables/useListFilter.test.ts | 筛选 composable | 防抖窗口、URL 恢复与回写、reset/clearAll 语义 |
src/composables/usePagination.test.ts | 分页 composable | 页码钳制、totalPages 边界 |
src/utils/date.test.ts | 日期工具 | 格式化边界 |
src/utils/debounce.test.ts | 防抖工具 | 计时行为 |
src/utils/format.test.ts | 格式化工具 | 数值/文本格式化 |
src/components/layout/__tests__/EmptyState.test.ts | 空态组件 | 四型(loading/error/not-found/empty)渲染分支 |
src/components/layout/__tests__/SiteFooter.test.ts | 页脚组件 | 结构与链接渲染 |
src/components/chart-examples/__tests__/VueSfcRenderer.test.ts | SFC 渲染器(历史目录残留) | 图表示例板块的 SFC 渲染——该板块前端已拆分,测试与组件同属遗留 |
前四个是"底座测试",对应 §0 说的第二道防线,价值密度最高;中间三个是工具纯函数;最后三个是仅有的组件级测试(没有快照断言,行为断言为主)。值得注意的是 __tests__ 目录形式(EmptyState/SiteFooter/VueSfcRenderer)与同目录同名 .test.ts 形式(client/useListFilter 等)并存——项目自身没有统一约定,新增用例时建议沿用同目录 .test.ts 形式(与 vitest.config 的 coverage exclude 规则更契合)。
vitest.config.ts 的 coverage 段:provider v8、reporter text+html、include 仅 src/utils/** 与 src/composables/**、exclude 用例文件与各 index.ts。这个口径是刻意收窄的:utils 与 composables 是"被所有页面复用的底座",一处缺陷放大为全站缺陷,值得用数字盯防;views 与 components 不进统计不代表放任,而是优先级排序(组件正确性主要由类型检查与目检兜底)。运行 pnpm test:coverage 得到的数字应理解为"底座覆盖率"而非全站覆盖率——向前沟读覆盖率报表时必须带上这个前提。
@playwright/test + playwright,server 亦装 playwright):承担 GUI 目检与截图流水线。根目录 gui-test-screenshots/ 存放历史测试截图;scripts/ 下有批量截图脚本(如 screenshot-static-sites-batch.ts)。这些脚本不进 CI,属于"改版前后人工跑一遍对照"的性质。docs/ 知识库:项目自建文档体系(20+ 篇,见 docs/目录索引.md),其中"开发指南/示例与组件编写规范""上线运维/运维检查清单"等篇目承担了"评审 checklist"职能。AGENTS.md(67KB):AI 协作规范总纲,含设计令牌、页面型谱、板块准入三问等强约束——在人机协作工作流里充当"评审员"角色。.github/workflows/ci.yml)触发条件:push main 与所有 PR。单 job verify,步骤依次:checkout → pnpm v10(pnpm/action-setup@v4)→ Node 20(actions/setup-node@v4,cache pnpm)→ pnpm install --frozen-lockfile → pnpm lint(注释标注"零错误门禁")→ pnpm typecheck(vue-tsc)→ pnpm test(vitest run)。三道门禁全部串行阻塞合并——任何一道红灯即不可合入。
eslint . flat config(eslint.config.js)。要点:tsconfigRootDir 显式指定(根与 server 各有 tsconfig);基线 js.configs.recommended + tseslint.configs.recommended + pluginVue.configs['flat/essential'];对 .vue 关闭三条规则(multi-word-component-names / no-reserved-component-names / no-side-effects-in-computed-properties);prettier 经 eslint-config-prettier + eslint-plugin-prettier 接入。ignores 清单值得一看——server/**、src/chart-examples/**、src/views/data-visualization/bigscreen/** 等生成代码与遗留目录被显式豁免(豁免项本身是技术债务信号,见《13-技术债务》第 10 条)。.prettierrc:无分号、单引号、行宽 100、LF),pnpm format 作用于 src/**/*.{ts,vue,js,css,json}。lint-staged 对 *.{ts,vue,js} 依次 eslint --fix + prettier --write,对 *.{css,json,md} 仅 prettier——JSON 示例文件(内容域的核心资产)因此被强制格式化,这是"改 JSON 也会触发门禁"的机制保证。pnpm format && pnpm lint && pnpm typecheck && pnpm build && pnpm dev。pnpm build 前置 vue-tsc --noEmit(8GB 堆内存构建),等于把类型门禁在生产构建里再跑一遍,防止"绕过 CI 的直发"引入类型回归。build:fast 跳过该检查,仅限本地快速迭代。
| # | 缺口 | 现状证据 | 风险 |
|---|---|---|---|
| 1 | 后端零测试 | server/package.json scripts 无 test 命令、无 jest/vitest 依赖;CI 只覆盖根目录 | auth 状态机(token 旋转/泄露检测)、favorites 幂等、分页钳制这些纯逻辑完全无回归保护;auth.service 是全项目安全关键代码却处于"裸奔"状态 |
| 2 | 后端无独立 CI | .github/workflows/ci.yml 仅一个 job,不触碰 server/ | server 的 lint(且 script 本身是 --fix 形态,无纯 check 模式)与 nest build 是否通过无人把关 |
| 3 | 无集成测试 | 全项目无 supertest / 端到端 API 测试 | ValidationPipe、全局守卫、@Public 元数据这些"装配期行为"只能靠真机手测 |
| 4 | 无 E2E | Playwright 仅用于截图目检,无断言型 e2e 用例 | 五条核心流程(见《05-核心流程》)的回归靠人工 |
| 5 | 组件测试近乎为零 | 仅 2 个布局组件有测试 | 但这属于有意取舍,风险已由类型+目检部分覆盖 |
| 6 | 遗留测试目标失配 | src/components/chart-examples/__tests__/ 指向已拆分板块 | 测试仍会执行(匹配 src/**/*.test.ts),对已不存在于页面的组件做无效守护,徒增维护噪音 |
| 7 | 竞态/时序类回归无守护 | useResourceFetch 的 last-request-wins、authStore 的刷新定时器重挂等时序敏感逻辑仅有单测快照式验证 | 这类缺陷的特点是单测全绿但线上偶发;需要假计时器/序列化调度的精细用例 |
第一优先:给 auth 补上服务端测试。auth.service.ts 是纯逻辑密度最高、安全权重最大的文件:refresh token 旋转(用一次即吊销)、吊销后重用触发全量吊销、改密后全量吊销、normalizeAccount 的邮箱/手机号/用户名三态识别、唯一约束冲突(code 23505)转 409——全部可以用一个内存 SQLite 或 mock Repository 测到位。相应地给 server 补 vitest(与前端同构,成本低)并在 CI 加第四步 server: pnpm lint --max-warnings=0 && pnpm test。
第二优先:为"装配期行为"补集成冒烟。用 supertest 对 NestJS 应用实例打真实 HTTP:验证全局 ValidationPipe 对多余字段的 400、@Public() 元数据短路、宽容模式守卫(无凭证 GET 公开端点 200 / 带坏凭证 401)、AdminGuard 的 401/403 分叉、分页钳制。这一层测的是"模块四件套 + app.module 装配"的正确性,恰好是单测的盲区。
第三优先:一条金路径 E2E。不必铺满五条流程,只做一条:启动前端(mock 或真实后端)→ 打开 /examples → 断言卡片渲染 → 进详情 → 断言 iframe srcdoc 非空 → 编辑保存 → 断言 localStorage 覆盖层写入。这一条能同时守护 JSON 懒加载、沙箱拼装、覆盖层三个最高频变更点。
第四优先:清理与守护遗留。把 chart-examples/__tests__ 随板块残留一起移除(与《13-技术债务》第 2 条的清理任务绑定);给 useResourceFetch 补"过期结果不落状态"与"AbortError 静默"两个时序用例(vi.useFakeTimers + 手动控制 Promise resolve 顺序即可);给 api/examples.ts 补排序稳定性与覆盖层 apply 的用例——它是内容域的心脏,目前完全依赖消费方间接验证。
不建议做的:为 63 个 CSS 生成器补渲染快照——它们是高度模板化的同构组件,BaseGenerator 骨架一处测试即可代表共性;为 750 个 JSON 示例写"结构校验测试"——更适合做成独立的校验脚本(scripts/ 已有先例)挂在 extract 之后按需跑,而不是塞进每次 CI。
把现状压缩成一张记账卡:有——CI 三门禁(lint/typecheck/test)、strict 类型全量、底座模块 10 个用例文件、覆盖率口径锁定底座、lint-staged 提交钩子、Playwright 截图目检、AGENTS.md 评审规范;无——后端测试与 CI、集成测试、E2E 断言、组件测试(有意取舍);错位——遗留测试目录指向已拆分板块、server lint 只有 --fix 形态。这套体系与项目"内容平台 + 底座复用"的形态是匹配的,真正的当务之急只有一件事:让安全关键的后端 auth 链路进入任何一种自动化的保护伞之下。在这件事完成之前,任何涉及 auth.service.ts 的改动都应以"人工 code review + 手动全链路验证"双保险对待,并把验证步骤写进 PR 描述——这是当前门禁体系下对最高风险文件的务实补偿。
本篇完。下一篇:10-性能设计与优化.md