mcpField
P
playwright-testing

playwright-testing

github.com/terminalskills测试与质量18447 次安装今天更新
代码仓库

关于 playwright-testing

playwright-testing 是帮助 AI Agent 使用 Playwright 编写端到端测试的 Skill。内容涵盖项目配置、浏览器交互、身份验证、API 模拟、视觉与无障碍检查以及 CI 集成。

主要功能

  1. 01配置 Playwright 并编写测试
  2. 02自动等待与语义化定位器
  3. 03身份验证和页面对象模式
  4. 04模拟 API 响应
  5. 05视觉与无障碍测试
  6. 06CI 集成与调试命令

适用场景

  1. 01使用可复用页面对象测试登录流程
  2. 02通过模拟 API 验证错误和空状态
  3. 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 将:

  1. 创建 tests/pages/login.page.ts,使用基于角色的选择器(getByLabel、getByRole)定位电子邮件、密码、提交按钮和错误消息
  2. 为页面对象添加 goto()、login() 和 expectError() 方法
  3. 编写三个测试:验证成功登录后跳转到 /dashboard,检查无效凭据时显示的错误提示,以及检查提交空表单时显示的验证消息
  4. 设置 beforeEach,用于实例化页面对象并导航到 /login
示例 2:模拟 API 响应以测试错误状态和空状态

用户提示词:“为我们的仪表板添加测试,验证它能妥善处理 API 错误,并在没有项目时显示空状态。”

Agent 将:

  1. 使用 page.route('**/api/projects', ...) 拦截 API 调用
  2. 对错误测试:返回状态码 500,并验证出现“加载项目失败”消息和“重试”按钮
  3. 对空状态测试:返回状态码 200 和空的项目数组,然后验证显示“还没有项目”空状态
  4. 两个测试都独立运行,无需真实后端,因此在 CI 中快速且可靠

指南

  • 优先使用基于角色的定位器(getByRole、getByLabel、getByText),而非 CSS 选择器——它们更能适应 DOM 变更
  • 仅在没有可用的语义定位器时添加 data-testid 属性
  • 切勿使用 page.waitForTimeout()——请使用自动等待定位器,或设置超时的 expect
  • 只运行一次身份验证设置,并通过 storageState 在测试间共享状态
  • 对于复杂页面,使用页面对象,让测试更易读
  • 在测试中模拟外部 API——测试 UI 行为,而非第三方服务
  • 并行运行测试(fullyParallel: true)以提高速度
  • 在首次重试时捕获跟踪记录——这对排查 CI 中不稳定的测试非常有帮助
  • 使用 webServer 配置在测试期间自动启动开发服务器
  • 将视觉快照纳入版本控制,并在 PR 中检查变更

评论

支持 Markdown