mcpField
P
playwright

playwright

github.com/prowler-cloudテスト・品質15.1k119 回インストール1 日前に更新
リポジトリ

playwright について

UI のエンドツーエンドテストを作成するチーム向けの Playwright スキルです。利用可能な Playwright MCP ツールでページを調べてから実装し、再利用可能なページオブジェクトでテストを構成します。

主な機能

  1. 01テスト作成前のページ調査
  2. 02ロールとラベルのセレクター優先
  3. 03ページオブジェクトの再利用
  4. 04テストとドキュメントの整理
  5. 05優先度と機能によるタグ付け

活用シーン

  1. 01サインアップフローの E2E テスト作成
  2. 02既存ページのテスト仕様への追加
  3. 03個別テストケースの文書化

MCP 対応

Playwright MCP ツールが利用可能な場合、テスト作成前にページへの移動、スナップショット確認、操作、画面遷移の検証を行う手順を示します。

インストール

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

$npx skills add https://github.com/prowler-cloud/prowler --skill playwright

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

同梱ファイルreferences/prowler-e2e.md

詳細

MCP ワークフロー(利用可能な場合は必須)

⚠️ Playwright MCP ツールがある場合は、テストを作成する前に必ず使用してください。

  1. 移動:対象ページに移動します
  2. スナップショットを取得:ページ構造と要素を確認します
  3. 操作:フォームや要素を操作し、正確なユーザーフローを確認します
  4. スクリーンショットを撮影:想定される状態を記録します
  5. ページ遷移を確認:一連のフロー(読み込み、成功、エラー)を通じて検証します
  6. 実際のセレクターを記録:スナップショットから実際の ref とラベルを使用します
  7. 調査後にのみ、検証済みのセレクターを使ってテストコードを作成します

MCP が利用できない場合: ドキュメントとコードの分析に基づいてテストを作成します。

これが重要な理由:

  • ✅ 正確なテスト:必要な手順が明確になり、推測が不要です
  • ✅ 正確なセレクター:想像上のものではなく、実際の DOM 構造に基づきます
  • ✅ 実際のフローを検証:一連の操作が実際に機能することを確認します
  • ✅ 過剰な設計を回避:実在する機能に対して最小限のテストを作成します
  • ✅ 不安定なテストを防止:実際に調査することで、安定したテストになります
  • ❌ UI が「どう動くべきか」を決して推測しないでください

ファイル構成

tests/
├── base-page.ts              # Parent class for ALL pages
├── helpers.ts                # Shared utilities
└── {page-name}/
    ├── {page-name}-page.ts   # Page Object Model
    ├── {page-name}.spec.ts   # ALL tests here (NO separate files!)
    └── {page-name}.md        # Test documentation

ファイルの命名規則:

  • ✅ sign-up.spec.ts(サインアップのテストすべて)
  • ✅ sign-up-page.ts(ページオブジェクト)
  • ✅ sign-up.md(ドキュメント)
  • ❌ sign-up-critical-path.spec.ts(誤り — ファイルを分割しないでください)
  • ❌ sign-up-validation.spec.ts(誤り)

セレクターの優先順位(必須)

// 1. BEST - getByRole for interactive elements
this.submitButton = page.getByRole("button", { name: "Submit" });
this.navLink = page.getByRole("link", { name: "Dashboard" });

// 2. BEST - getByLabel for form controls
this.emailInput = page.getByLabel("Email");
this.passwordInput = page.getByLabel("Password");

// 3. SPARINGLY - getByText for static content only
this.errorMessage = page.getByText("Invalid credentials");
this.pageTitle = page.getByText("Welcome");

// 4. LAST RESORT - getByTestId when above fail
this.customWidget = page.getByTestId("date-picker");

// ❌ AVOID fragile selectors
this.button = page.locator(".btn-primary");  // NO
this.input = page.locator("#email");         // NO

スコープの判定(曖昧な場合は確認)

ユーザーの指示対応
「テストを1つ」「新しいテスト」「テストを追加」既存の spec に test() を1つ作成します
「包括的なテスト」「すべてのテスト」「テストスイート」「テストを生成」テストスイート全体を作成します

例:

  • 「ユーザーのサインアップのテストを作成」→ テストは1つのみ
  • 「ログインページの E2E テストを生成」→ テストスイート全体
  • 「フォームの検証を確認するテストを追加」→ 既存の spec にテストを1つ追加

ページオブジェクトパターン

import { Page, Locator, expect } from "@playwright/test";

// BasePage - ALL pages extend this
export class BasePage {
  constructor(protected page: Page) {}

  async goto(path: string): Promise<void> {
    await this.page.goto(path);
    await this.page.waitForLoadState("networkidle");
  }

  // Common methods go here (see Refactoring Guidelines)
  async waitForNotification(): Promise<void> {
    await this.page.waitForSelector('[role="status"]');
  }

  async verifyNotificationMessage(message: string): Promise<void> {
    const notification = this.page.locator('[role="status"]');
    await expect(notification).toContainText(message);
  }
}

// Page-specific implementation
export interface LoginData {
  email: string;
  password: string;
}

export class LoginPage extends BasePage {
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;

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

  async goto(): Promise<void> {
    await super.goto("/login");
  }

  async login(data: LoginData): Promise<void> {
    await this.emailInput.fill(data.email);
    await this.passwordInput.fill(data.password);
    await this.submitButton.click();
  }

  async verifyCriticalOutcome(): Promise<void> {
    await expect(this.page).toHaveURL("/dashboard");
  }
}

ページオブジェクトの再利用(重要)

新しいページオブジェクトを作成する前に、必ず既存のページオブジェクトを確認してください。

// ✅ GOOD: Reuse existing page objects
import { SignInPage } from "../sign-in/sign-in-page";
import { HomePage } from "../home/home-page";

test("User can sign up and login", async ({ page }) => {
  const signUpPage = new SignUpPage(page);
  const signInPage = new SignInPage(page);  // REUSE
  const homePage = new HomePage(page);      // REUSE

  await signUpPage.signUp(userData);
  await homePage.verifyPageLoaded();  // REUSE method
  await homePage.signOut();           // REUSE method
  await signInPage.login(credentials); // REUSE method
});

// ❌ BAD: Recreating existing functionality
export class SignUpPage extends BasePage {
  async logout() { /* ... */ }  // ❌ HomePage already has this
  async login() { /* ... */ }   // ❌ SignInPage already has this
}

ガイドライン:

  • まず tests/ で既存のページオブジェクトを確認します
  • 既存のページをインポートして再利用します
  • ページが存在しない場合にのみ、ページオブジェクトを作成します
  • テストで複数のページが必要な場合は、すべてのページオブジェクトが存在することを確認します(必要に応じて作成します)

リファクタリングのガイドライン

次の場合は BasePage に移動します
  • ✅ 複数のページで使用するナビゲーション用ヘルパー(waitForPageLoad()、getCurrentUrl())
  • ✅ 共通の UI 操作(通知、モーダル、テーマ切り替え)
  • ✅ 複数のページで繰り返し使用する検証パターン(isVisible()、waitForVisible())
  • ✅ すべてのページに適用されるエラー処理
  • ✅ デバッグ用のスクリーンショットユーティリティ
次の場合は helpers.ts に移動します
  • ✅ テストデータの生成(generateUniqueEmail()、generateTestUser())
  • ✅ セットアップ/ティアダウン用ユーティリティ(createTestUser()、cleanupTestData())
  • ✅ カスタムアサーション(expectNotificationToContain())
  • ✅ テストセットアップ用の API ヘルパー(seedDatabase()、resetState())
  • ✅ 時間関連のユーティリティ(waitForCondition()、retryAction())

変更前(不適切):

// Repeated in multiple page objects
export class SignUpPage extends BasePage {
  async waitForNotification(): Promise<void> {
    await this.page.waitForSelector('[role="status"]');
  }
}
export class SignInPage extends BasePage {
  async waitForNotification(): Promise<void> {
    await this.page.waitForSelector('[role="status"]');  // DUPLICATED!
  }
}

変更後(適切):

// BasePage - shared across all pages
export class BasePage {
  async waitForNotification(): Promise<void> {
    await this.page.waitForSelector('[role="status"]');
  }
}

// helpers.ts - data generation
export function generateUniqueEmail(): string {
  return `test.${Date.now()}@example.com`;
}

export function generateTestUser() {
  return {
    name: "Test User",
    email: generateUniqueEmail(),
    password: "TestPassword123!",
  };
}

タグを使ったテストパターン

import { test, expect } from "@playwright/test";
import { LoginPage } from "./login-page";

test.describe("Login", () => {
  test("User can login successfully",
    { tag: ["@critical", "@e2e", "@login", "@LOGIN-E2E-001"] },
    async ({ page }) => {
      const loginPage = new LoginPage(page);

      await loginPage.goto();
      await loginPage.login({ email: "[email protected]", password: "pass123" });

      await expect(page).toHaveURL("/dashboard");
    }
  );
});

タグのカテゴリ:

  • 優先度:@critical、@high、@medium、@low
  • 種類:@e2e
  • 機能:@signup、@signin、@dashboard
  • テスト ID:@SIGNUP-E2E-001、@LOGIN-E2E-002

テストドキュメントの形式({page-name}.md)

### E2E Tests: {Feature Name}

**Suite ID:** `{SUITE-ID}`
**Feature:** {Feature description}

---

## Test Case: `{TEST-ID}` - {Test case title}

**Priority:** `{critical|high|medium|low}`

**Tags:**
- type → @e2e
- feature → @{feature-name}

**Description/Objective:** {Brief description}

**Preconditions:**
- {Prerequisites for test to run}
- {Required data or state}

### Flow Steps:
1. {Step 1}
2. {Step 2}
3. {Step 3}

### Expected Result:
- {Expected outcome 1}
- {Expected outcome 2}

### Key verification points:
- {Assertion 1}
- {Assertion 2}

### Notes:
- {Additional considerations}

ドキュメントの規則:

  • ❌ 一般的なテスト実行手順は記載しない
  • ❌ ファイル構成の説明は記載しない
  • ❌ コード例やチュートリアルは記載しない
  • ❌ トラブルシューティングのセクションは設けない
  • ✅ 特定のテストケースのみに焦点を当てる
  • ✅ 可能な限り60行以内に収める

コマンド

npx playwright test                    # Run all
npx playwright test --grep "login"     # Filter by name
npx playwright test --ui               # Interactive UI
npx playwright test --debug            # Debug mode
npx playwright test tests/login/       # Run specific folder

Prowler 固有のパターン

認証セットアップ、環境変数、テスト ID を使った Prowler UI の E2E テストについては、以下を参照してください。

コメント