mcpField
P
playwright-testing

playwright-testing

github.com/terminalskillsテスト・品質18447 回インストール今日更新
リポジトリ

playwright-testing について

playwright-testing は、AI エージェントによる Playwright のエンドツーエンドテスト作成を支援するスキルです。セットアップ、ブラウザー操作、認証、API モック、視覚・アクセシビリティ検査、CI 連携を扱います。

主な機能

  1. 01Playwright の設定とテスト作成
  2. 02自動待機とセマンティックロケーター
  3. 03認証とページオブジェクトパターン
  4. 04API レスポンスのモック
  5. 05視覚・アクセシビリティテスト
  6. 06CI 連携とデバッグコマンド

活用シーン

  1. 01再利用可能なページオブジェクトでログインをテスト
  2. 02API モックでエラー状態と空状態を検証
  3. 03CI でブラウザーテストとアクセシビリティ検査を実行

インストール

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

$npx skills add https://github.com/terminalskills/skills --skill playwright-testing

インストール後、Claude Code で/playwright-testingを入力して読み込まれたか確認します。

同梱ファイル_scores.json

詳細

Playwright テスト

概要

このスキルは、AI エージェントが 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:ページオブジェクトを使ってログインフローの E2E テストを追加する

ユーザープロンプト:「ページオブジェクトパターンを使って、ログイン成功、認証情報の誤り、フォームの検証をテストする、ログインページ用の Playwright テストを作成してください。」

エージェントは次の作業を行います:

  1. tests/pages/login.page.ts を作成し、ロールベースのセレクター(getByLabel、getByRole)を使って、メールアドレス、パスワード、送信ボタン、エラーメッセージのロケーターを定義します
  2. ページオブジェクトに goto()、login()、expectError() メソッドを追加します
  3. 3 つのテストを作成します。ログイン成功後に /dashboard へのリダイレクトを確認するテスト、認証情報が誤っている場合にエラーアラートを確認するテスト、空のフォームを送信した際に検証メッセージを確認するテストです
  4. beforeEach を設定してページオブジェクトを初期化し、/login に移動します
例 2:API レスポンスをモックしてエラー状態と空の状態をテストする

ユーザープロンプト:「ダッシュボードが API エラーを適切に処理し、プロジェクトがない場合に空の状態を表示することを確認するテストを追加してください。」

エージェントは次の作業を行います:

  1. page.route('**/api/projects', ...) を使って API 呼び出しをインターセプトします
  2. エラーのテストでは、ステータス 500 でレスポンスを返し、「Failed to load projects」メッセージと再試行ボタンが表示されることを確認します
  3. 空の状態のテストでは、ステータス 200 と空のプロジェクト配列でレスポンスを返し、「No projects yet」の空の状態が表示されることを確認します
  4. どちらのテストも実際のバックエンドを必要とせず、独立して実行できるため、CI で高速かつ信頼性の高いテストが可能です

ガイドライン

  • CSS セレクターよりもロールベースのロケーター(getByRole、getByLabel、getByText)を使います。DOM の変更に対する耐性が高くなります
  • セマンティックなロケーターが使えない場合に限り、data-testid 属性を追加します
  • page.waitForTimeout() は使わず、自動待機するロケーターか、タイムアウトを指定した expect を使います
  • 認証のセットアップは一度だけ実行し、storageState を介してテスト間で状態を共有します
  • 複雑なページにはページオブジェクトを使い、テストを読みやすく保ちます
  • テストでは外部 API をモックし、サードパーティのサービスではなく UI の動作をテストします
  • 速度を上げるため、テストを並列実行します(fullyParallel: true)
  • 最初の再試行時にトレースを取得します。CI で不安定なテストをデバッグする際に非常に役立ちます
  • webServer 設定を使って、テスト中に開発サーバーを自動起動します
  • ビジュアルスナップショットはバージョン管理に含め、PR で変更をレビューします

コメント