自动化测试与构建检查
MyIP 使用 Node.js 内置测试运行器执行自动化测试,并通过 check 脚本将测试与 Vite 构建串联为开发自检入口。测试主要约束非视觉逻辑;浏览器渲染与视觉变更仍需要人工审查。
目的与范围
本页说明测试入口、预加载环境、构建检查顺序、配置影响与故障定位,适用于提交代码前自检和维护测试基础设施。业务功能、国际化翻译流程、生产部署和监控配置只讨论与检查结果直接相关的部分,不展开其完整实现。
本文依据已核验的脚本、测试初始化文件、贡献规范及 Vite 配置片段编写。未读取具体测试用例或 CI workflow,因此不宣称某项业务已有测试覆盖,也不推断 CI 的触发条件、矩阵或合并门禁。 本页描述检查机制,并非一次测试执行报告。
概述
项目的检查入口分为三个层次:
| 入口 | 已定义行为 | 适用场景 |
|---|---|---|
pnpm test | 预加载测试环境,然后执行 tests/*.test.js | 修改逻辑后的快速回归 |
pnpm run build | 执行 vite build | 检查前端代码与构建期转换是否可完成 |
pnpm check | 执行 pnpm run test && pnpm run build | 提交前组合自检 |
这里的“构建通过”不等于“页面功能正确”:贡献规范明确将 UI 渲染和浏览器 API 排除在 Node runner 的验证范围之外。视觉变更由维护者在审查阶段验证,PR 应说明观察点。
依据:package.json、CONTRIBUTING.md。
检查架构
Sources: package.json、setup.js、vite.config.js。
这个结构将两类错误分开处理:测试先验证可在 Node 环境运行的逻辑,再由构建阶段处理模块、模板及构建期资源转换。顺序由 && 明确约束,不是两个并行任务。
自动化测试机制
1. 测试发现与执行入口
测试脚本原文为:
"test": "node --import ./tests/setup.js --test tests/*.test.js",
"check": "pnpm run test && pnpm run build",Source: package.json。
脚本使用 Node 自带的 --test,并通过 --import 在 spec 执行前载入初始化模块。显式匹配模式为 tests/*.test.js;新增测试应遵循这一目录与后缀约定,不能仅凭文件放在更深目录就假设默认命令一定会发现它。
check 在测试返回失败状态时不进入构建。因此排查组合检查失败时,应先确定失败发生在测试阶段还是构建阶段,而不是把所有失败统称为 Vite 问题。
2. 预加载:避免日志线程妨碍退出
初始化代码很小,但解决的是测试进程生命周期问题:
1// Preloaded by `pnpm test` (node --import) before any spec. Keeps the shared
2// logger off its pino-pretty worker thread: under a loaded parallel run that
3// thread can keep a finished spec process from exiting. JSON output writes
4// synchronously, with no worker.
5process.env.LOG_FORMAT ??= 'json';Source: setup.js。
源码注释指出,在负载较高的并行测试中,pino-pretty 的 worker thread 可能让已完成的 spec 进程无法退出。初始化模块通过默认选择 JSON 日志避开该 worker,注释同时说明 JSON 输出采用同步写入。
这里使用的是 ??=,不是无条件赋值:外部已经设置 LOG_FORMAT 时,其值不会被覆盖。若测试看似完成却持续不退出,应检查继承的环境变量以及是否绕过了标准测试入口。不能将这个默认值理解为测试框架对日志格式的强制隔离。
3. 测试边界与贡献约定
CONTRIBUTING.md 给出了以下要求:
- 非视觉逻辑,包括纯函数、composable、数据转换和校验器,应在同一个 PR 中附带 spec。
- 测试不访问真实上游服务。这是贡献约定;本文没有读取各个 spec,不能据此证明所有测试实现都符合约定。
- 例外是连通性数据测试:本地运行时可能下载缺失 favicon,CI 中不下载。
- Node runner 不负责 UI 渲染和浏览器 API 的验证。视觉变更需要在 PR 中说明审查方式。
因此,网络服务响应正常和浏览器交互正确,都不属于仅凭 pnpm test 成功就能作出的结论。具体断言示例未在本次阅读范围内核验,本文不提供虚构 spec。
构建检查的实际范围
1. Vite 是构建入口,不是浏览器验收
package.json 将 build 定义为 vite build。已读取的 vite.config.js 配置了 Vue、Tailwind CSS 和三个自定义插件;Vue 编译器将 pwa-install 视为自定义元素。
执行构建可以暴露构建期处理链中的错误,但不能代替启动页面后的行为验证。本次未读取完整配置末尾,故不列举输出目录、完整 sourcemap 配置或开发服务器代理规则。
2. 站点 URL 的 HTML 转换
siteUrlHtmlPlugin() 读取 VITE_SITE_URL,去除两端空白和末尾斜线。在 HTML 的预处理阶段:
- URL 为空时删除
@site-url:open与@site-url:close包围的条件块。 - URL 非空时删除标记本身,再将
__SITE_URL__替换为规范化后的 URL。
因此,缺少站点 URL 不会在这段实现中直接抛出“配置缺失”异常,而是改变生成 HTML 的内容。排查构建结果差异时,应核对构建环境,而不只比较代码。依据:vite.config.js。
3. 语言包转换是构建检查的一部分
1const localeStripPlugin = () => ({
2 name: 'locale-strip-untranslated',
3 enforce: 'pre',
4 transform(code, id) {
5 const file = id.replaceAll('\\', '/').split('?')[0];
6 if (!file.includes('/frontend/locales/') || !file.endsWith('.json')) return null;
7 return { code: JSON.stringify(stripPack(JSON.parse(code))), map: null };
8 },
9});Source: vite.config.js。
插件先统一路径分隔符并移除查询部分,再筛选语言包 JSON。匹配的内容经过 JSON.parse、stripPack 和重新序列化,非目标模块返回 null。注释说明,该预转换在内置 JSON 处理前移除未翻译的空字符串,使运行时回退链能够处理缺失键。
这意味着修改语言包除了需要逻辑检查,还应运行构建:JSON 解析与资源转换路径属于构建阶段。JSON.parse 周围没有局部捕获,格式错误不会在这里被静默忽略。stripPack 内部细节未在本页核验。
4. 构建产物相关行为
localePreloadPlugin() 在 HTML 后处理阶段遍历 ctx.bundle 中的 chunk,匹配顶层语言包的 facadeModuleId,按 LOCALE_CODES 排序,并在存在匹配结果时向 HTML head 注入预加载脚本。没有匹配结果时,返回原 HTML。脚本根据偏好、?hl=、浏览器语言和英语回退选择资源。
这一转换依赖实际 bundle,说明构建检查与开发模式检查具有不同覆盖范围;但是否正确加速了浏览器首屏加载,仍需要浏览器验证。依据:vite.config.js。
完整自检流程与构建后钩子
Sources: package.json、setup.js。
postbuild 需要单独识别
包脚本还定义了以下钩子:
"postbuild": "node -e \"const{existsSync}=require('fs');const{spawnSync}=require('child_process');if(existsSync('scripts/purge-index-cache.js')){const r=spawnSync(process.execPath,['scripts/purge-index-cache.js'],{stdio:'inherit'});process.exit(r.status??1)}\"",Source: package.json。
其逻辑不是运行测试,而是条件执行缓存清理脚本:
- 用
existsSync检查目标脚本是否存在。 - 存在时,以当前 Node 可执行文件
process.execPath同步启动子进程。 - 通过
stdio: 'inherit'直接保留子进程输出。 - 将子进程退出状态传给当前进程;状态为
null或undefined时返回1。 - 文件不存在时跳过执行。
因此,当包管理器实际执行 postbuild 时,Vite 编译成功仍不代表整个构建命令最终成功。还要看清理子进程的结果。这里仅确认钩子定义及其执行逻辑;本次未读取包管理器生命周期设置,也未读取清理脚本,不能断言其触发策略、清理范围或重试机制。直接运行 vite build 本身也不等同于执行包脚本生命周期。
配置与版本约束
工具链与脚本接口
1 "type": "module",
2 "packageManager": "pnpm@12.4.2",
3 "engines": {
4 "node": "^24.15.0 || >=26.0.0"
5 },Source: package.json。
| 项目 | 类型 | 仓库声明或默认值 | 对检查的影响 |
|---|---|---|---|
type | 字符串 | module | 项目采用 ESM 模块语义 |
packageManager | 字符串 | pnpm@12.4.2 | 本地复现时应与声明的包管理器版本对齐 |
engines.node | semver 范围 | ^24.15.0 || >=26.0.0 | 不应简化成“任意 Node 24 及以上” |
test | 包脚本 | Node --test 与 --import | 同时决定测试发现和初始化行为 |
check | 包脚本 | test && build | 串行执行,测试失败时不构建 |
postbuild | 包脚本 | 条件运行缓存清理脚本 | 若被执行,其失败状态可影响最终结果 |
版本约束是包声明,不是对当前执行机器版本的检测结果。依据:package.json。
环境变量
| 变量 | 类型 | 已核验的默认或判定 | 影响 |
|---|---|---|---|
LOG_FORMAT | 字符串 | 测试 setup 中为空值时设为 json | 避免注释所述的 pretty logger worker 退出问题;保留显式设置 |
VITE_SITE_URL | 字符串 | '',再去空白与末尾斜线 | 决定条件 HTML 块的删除或 URL 替换 |
SENTRY_AUTH_TOKEN | 字符串 | 通过 !! 判定是否存在非空值 | 参与 sentryUploadEnabled 条件计算 |
SENTRY_ENVIRONMENT | 字符串 | 空值回退为 production | 必须严格等于 production 才满足该条件 |
Vite 配置先调用 dotenv.config(),再读取相关变量。Sentry 条件原文为:
const sentryUploadEnabled = !!process.env.SENTRY_AUTH_TOKEN
&& (process.env.SENTRY_ENVIRONMENT || 'production') === 'production';Source: vite.config.js。
这不是根据命令名字判断“测试构建”:在该条件中,未设置环境名称会按 production 处理。配置注释说明其用途是控制构建期 source map 上传,但插件最终接线位于未读取部分,本页不据此承诺上传、删除或错误处理的完整行为。依据:vite.config.js。
使用方式与检查结果解读
本页的实际代码示例均来自现有入口或配置,不新增未经验证的命令封装。建议按变更范围选择检查:
| 变更类型 | 建议操作 | 通过后仍需注意 |
|---|---|---|
| 纯函数、校验器、数据转换、composable | 同一 PR 添加 spec,执行 pnpm test,提交前执行 pnpm check | 检查断言是否覆盖变更,而不仅是命令成功 |
| Vue 模板、样式或构建配置 | 执行 pnpm run build,提交前执行组合自检 | 浏览器渲染仍需人工验证 |
| 语言包 | 执行组合自检,关注 JSON 转换阶段 | 回退效果与浏览器加载行为不能只靠构建证明 |
| 连通性数据 | 执行 pnpm test,注意本地下载缺失图标的例外 | 本地与 CI 的下载行为不同 |
| 日志或测试基础设施 | 保留标准入口的预加载路径 | 关注测试完成后进程能否退出 |
以上是依据现有机制给出的开发建议,不代表已核验 CI 自动执行这些步骤。数据测试的例外与人工验证边界见 CONTRIBUTING.md。
失败模式、并发与运维注意事项
| 现象 | 已知机制 | 排查重点 |
|---|---|---|
check 未进入构建 | && 在测试失败时短路 | 先查看测试退出状态 |
| 断言似乎已结束,进程仍存活 | setup 注释指出 pretty logger worker 在高负载并行运行中的退出问题 | 检查 LOG_FORMAT 和预加载是否生效 |
| 新测试没有执行 | 入口只显式指定 tests/*.test.js | 核对路径、后缀与实际测试输出 |
| 语言包变更导致构建失败 | 插件直接执行 JSON.parse,未局部捕获 | 检查目标 JSON 是否有效 |
| 构建成功但 HTML 不含预期站点信息 | 站点 URL 为空时主动删除条件块 | 检查 VITE_SITE_URL |
| 编译完成后命令仍失败 | 若运行 postbuild,它会传播清理子进程的失败状态 | 区分 Vite 输出与子进程输出 |
| 本地测试出现网络访问 | 贡献规范允许本地连通性数据测试补下载 favicon | 先确认是否属于明确例外 |
| 测试和构建成功但 UI 回归 | Node runner 不承担视觉与浏览器 API 验证 | 按 PR 观察点进行人工审查 |
依据:package.json、setup.js、vite.config.js、CONTRIBUTING.md。
并发、耗时与可重复性
check的测试与构建是串行关系;不能将其视为并行提速配置。- 测试 setup 的注释明确考虑并行运行,但包脚本没有显式设置并发数。本页不推断 Node 内部调度细节。
- JSON 日志的目的在于规避 worker 生命周期问题,不是已测量的吞吐量优化。
postbuild使用spawnSync,被执行时会等待子进程结束;该包装逻辑未设置超时或重试。- 环境变量、可选脚本是否存在,以及本地缺失 favicon,均可能改变检查过程或产物。复现问题时应记录这些差异。
- 已读取的入口未声明覆盖率阈值、测试超时或专门的 lint/typecheck 脚本。不能把
check的名字理解为包含所有静态质量检查。
扩展与维护建议
- 新增测试优先沿用现有入口。 遵循 spec 命名,并保留 setup 的预加载行为,避免自建入口丢失日志初始化。
- 非视觉逻辑与测试同 PR 提交。 这是贡献规范的明确要求,而非仅在发布前补测。
- 维护上游隔离边界。 新测试应遵守不访问真实上游的约定;具体 mock 方案需参照实际 spec,本页没有足够源码给出统一模板。
- 修改构建插件时同时检查匹配与退化路径。 例如语言包之外返回
null、站点 URL 为空时删除条件块、无语言 chunk 时返回原 HTML。这些是源码可见的扩展边界,不代表已存在对应自动化测试。 - 若要强化 CI 门禁,先核对 workflow。 本页没有核验 CI 的步骤、缓存、产物上传或分支保护,不能将本地
check等同于远程合并要求。
相关链接
- 贡献指南:Testing:新增逻辑的测试要求与人工验证边界。
- 贡献指南:UI translations:翻译贡献的导航入口;完整翻译流程不属于本页范围。
- 包脚本接口:测试、构建及辅助命令的权威定义。
当前没有提供其他 Wiki 目录项的精确路径,因此不构造可能失效的同级页面链接。