freeCodeCamp 课程挑战测试如何用 Vitest 运行?
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
freeCodeCamp 单仓中,课程挑战内容(curriculum/下的挑战 Markdown 与课程结构)由curriculum包内的一套 Vitest 测试来验证。当你修改了课程挑战文件、schema 或课程结构后,需要跑通这套测试确认没有破坏挑战的测试套件。测试入口是 curriculum/package.json 里的一组 pnpm 脚本。环境要求以 curriculum/package.json 的engines声明为准:Node.js>=24、pnpm>=10;curriculum 包使用 Vitest 4.1.5。
先理解测试被拆成了哪两个项目
curriculum/vitest.config.mjs 使用 Vitest 的 projects 模式,聚合了两个测试项目:
import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { projects: ['vitest.tooling.config.mjs', 'src/test/vitest.config.mjs'] } });- 工具测试项目:vitest.tooling.config.mjs 只声明了
exclude: ['src/test/blocks-generated/**/*.test.js', 'dist', 'node_modules'],用于运行 curriculum 工具代码(schema 校验、过滤器等)自身的测试。 - 课程内容测试项目:src/test/vitest.config.mjs 只包含
blocks-generated/**/*.test.js,environment: 'node',hookTimeout: 60000、testTimeout: 30000、isolate: false,并声明了globalSetup: 'vitest-global-setup.mjs'和setupFiles: 'vitest-setup.mjs'。真正"运行课程挑战测试"的就是这个项目。
准备条件
课程内容测试的全局 setup(src/test/vitest-global-setup.mjs)在每次运行时做三件事:
- 把
tools/client-plugins/browser-scripts/dist/js拷贝到src/test/stubs/js(先删除该目标目录再重建); - 在
127.0.0.1:8080启动本地静态服务,用于向浏览器提供 test-runner 与 stubs 文件; - 通过 Puppeteer 启动一个 headless Chrome(参数含
--no-sandbox、--disable-setuid-sandbox、--disable-dev-shm-usage),并把连接地址导出为PUPPETEER_WS_ENDPOINT,供各测试池复用(src/test/vitest-setup.mjs 按VITEST_POOL_ID为每个池创建独立的 browser context)。
因此运行前需要满足:
在仓库根目录安装依赖:
pnpm installtools/client-plugins/browser-scripts必须先构建出dist/js,否则全局 setup 没有可拷贝的产物。该包的构建脚本(tools/client-plugins/browser-scripts/package.json)是 webpack production 打包:cd tools/client-plugins/browser-scripts && pnpm buildPuppeteer 需要本地 Chrome 可执行文件,curriculum 包提供了安装脚本(即
puppeteer browsers install chrome --force):cd curriculum && pnpm install-puppeteersetup 使用固定端口
127.0.0.1:8080,该端口被占用会导致本地服务无法监听,运行前先确认空闲。
需要注意的副作用:全局 setup 会删除并重建src/test/stubs/js;测试文件生成步骤会删除并重建src/test/blocks-generated目录。两者都是本地测试产物目录,不属于课程源文件。
生成 block 测试文件(test-gen)
课程内容测试不是直接指向挑战 Markdown,而是先为每个 block 生成一个测试文件:
cd curriculum pnpm test-gen该脚本是tsx ./src/test/utils/generate-block-tests.ts(generate-block-tests.ts):解析课程结构后,为每个 block 在src/test/blocks-generated/下写出一个${block}.test.js,文件内容形如(文档示例):
import { defineTestsForBlock } from '../test-challenges.js'; await defineTestsForBlock({ "block": "basic-javascript" });实际参数是JSON.stringify后的 filter 对象(含block字段)。成功后会打印Generated N block test file(s).,其中 N 为课程中的 block 数量。
真正的测试断言由 test-challenges.js 中的defineTestsForBlock提供:它使用 Vitest 的describe/it/expect,结合challenge-schema.js、curriculum-schema.js、meta-schema.js等 Joi schema 校验挑战与课程结构,并通过 Puppeteer 浏览器执行挑战内嵌的测试代码(TypeScript 编译在测试中被 mock 为进程内编译,见该文件顶部的vi.mock)。
运行测试
curriculum/package.json 提供了四个脚本,按需选择:
| 命令 | 实际执行 | 适用场景 |
|---|---|---|
pnpm test | NODE_OPTIONS='--max-old-space-size=7168' pnpm test-gen && vitest run | 完整运行两个项目(一次性验证) |
pnpm test-content | pnpm test-gen && pnpm test --project test | 只验证课程内容测试项目 |
pnpm test-tooling | pnpm test --project @freecodecamp/curriculum | 只验证工具代码测试项目 |
pnpm test:watch | pnpm test-gen && vitest | 修改代码时的 watch 模式 |
在仓库根目录也可以用pnpm test-curriculum-content,它等价于turbo -F=@freecodecamp/curriculum test-content。
最短主路径如下(按顺序执行):
pnpm install cd tools/client-plugins/browser-scripts && pnpm build cd ../.. cd curriculum && pnpm install-puppeteer pnpm test-content其中pnpm install-puppeteer在本地已装好 Chrome 时可跳过;NODE_OPTIONS='--max-old-space-size=7168'已由test脚本内置,用于给整轮测试扩大 Node 堆内存,无需手动设置。
结果验证
test-gen阶段:输出Generated N block test file(s).,可对照src/test/blocks-generated/下的文件数量确认生成完整。- 测试阶段:
vitest run结束时,输出中没有失败用例且进程退出码为 0 即为通过;若有挑战的测试用例失败,Vitest 会列出失败的测试文件与断言信息,退出码非 0。
排查与限制
- 全局 setup 失败、浏览器侧加载不到 test-runner:先确认
tools/client-plugins/browser-scripts/dist/js是否存在,不存在则回到准备条件中执行 browser-scripts 的构建。 - Chrome 启动失败:执行
pnpm install-puppeteer重新安装 Chrome 可执行文件。 - 超时类失败:内容项目已把
hookTimeout放宽到 60000、testTimeout到 30000 并关闭 isolate;整轮运行依赖--max-old-space-size=7168的堆内存配置,不要去掉它。 - 课程内容项目只运行
src/test/blocks-generated/下生成的文件;修改了curriculum/challenges/下的挑战文件后,重新运行pnpm test-content(它包含 test-gen)即可重新生成并执行测试。 - 课程内容的浏览器执行路径依赖固定端口 127.0.0.1:8080 上的本地静态服务,测试运行期间该端口由 setup 占用,运行结束时 setup 的 teardown 会关闭浏览器与服务。
本地内容测试通过后,从仓库根目录执行pnpm test-curriculum-content可以复现与 turbo 构建链路一致的运行方式。
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考