ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenScreen 测试体系实战:用 Vitest 双配置写单元测试与真实浏览器测试

OpenScreen 测试体系实战:用 Vitest 双配置写单元测试与真实浏览器测试 OpenScreen 测试体系实战用 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/openscreenOpenScreen 是一个基于 Electron React 的开源录屏与视频编辑工具其核心导出管线依赖 WebCodecs、MediaRecorder、OffscreenCanvas 等真实浏览器 API无法在纯 Node 环境下验证。为此项目建立了一套双配置的 Vitest 测试体系vitest.config.ts 面向 jsdom 环境的单元测试vitest.browser.config.ts 面向 Playwright 驱动的无头 Chromium 浏览器测试。读完后你将掌握如何根据代码是否依赖真实浏览器 API 来选择测试类型、如何按项目规范放置测试文件、如何加载测试固件fixture资源以及如何在本地一键运行两套测试。双配置体系总览项目使用 Vitest 同时承担单元测试/集成测试与浏览器测试两套配置各管各的文件互不干扰单元测试浏览器测试配置文件vitest.config.tsvitest.browser.config.ts运行环境jsdom模拟 DOM无真实浏览器真实 ChromiumPlaywright 驱动无头模式文件匹配*.test.*/*.spec.*排除.browser.test.*src/**/*.browser.test.ts(x)本地命令npm run test一次、npm run test:watch监听先npm run test:browser:install再npm run test:browser两套配置在 package.json 中对应明确的脚本入口test: vitest --run, test:watch: vitest, test:browser: vitest --config vitest.browser.config.ts --run, test:browser:install: playwright install --with-deps chromium-headless-shell其中test:browser:install是首次运行前的一次性步骤用于下载 Chromium headless shell 及其系统依赖日常运行只需test:browser。单元测试jsdom 环境与纯逻辑验证配置与适用场景单元测试运行在 jsdom 提供的模拟 DOM 中适合纯函数、工具函数、数据转换以及一切不依赖真实浏览器 APICanvas、WebCodecs、MediaRecorder 等的代码。vitest.config.ts 的关键配置如下import path from node:path; import { defineConfig } from vitest/config; export default defineConfig({ test: { globals: true, environment: jsdom, include: [{src,electron}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}], exclude: [src/**/*.browser.test.{ts,tsx}], }, resolve: { alias: { : path.resolve(__dirname, src), }, }, });几个值得注意的细节globals: true开启全局 API但项目中的测试文件仍然显式import { describe, expect, it } from vitest两种写法并存实际的匹配范围是{src,electron}两个目录下的*.test.*和*.spec.*文件——从源码看主进程Electron side代码同样被纳入单元测试范围例如 recordingStream.test.ts 就在electron/ipc/下直接测试RecordingStreamRegistry的流式落盘逻辑exclude显式排除src/**/*.browser.test.ts(x)保证两套配置的文件集合互斥不会重复执行别名指向src/与浏览器测试配置保持一致。文件放置约定测试文件与源码文件同目录放置co-locate或放入同目录下的__tests__/文件夹集中管理src/lib/compositeLayout.ts src/lib/compositeLayout.test.ts # co-located src/i18n/__tests__/tutorialHelpTranslations.test.ts # grouped典型示例布局计算的单元测试compositeLayout.test.ts 测试的是导出画布中屏幕与摄像头窗口的布局计算函数computeCompositeLayout——这是纯几何计算天然适合单元测试。项目文档给出的最小示例import { describe, expect, it } from vitest; import { computeCompositeLayout } from ./compositeLayout; describe(computeCompositeLayout, () { it(anchors the overlay in the lower-right corner, () { const layout computeCompositeLayout({ canvasSize: { width: 1920, height: 1080 }, screenSize: { width: 1920, height: 1080 }, webcamSize: { width: 1280, height: 720 }, }); expect(layout).not.toBeNull(); expect(layout!.webcamRect!.x).toBeGreaterThan(1920 / 2); expect(layout!.webcamRect!.y).toBeGreaterThan(1080 / 2); }); });实际仓库中的该文件覆盖了更多边界场景可作为编写高质量单元测试的参考模板越界约束断言webcamRect的右下角不超过画布尺寸x width 1920、y height 1080参数钳制验证webcamSizePreset被钳制在 10–50 的有效范围内传入1与传入10的结果一致传入100与传入50的结果一致横竖屏一致性同像素总量下1920×1080 横屏与 1080×1920 竖屏产生的摄像头窗口面积应完全相等布局预设分别验证vertical-stack上下堆叠与dual-frame双框 2:1 分屏两种布局下的具体坐标与比例蒙版形状圆形/方形蒙版强制宽高相等rounded蒙版的borderRadius大于rectangle。i18n 键值覆盖率测试文档将i18n key coverage列为单元测试场景仓库中的对应实现是 tutorialHelpTranslations.test.ts。它的做法是导入全部 13 个语言包与 config.ts 中SUPPORTED_LOCALES定义的en、ar、es、fr、it、ja-JP、ko-KR、ru、tr、vi、pt-BR、zh-CN、zh-TW一致遍历一组固定的tutorialHelpKeys键列表断言每个语言包的每个键都存在、是字符串且除白名单内的可空键外非空for (const locale of SUPPORTED_LOCALES) { const tutorial dialogsByLocale[locale].tutorial; for (const key of tutorialHelpKeys) { const message tutorial[key]; const label ${locale} dialogs.tutorial.${key}; expect(message, label).toEqual(expect.any(String)); if (!keysThatMayBeEmpty.has(key)) { expect((message as string).trim().length, label).toBeGreaterThan(0); } } }expect的第二个参数label会在断言失败时打印出哪个语言包的哪个键出了问题这是批量校验类测试的可读性技巧。路径别名/别名解析到src/用于替代冗长的相对路径导入import { SUPPORTED_LOCALES } from /i18n/config;本地运行npm run test # 运行一次 npm run test:watch # watch 模式浏览器测试Playwright 无头 Chromium 与真实 Web API配置细节为什么要开 SwiftShader当被测代码依赖 jsdom 没有实现的真浏览器 API——VideoDecoder、VideoEncoder、MediaRecorder、OffscreenCanvas、WebGL 等——时应写浏览器测试。vitest.browser.config.ts 的完整内容很短但每个参数都有存在理由import path from node:path; import { playwright } from vitest/browser-playwright; import { defineConfig } from vitest/config; 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-glswiftshader], }, }), headless: true, instances: [{ browser: chromium }], }, testTimeout: 120_000, hookTimeout: 30_000, }, resolve: { alias: { : path.resolve(__dirname, src), }, }, assetsInclude: [**/*.webm], });软件 WebGL--enable-unsafe-swiftshader与--use-glswiftshader两个启动参数让无头 CI 环境在没有 GPU 的情况下也能跑 Pixi.js 的 WebGL 渲染——这是该项目渲染引擎的硬性前提本地有 GPU 的环境可忽略此问题CI 上则必须超时设置每个测试 120 秒testTimeout: 120_000、每个 hook 30 秒hookTimeout: 30_000。文档提示导出操作本身很慢因此要优先使用小尺寸固件320×180和低码率来保持测试快速assetsInclude: [**/*.webm]让 Vite 把.webm识别为可导入的静态资源配合?url导入语法工作下文说明。文件放置约定文件名固定为subject.browser.test.ts并与源码同目录src/lib/exporter/videoExporter.ts src/lib/exporter/videoExporter.browser.test.ts当前仓库中该规则下的文件包括 videoExporter.browser.test.ts、gifExporter.browser.test.ts 和 streamingDecoder.test.ts 之外的 videoDecoder 相关用例均以导出管线为测试对象。加载固件资源fixture视频、图片等静态资源统一放在tests/fixtures/目录当前包含sample.webm与sample-inflated-duration.webm通过 Vite 的?url后缀导入由 dev server 在浏览器中提供import sampleVideoUrl from ../../../tests/fixtures/sample.webm?url;?url返回的是 dev server 上的资源 URL浏览器测试里的真实 Chromium 可以直接用video/fetch拉取——这是 jsdom 环境做不到的关键一步。示例从真实视频导出 MP4 Blob文档给出的浏览器测试示例验证VideoExporter从真实视频导出一份合法 MP4import { 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); }); });注意参数取值对测试速度的影响width: 320 / height: 180的小画布、frameRate: 15的低帧率、bitrate: 1_000_0001 Mbps的低码率正是文档用小尺寸 低码率保持测试快速建议的落地。实际仓库中的 videoExporter.browser.test.ts 在此基础上做了三处深化值得借鉴魔数校验不只看Blob存在还解码字节流检查 MP4 的ftypbox——bytes.slice(4, 8)解码后应等于ftyp确认产出物是真实可识别的 MP4 容器而非空壳进度事件断言通过onProgress回调收集ExportProgress事件断言phase finalizing的事件存在且最后一个percentage为 100把 UI 进度反馈也纳入测试负向路径传入不存在的壁纸路径/wallpapers/does-not-exist.jpg断言导出以BackgroundLoadError拒绝reject且错误对象携带出错的url——验证加载失败时不静默回退为黑底这一产品行为。gifExporter.browser.test.ts 采用同样的模式另外用/^GIF8[79]a/正则校验 GIF 文件头。本地运行首次运行先安装浏览器一次性npm run test:browser:install之后每次运行npm run test:browser如何选择合适的测试类型文档给出的决策表场景使用纯函数 / 数据转换单元测试i18n 键覆盖率单元测试React hook 逻辑不依赖真实浏览器 API单元测试VideoDecoder/VideoEncoder/MediaRecorder浏览器测试OffscreenCanvas/ WebGL / Pixi.js 渲染浏览器测试产出真实Blob的文件导出浏览器测试判断的核心问题只有一个被测代码是否触碰到 jsdom 无法实现的浏览器 API是则写.browser.test.ts否则写普通.test.ts。两套配置的文件互斥保证每次vitest调用只跑其中一类避免在无浏览器环境下误跑浏览器测试。小结与延伸阅读两套配置vitest.config.tsjsdom 单元测试与 vitest.browser.config.tsPlaywright 无头 Chromium 浏览器测试通过文件后缀约定互斥单元测试适合纯逻辑、i18n 覆盖、hook 逻辑与 Electron 主进程逻辑{src,electron}均在匹配范围内浏览器测试适合 WebCodecs、WebGL/Pixi.js 渲染与 Blob 导出注意 SwiftShader 启动参数与小尺寸固件带来的提速相关真实用例compositeLayout.test.ts、tutorialHelpTranslations.test.ts、videoExporter.browser.test.ts、gifExporter.browser.test.ts、recordingStream.test.ts固件资源位于 tests/fixtures/脚本入口见 package.json 中的test、test:watch、test:browser、test:browser:install。【免费下载链接】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),仅供参考
返回列表