WR

write-playwright-e2e-code

Generates structured Playwright E2E tests using localized step definitions and minimal comments.

Install

mkdir -p .claude/skills/write-playwright-e2e-code && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/1899" && unzip -o skill.zip -d .claude/skills/write-playwright-e2e-code && rm skill.zip

Installs to .claude/skills/write-playwright-e2e-code

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Playwright E2E テストコードを生成。test.step で日本語ステップ名を使用し、コメント禁止。E2E テスト作成・Playwright コード生成時に使用。
87 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Structure tests using test.step
  • Enforce comment-free code style
  • Manage locator scope for reusability
  • Handle value passing between steps
  • Standardize logical test flow

How it works

It generates Playwright code by wrapping actions in test.step blocks and enforcing strict naming conventions that describe intent without redundant comments.

Inputs & outputs

You give it
Manual test case description
You get back
Playwright E2E test code

When to use write-playwright-e2e-code

  • Generate E2E test steps for a login flow
  • Convert manual test cases to Playwright code
  • Refactor existing tests to use test.step

About this skill

Playwright E2E テスト

test.step 区切り

  • 論理的な区切りは test.step("日本語", async () => { ... }) で表現
  • 適切な粒度: ユーザー操作単位・UI 状態確認単位で区切る。入れ子も可
  • expect は step 内に含める
  • 既に step に分かれている関数は step の外で呼び出す

Good:

await navigateToMain(page);

await test.step("ダウンロードモーダルを表示する", async () => {
  await page.getByRole("button", { name: "ダウンロード" }).click();
  await expect(page.getByRole("dialog")).toBeVisible();
});

await test.step("モーダルを閉じると消える", async () => {
  await page.getByRole("button", { name: "閉じる" }).click();
  await expect(page.getByRole("dialog")).toBeHidden();
});

step 名

  • 「何をするか」「どうあるべきか」が分かる表現にする
  • 詳細な説明文にはせず、フローの節目を表す短い動詞句にする
  • 「~を確認する」など冗長な表現は避ける
  • 体言止めは使わない

Good:

await test.step("ダウンロードモーダルを表示する", async () => {});

Bad 1 (step 名が体言止め):

await test.step("ダウンロードモーダルを表示", async () => {});

Bad 2 (step 名が冗長):

await test.step("ダウンロードボタンをクリックしてモーダルが表示されることを確認する", async () => {});

Bad 3 (step 名に「~を確認する」を含む):

await test.step("モーダルを閉じると消えることを確認する", async () => {});

コメント

  • What を説明するコメントは削除: step 名で表現する
  • Why(目的・意図・理由)を説明するコメントは残す: なぜその待機時間が必要か、なぜその順序で実行するか等

Good (意図を説明):

await page.waitForTimeout(5000); // エンジン読み込みを待機

locator の選択

  • locator はユーザー向けの情報から取得できるものを優先する
  • 適切な locator が書けない場合は、WCAG の基準を満たしつつ WAI-ARIA の要件に合うようソースコードの変更を検討する
  • DOM 構造に依存するセレクターは原則使わず、他に取得方法がない場合だけ例外として使う

優先順位:

  1. getByRole: 役割と名前で自然に取得できる場合に使う
  2. 他の getBy...: getByLabelgetByTextgetByPlaceholdergetByAltTextgetByTitle などから対象を自然に表せるものを使う
  3. コンポーネント名のクラス: コンポーネントの名前と同じ CSS クラス、あるいはコンポーネント名とパーツ名を組み合わせた CSS クラスは使ってよい
  4. data-testid: ここまでの方法で自然に取得するのが難しい場合に使う

Good 1:

// コード
<button>ダウンロード</button>
// テスト
await page.getByRole("button", { name: "ダウンロード" }).click();

Bad 1 (不要なtestid):

// コード
<button data-testid="download-button">ダウンロード</button>
// テスト
await page.getByTestId("download-button").click();

Good 2:

// コード
<button aria-label="閉じる"><CloseIcon /></button>
// テスト
await page.getByRole("button", { name: "閉じる" }).click();

Bad 2 (不明な名前):

// コード
<button aria-label="close-icon"><CloseIcon /></button>
// テスト
await page.getByRole("button", { name: "close-icon" }).click();

locator・変数の共有

locator の宣言場所は使用範囲で決める:

  • 複数 step で使う: test 関数直下で宣言
  • 1 つの step でのみ使う: その step 内で宣言(外に出さない)

Good:

test("テスト名", async ({ page }) => {
  const input = page.getByLabel("入力欄");

  await test.step("入力する", async () => {
    await input.fill("テスト");
  });

  await test.step("入力値が反映される", async () => {
    await expect(input).toHaveValue("テスト");
  });
});

Bad 1 (不要な外部宣言):

test("テスト名", async ({ page }) => {
  const accentPhrase = page.locator(".accent-phrase");

  await test.step("検証する", async () => {
    await expect(accentPhrase).toBeVisible();
  });
});

Bad 2 (重複宣言):

test("テスト名", async ({ page }) => {
  await test.step("入力する", async () => {
    const input = page.getByLabel("入力欄");
    await input.fill("テスト");
  });

  await test.step("検証する", async () => {
    const input = page.getByLabel("入力欄");
    await expect(input).toHaveValue("テスト");
  });
});

step 間の値の受け渡し

  • 再代入しない: step から return して const で受け取る
  • 再代入する: let で宣言して step 内で代入

Good 1:

const before = await test.step("初期値を取得する", async () => {
  return await getValue(page);
});

await test.step("値が変化している", async () => {
  expect(await getValue(page)).not.toEqual(before);
});

Bad 1 (再代入しないのに let で宣言):

let before: number;

await test.step("初期値を取得する", async () => {
  before = await getValue(page);
});

await test.step("値が変化している", async () => {
  expect(await getValue(page)).not.toEqual(before);
});

Good 2 (再代入する場合は let でも OK):

let count: number;

await test.step("1回目の操作をする", async () => {
  await page.getByRole("button").click();
  count = await getCount(page);
  expect(count).toBe(1);
});

await test.step("2回目の操作をする", async () => {
  await page.getByRole("button").click();
  count = await getCount(page);
  expect(count).toBe(2);
});

ロジックの共通化

スコープ方法
test 内のみtest 関数内で変数やローカル関数を定義
ファイル内の複数 testファイルスコープでローカル関数を定義
複数ファイル共通ファイルにエクスポート関数を追加

既存コードとの整合

既存ファイル・関数名の指定がある場合はそれを優先。ない場合は既存 E2E の命名・スタイルに合わせる。

When not to use it

  • Writing unit tests
  • Adding explanatory comments for code logic

Prerequisites

Playwright installed

Limitations

  • Prohibits What-style comments
  • Requires specific naming conventions for steps

How it compares

Unlike manual coding, this enforces a specific architectural pattern that separates test steps and removes non-essential comments.

Compared to similar skills

write-playwright-e2e-code side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
write-playwright-e2e-code (this skill)36moNo flagsIntermediate
vitest416moNo flagsIntermediate
zod-4127moNo flagsIntermediate
write-unit-tests53moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

vitest

antfu

Vitest fast unit testing framework powered by Vite with Jest-compatible API. Use when writing tests, mocking, configuring coverage, or working with test filtering and fixtures.

41183

zod-4

prowler-cloud

Zod 4 schema validation patterns. Trigger: When creating or updating Zod v4 schemas for validation/parsing (forms, request payloads, adapters), including v3 -> v4 migration patterns.

1260

write-unit-tests

tldraw

Writing unit and integration tests for the tldraw SDK. Use when creating new tests, adding test coverage, or fixing failing tests in packages/editor or packages/tldraw. Covers Vitest patterns, TestEditor usage, and test file organization.

545

develop-ai-functions-example

vercel

Develop examples for AI SDK functions. Use when creating, running, or modifying examples under examples/ai-functions/src to validate provider support, demonstrate features, or create test fixtures.

536

javascript-testing-patterns

wshobson

Implement comprehensive testing strategies using Jest, Vitest, and Testing Library for unit tests, integration tests, and end-to-end testing with mocking, fixtures, and test-driven development. Use when writing JavaScript/TypeScript tests, setting up test infrastructure, or implementing TDD/BDD workflows.

323

ts-library

onmax

Use when authoring TypeScript libraries - covers project setup, package exports, build tooling (tsdown/unbuild), API design patterns, type inference tricks, testing, and release workflows. Patterns extracted from 20+ high-quality ecosystem libraries.

323

Search skills

Search the agent skills registry