OpenScreen 如何为依赖真实浏览器 API 的代码编写 Vitest 浏览器测试?
2026/9/10 13:41:09 网站建设 项目流程

OpenScreen 如何为依赖真实浏览器 API 的代码编写 Vitest 浏览器测试?

【免费下载链接】openscreenCreate stunning demos for free. Open-source, no subscriptions, no watermarks, and free for commercial use. An alternative to Screen Studio.项目地址: https://gitcode.com/GitHub_Trending/open/openscreen

OpenScreen 的测试体系由两套相互独立的 Vitest 配置组成:一套跑在 jsdom(模拟 DOM、没有真实浏览器)里,另一套通过 Playwright 驱动真实 Chromium。当被测代码依赖 jsdom 没有实现的真实浏览器 API——VideoDecoderVideoEncoderMediaRecorderOffscreenCanvasWebGL等——时,就必须把测试写成浏览器测试。这篇基于 docs/tests/writing-tests.md 说明完整的操作路径:判断、写文件、加载素材、运行与验证。

先判断:这段代码该进哪套测试

文档给出的选型依据如下:

情况使用
纯函数 / 数据转换单元测试(jsdom)
i18n key 覆盖单元测试
React hook 逻辑(不依赖真实浏览器 API)单元测试
VideoDecoder/VideoEncoder/MediaRecorder浏览器测试
OffscreenCanvas/ WebGL / Pixi.js 渲染浏览器测试
文件导出产生真实Blob浏览器测试

两套配置的划分靠文件命名完成,这也是浏览器测试的第一道门槛:

  • 浏览器测试:文件名必须以.browser.test.ts(或.tsx)结尾,且位于src/**下。vitest.browser.config.ts 的include就是src/**/*.browser.test.{ts,tsx}
  • 单元测试:vitest.config.ts 的include{src,electron}/**/*.{test,spec}...,同时exclude掉了src/**/*.browser.test.{ts,tsx},所以npm run test不会误跑浏览器测试。

文件放置规则:命名为<subject>.browser.test.ts,放在被测源码旁边。例如 videoExporter.browser.test.ts 就与 videoExporter.ts 同目录。

检查浏览器测试配置

vitest.browser.config.ts 的关键字段(节选):

export default defineConfig({ test: { include: ["src/**/*.browser.test.{ts,tsx}"], browser: { enabled: true, provider: playwright({ launch: { // Software WebGL so Pixi.js works in headless CI without a GPU. args: ["--enable-unsafe-swiftshader", "--use-gl=swiftshader"], }, }), headless: true, instances: [{ browser: "chromium" }], }, testTimeout: 120_000, hookTimeout: 30_000, }, resolve: { alias: { "@": path.resolve(__dirname, "src"), }, }, assetsInclude: ["**/*.webm"], });

对写测试有直接影响的是这几项:

  • 测试在 headless Chromium 中执行,启动参数--enable-unsafe-swiftshader--use-gl=swiftshader启用软件 WebGL,让 Pixi.js 在无 GPU 的 CI 环境可以运行。
  • 每个测试默认超时 120 秒,每个 hook 30 秒。导出类操作很慢,超时设置就是为此留的余量。
  • @别名解析到src/,测试里可以直接写import { ... } from "@/i18n/config"这类导入。
  • assetsInclude: ["**/*.webm"].webm素材能被 Vite 当作静态资源处理(配合下一节的?url导入)。

编写测试:素材加载与示例

视频、图片等静态素材统一放在tests/fixtures/目录(当前仓库中有 tests/fixtures/sample.webm 和 tests/fixtures/sample-inflated-duration.webm)。导入时加 Vite 的?url后缀,让 Vite 通过 dev server 提供该文件:

import sampleVideoUrl from "../../../tests/fixtures/sample.webm?url";

注意这条路径是相对于测试文件自身位置的:上面的写法适用于放在src/lib/exporter/下的测试(如 videoExporter.browser.test.ts),如果你的测试文件放在其他目录,../层数要相应调整。

文档给出的完整浏览器测试示例(对VideoExporter做真实导出):

import { describe, expect, it } from "vitest"; import sampleVideoUrl from "../../../tests/fixtures/sample.webm?url"; import { VideoExporter } from "./videoExporter"; describe("VideoExporter (real browser)", () => { it("exports a valid MP4 blob from a real video", async () => { const exporter = new VideoExporter({ videoUrl: sampleVideoUrl, width: 320, height: 180, frameRate: 15, bitrate: 1_000_000, wallpaper: "#1a1a2e", zoomRegions: [], showShadow: false, shadowIntensity: 0, showBlur: false, cropRegion: { x: 0, y: 0, width: 1, height: 1 }, }); const result = await exporter.export(); expect(result.success, result.error).toBe(true); expect(result.blob).toBeInstanceOf(Blob); }); });

仓库中的实际测试还展示了更强的校验方式,可以照着写:

  • MP4 导出后读取Blob的二进制内容,把第 4–8 字节解码后断言为"ftyp",确认产物是合法 MP4 结构(见 videoExporter.browser.test.ts);
  • GIF 导出后断言文件头匹配/^GIF8[79]a/(见 gifExporter.browser.test.ts);
  • 断言result.blob.size大于 1024,以及onProgress回调收到过phase === "finalizing"percentage为 100 的进度事件。

这些断言依赖真实解码管线在浏览器里跑通,jsdom 下无法得到同样的结果——这正是浏览器测试存在的意义。

运行与验证

准备条件:package.jsonengines声明 Node 22.22.1 与 npm 10.9.4,测试依赖(vitest@vitest/browser@vitest/browser-playwright均为 ^4.1.4,@playwright/test^1.59.1)已包含在项目 devDependencies 中,正常npm install即可获得。

第一步(一次性)安装浏览器。该命令会下载 Playwright 的chromium-headless-shell浏览器构建,--with-deps参数意味着还会尝试安装其系统依赖,在 Linux 上可能需要相应权限,请在本机或具备权限的 CI 环境中执行:

npm run test:browser:install

对应脚本为playwright install --with-deps chromium-headless-shell

第二步运行测试:

npm run test:browser

对应脚本为vitest --config vitest.browser.config.ts --run。只有以.browser.test.ts(x)结尾的文件会被执行,一次跑完即退出。

验证方式就是测试自身的断言结果:result.success为 true、result.blobBlob实例、文件头(ftyp/GIF8[79]a)符合预期、进度事件走到 100。全部通过且无失败用例,即表示被测代码在真实 Chromium 中按预期工作。CI 中的用法与本地一致:先npm run test:browser:install,再npm run test:browser

限制与注意点

  • 速度:文档明确说明导出操作很慢,建议 fixture 用小尺寸(示例为 320×180)和低码率来保持测试速度。不要为了"更真实"而引入大分辨率素材。
  • 超时:单测 120 秒、单 hook 30 秒(testTimeout/hookTimeout)。如果你的用例超过这些值,应优先缩减素材规模而不是盲目调大超时。
  • 划分边界:纯逻辑、数据转换、无浏览器 API 的 hook 逻辑仍应留在单元测试里(npm run test运行 jsdom 套件),不要把两套测试的职责混在一起。
  • 无 GPU 环境:配置里的 SwiftShader 参数已经处理了 headless CI 的 WebGL 问题,本地开发与 CI 行为一致,不需要额外配置。

完成一次浏览器测试的写法后,可以继续参考 docs/tests/writing-tests.md 中的单元测试部分(文件放置、@/别名、npm run test/npm run test:watch)补齐其余逻辑的覆盖。

【免费下载链接】openscreenCreate stunning demos for free. Open-source, no subscriptions, no watermarks, and free for commercial use. An alternative to Screen Studio.项目地址: https://gitcode.com/GitHub_Trending/open/openscreen

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询