Playwright E2E テストコードを生成。test.step で日本語ステップ名を使用し、コメント禁止。E2E テスト作成・Playwright コード生成時に使用。
test.step("日本語", async () => { ... }) で表現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();
});
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 () => {});
Good (意図を説明):
await page.waitForTimeout(5000); // エンジン読み込みを待機
locator はユーザー向けの情報から取得できるものを優先するlocator が書けない場合は、WCAG の基準を満たしつつ WAI-ARIA の要件に合うようソースコードの変更を検討する優先順位:
getByRole: 役割と名前で自然に取得できる場合に使うgetBy...: getByLabel・getByText・getByPlaceholder・getByAltText・getByTitle などから対象を自然に表せるものを使う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 の宣言場所は使用範囲で決める:
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("テスト");
});
});
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 の命名・スタイルに合わせる。