ARTICLE DETAIL

资讯详情

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

Playwright 组件测试迁移指南:从 @playwright/experimental-ct-react / -vue 平滑升级到 Story Gallery 模式

Playwright 组件测试迁移指南:从 @playwright/experimental-ct-react / -vue 平滑升级到 Story Gallery 模式 Playwright 组件测试迁移指南从 playwright/experimental-ct-react / -vue 平滑升级到 Story Gallery 模式【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwrightPlaywright 自 1.63 起移除了playwright/experimental-ct-react/playwright/experimental-ct-vue等旧组件测试CT运行时组件测试改由Story Gallery故事画廊模式承接被测组件以 story 导出形式运行在真实浏览器中测试则退化为普通的playwright/test端到端用例。本文基于 migration.md 展开面向正在使用旧 CT 包的项目提供从概念映射、分步迁移到完整代码对照的实操方案读完即可按图索骥把最后一个 CT spec 平滑移植到 Gallery 模式下。为什么需要迁移两种组件测试架构的本质差异理解迁移动机要先看清旧 CT 与新 Gallery 模式的架构分界旧 CTplaywright/experimental-ct-*测试文件中的 JSX 在 Node 进程里被编译再把组件结构marshalling序列化搬运进浏览器渲染。这意味着组件树、children、回调闭包都需要跨越进程边界传递这也是它必须自带一套独立运行时playwright/index.html、ctViteConfig、ctPort等的原因。Story Gallery 模式测试中的场景被改写为story 导出在浏览器里原生运行——结构用哪个组件、它的 children、providers与行为状态与回调被记录进一个隐藏表单供测试断言全部落在 story 内纯数据型 props 通过mount(storyId, props)传入。该模式不再需要任何独立测试运行时Story 就是组件源码旁的普通导出Gallery 页面运行在应用自己的 dev server 上测试仅用playwright/test内置的mountfixture 驱动它。二者对比如下维度旧 CTGallery 模式JSX 编译位置Node 进程内编译后 marshall 进浏览器story 在浏览器内原生执行结构 行为由测试文件内联 JSX 提供由 story 导出承载组件、children、providers 状态/回调运行时独立 CT 运行时playwright/index.ts、.cache等应用自身的 dev server 一个 gallery 页面测试形态专用mountfixture 的特殊测试普通playwright/teste2e 测试版本前提请在 1.62 完成迁移文档明确提示CT 相关包已在 Playwright 1.63 被移除且不再发布因此迁移窗口是钉在 Playwright 1.62 上逐个移植 spec移植完成后一次性解除版本锁定并升级。本仓库当前的 packages/playwright-core/package.json 版本号为1.64.0-next仓库根目录packages/下也确已不再包含任何playwright/experimental-ct-*实现——这从侧面印证了 1.63 已是过去式停留在旧 CT 上的项目只能主动迁移不能指望依赖升级。核心概念映射表把每条 CT 用法翻译成 Gallery 用法迁移的本质是逐条翻译。文档给出的映射表是移植工作的总纲这里完整保留并逐条解读playwright/experimental-ct-*用法Gallery 模式对应写法mount(Button title… onClick{spy} /)有状态 storystory 提供onClick把效果记录进一个隐藏表单 input测试用toHaveValue()断言测试里传入的纯数据 props语义不变mount(id, props)测试里传入的 JSX children / 插槽无法跨进程边界——每种组合各烘焙成一个 story 导出Vue 中插槽密集的场景推荐.story.vuecomponent.update(Button count{2} /)component.update({ count: 2 })——保留状态的重渲染需要 gallery 复用其根实例见 gallery-spec.mdcomponent.unmount()component.unmount()——由 gallery 的window.unmount()支撑playwright/index.ts中的beforeMount/afterMountgallery 的window.mount函数体全局级或 story decorator单 story 级按测试变化的hooksConfigpropsmount(App/Routing, { route: /dashboard })——由 story/decorator 解释这些 propsNode 侧的routerfixture / MSW handlers测试里的page.route()或 story/decorator 内启动 MSWsetupWorkerplaywright/index.html样式、字体、主题gallery 的index.html/ 入口模块里的 importsctViteConfig、ctPort、ctTemplateDir、ctCacheDir全部废弃——gallery 运行在应用自己的 dev server 上端口配置在webServerbaseURL目录固定在playwright/gallery/从playwright/experimental-ct-react导入defineConfig改从playwright/test导入普通defineConfig配baseURL gallery 地址、serviceWorkers: block、reuseContext: true详见 SKILL.md读这张表的要点是三个思维转换回调从测试侧传入变为story 侧记录。跨进程传闭包是旧 CT 的根源性复杂Gallery 模式的约定是一切组件需要的东西都在 story 里搭好一切测试要断言的东西都通过页面可观测。story 创建状态、提供回调、把状态写进隐藏表单测试再做 web-first 断言。结构与数据分离。测试只负责选哪个 story 传什么纯数据 props不再负责拼组件树。参数化让位给导出化。同一组件的不同形态优先各自成为一个命名导出而不是通过参数在测试里展开——story 本身是可检索、可评审的组件状态文档。迁移五步流程第 1 步按 SKILL.md 搭好 gallery 与配置旧项目并行保留先搭建新底座、保持旧 CT 项目继续运行直到最后一个 spec 移植完毕。搭建工作完全遵循 SKILL.md在project/playwright/gallery/下实现 gallery 页面暴露出window.mount/window.unmountstory 文件与组件源码同目录测试目录放普通 Playwright spec。若应用本身跑在 Vite 上gallery 由现有 dev server 直接服务其他场景Next.js、webpack 或无 dev server则起一个独立小 dev server 服务 gallery 页面。playwright.config.ts参考配置如下有既有配置时是合并而非覆盖projects: [ { name: components, testDir: ./tests/components, use: { ...devices[Desktop Chrome], baseURL: http://localhost:5173/playwright/gallery/index.html, serviceWorkers: block, // 防止应用自身的 service worker 用缓存响应遮蔽 page.route() mock reuseContext: true, // worker 内复用 browser context大幅加速组件套件 }, }, ], webServer: { command: npm run dev, // 或: npx vite --config playwright/vite.config.ts url: http://localhost:5173/playwright/gallery/index.html, // 独立 server 则指向其端口 reuseExistingServer: !process.env.CI, },其中两个关键选项mount会导航到baseURL因此它必须指向 gallery 地址reuseContext: true复刻了旧 CT 运行时按 worker 复用 context 的行为是组件套件的大幅提速来源在 packages/playwright/src/index.ts 中它作为 worker 作用域的选项与PW_TEST_REUSE_CONTEXT环境变量一起生效。第 2 步逐个拆分 CT spec 中的mount(…/)调用对每个 CT spec把每个mount(…/)拆成两部分JSX 结构 → 组件旁的 story 导出。providers、mock 数据、状态、回调都焊进 story纯数据 props → 留在测试里作为mount的第二个参数。回调 spy 转成story 状态 隐藏表单记录仅变化数据 props 的调用点通常只需要一个把 props 透传下去的通用 storyexport const Default (props: ButtonProps) Button titleSubmit {...props} /;这样不同测试对同一 story 传入不同 props 即可覆盖不同数据面story 文件本身保持极薄。第 3 步重写 spec 的 API 调用面重写时遵循三条机械替换规则导入改自playwright/testimport { test, expect } from playwright/test;mount(X a{1}/)→mount(X/Default, { a: 1 })update(X a{2}/)→update({ a: 2 })unmount()保持不变。注意返回值的语义变化mount返回的是gallery 根节点#root的 locator因此查询必须从它作用域化出发——component.getByRole(button).click()而不是对根节点整体click()。spy 断言改写为对 story 记录状态的toHaveValue()断言。第 4 步移植beforeMount钩子beforeMount/afterMount的归宿分两层应用级全局初始化providers、store、插件等→ 放进 gallery 的window.mount函数体。gallery-spec 明确说明window.mount本身就是你的 setup/teardown 钩子没有独立的钩子注册表你拥有的这个函数就是钩子——在渲染前装 providers/种子数据/启动浏览器内 mock server渲染后做收尾全部按测试传入的story/props分支按测试变化的hooksConfig分支→ 变为 props由 story 或 decorator 解释执行例如mount(App/Routing, { route: /dashboard })。第 5 步全绿后清理旧 CT 残留当所有 spec 在 Gallery 模式下通过后一次性执行清理从 Playwright 配置中删除 CT project移除playwright/experimental-ct-*依赖删除playwright/index.html、playwright/index.ts*与playwright/.cache解除 Playwright 版本锁定并升级。Before / After 完整代码对照以点击按钮并收集回调数据这一最典型的旧 CT 场景为例完整对照如下。迁移前CT spec// Before (CT) import { test, expect } from playwright/experimental-ct-react; import Button from ../src/components/Button; test(click, async ({ mount }) { const messages: string[] []; const component await mount(Button titleSubmit onClick{data messages.push(data)} /); await component.click(); expect(messages).toEqual([hello]); });迁移后——story 文件与组件同目录// After: src/components/Button.story.tsx import Button from ./Button; export const Default (props: { onClick?: (data: string) void }) Button titleSubmit {...props} /;迁移后——spec 文件// After: src/components/Button.spec.ts import { test, expect } from playwright/test; test(click, async ({ mount }) { const messages: string[] []; const component await mount(components/Button/Default, { onClick: (data: string) messages.push(data) }); await component.click(); expect(messages).toEqual([hello]); });对照要点测试不再 import 组件本身改 importplaywright/test组件树彻底从测试文件中消失story idcomponents/Button/Default由src/下路径去掉.story.*后缀 导出名推导而来组件导入、props 类型与渲染都收敛到了 story 文件内浏览器端只有 story 需要知道组件的一切。对回调处理这里展示的是props 透传形态文档在概念映射中更推荐另一种更稳健的形态——有状态 storystory 内部用useState持有状态、提供回调、把结果写入带data-testid的隐藏输入框测试断言toHaveValue(true)之类的值。完整可运行的例子可直接参考模板文件 templates/react/Button.story.tsx 与 templates/react/button.spec.ts其中CountsClicksstory 就是标准写法export const CountsClicks () { const [clicks, setClicks] useState(0); return Button titleSubmit onClick{() setClicks(count count 1)} / form hiddeninput contenteditable="false">【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表