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 対応

包括的なPlaywrightドキュメントの参照先として、`mcp__documentation__fetch_docs`を案内しています。

インストール

コマンドをコピーしてターミナルで実行します。

$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 コア知識

深い知識:包括的なドキュメントを参照するには、technology: playwright を指定して mcp__documentation__fetch_docs を使用してください。

このスキルを使用しない場合

  • 単体テスト - 独立した関数/モジュールのテストには vitest または jest を使用してください
  • コンポーネントテスト - React/Vue コンポーネントのテストには testing-library を使用してください
  • 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/
監視メトリクス
メトリクス目標
E2E テスト成功率> 99%
テスト実行時間< 10min
不安定なテストの割合< 1%
ビジュアル差分による失敗すべて確認
チェックリスト
  • 複数ブラウザーでのテストを設定済み
  • モバイルビューポートのテスト
  • 認証状態を再利用
  • 失敗時に再試行(CI のみ)
  • 失敗時のスクリーンショット
  • トレース収集を有効化
  • 分離のための API モック
  • ビジュアルリグレッションテスト
  • CI/CD との統合
  • テストの並列化
  • ページオブジェクトパターンを使用

アンチパターン

アンチパターン問題点解決策
CSS セレクターを使ったテスト脆く、スタイル変更で壊れますgetByRole、getByLabel、getByTestId を使用してください
要素を待たないテストが不安定になります自動待機ロケーターを使用し、waitForTimeout は避けてください
固定時間の待機(page.waitForTimeout)遅く、信頼性に欠けますpage.waitForSelector または自動待機を使用してください
ページオブジェクトモデルがないコードが重複し、保守が困難になります共通のアクションをページオブジェクトに抽出してください
1 つのテストで多くをテストしすぎる失敗のデバッグが困難になります1 テストにつきユーザーフローを 1 つにしてください
認証状態を再利用しないテストごとのログインに時間がかかりますstorageState を保存し、テスト間で再利用してください
不安定なテストを放置する誤った信頼につながります不安定なテストを修正し、再試行は必要最小限にしてください

簡単なトラブルシューティング

問題考えられる原因解決策
"Timeout waiting for selector"要素が描画されていない、またはセレクターが間違っていますセレクターを確認し、要素が存在することを確認してください
不安定なテスト(ランダムに成功/失敗する)競合状態、ネットワークの遅延自動待機を使用し、waitForTimeout は避けてください
"Element is not visible"要素が非表示、またはビューポートの外にあります要素をスクロールして表示し、CSS の display を確認してください
"Execution context destroyed"アクション中にナビゲーションが発生しましたナビゲーションの完了を待ってください
スクリーンショットが一致しないフォントの描画、アニメーションアニメーションを無効にし、ビューポートを固定してください
テストがいつまでも停止するawait が不足していますすべての非同期呼び出しが await されていることを確認してください

リファレンスドキュメント

コメント