mcpField
P
playwright

playwright

github.com/secondskyテスト・品質2401.0k 回インストール5 週間前に更新
リポジトリ

playwright について

Playwright によるブラウザー自動化と E2E テストの手順を扱うスキルです。ローカル開発サーバーの検出から、画面を表示した状態でのスクリプト実行、一般的なテスト手法までを開発者向けに説明します。

主な機能

  1. 01開発サーバーの検出
  2. 02ラッパー経由でのスクリプト実行
  3. 03ブラウザー操作とアサーション
  4. 04ネットワークモックとスクリーンショット
  5. 05認証とページオブジェクトパターン

活用シーン

  1. 01複数の画面幅でローカルアプリをテスト
  2. 02ログインとページ操作の自動化
  3. 03テスト中の API レスポンスのモック
  4. 04目視確認用スクリーンショットの撮影

インストール

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

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

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

package.json
run.js
examples/
form-test.js
link-checker.js
responsive-test.js
lib/
helpers.js
references/
API_REFERENCE.md

詳細

Playwright - ブラウザー自動化とE2Eテスト

最新のクロスブラウザーテストフレームワークであるPlaywrightを使った、ブラウザー自動化とエンドツーエンドテストの専門知識です。

重要 - パスの解決: このスキルはさまざまな場所にインストールできます。コマンドを実行する前に、このSKILL.mdファイルを読み込んだ場所を基にスキルディレクトリを特定し、すべてのコマンドでそのパスを使用してください。$SKILL_DIR は、実際に特定したパスに置き換えてください。

一般的なインストール先:

  • プラグインシステム:~/.claude/plugins/*/playwright/skills/playwright
  • 手動でのグローバルインストール:~/.claude/skills/playwright
  • プロジェクト固有:<project>/.claude/skills/playwright

重要なワークフロー - 以下の手順に従ってください

ブラウザータスクを自動化するとき:

  1. 開発サーバーを自動検出する - localhost のテストでは、必ず最初にサーバー検出を実行してください:

    cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(servers => console.log(JSON.stringify(servers)))"
    
    • サーバーが1つ見つかった場合:自動的に使用し、ユーザーに知らせます
    • 複数のサーバーが見つかった場合:どれをテストするかユーザーに尋ねます
    • サーバーが見つからない場合:URLを尋ねるか、開発サーバーの起動を支援することを提案します
  2. スクリプトは /tmp に書き込む - スキルディレクトリにテストファイルを書き込んではいけません。必ず /tmp/playwright-test-*.js を使用してください

  3. デフォルトでは表示ブラウザーを使用する - ユーザーがヘッドレスモードを明示的に要求した場合を除き、常に headless: false を使用してください

  4. URLをパラメーター化する - スクリプトの先頭にある定数で、常にURLを設定できるようにしてください

  5. run.js 経由で実行する - 常に次のコマンドを実行してください:cd $SKILL_DIR && node run.js /tmp/playwright-test-*.js

クイックスタート

初回セットアップ
# Navigate to skill directory
cd $SKILL_DIR

# Install using bun (preferred)
bun run setup

# Or using npm
npm run setup:npm

これによりPlaywrightとChromiumブラウザーがインストールされます。必要なのは一度だけです。

インストール(E2Eテストプロジェクト向け)
# Using Bun (preferred)
bun add -d @playwright/test
bunx playwright install

# Using npm
npm init playwright@latest
設定
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  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: 'bun run dev',
    url: 'http://localhost:3000',
  },
})

ブラウザー自動化のパターン

動作の仕組み
  1. テスト/自動化したい内容を説明します
  2. 実行中の開発サーバーを自動検出します(またはURLを尋ねます)
  3. /tmp/playwright-test-*.js にカスタムPlaywrightコードを書き込みます
  4. 次のコマンドで実行します:cd $SKILL_DIR && node run.js /tmp/playwright-test-*.js
  5. 結果がリアルタイムで表示され、ブラウザーウィンドウも表示されます
ページをテストする(複数のビューポート)
// /tmp/playwright-test-responsive.js
const { chromium } = require('playwright');

const TARGET_URL = 'http://localhost:3001'; // Auto-detected

(async () => {
  const browser = await chromium.launch({ headless: false, slowMo: 100 });
  const page = await browser.newPage();

  // Desktop test
  await page.setViewportSize({ width: 1920, height: 1080 });
  await page.goto(TARGET_URL);
  console.log('Desktop - Title:', await page.title());
  await page.screenshot({ path: '/tmp/desktop.png', fullPage: true });

  // Mobile test
  await page.setViewportSize({ width: 375, height: 667 });
  await page.screenshot({ path: '/tmp/mobile.png', fullPage: true });

  await browser.close();
})();

実行:cd $SKILL_DIR && node run.js /tmp/playwright-test-responsive.js

ログインフローをテストする
// /tmp/playwright-test-login.js
const { chromium } = require('playwright');

const TARGET_URL = 'http://localhost:3001'; // Auto-detected

(async () => {
  const browser = await chromium.launch({ headless: false });
  const page = await browser.newPage();

  await page.goto(`${TARGET_URL}/login`);
  await page.fill('input[name="email"]', '[email protected]');
  await page.fill('input[name="password"]', 'password123');
  await page.click('button[type="submit"]');

  await page.waitForURL('**/dashboard');
  console.log('✅ Login successful, redirected to dashboard');

  await browser.close();
})();
リンク切れを確認する
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: false });
  const page = await browser.newPage();

  await page.goto('http://localhost:3000');

  const links = await page.locator('a[href^="http"]').all();
  const results = { working: 0, broken: [] };

  for (const link of links) {
    const href = await link.getAttribute('href');
    try {
      const response = await page.request.head(href);
      if (response.ok()) {
        results.working++;
      } else {
        results.broken.push({ url: href, status: response.status() });
      }
    } catch (e) {
      results.broken.push({ url: href, error: e.message });
    }
  }

  console.log(`✅ Working links: ${results.working}`);
  console.log(`❌ Broken links:`, results.broken);

  await browser.close();
})();

E2Eテストのパターン

テストの実行
# Run all tests
bunx playwright test

# Headed mode (see browser)
bunx playwright test --headed

# Specific file
bunx playwright test tests/login.spec.ts

# Debug mode
bunx playwright test --debug

# UI mode (interactive)
bunx playwright test --ui

# Specific browser
bunx playwright test --project=chromium

# Generate report
bunx playwright show-report
テストの作成
import { test, expect } from '@playwright/test'

test.describe('Login flow', () => {
  test('successful login', async ({ page }) => {
    await page.goto('/')
    await page.getByRole('link', { name: 'Login' }).click()
    await page.getByLabel('Email').fill('[email protected]')
    await page.getByLabel('Password').fill('password123')
    await page.getByRole('button', { name: 'Sign in' }).click()

    await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible()
  })

  test('shows error for invalid credentials', async ({ page }) => {
    await page.goto('/login')
    await page.getByLabel('Email').fill('[email protected]')
    await page.getByLabel('Password').fill('wrongpassword')
    await page.getByRole('button', { name: 'Sign in' }).click()

    await expect(page.getByText('Invalid credentials')).toBeVisible()
  })
})

セレクター(ベストプラクティス)

// ✅ Role-based (recommended)
await page.getByRole('button', { name: 'Submit' })
await page.getByRole('link', { name: 'Home' })

// ✅ Text/Label
await page.getByText('Hello World')
await page.getByLabel('Email')

// ✅ Test ID (fallback)
await page.getByTestId('submit-button')

// ❌ Avoid CSS selectors (brittle)
await page.locator('.btn-primary')

アサーション

// Visibility
await expect(page.getByText('Success')).toBeVisible()
await expect(page.getByRole('button')).toBeEnabled()

// Text
await expect(page.getByRole('heading')).toHaveText('Welcome')
await expect(page.getByRole('alert')).toContainText('error')

// Attributes
await expect(page.getByRole('link')).toHaveAttribute('href', '/home')

// URL/Title
await expect(page).toHaveURL('/dashboard')
await expect(page).toHaveTitle('Dashboard')

// Count
await expect(page.getByRole('listitem')).toHaveCount(5)

アクション

// Clicking
await page.getByRole('button').click()
await page.getByText('File').dblclick()

// Typing
await page.getByLabel('Email').fill('[email protected]')
await page.getByLabel('Search').press('Enter')

// Selecting
await page.getByLabel('Country').selectOption('us')

// File Upload
await page.getByLabel('Upload').setInputFiles('path/to/file.pdf')

ネットワークのモック

test('mocks API response', async ({ page }) => {
  await page.route('**/api/users', async route => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([{ id: 1, name: 'Test User' }]),
    })
  })

  await page.goto('/users')
  await expect(page.getByText('Test User')).toBeVisible()
})

ビジュアルテスト

test('captures screenshot', async ({ page }) => {
  await page.goto('/')
  await page.screenshot({ path: 'screenshot.png', fullPage: true })
  await expect(page).toHaveScreenshot('homepage.png')
})

認証状態

// Save state after login
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 page.context().storageState({ path: 'auth.json' })
})

// Reuse in config
use: { storageState: 'auth.json' }

ページオブジェクトモデル

// pages/LoginPage.ts
import { Page, Locator } from '@playwright/test'

export class LoginPage {
  readonly emailInput: Locator
  readonly passwordInput: Locator
  readonly submitButton: Locator

  constructor(page: Page) {
    this.emailInput = page.getByLabel('Email')
    this.passwordInput = page.getByLabel('Password')
    this.submitButton = page.getByRole('button', { name: 'Sign in' })
  }

  async login(email: string, password: string) {
    await this.emailInput.fill(email)
    await this.passwordInput.fill(password)
    await this.submitButton.click()
  }
}

// Usage
const loginPage = new LoginPage(page)
await loginPage.login('[email protected]', 'password123')

利用可能なヘルパー

lib/helpers.js には、任意で使用できるユーティリティ関数があります:

const helpers = require('./lib/helpers');

// Detect running dev servers (CRITICAL - use this first!)
const servers = await helpers.detectDevServers();
console.log('Found servers:', servers);

// Safe click with retry
await helpers.safeClick(page, 'button.submit', { retries: 3 });

// Safe type with clear
await helpers.safeType(page, '#username', 'testuser');

// Take timestamped screenshot
await helpers.takeScreenshot(page, 'test-result');

// Handle cookie banners
await helpers.handleCookieBanner(page);

// Extract table data
const data = await helpers.extractTableData(page, 'table.results');

// Create context with custom headers
const context = await helpers.createContext(browser);

カスタムHTTPヘッダー

環境変数を使って、すべてのHTTPリクエストに適用するカスタムヘッダーを設定します:

# Single header (common case)
PW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill \
  cd $SKILL_DIR && node run.js /tmp/my-script.js

# Multiple headers (JSON format)
PW_EXTRA_HEADERS='{"X-Automated-By":"playwright-skill","X-Debug":"true"}' \
  cd $SKILL_DIR && node run.js /tmp/my-script.js

helpers.createContext() を使用すると、ヘッダーが自動的に適用されます。

インライン実行(簡単なタスク)

手早く済ませる単発タスクの場合:

cd $SKILL_DIR && node run.js "
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto('http://localhost:3001');
await page.screenshot({ path: '/tmp/quick-screenshot.png', fullPage: true });
console.log('Screenshot saved');
await browser.close();
"

使用する場面:

  • インライン:単発の簡単なタスク(スクリーンショット、要素の確認)
  • ファイル:複雑なテスト、再利用可能な自動化

ベストプラクティス

  • 重要:最初にサーバーを検出する - テストコードを書く前に、必ず detectDevServers() を実行してください
  • テストファイルには /tmp を使用する - /tmp/playwright-test-*.js に書き込み、スキルディレクトリには書き込まないでください
  • URLをパラメーター化する - 検出または指定されたURLを TARGET_URL 定数に設定してください
  • デフォルト:表示ブラウザー - 明示的に要求されない限り、常に headless: false を使用してください
  • ロールベースのセレクターを優先する - CSSセレクターより安定しています
  • 自動待機を活用する - 手動でのスリープは不要です
  • 各テストに新しいコンテキストを割り当てる - 分離は自動的に行われます
  • テストを並列実行する - デフォルトの動作です
  • 外部依存関係をモックする - page.route() を使用してください
  • トレースビューアーを使用する - タイムトラベルデバッグに役立ちます

ヒント

  • 動作をゆっくりにする: slowMo: 100 を使用すると、アクションを確認できます
  • 待機方法: 固定のタイムアウトではなく、waitForURL、waitForSelector、waitForLoadState を使用してください
  • エラー処理: 堅牢な自動化のため、必ず try-catch を使用してください
  • コンソール出力: 進行状況の記録には console.log() を使用してください

トラブルシューティング

Playwright がインストールされていない場合:

cd $SKILL_DIR && bun run setup

モジュールが見つからない場合: run.js ラッパーを使用して、スキルのディレクトリから実行してください

ブラウザーが開かない場合: headless: false に設定されていることと、ディスプレイが利用可能であることを確認してください

要素が見つからない場合: 待機処理を追加してください:await page.waitForSelector('.element', { timeout: 10000 })

セキュアなインストール

Playwright とブラウザーバイナリをインストールする際は、サプライチェーンセキュリティのベストプラクティスに従ってください。

  • インストール後のスクリプトをブロックする — npm config set ignore-scripts true(その後、ブラウザーをダウンロードするために npx playwright install を手動で実行します)
  • クールダウン期間 — 新しいパッケージバージョンがコミュニティによって検証されるまで、7 日間待ちます
  • インストール前に監査する — socket package score npm <pkg> を実行するか、socket npm install <pkg> を使用してパッケージを確認します

Socket CLI 統合、クールダウンの設定、ロックファイルの検証、CI による適用など、セキュリティ設定の詳細については、dependency-upgrade スキルを読み込んでください。

関連項目

  • vitest-testing - 単体テストと統合テスト
  • api-testing - HTTP API のテスト
  • test-quality-analysis - テスト品質のパターン

参照資料を読み込むタイミング

次の情報が必要な場合は、references/API_REFERENCE.md を読み込んでください。

  • 高度なセレクターパターンとロケーター戦略
  • ネットワークのインターセプトとリクエスト/レスポンスのモック
  • 認証パターンとセッション管理
  • ビジュアルリグレッションテストのセットアップ
  • モバイルデバイスのエミュレーション設定
  • パフォーマンステストとメトリクス
  • デバッグ手法(トレースビューアー、インスペクター)
  • CI/CD パイプラインとの統合
  • axe-core によるアクセシビリティテスト
  • データ駆動型テストとパラメーター化テスト
  • Page Object Model の高度なパターン
  • 並列実行の戦略

コメント