mcpField
P
playwright-expert

playwright-expert

github.com/cin12211测试与质量22658 次安装4 天前更新
代码仓库

关于 playwright-expert

playwright-expert 是面向 Playwright 端到端测试与浏览器自动化的技能,帮助开发者配置测试、处理常见问题并审查测试代码。

主要功能

  1. 01Playwright 配置指导
  2. 02端到端测试示例
  3. 03Page Object Model 指导
  4. 04网络拦截与模拟
  5. 05视觉回归检查
  6. 06处理不稳定测试

适用场景

  1. 01在项目中启动 Playwright 测试
  2. 02测试登录等用户流程
  3. 03在浏览器测试中模拟 API 响应
  4. 04审查测试可靠性问题

安装

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

$npx skills add https://github.com/cin12211/orca-q --skill playwright-expert

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

详细介绍

Playwright 专家

专精于 Playwright,用于 E2E 测试、浏览器自动化和跨浏览器测试。

调用时

推荐专家
  • 单元/集成测试:推荐 jest-expert 或 vitest-expert
  • React 组件测试:推荐 testing-expert
  • 仅 API 测试:推荐 rest-api-expert
环境检测
npx playwright --version 2>/dev/null
ls playwright.config.* 2>/dev/null
find . -name "*.spec.ts" -path "*e2e*" | head -5

问题处理手册

项目设置
# Initialize Playwright
npm init playwright@latest

# Install browsers
npx playwright install
// 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',
  use: {
    baseURL: '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'] } },
  ],
  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});
编写测试
import { test, expect } from '@playwright/test';

test.describe('Authentication', () => {
  test('should login successfully', async ({ page }) => {
    await page.goto('/login');
    
    await page.fill('[data-testid="email"]', '[email protected]');
    await page.fill('[data-testid="password"]', 'password123');
    await page.click('[data-testid="submit"]');
    
    await expect(page).toHaveURL('/dashboard');
    await expect(page.locator('h1')).toContainText('Welcome');
  });

  test('should show error for invalid credentials', async ({ page }) => {
    await page.goto('/login');
    
    await page.fill('[data-testid="email"]', '[email protected]');
    await page.fill('[data-testid="password"]', 'wrong');
    await page.click('[data-testid="submit"]');
    
    await expect(page.locator('.error-message')).toBeVisible();
  });
});
页面对象模型
// pages/login.page.ts
import { Page, Locator } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;

  constructor(page: Page) {
    this.page = page;
    this.emailInput = page.locator('[data-testid="email"]');
    this.passwordInput = page.locator('[data-testid="password"]');
    this.submitButton = page.locator('[data-testid="submit"]');
  }

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

// Usage in test
test('login test', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login('[email protected]', 'password');
});
网络拦截
test('mock API response', 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.locator('.user-name')).toContainText('Mock User');
});
视觉回归
test('visual comparison', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveScreenshot('homepage.png', {
    maxDiffPixelRatio: 0.1,
  });
});
处理不稳定测试
// Retry flaky tests
test('flaky network test', async ({ page }) => {
  test.slow(); // Triple timeout
  
  await page.goto('/');
  await page.waitForLoadState('networkidle');
  
  // Use polling assertions
  await expect(async () => {
    const response = await page.request.get('/api/status');
    expect(response.ok()).toBeTruthy();
  }).toPass({ timeout: 10000 });
});

运行测试

# Run all tests
npx playwright test

# Run specific file
npx playwright test login.spec.ts

# Run in headed mode
npx playwright test --headed

# Run in UI mode
npx playwright test --ui

# Debug mode
npx playwright test --debug

# Generate report
npx playwright show-report

代码审查清单

  • 选择器使用 data-testid 属性
  • 复杂流程使用页面对象模型
  • 按需模拟网络请求
  • 使用恰当的等待策略(不使用任意等待)
  • 配置失败时截图
  • 启用并行执行

反模式

  1. 硬编码等待——使用恰当的断言
  2. 脆弱的选择器——使用 data-testid
  3. 测试之间共享状态——隔离测试
  4. CI 中不重试——添加重试以应对不稳定情况
  5. 测试实现细节——测试用户行为

评论

支持 Markdown