Repository Wiki
jason5ng32/MyIP

自动化测试与构建检查

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。

检查架构

Loading diagram...

Sources: package.json、setup.js、vite.config.js。

这个结构将两类错误分开处理:测试先验证可在 Node 环境运行的逻辑,再由构建阶段处理模块、模板及构建期资源转换。顺序由 && 明确约束,不是两个并行任务。

自动化测试机制

1. 测试发现与执行入口

测试脚本原文为:

json
"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. 预加载:避免日志线程妨碍退出

初始化代码很小,但解决的是测试进程生命周期问题:

javascript
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 的预处理阶段:

  1. URL 为空时删除 @site-url:open 与 @site-url:close 包围的条件块。
  2. URL 非空时删除标记本身,再将 __SITE_URL__ 替换为规范化后的 URL。

因此,缺少站点 URL 不会在这段实现中直接抛出“配置缺失”异常,而是改变生成 HTML 的内容。排查构建结果差异时,应核对构建环境,而不只比较代码。依据:vite.config.js。

3. 语言包转换是构建检查的一部分

javascript
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。

完整自检流程与构建后钩子

Loading diagram...

Sources: package.json、setup.js。

postbuild 需要单独识别

包脚本还定义了以下钩子:

json
"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。

其逻辑不是运行测试,而是条件执行缓存清理脚本:

  1. 用 existsSync 检查目标脚本是否存在。
  2. 存在时,以当前 Node 可执行文件 process.execPath 同步启动子进程。
  3. 通过 stdio: 'inherit' 直接保留子进程输出。
  4. 将子进程退出状态传给当前进程;状态为 null 或 undefined 时返回 1。
  5. 文件不存在时跳过执行。

因此,当包管理器实际执行 postbuild 时,Vite 编译成功仍不代表整个构建命令最终成功。还要看清理子进程的结果。这里仅确认钩子定义及其执行逻辑;本次未读取包管理器生命周期设置,也未读取清理脚本,不能断言其触发策略、清理范围或重试机制。直接运行 vite build 本身也不等同于执行包脚本生命周期。

配置与版本约束

工具链与脚本接口

json
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.nodesemver 范围^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 条件原文为:

javascript
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 的名字理解为包含所有静态质量检查。

扩展与维护建议

  1. 新增测试优先沿用现有入口。 遵循 spec 命名,并保留 setup 的预加载行为,避免自建入口丢失日志初始化。
  2. 非视觉逻辑与测试同 PR 提交。 这是贡献规范的明确要求,而非仅在发布前补测。
  3. 维护上游隔离边界。 新测试应遵守不访问真实上游的约定;具体 mock 方案需参照实际 spec,本页没有足够源码给出统一模板。
  4. 修改构建插件时同时检查匹配与退化路径。 例如语言包之外返回 null、站点 URL 为空时删除条件块、无语言 chunk 时返回原 HTML。这些是源码可见的扩展边界,不代表已存在对应自动化测试。
  5. 若要强化 CI 门禁,先核对 workflow。 本页没有核验 CI 的步骤、缓存、产物上传或分支保护,不能将本地 check 等同于远程合并要求。

相关链接

当前没有提供其他 Wiki 目录项的精确路径,因此不构造可能失效的同级页面链接。