Skip to main content

实验性:组件

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 init playwright@latest -- --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}

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');
});

步骤 3. 运行测试

您可以使用 VS Code 扩展或命令行运行测试。

npm run test-ct

延伸阅读:配置报告、浏览器、跟踪

请参阅 Playwright 配置以配置您的项目。

钩子

您可以使用 beforeMountafterMount 钩子来配置您的应用程序。这使您可以设置应用程序路由器、假服务器等,为您提供所需的灵活性。您还可以从测试的 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:

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')
})

此外,它还添加了一些您可以在 playwright-ct.config.{ts,js} 中使用的配置选项。

最后,在底层,每个测试都重用 contextpage fixture 作为组件测试的速度优化。它在每个测试之间重置它们,因此在功能上应该等同于 @playwright/test 保证您每个测试获得一个新的、隔离的 contextpage 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')
}
},
});