关于 playwright-testing
playwright-testing 是帮助 AI Agent 使用 Playwright 编写端到端测试的 Skill。内容涵盖项目配置、浏览器交互、身份验证、API 模拟、视觉与无障碍检查以及 CI 集成。
主要功能
- 01配置 Playwright 并编写测试
- 02自动等待与语义化定位器
- 03身份验证和页面对象模式
- 04模拟 API 响应
- 05视觉与无障碍测试
- 06CI 集成与调试命令
适用场景
- 01使用可复用页面对象测试登录流程
- 02通过模拟 API 验证错误和空状态
- 03在 CI 中运行浏览器与无障碍测试
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ terminalskills/ skills --skill playwright-testing安装后在 Claude Code 中输入/playwright-testing确认已加载。
附带文件_scores.json
详细介绍
Playwright 测试
概述
此技能可帮助 AI Agent 使用 Playwright 编写可靠的端到端测试。内容涵盖项目设置、使用自动等待定位器编写测试、页面对象模式、身份验证处理、API 模拟、视觉回归、无障碍测试以及 CI/CD 集成。
说明
步骤 1:项目设置
npm init playwright@latest
# Or add to existing project:
npm install -D @playwright/test && npx playwright install
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [['html', { open: 'never' }], ['junit', { outputFile: 'test-results/junit.xml' }]],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
步骤 2:编写测试
import { test, expect } from '@playwright/test';
test.describe('Homepage', () => {
test('displays hero and navigates to features', async ({ page }) => {
await page.goto('/');
await expect(page.getByRole('heading', { name: /welcome/i })).toBeVisible();
await page.getByRole('link', { name: 'View Features' }).click();
await expect(page).toHaveURL(/.*features/);
});
test('shows search results', async ({ page }) => {
await page.goto('/');
await page.getByPlaceholder('Search...').fill('playwright');
await page.getByPlaceholder('Search...').press('Enter');
await expect(page.getByTestId('search-results')).toBeVisible();
await expect(page.getByTestId('search-result-item')).toHaveCount(10);
});
});
步骤 3:身份验证模式
// tests/auth.setup.ts — authenticate once, reuse across tests
import { test as setup } from '@playwright/test';
import path from 'path';
const authFile = path.join(__dirname, '.auth/user.json');
setup('authenticate', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('[email protected]');
await page.getByLabel('Password').fill('password123');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page).toHaveURL('/dashboard');
await page.context().storageState({ path: authFile });
});
// In config: add setup dependency
// projects: [
// { name: 'setup', testMatch: /.*\.setup\.ts/ },
// { name: 'chromium', use: { ...devices['Desktop Chrome'], storageState: 'tests/.auth/user.json' }, dependencies: ['setup'] },
// ]
步骤 4:页面对象模式
// tests/pages/login.page.ts
import { Page, Locator, expect } from '@playwright/test';
export class LoginPage {
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
readonly errorMessage: Locator;
constructor(private page: Page) {
this.emailInput = page.getByLabel('Email');
this.passwordInput = page.getByLabel('Password');
this.submitButton = page.getByRole('button', { name: 'Sign in' });
this.errorMessage = page.getByRole('alert');
}
async goto() { await this.page.goto('/login'); }
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
async expectError(msg: string) { await expect(this.errorMessage).toContainText(msg); }
}
// tests/login.spec.ts
test.describe('Login', () => {
let loginPage: LoginPage;
test.beforeEach(async ({ page }) => { loginPage = new LoginPage(page); await loginPage.goto(); });
test('successful login redirects to dashboard', async ({ page }) => {
await loginPage.login('[email protected]', 'password123');
await expect(page).toHaveURL('/dashboard');
});
test('invalid credentials show error', async () => {
await loginPage.login('[email protected]', 'wrong');
await loginPage.expectError('Invalid email or password');
});
});
步骤 5:API 模拟
test('shows error state when API fails', async ({ page }) => {
await page.route('**/api/projects', (route) => route.fulfill({
status: 500, contentType: 'application/json',
body: JSON.stringify({ error: 'Internal server error' }),
}));
await page.goto('/dashboard');
await expect(page.getByText('Failed to load projects')).toBeVisible();
});
test('modify API response for premium features', async ({ page }) => {
await page.route('**/api/user', async (route) => {
const response = await route.fetch();
const json = await response.json();
json.plan = 'enterprise';
await route.fulfill({ response, json });
});
await page.goto('/settings');
await expect(page.getByText('Enterprise Plan')).toBeVisible();
});
步骤 6:视觉与无障碍测试
test('homepage visual regression', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png', { fullPage: true, maxDiffPixelRatio: 0.01 });
});
// Update snapshots: npx playwright test --update-snapshots
import AxeBuilder from '@axe-core/playwright';
test('no accessibility violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa']).analyze();
expect(results.violations).toEqual([]);
});
步骤 7:CI 集成
# .github/workflows/e2e.yml
name: E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with: { name: playwright-report, path: playwright-report/, retention-days: 30 }
常用命令
npx playwright test # Run all tests
npx playwright test tests/login.spec.ts # Specific file
npx playwright test --headed # See browser
npx playwright test --project=chromium # Specific browser
npx playwright test --debug # Step-through debugger
npx playwright codegen http://localhost:3000 # Record actions
npx playwright show-report # View HTML report
示例
示例 1:使用页面对象为登录流程添加端到端测试
用户提示词:“为我们的登录页面编写 Playwright 测试,使用页面对象模式,测试成功登录、无效凭据和表单验证。”
Agent 将:
- 创建
tests/pages/login.page.ts,使用基于角色的选择器(getByLabel、getByRole)定位电子邮件、密码、提交按钮和错误消息 - 为页面对象添加
goto()、login()和expectError()方法 - 编写三个测试:验证成功登录后跳转到
/dashboard,检查无效凭据时显示的错误提示,以及检查提交空表单时显示的验证消息 - 设置
beforeEach,用于实例化页面对象并导航到/login
示例 2:模拟 API 响应以测试错误状态和空状态
用户提示词:“为我们的仪表板添加测试,验证它能妥善处理 API 错误,并在没有项目时显示空状态。”
Agent 将:
- 使用
page.route('**/api/projects', ...)拦截 API 调用 - 对错误测试:返回状态码 500,并验证出现“加载项目失败”消息和“重试”按钮
- 对空状态测试:返回状态码 200 和空的项目数组,然后验证显示“还没有项目”空状态
- 两个测试都独立运行,无需真实后端,因此在 CI 中快速且可靠
指南
- 优先使用基于角色的定位器(
getByRole、getByLabel、getByText),而非 CSS 选择器——它们更能适应 DOM 变更 - 仅在没有可用的语义定位器时添加
data-testid属性 - 切勿使用
page.waitForTimeout()——请使用自动等待定位器,或设置超时的expect - 只运行一次身份验证设置,并通过
storageState在测试间共享状态 - 对于复杂页面,使用页面对象,让测试更易读
- 在测试中模拟外部 API——测试 UI 行为,而非第三方服务
- 并行运行测试(
fullyParallel: true)以提高速度 - 在首次重试时捕获跟踪记录——这对排查 CI 中不稳定的测试非常有帮助
- 使用
webServer配置在测试期间自动启动开发服务器 - 将视觉快照纳入版本控制,并在 PR 中检查变更
评论
支持 Markdown