mcpField
P
playwright

playwright

github.com/claude-dev-suite测试与质量4643 次安装3 天前更新
代码仓库

关于 playwright

面向编写浏览器端端到端测试的 Playwright 技能,涵盖测试配置、定位器、操作与断言,也介绍视觉回归和 CI 等实践。

主要功能

  1. 01浏览器测试配置
  2. 02定位器、操作与断言
  3. 03Page Object 模式
  4. 04复用身份验证状态
  5. 05视觉回归与 CI 指引

适用场景

  1. 01编写浏览器用户流程测试
  2. 02检查页面元素与交互
  3. 03在测试间复用登录状态
  4. 04通过截图审查视觉变化

MCP 相关

该技能指向 `mcp__documentation__fetch_docs`,用于获取完整的 Playwright 文档。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill playwright

安装后在 Claude Code 中输入/playwright确认已加载。

附带文件quick-ref/locators.mdquick-ref/page-objects.md

详细介绍

Playwright 核心知识

深入了解:使用 mcp__documentation__fetch_docs 并指定技术:playwright,获取完整文档。

何时不应使用此技能

  • 单元测试 - 使用 vitest 或 jest 测试独立的函数/模块
  • 组件测试 - 使用 testing-library 测试 React/Vue 组件
  • 仅 API 测试 - 使用框架专用的 HTTP 客户端或 REST 客户端
  • 性能测试 - 使用 k6 或 Lighthouse 等专用工具
  • 移动应用测试 - 使用 Appium 或 Detox 测试原生移动应用

基本测试

import { test, expect } from '@playwright/test';

test('user can login', async ({ page }) => {
  await page.goto('/login');
  await page.fill('[name="email"]', '[email protected]');
  await page.fill('[name="password"]', 'password123');
  await page.click('button[type="submit"]');

  await expect(page).toHaveURL('/dashboard');
  await expect(page.locator('h1')).toContainText('Welcome');
});

定位器

// By role (preferred)
page.getByRole('button', { name: 'Submit' });
page.getByRole('link', { name: 'Home' });
page.getByLabel('Email');
page.getByPlaceholder('Enter email');
page.getByText('Welcome');

// By test id
page.getByTestId('submit-button');

// CSS/XPath (fallback)
page.locator('.submit-btn');
page.locator('#user-form');

断言

// Visibility
await expect(locator).toBeVisible();
await expect(locator).toBeHidden();

// Text
await expect(locator).toHaveText('Hello');
await expect(locator).toContainText('Hello');

// Attributes
await expect(locator).toHaveAttribute('href', '/home');
await expect(locator).toHaveClass(/active/);

// Input
await expect(locator).toHaveValue('[email protected]');

// Page
await expect(page).toHaveURL(/dashboard/);
await expect(page).toHaveTitle('Dashboard');

操作

await page.click('button');
await page.fill('input', 'text');
await page.check('input[type="checkbox"]');
await page.selectOption('select', 'option1');
await page.hover('.menu-item');
await page.keyboard.press('Enter');

页面对象

class LoginPage {
  constructor(private page: Page) {}

  async login(email: string, password: string) {
    await this.page.fill('[name="email"]', email);
    await this.page.fill('[name="password"]', password);
    await this.page.click('button[type="submit"]');
  }
}

配置

// playwright.config.ts
export default defineConfig({
  testDir: './e2e',
  use: { baseURL: 'http://localhost:3000' },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } }
  ]
});

生产就绪

测试配置
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './e2e',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: [
    ['html'],
    ['junit', { outputFile: 'test-results/junit.xml' }],
  ],

  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'on-first-retry',
  },

  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
    { name: 'mobile', use: { ...devices['iPhone 13'] } },
  ],

  webServer: {
    command: 'npm run start',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
    timeout: 120000,
  },
});
身份验证状态
// Save auth state for reuse
// auth.setup.ts
import { test as setup } from '@playwright/test';

setup('authenticate', async ({ page }) => {
  await page.goto('/login');
  await page.fill('[name="email"]', process.env.TEST_USER_EMAIL!);
  await page.fill('[name="password"]', process.env.TEST_USER_PASSWORD!);
  await page.click('button[type="submit"]');
  await page.waitForURL('/dashboard');

  // Save storage state
  await page.context().storageState({ path: '.auth/user.json' });
});

// Use in tests
import { test } from '@playwright/test';

test.use({ storageState: '.auth/user.json' });

test('authenticated test', async ({ page }) => {
  await page.goto('/dashboard');
  // Already logged in
});
API 测试
test('API test', async ({ request }) => {
  const response = await request.post('/api/users', {
    data: { name: 'John', email: '[email protected]' },
    headers: { Authorization: `Bearer ${token}` },
  });

  expect(response.ok()).toBeTruthy();
  const user = await response.json();
  expect(user.name).toBe('John');
});

// Mock API responses
test('with mocked API', async ({ page }) => {
  await page.route('/api/users', async route => {
    await route.fulfill({
      status: 200,
      body: JSON.stringify([{ id: 1, name: 'Mock User' }]),
    });
  });

  await page.goto('/users');
  await expect(page.getByText('Mock User')).toBeVisible();
});
视觉回归
test('visual regression', async ({ page }) => {
  await page.goto('/dashboard');
  await expect(page).toHaveScreenshot('dashboard.png', {
    maxDiffPixels: 100,
    threshold: 0.2,
  });
});

// Update snapshots: npx playwright test --update-snapshots
CI 配置
# GitHub Actions
- name: Install Playwright
  run: npx playwright install --with-deps

- name: Run E2E tests
  run: npx playwright test
  env:
    BASE_URL: ${{ secrets.STAGING_URL }}

- name: Upload report
  uses: actions/upload-artifact@v3
  if: always()
  with:
    name: playwright-report
    path: playwright-report/
监控指标
指标目标
端到端测试通过率> 99%
测试执行时间< 10min
不稳定测试率< 1%
视觉差异失败全部审核
检查清单
  • 已配置多浏览器测试
  • 移动端视口测试
  • 复用身份验证状态
  • 失败时重试(仅 CI)
  • 失败时截图
  • 已启用跟踪收集
  • 使用 API 模拟实现隔离
  • 视觉回归测试
  • CI/CD 集成
  • 测试并行化
  • 使用页面对象模式

反模式

反模式缺点解决方案
通过 CSS 选择器测试脆弱,样式变化时会失效使用 getByRole、getByLabel、getByTestId
未等待元素出现测试不稳定使用自动等待定位器,避免使用 waitForTimeout
硬编码等待(page.waitForTimeout)速度慢、不可靠使用 page.waitForSelector 或自动等待
未使用页面对象模型代码重复,难以维护将常用操作提取到页面对象中
在单个测试中测试过多内容难以调试失败每个测试覆盖一个用户流程
未复用身份验证状态每个测试都要重新登录,速度慢保存 storageState,并在测试间复用
忽略不稳定测试造成虚假的信心修复不稳定测试,谨慎使用重试

快速故障排除

问题可能原因解决方案
“等待选择器超时”元素尚未渲染或选择器错误检查选择器,确保元素存在
测试不稳定(随机通过/失败)竞态条件、网络缓慢使用自动等待,避免使用 waitForTimeout
“元素不可见”元素被隐藏或不在视口内滚动至元素可见处,检查 CSS display
“执行上下文已销毁”操作期间发生了导航等待导航完成
截图不匹配字体渲染、动画禁用动画,使用固定视口
测试一直挂起缺少 await确保所有异步调用都使用 await

参考文档

评论

支持 Markdown