playwright-e2e-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePlaywright E2E Testing (TypeScript)
Playwright 端到端测试(TypeScript)
Comprehensive toolkit for end-to-end testing of web applications using Playwright with TypeScript. Enables robust UI testing, API validation, and responsive design verification following best practices.
Activation: This skill is triggered when working with Playwright tests, browser automation, E2E testing, API testing with Playwright, or test infrastructure setup.
这是一套使用Playwright与TypeScript进行Web应用端到端测试的综合工具集,可遵循最佳实践实现可靠的UI测试、API验证及响应式设计验证。
激活条件: 当处理Playwright测试、浏览器自动化、端到端测试、基于Playwright的API测试或测试基础设施搭建时,将触发本技能。
When to Use This Skill
何时使用本技能
- Write E2E tests for user flows, forms, navigation, and authentication
- API testing via fixture or network interception during UI tests
request - Responsive testing across mobile, tablet, and desktop viewports
- Debug flaky tests using traces, screenshots, videos, and Playwright Inspector
- Setup test infrastructure with Page Object Model and fixtures
- Mock/intercept APIs for isolated, deterministic testing
- Visual regression testing with screenshot comparisons
- 编写端到端测试:针对用户流程、表单、导航及身份验证
- API测试:通过测试夹具或UI测试中的网络拦截实现
request - 响应式测试:覆盖移动端、平板及桌面端视口
- 调试不稳定测试:使用追踪记录、截图、视频及Playwright Inspector
- 搭建测试基础设施:基于Page Object Model和测试夹具
- 模拟/拦截API:实现隔离、可预测的测试
- 视觉回归测试:通过截图对比实现
Prerequisites
前置要求
| Requirement | Details |
|---|---|
| Node.js | v18+ recommended |
| Package Manager | npm, yarn, or pnpm |
| Playwright | |
| TypeScript | |
| Browsers | Installed via |
| 要求 | 详情 |
|---|---|
| Node.js | 推荐v18及以上版本 |
| 包管理器 | npm、yarn或pnpm |
| Playwright | |
| TypeScript | |
| 浏览器 | 通过 |
Quick Setup
快速搭建
bash
undefinedbash
undefinedInitialize new project
Initialize new project
npm init playwright@latest
npm init playwright@latest
Or add to existing project
Or add to existing project
npm install -D @playwright/test
npx playwright install
undefinednpm install -D @playwright/test
npx playwright install
undefinedFirst Questions to Ask
需先确认的问题
Before writing tests, clarify:
- App URL: Local dev server command + port, or staging URL?
- Critical flows: Which user journeys must be covered (happy path + error states)?
- Browsers/devices: Chrome, Firefox, Safari? Mobile viewports?
- API strategy: Real backend, mocked responses, or hybrid?
- Test data: Seed data available? Reset/cleanup strategy?
编写测试前,请明确:
- 应用URL:本地开发服务器命令+端口,或预发布环境URL?
- 核心流程:必须覆盖哪些用户旅程(主流程+异常场景)?
- 浏览器/设备:Chrome、Firefox、Safari?移动端视口?
- API策略:使用真实后端、模拟响应还是混合方式?
- 测试数据:是否有预置数据?重置/清理策略是什么?
Core Principles
核心原则
1. Test Runner & TypeScript
1. 测试运行器与TypeScript
Always use with TypeScript for type safety and better IDE support.
@playwright/testtypescript
import { test, expect } from '@playwright/test';
test('user can login', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('user@test.com');
await page.getByLabel('Password').fill('password123');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page).toHaveURL(/.*dashboard/);
});始终结合使用与TypeScript,以获得类型安全及更好的IDE支持。
@playwright/testtypescript
import { test, expect } from '@playwright/test';
test('user can login', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('user@test.com');
await page.getByLabel('Password').fill('password123');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page).toHaveURL(/.*dashboard/);
});2. Locator Strategy (Priority Order)
2. 定位器策略(优先级顺序)
| Priority | Locator | Example |
|---|---|---|
| 1 | Role + accessible name | |
| 2 | Label | |
| 3 | Placeholder | |
| 4 | Text | |
| 5 | Test ID | |
| 6 | CSS (avoid) | |
See Locator Strategies Guide for detailed patterns.
| 优先级 | 定位器 | 示例 |
|---|---|---|
| 1 | 角色+可访问名称 | |
| 2 | 标签 | |
| 3 | 占位符 | |
| 4 | 文本 | |
| 5 | 测试ID | |
| 6 | CSS(避免使用) | |
查看定位器策略指南获取详细模式。
3. Auto-Waiting & Web-First Assertions
3. 自动等待与Web优先断言
Playwright auto-waits for elements. Never use or arbitrary timeouts.
sleep()typescript
// ✅ Web-first assertions (auto-retry)
await expect(page.getByRole('alert')).toBeVisible();
await expect(page).toHaveURL(/dashboard/);
await expect(page.getByTestId('status')).toHaveText('Success!');
// ❌ Avoid manual waits
await page.waitForTimeout(2000); // Bad practicePlaywright会自动等待元素加载完成。切勿使用或任意超时设置。
sleep()typescript
// ✅ Web-first assertions (auto-retry)
await expect(page.getByRole('alert')).toBeVisible();
await expect(page).toHaveURL(/dashboard/);
await expect(page.getByTestId('status')).toHaveText('Success!');
// ❌ Avoid manual waits
await page.waitForTimeout(2000); // Bad practice4. Test Structure with Steps
4. 带步骤的测试结构
Use for readable reports and failure localization:
test.step()typescript
test('checkout flow', async ({ page }) => {
await test.step('Add item to cart', async () => {
await page.goto('/products/1');
await page.getByRole('button', { name: 'Add to Cart' }).click();
});
await test.step('Complete checkout', async () => {
await page.goto('/checkout');
await page.getByRole('button', { name: 'Pay Now' }).click();
});
await test.step('Verify confirmation', async () => {
await expect(page.getByRole('heading')).toContainText('Order Confirmed');
});
});使用提升报告可读性并定位失败点:
test.step()typescript
test('checkout flow', async ({ page }) => {
await test.step('Add item to cart', async () => {
await page.goto('/products/1');
await page.getByRole('button', { name: 'Add to Cart' }).click();
});
await test.step('Complete checkout', async () => {
await page.goto('/checkout');
await page.getByRole('button', { name: 'Pay Now' }).click();
});
await test.step('Verify confirmation', async () => {
await expect(page.getByRole('heading')).toContainText('Order Confirmed');
});
});Key Workflows
关键工作流
Forms & Navigation
表单与导航
typescript
// Form submit and wait for navigation (auto-waiting)
await page.getByRole('button', { name: 'Login' }).click();
await expect(page).toHaveURL(/.*dashboard/);
// Form with API response validation
const responsePromise = page.waitForResponse(
r => r.url().includes('/api/login') && r.status() === 200
);
await page.getByRole('button', { name: 'Login' }).click();
const response = await responsePromise;typescript
// Form submit and wait for navigation (auto-waiting)
await page.getByRole('button', { name: 'Login' }).click();
await expect(page).toHaveURL(/.*dashboard/);
// Form with API response validation
const responsePromise = page.waitForResponse(
r => r.url().includes('/api/login') && r.status() === 200
);
await page.getByRole('button', { name: 'Login' }).click();
const response = await responsePromise;API Testing (Request Fixture)
API测试(Request夹具)
typescript
test('API health check', async ({ request }) => {
const response = await request.get('/api/health');
expect(response.ok()).toBeTruthy();
expect(await response.json()).toMatchObject({ status: 'ok' });
});typescript
test('API health check', async ({ request }) => {
const response = await request.get('/api/health');
expect(response.ok()).toBeTruthy();
expect(await response.json()).toMatchObject({ status: 'ok' });
});API Mocking & Interception
API模拟与拦截
typescript
test('handles API error', async ({ page }) => {
await page.route('**/api/users', route =>
route.fulfill({ status: 500, body: JSON.stringify({ error: 'Server error' }) })
);
await page.goto('/users');
await expect(page.getByRole('alert')).toContainText('Something went wrong');
});typescript
test('handles API error', async ({ page }) => {
await page.route('**/api/users', route =>
route.fulfill({ status: 500, body: JSON.stringify({ error: 'Server error' }) })
);
await page.goto('/users');
await expect(page.getByRole('alert')).toContainText('Something went wrong');
});Responsive Testing
响应式测试
typescript
const viewports = [
{ width: 375, height: 667, name: 'mobile' },
{ width: 768, height: 1024, name: 'tablet' },
{ width: 1280, height: 720, name: 'desktop' },
];
for (const vp of viewports) {
test(`navigation works on ${vp.name}`, async ({ page }) => {
await page.setViewportSize(vp);
await page.goto('/');
// Mobile: hamburger menu
if (vp.width < 768) {
await page.getByRole('button', { name: /menu/i }).click();
}
await page.getByRole('link', { name: 'About' }).click();
await expect(page).toHaveURL(/about/);
});
}typescript
const viewports = [
{ width: 375, height: 667, name: 'mobile' },
{ width: 768, height: 1024, name: 'tablet' },
{ width: 1280, height: 720, name: 'desktop' },
];
for (const vp of viewports) {
test(`navigation works on ${vp.name}`, async ({ page }) => {
await page.setViewportSize(vp);
await page.goto('/');
// Mobile: hamburger menu
if (vp.width < 768) {
await page.getByRole('button', { name: /menu/i }).click();
}
await page.getByRole('link', { name: 'About' }).click();
await expect(page).toHaveURL(/about/);
});
}Configuration
配置
Use for project-wide settings:
playwright.config.tstypescript
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
retries: process.env.CI ? 2 : 0,
reporter: [['html'], ['junit', { outputFile: 'results.xml' }]],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{ name: 'chromium', use: devices['Desktop Chrome'] },
{ name: 'mobile', use: devices['Pixel 5'] },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});使用进行项目级设置:
playwright.config.tstypescript
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
retries: process.env.CI ? 2 : 0,
reporter: [['html'], ['junit', { outputFile: 'results.xml' }]],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{ name: 'chromium', use: devices['Desktop Chrome'] },
{ name: 'mobile', use: devices['Pixel 5'] },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});Troubleshooting
故障排查
| Problem | Cause | Solution |
|---|---|---|
| Element not found | Wrong locator or not rendered | Use |
| Timeout waiting | Element hidden or slow load | Check for overlays, increase timeout, use |
| Flaky tests | Race conditions, animations | Add |
| Strict mode violation | Multiple elements match | Use |
| Screenshots differ | Dynamic content | Mask dynamic areas, use deterministic data |
| CI fails, local passes | Environment differences | Check |
| API mock not working | Route pattern mismatch | Use |
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 元素未找到 | 定位器错误或元素未渲染 | 使用 |
| 等待超时 | 元素隐藏或加载缓慢 | 检查是否有遮罩层,增加超时时间,使用 |
| 测试不稳定 | 竞态条件、动画 | 添加 |
| 严格模式违规 | 多个元素匹配定位器 | 使用 |
| 截图不一致 | 动态内容 | 遮盖动态区域,使用可预测的测试数据 |
| CI环境失败,本地正常 | 环境差异 | 检查 |
| API模拟不生效 | 路由模式不匹配 | 使用 |
CLI Quick Reference
CLI快速参考
| Command | Description |
|---|---|
| Run all tests headless |
| Open UI mode (interactive) |
| Run with visible browser |
| Run with Playwright Inspector |
| Run tests matching pattern |
| Run specific project |
| Open HTML report |
| Generate tests by recording |
| Debug with Inspector |
| Verbose API logging |
| 命令 | 说明 |
|---|---|
| 以无头模式运行所有测试 |
| 打开UI模式(交互式) |
| 以可见浏览器模式运行测试 |
| 通过Playwright Inspector运行测试 |
| 运行匹配指定模式的测试 |
| 运行指定项目的测试 |
| 打开HTML测试报告 |
| 通过录制生成测试代码 |
| 使用Inspector调试测试 |
| 启用详细API日志 |
References
参考文档
| Document | Content |
|---|---|
| Snippets | Ready-to-use code patterns |
| Locator Strategies | Complete locator guide |
| Page Object Model | POM implementation patterns |
| Debugging Guide | Troubleshooting & debugging techniques |
| 文档 | 内容 |
|---|---|
| Snippets | 可直接使用的代码模板 |
| Locator Strategies | 完整的定位器指南 |
| Page Object Model | Page Object Model实现模式 |
| Debugging Guide | 故障排查与调试技巧 |