Vitest 仓库贡献者与 AI Agent 开发指南:从环境搭建、测试编写到 CI 规范
【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest
这篇指南面向希望在 Vitest 仓库(GitHub_Trending/vi/vitest 根目录的 pnpm monorepo)中贡献代码的开发者与 AI Agent,系统讲解从零搭建开发环境、编写高质量测试、遵守代码规范,到理解 CI 生成文件约束的完整工作流。读完本文,你将掌握 Vitest 仓库的开发命令、测试工具链(runVitest/runInlineTests)、包重建策略、依赖管理约定以及提交信息规范,可以直接上手提交第一个经过验证的 PR。
本文内容以仓库根目录 AGENTS.md 为骨架,结合 CONTRIBUTING.md、docs/AGENTS.md、package.json、pnpm-workspace.yaml、eslint.config.js 及测试工具源码展开,所有命令与配置均可在当前仓库中验证。
仓库概览:基于 pnpm workspaces 的 TypeScript monorepo
Vitest 是一个由 Vite 驱动的下一代测试框架(package.json 中描述为 "Next generation testing framework powered by Vite")。仓库本身是一个使用 pnpm workspaces 组织的 monorepo,核心特征如下:
- 语言:TypeScript/JavaScript,采用 ESM-first 策略;
- 包管理器:pnpm(强制要求,见 CONTRIBUTING.md "The package manager used to install and link dependencies must be pnpm");
- 构建系统:Vite + Rollup;
- Monorepo 结构:所有核心包位于
packages/目录,测试按功能分散在test/目录下。
pnpm-workspace.yaml明确列出了工作区成员:docs、packages/*、examples/*、test/*等目录都被纳入同一个 workspace,这意味着对@vitest/*任意包的修改都能被其他包通过 workspace 链接即时感知。
核心包职责一览
AGENTS.md 将packages/下的核心包职责归纳如下,理解这份地图是定位修改点的第一步:
| 包 | 职责 |
|---|---|
vitest | 主测试框架,包含测试运行器核心;除@vitest/mocker外,仓库内被引用的包都会被打包(inline)进它的 bundle |
browser | 浏览器模式测试支持 |
browser-playwright/browser-preview | 浏览器模式的两种 provider |
ui | 测试结果 Web UI |
expect | 断言库 |
spy | Mock 与 spy 工具 |
snapshot | 快照测试 |
coverage-v8/coverage-istanbul | 代码覆盖率 |
utils | 共享工具 |
mocker | 模块 Mock |
pretty-format | 值序列化 |
web-worker | Node.js 下的 Web Worker 模拟 |
测试组织方面:test/unit是核心功能测试,test/e2e通过runVitest/runInlineTests运行端到端测试,test/browser是浏览器专项测试,test/node-runner则要求测试进程内完全无法访问 Vitest API(用node --test运行)。
开发环境搭建与常用脚本
初始设置三步走
# 1. 安装依赖 pnpm install # 2. 构建所有包 pnpm build # 3. 涉及浏览器特性时安装 Playwright 浏览器 npx playwright install --with-deps值得注意:仓库根 package.json 声明了 Node 版本要求^22.12.0 || ^24.0.0 || >=26.0.0,packageManager锁定为pnpm@11.24.0。搭建环境前建议先确认本地 Node 与 pnpm 版本符合要求(AGENTS.md 的 Troubleshooting 也提示了这一点)。
关键脚本速查
| 脚本 | 作用 |
|---|---|
pnpm build | 构建所有包(pnpm -r --filter @vitest/ui --filter='./packages/**' run build) |
pnpm dev | 监听模式开发,持续重建包(需NODE_OPTIONS="--max-old-space-size=8192") |
pnpm lint | 运行 ESLint(eslint --cache .) |
pnpm lint:fix | 自动修复 lint 问题 |
pnpm typecheck | 运行 TypeScript 类型检查(tsc -p tsconfig.check.json --noEmit) |
pnpm docs | 启动文档站开发服务器(pnpm -C docs run dev) |
pnpm docs:build | 构建文档站,会先重新生成 CLI 表格 |
运行测试:命令、陷阱与正确姿势
测试命令矩阵
- 全部测试:
CI=true pnpm test:ci(对应 package.json 中的test:ci,会依次运行@vitest/test-*下除test-browser外的所有测试包); - 示例测试:
CI=true pnpm test:examples; - 指定测试套件:
CI=true cd test/<test-folder> && pnpm test <test-file>; - 单元目录测试:
CI=true pnpm test <test-file>(针对test/unit); - 浏览器测试:
CI=true pnpm test:browser:playwright(即pnpm -C test/browser run test:playwright)。
重要陷阱:不要给 pnpm 传--
AGENTS.md 特别强调了一个极易踩坑的细节:向 pnpm 传测试过滤器时不要使用--。--会让 pnpm 丢弃过滤器,导致过滤器失效、变成全量测试运行:
# 错误 —— 会运行所有测试(过滤器被忽略): pnpm test -- basic.test.ts -t 'expect' # 正确 —— 只运行匹配的测试: pnpm test basic.test.ts -t 'expect'断言风格约定
写测试时避免使用toContain做校验,优先使用toMatchInlineSnapshot把测试错误及其堆栈一并纳入快照。如果快照失败,应更新快照,而不是改回toContain——这样做的目的是让失败信息更完整、回归可追溯。
测试工具链:runVitest 与 runInlineTests
AGENTS.md 指明,复杂文件系统场景(>1 个文件)必须使用runInlineTests,普通场景可以使用runVitest以编程方式运行 Vitest。这两个工具都定义在 test/test-utils/index.ts,是 e2e 测试的核心基础设施。
runVitest 的行为约定
runVitest(config)的第一个参数被当作磁盘上的配置文件处理,因此 fixture 自带的配置文件优先级更高。源码注释(test/test-utils/index.ts)明确警告:如果root中的 fixture 有配置文件,其选项会覆盖这里传入的配置,只有$cliOptions中的选项例外。传递 CLI-only 选项或覆盖配置的方式:
- 通过
$cliOptions传 CLI 选项; - 通过
$viteConfig传 Vite 配置属性; - 传
config: false跳过配置文件发现。
除非显式覆盖,runVitest会强制设置:watch: false、maxWorkers: 1、reporters: ['verbose'](传reporters: 'none'可恢复 Vitest 真实默认值)、cache: false,并注入NO_COLOR环境变量(见 test/test-utils/index.ts)。返回的对象不会抛异常,并会自动关闭启动的 Vitest 实例;断言方式为expect(stderr).toBe('')配合expect(testTree()).toMatchInlineSnapshot(...)(失败场景用errorTree()),输出已被剥离 ANSI 颜色、路径被规范化为<root>/。启动错误需要检查返回的thrown与stderr。
runInlineTests 的临时文件系统
runInlineTests会在 cwd 下创建vitest-test-<uuid>目录写入测试文件;当目录结构中没有.config.文件时,会自动补一个空的vitest.config.js;测试结束后删除该目录(设置VITEST_FS_CLEANUP=false可保留目录用于调试)。需要留意:配置对象会用JSON.stringify序列化,函数和正则会被静默丢弃,这类配置应以整文件字符串的形式书写。
runVitestCli 与真实 CLI
runVitestCli会启动真实的 CLI 二进制(同样走dist产物),并总是追加--maxWorkers=1,测试结束时杀掉子进程。交互方式是通过vitest.waitForStdout()和vitest.write(),注意write()会清空已捕获的输出。
可靠测试的纪律
AGENTS.md 对测试可靠性有一组硬性要求,全部可以在测试工具源码中找到对应实现:
- 绝不修改已提交的 fixture 文件:e2e 测试并行运行,需要可编辑目录的测试必须用
runInlineTests;确实需要 git 跟踪文件的测试(如--changed)必须加入 test/e2e/vitest.config.ts 的serialTests列表; - watch 模式只通过
createFile/editFile修改文件:它们会在测试后恢复内容和 mtime(源码见 test/test-utils/index.ts),防止下一个测试的 watcher 看到幻影变更;restoreFile会连同 atime/mtime 一起恢复,任何基于 stat 的比较都无法报告文件被修改; - 在测试内(而非 hook 中)调用这些函数,清理通过
onTestFinished注册;watch 场景要传显式的小root,runVitest({ watch: true, root })会等待 watcher 就绪才 resolve(对应源码中的waitForWatcherReady,见 test/test-utils/index.ts); - ESLint 的测试规则在本仓库被禁用(eslint.config.js 中
test: false),因此漏网的.only不会被 lint 拦住,需要自查并移除; - CI 在 Windows 上运行 unit、e2e、coverage 和 browser 套件,并在 macOS 上运行一条 e2e 任务。Vitest 报告路径使用正斜杠,比较
import.meta.filename、process.execArgv等原始 OS 路径前要把\规范化为/;package.json 脚本中绝不使用rm -rf、cp -r这类 Unix-only 命令,改用 node 脚本或 rimraf。
包重建策略:测试跑的是 dist,不是源码
AGENTS.md 花费大量篇幅解释了一个新手最容易困惑的机制:测试执行的是构建产物。测试套件通过 workspace 符号链接解析vitest,而包导出指向dist/;pnpm typecheck则解析 TypeScript 源码。所以 typecheck 通过绝不证明dist是最新的,重跑测试前必须先重建。
重建的精细化规则:
vitest和@vitest/browser会通过__vitest_source__导出条件把其他@vitest/*workspace 包从 TypeScript 源码内联进来(packages/vitest/package.json 的imports字段和 packages/vitest/rollup.config.js 的exportConditions: ['__vitest_source__']可佐证)。因此修改@vitest/utils、@vitest/expect、@vitest/snapshot、@vitest/spy、@vitest/pretty-format后,只需pnpm --filter vitest build即可覆盖走 vitest bundle 的测试;- 只有当测试直接 import 子包时(例如
test/unit从 dist 导入@vitest/utils/*),才需要单独重建该子包; @vitest/mocker是例外:它是vitest的运行时依赖且永不内联(见 packages/vitest/package.json 中"@vitest/mocker": "workspace:*"位于dependencies),重建vitest不会带上 mocker 的改动,必须执行pnpm --filter @vitest/mocker build;packages/vitest/src/runtime/下的 worker 端代码从构建后的dist/workers/*.js加载,运行时改动同样需要重建vitest;pnpm dev(watch 模式)只重建 JS,.d.ts打包配置在 watch 模式下被跳过。修改公共类型后,在对照dist/*.d.ts检查前需要跑一次完整构建。
代码风格与 ESLint 硬性规则
AGENTS.md 要求每次改动后运行pnpm lint:fix,非自动修复的错误手动修复;且在编辑器或 Agent 环境中应使用CI=true pnpm lint运行——因为配置在检测到编辑器环境时会禁用部分规则,而这些规则在 CI 中仍会失败。
以下规则lint:fix无法自动修复,需要人工遵守:
- 禁止
import ... from 'path',这在所有文件中都是 ESLint 错误(见 eslint.config.js 的no-restricted-imports)。优先使用pathe(仓库主流约定,路径会规范化为 posix),Node-only 代码允许node:path; packages/*/src不得 importvitest或vitest/node,即使仅类型导入也不行;例外是声明了 vitest 为 peer dependency 的包(coverage-*、ui、browser、browser-*、web-worker)——eslint.config.js 的两个规则块正好对应这一约束;console.log在包源码中是 ESLint 错误,只允许console.warn和console.error;删除调试日志,有意的控制台输出需加显式的 eslint-disable 注释;- 使用
globalThis,绝不用global或self(仅docs/、packages/web-worker/、test/unit/例外); packages/*/src禁止顶层await(test/、scripts/和配置文件允许);禁止const enum;禁止export =;- 在
packages/browser中,不要从未使用ivya的文件里 importivya,ESLint 强制该依赖保持单一 rollup chunk(见 eslint.config.js),应复用既有入口点。
TypeScript 严格模式与检查边界
仓库采用严格 TypeScript 配置。根pnpm typecheck使用 tsconfig.check.json,该文件明确排除了test/e2e、test/browser、test/typescript、docs、examples,这些目录的类型错误不会从根命令暴露。根 typecheck 也不覆盖 UI client 的 Vue 代码,修改packages/ui/client时还需运行pnpm -C packages/ui typecheck:client。
tsconfig.base.json中值得注意的细节:customConditions: ["__vitest_source__"](tsconfig.base.json)让 TypeScript 与 rollup 的__vitest_source__导出条件保持一致,源码路径映射(paths)把@vitest/*各包直接指到src/目录,这印证了"typecheck 解析源码、测试解析 dist"的双轨机制。
代码注释政策
- 避免为每次改动写注释,代码表达力足够就不需要注释;
- 只有公共方法必须有注释;导出的内部函数、属性、常量不应有注释,命名应足够自解释;
- 只有当某行代码处理了上下文不明显的边界情况时,才可以留注释;如果为了解释逻辑需要在多个文件间拆散并写大段注释,应重新考虑是否还有更简单的方案;
- 注释要简短、避免过于专业的行话;禁止写"仅为对比先前实现做辩护"的注释。
通用工作流与文档维护
新增功能的标准流程
- 在
packages/中定位合适的包; - 遵循既有代码模式;
- 使用测试工具添加测试;
- 运行
pnpm build && pnpm typecheck && pnpm lint:fix; - 在相关测试套件中补齐测试。
调试
使用 VS Code:⇧⌘B(Shift+Cmd+B)或Ctrl+Shift+B启动开发任务;也可参考scripts/目录中的专项开发工具。
文档规范
- 文档位于
docs/(VitePress 驱动),动手前先读 docs/AGENTS.md; - 改动 CLI 选项或其描述后(文件在
packages/vitest/src/node/cli/cli-config.ts),必须运行pnpm -C docs run cli-table并提交重新生成的 docs/guide/cli-generated.md,该文件绝不可手改。
生成文件与 CI 检查:过期产物会让 CI 挂掉
CI 会构建全部内容然后执行git diff --exit-code,因此过期的生成文件会导致 CI 失败。规则是:提交重新生成的文件,而不是回滚它们;绝不手改。需要关注的生成文件包括:
| 文件 | 生成方式 |
|---|---|
packages/vitest/LICENSE.md | pnpm --filter vitest build在打包依赖变化时重写 |
docs/guide/cli-generated.md | 从packages/vitest/src/node/cli/cli-config.ts生成 |
pnpm-workspace.yaml | pnpm install可能改动(cleanupUnusedCatalogs、minimumReleaseAgeExclude) |
docs/.vitepress/contributor-names.json | 由pnpm docs:contributors生成 |
其他会阻塞 CI 的任务:
- Knip(
pnpm knip)会检测未使用的文件、导出和依赖,失败即阻塞。应删除死代码而不是保留未用导出,例外统一维护在 knip.jsonc; .github/workflows/的改动受 actionlint 和 zizmor 门禁。uses:必须锁定到完整 commit SHA;zizmor 误报用行内# zizmor: ignore[rule]加理由注释抑制——不要自动加注释,改 workflow 文件前必须先跑 zizmor。
依赖管理约定
关键依赖
AGENTS.md 列出的核心依赖:Vite(构建工具与 dev server)、Rollup(打包器)、ESLint(lint)、TypeScript(类型检查)、Playwright(浏览器测试)、Chai/Expect(断言)、Tinybench(基准测试)。
新增与升级依赖的规则
packages/*的新运行时依赖通常放入devDependencies:Rollup 只把dependencies视为 external,其余全部打包。只有@types/*包、无法打包的依赖(二进制)、或类型出现在 Vitest 公共类型中的依赖才用dependencies(详见 CONTRIBUTING.md 的 "Notes on Dependencies");- 用
pnpm add <pkg>在目标包内添加依赖:catalogMode: prefer会把catalog:写进 package.json,并自动把版本加入 pnpm-workspace.yaml 的默认 catalog。升级共享依赖时改 catalog 条目,不要改各包的版本范围; - pnpm-workspace.yaml 的
overrides在整个 workspace 强制vite、rollup、@types/node、acorn、mlly各只有一个版本;改单个 package.json 的 range 只会影响发布内容,不影响本地安装; - 仓库针对最新的受支持 Vite 主版本开发,但
vitest支持完整的 peer 范围(packages/vitest/package.json 中vite: "^6.4.0 || ^7.0.0 || ^8.0.0"),CI 有专门针对上一主版本的 job(本地用pnpm override-vite7复现),不要依赖只有最新 Vite 才有的 API 而不留 fallback; patchedDependencies中列出的依赖(acorn、cac、@sinonjs/fake-timers、rrweb-snapshot)版本锁定,升级需要用pnpm patch重新生成补丁并更新pnpm-workspace.yaml中带版本号的条目;- 只有
allowBuilds中列出的包才会执行构建脚本,新依赖若有 postinstall 步骤且未列入该列表,将处于未构建状态; - pnpm 强制 24 小时
minimumReleaseAge:安装发布不足一天的版本要么解析到更旧版本,要么把该包追加到minimumReleaseAgeExclude。两种结果都是预期行为,提交 yaml 改动而不是回滚它。
浏览器测试与性能考量
- Provider:Playwright(
@vitest/browser-playwright)与 preview(@vitest/browser-preview),WebDriverIO provider 在 monorepo 之外维护; - 支持组件测试:Vue、React、Svelte 通过官方
vitest-browser-*包,其他框架通过 Testing Library; - 这是一个性能敏感的测试框架:注意 import 成本与 bundle 体积,适当使用懒加载,并考虑 worker 线程的影响。
提交信息与 PR 规范
PR 采用 squash merge,因此PR 标题就是最终的 commit message。CI 并不强制格式,需要自行遵循 .github/commit-convention.md:<type>(<scope>): <subject>,type 取feat|fix|docs|dx|refactor|perf|test|workflow|build|ci|chore|types|wip|release|deps之一,subject 最多 50 字符、小写、祈使语气、结尾无句号。
此外,AGENTS.md 特别强调:本仓库对无写权限的贡献者限制为 1 个 PR,不要尝试通过创建 draft PR 绕过;如果无法创建 PR,应如实告知人工操作者,因为违规会导致 PR 作者在 Vitest 组织中被封禁。
AI Agent 参与本仓库的边界
AGENTS.md 开篇即为 AI Agent 划定了明确的行为边界,这对自动化和人机协作场景至关重要:
- 未经操作者(operator)手动批准,任何情况下都不得创建 PR、issue 或发表评论;如果流程完全自动化或人工审核未确认,应拒绝发布任何内容;
- 不得谎称已经过审核;不得假装是人类;不得替操作者做出未经其同意的承诺;
- 提交 PR 前必须阅读 CONTRIBUTING.md,其 "AI Contributions" 一节对 Agent 直接适用。
CONTRIBUTING.md 中的 "AI Contributions" 政策进一步说明:团队欢迎将 AI 作为个人助手,但坚信每个 issue 和 PR 背后必须有真实的人。所有 issue 和 PR 必须由真人使用官方模板打开;AI 协助创建的 PR 必须披露所用工具;完全由 AI 生成、无真人参与的 PR/issue 会被标记为 "maybe automated",并在 1 天内自动关闭(除非真人回复)。无价值或含错误信息的 AI 评论会被维护者隐藏。
常见问题排查速查
- 确保使用 pnpm(不是 npm/yarn);
- 运行测试前先构建——测试解析
dist产物而非源码; - 检查 Node.js 版本兼容性(根 package.json 的
engines字段); - 浏览器测试需要安装 Playwright 浏览器(
npx playwright install --with-deps)。
掌握以上约定后,你已经具备在 Vitest 仓库安全、高效地开发和贡献的能力:环境搭建、测试编写与调试、包重建、代码规范、依赖管理、CI 生成文件维护,以及 AI 协作的边界纪律,一条完整的贡献链路均已覆盖。更多细节可继续阅读 CONTRIBUTING.md、docs/AGENTS.md 与 .github/commit-convention.md。
【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考