实验性:组件
Playwright Test 现在可以测试您的组件。
示例
典型的组件测试如下所示:
test('event should work', async ({ mount }) => {
let clicked = false;
// 挂载组件。返回指向组件的定位器。
const component = await mount(<Button title="Submit"
onClick={() => clicked = true}>
</Button>);
// 与任何 Playwright 测试一样,断言定位器文本。
await expect(component).toContainText('Submit');
// 执行定位器点击。这将触发事件。
await component.click();
// 断言相应的事件已被触发。
expect(clicked).toBeTruthy();
});
如何开始
将 Playwright Test 添加到现有的 React、Vue、Svelte 或 Solid 项目很容易。以下是为使用 TypeScript 模板的示例 create-react-app 启用 Playwright Test 的步骤。
步骤 1:为您的相应框架安装 Playwright Test 组件
- npm
- yarn
npm init playwright@latest -- --ct
yarn create playwright --ct
此步骤在您的工作区中创建几个文件:
playwright/index.html
此文件定义了一个 html 文件,该文件将用于在测试期间渲染组件。它必须包含 id="root" 的元素,这是组件挂载的位置。它还必须链接名为 playwright/index.[tj]s 的脚本。
<html lang="en">
<body>
<div id="root"></div>
<script type="module" src="./index.ts"></script>
</body>
</html>
playwright/index.ts
您可以使用此脚本包含样式表、应用主题并将代码注入到挂载组件的页面中。它可以是 .js 或 .ts 文件。
// 在此处应用主题,在此处添加组件在运行时需要的任何内容。
步骤 2. 创建测试文件 src/App.spec.{ts,tsx}
- React
- Vue
- Svelte
import { test, expect } from '@playwright/experimental-ct-react';
import App from './App';
test.use({ viewport: { width: 500, height: 500 } });
test('should work', async ({ mount }) => {
const component = await mount(<App></App>);
await expect(component).toContainText('Learn React');
});
import { test, expect } from '@playwright/experimental-ct-vue';
import App from './App.vue';
test.use({ viewport: { width: 500, height: 500 } });
test('should work', async ({ mount }) => {
const component = await mount(<App></App>);
await expect(component).toContainText('Vite + Vue');
});
If using TypeScript and Vue make sure to add a vue.d.ts file to your project:
declare module '*.vue';
import { test, expect } from '@playwright/experimental-ct-svelte';
import App from './App.svelte';
test.use({ viewport: { width: 500, height: 500 } });
test('should work', async ({ mount }) => {
const component = await mount(App);
await expect(component).toContainText('Vite + Svelte');
});
步骤 3. 运行测试
您可以使用 VS Code 扩展或命令行运行测试。
npm run test-ct
延伸阅读:配置报告、浏览器、跟踪
请参阅 Playwright 配置以配置您的项目。
钩子
您可以使用 beforeMount 和 afterMount 钩子来配置您的应用程序。这使您可以设置应用程序路由器、假服务器等,为您提供所需的灵活性。您还可以从测试的 mount 调用中传递自定义配置,该配置可从 hooksConfig fixture 访问。
playwright/index.ts
这包括在挂载组件之前/之后需要运行的任何配置。以下是如何设置 miragejs 模拟库的示例:
import { beforeMount } from '@playwright/experimental-ct-react/hooks';
import { createServer } from "miragejs"
beforeMount(async ({ hooksConfig }) => {
// 如果未提供自定义配置,则设置默认值
const users = hooksConfig.users ?? [
{ id: "1", name: "Luke" },
{ id: "2", name: "Leia" },
{ id: "3", name: "Han" },
];
createServer({
routes() {
this.get("/api/users", () => users)
},
});
});
在您的测试文件中:
// src/Users.spec.tsx
import { test, expect } from "@playwright/experimental-ct-react";
import React from "react";
import { Users } from "./Users";
test("should work", async ({ mount }) => {
const component = await mount(<Users />, {
hooksConfig: {
users: [
{ id: "4", name: "Anakin" },
{ id: "5", name: "Padme" },
]
}
});
await expect(component.locator("li")).toContainText([
"Anakin",
"Padme",
]);
});
底层原理
当 Playwright Test 用于测试 Web 组件时,测试在 Node.js 中运行,而组件在真实浏览器中运行。这将两全其美结合在一起:组件在真实浏览器环境中运行,触发真实点击,执行真实布局,可以进行视觉回归。同时,测试可以使用 Node.js 的所有功能以及所有 Playwright Test 功能。因此,在组件测试期间可以使用相同的并行、参数化测试以及相同的事后跟踪故事。
实现方式如下:
- 一旦执行测试,Playwright 就会创建测试所需的组件列表。
- 然后它编译一个包含这些组件的包,并使用本地静态 Web 服务器提供服务。
- 在测试中调用
mount时,Playwright 导航到此包的外观页面/playwright/index.html并告诉它渲染组件。 - 事件被编组回 Node.js 环境以允许验证。
Playwright 使用 Vite 创建组件包并提供服务。
已知问题和限制
问)我无法从 TSX/JSX/组件文件中导入除组件之外的任何内容
如上所述,您只能从测试文件中导入组件。如果您在 TSX 文件中有实用方法或常量,建议将它们提取到 TS 文件中,并从组件文件和测试文件中导入这些实用方法和常量。这使我们能够不在基于 Node 的测试运行器中加载任何组件代码,并保持 Playwright 快速执行您的测试。
问)我有一个已经使用 Vite 的项目。我可以重用配置吗?
目前,Playwright 是与打包器无关的,因此它不会重用您现有的 Vite 配置。您的配置可能有很多我们无法重用的东西。因此,现在您需要将路径映射和其他高级设置复制到 Playwright 配置的 ctViteConfig 属性中。
import type { PlaywrightTestConfig } from '@playwright/experimental-ct-react';
const config: PlaywrightTestConfig = {
use: {
ctViteConfig: { ... },
},
};
export default config
问)@playwright/test 和 @playwright/experimental-ct-{react,svelte,vue,solid} 有什么区别?
test('…', async { mount, page, context } => {
// …
});
@playwright/experimental-ct-{react,svelte,vue,solid} 包装 @playwright/test 以提供一个名为 mount 的额外内置组件测试特定 fixture:
- React
- Vue
- Svelte
import { test, expect } from '@playwright/experimental-ct-react'
import HelloWorld from './HelloWorld.tsx'
test.use({ viewport: { width: 500, height: 500 } })
test('should work', async ({ mount }) => {
const component = await mount(<HelloWorld msg='greetings' />);
await expect(component).toContainText('Greetings')
})
import { test, expect } from '@playwright/experimental-ct-vue'
import HelloWorld from './HelloWorld.vue'
test.use({ viewport: { width: 500, height: 500 } })
test('should work', async ({ mount }) => {
const component = await mount(HelloWorld, {
props: {
msg: 'Greetings'
}
});
await expect(component).toContainText('Greetings')
})
import { test, expect } from '@playwright/experimental-ct-vue'
import NamedSlots from './NamedSlots.vue'
test.use({ viewport: { width: 500, height: 500 } })
test('named slots should work', async ({ mount }) => {
const component = await mount(NamedSlots, {
slots: {
header: 'Header',
main: 'Main Content',
footer: 'Footer'
}
})
await expect(component).toContainText('Header')
await expect(component).toContainText('Main Content')
await expect(component).toContainText('Footer')
})
此外,它还添加了一些您可以在 playwright-ct.config.{ts,js} 中使用的配置选项。
最后,在底层,每个测试都重用 context 和 page fixture 作为组件测试的速度优化。它在每个测试之间重置它们,因此在功能上应该等同于 @playwright/test 保证您每个测试获得一个新的、隔离的 context 和 page fixture。
问)我可以同时使用 @playwright/test 和 @playwright/experimental-ct-{react,svelte,vue} 吗?
可以。为每个使用 Playwright 配置并遵循各自的指南(E2E Playwright Test、组件测试)
问)为什么我不能将变量传递给 mount?
这是一个已知问题。以下模式不起作用:
const app = <App></App>;
await mount(app);
结果为
undefined: TypeError: Cannot read properties of undefined (reading 'map')
而这有效:
await mount(<App></App>);
问)如何使用 Vite 插件?
您可以通过 Vite 配置为测试设置指定插件。请注意,一旦您开始指定插件,您也有责任指定框架插件,在这种情况下为 vue():
import { type PlaywrightTestConfig, devices } from '@playwright/experimental-ct-vue'
import { resolve } from 'path'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
const config: PlaywrightTestConfig = {
testDir: './tests/component',
use: {
trace: 'on-first-retry',
ctViteConfig: {
plugins: [
vue(),
AutoImport({
imports: [
'vue',
'vue-router',
'@vueuse/head',
'pinia',
{
'@/store': ['useStore'],
},
],
dts: 'src/auto-imports.d.ts',
eslintrc: {
enabled: true,
},
}),
Components({
dirs: ['src/components'],
extensions: ['vue'],
}),
],
resolve: {
alias: {
'@': resolve(__dirname, './src'),
},
},
},
},
不要忘记初始化您的插件,例如,如果您使用 Pinia,请将初始化代码添加到您的 playwright/index.js 中:
import { createTestingPinia } from '@pinia/testing';
createTestingPinia({
createSpy: (args) => {
console.log('spy', args)
return () => {
console.log('spyreturns')
}
},
});