| name | e2e-bootstrap |
| description | E2E環境構築用。新規セットアップ・Playwright導入・既存プロジェクトの4層アーキテクチャ変換時に使用。Definition of Done・最小骨格・Fixture/constants雛形・コーディング規約・Playwrightデフォルトからの変換手順を含む。 |
E2E Bootstrap Skill
テスト追加が目的なら /e2e-test-create を使うこと。
§1. Definition of Done
npm test が実行できる(smokeが1本通る)
npx tsc --noEmit が通る(型エラー・未使用importがゼロ)
npm run gate が exit 0(機械ゲート — 正本は scripts/gate.sh)
- Playwrightとブラウザ依存が揃っている
- 4層アーキテクチャの最小ディレクトリが存在する
- 認証情報は
.env/CI環境変数で管理
- Fixtureファイル(app.fixture.ts)が存在しtest/expectをexport
§2. 4層の最小骨格
src/
├── tests/ # Layer 3: シナリオ
├── actions/ # Layer 2: ユーザ操作の流れ
├── pages/ # Layer 1: 画面要素と操作(Locatorはここ)
├── fixtures/ # Fixture定義
│ └── app.fixture.ts
├── utils/ # uniqueId.ts / formatDate.ts 等(§4 参照)
└── config/ # Layer 4: 環境差分・設定
├── env.ts
└── constants.ts
§3. 最小Fixture
正本 = fixture-template.md(同ディレクトリ)— Fixture の新規作成・4層変換・新規 Action の登録時に必ず読む。 収録: base.extend の全体構造(worker スコープ stepCounter 含む完全形)。
§4. 最小constants.ts
export const TIMEOUTS = {
SHORT: 3000,
MEDIUM: 10000,
LONG: 30000,
DEFAULT: 10000,
AUTH_STABILIZATION: 2000,
MODAL_ANIMATION: 1000,
SPA_RENDERING: 2000,
REDIRECT: 3000,
} as const;
export const SELECTORS = {
MODAL: '[role="dialog"]',
SUBMIT_BUTTON: 'button[type="submit"]',
} as const;
export const URL_PATTERNS = {
LOGIN: '**/login**',
DASHBOARD: '**/dashboard**',
LOGIN_PATH: '/login',
} as const;
拡張例(プロジェクト固有の要素が増えたら追加):
ELEMENT_VISIBLE: 5000,
AGREEMENT_CHECKBOX: 'input[type="checkbox"]:near(:text("同意する"))',
AUTH_EMAIL_INPUT: 'input[name="username"]',
AUTH_PASSWORD_INPUT: 'input[name="password"]',
あわせて作る: src/utils/uniqueId.ts(一意テストデータ名)
テストデータ名の一意性確保に必須(Date.now() 単独は並列ワーカー衝突 — 判定基準は prohibited-patterns.md「一意テストデータ名は uniqueId() で生成する」)。
export function uniqueId(): string {
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8).padEnd(6, '0')}`;
}
§5. playwright.config.ts 必須設定
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './src/tests',
globalSetup: './src/global-setup.ts',
timeout: 60000,
expect: {
timeout: 10000,
},
reporter: [
['json', { outputFile: 'test-results/report.json' }],
['html', { open: 'never' }],
['list'],
],
use: {
trace: 'retain-on-failure',
screenshot: 'on',
video: 'retain-on-failure',
},
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
: {
: [],
},
},
},
],
});
なぜこの設定が必須か:
timeout + expect.timeout: 無限に待たせない。偽Passの防止
json reporter: report.jsonにステップ構造が残る。Fail時の追跡に必須
trace/video retain-on-failure: 失敗時のみ保存。trace常時ONは長時間テストのheaded実行でブラウザクラッシュの原因になる。Pass時の証跡はreport.json + screenshotで代替
screenshot on: 全テストでスクリーンショット保存。Pass時の「正しく動作した証拠」として機能
html reporter: 人間がブラウザで結果を確認できる
projects: ブラウザを明示的に指定
--disable-crash-reporter: macOS arm64 + Chromium で crash reporter 残存の防御的設定。これだけでは完全に防げないため globalSetup と併用
globalSetup: テスト開始前に前回の残存 chrome_crashpad_handler をクリーンアップ(対症療法)
globalSetup(crashpad_handler クリーンアップ)
macOS arm64 + Chromium for Testing の headed モードで、テスト完了後に chrome_crashpad_handler が残存しプロセスがハングする問題の対症療法。テスト開始前に前回の残存プロセスをクリーンアップする。
import { execSync } from 'child_process';
export default function globalSetup() {
try {
execSync("pkill -f 'ms-playwright.*chrome_crashpad_handler' 2>/dev/null", { stdio: 'ignore' });
} catch {
}
}
playwright.config.ts に globalSetup: './src/global-setup.ts' を追加すること。
ms-playwright でパスを絞り込むため、通常の Chrome / VS Code / 他アプリには影響しない。
§6. BaseAction / StepCounter 雛形
全ActionはBaseActionを継承する。step()ヘルパーはコンソール(ユーザーストーリー粒度)とtest.step()(HTMLレポート階層表示)を両方出力する。prefix([Suite / Phase])は test.info().titlePath から自動導出される。
BaseAction.ts
import { Page, test } from '@playwright/test';
import { StepCounter } from './StepCounter';
export class BaseAction {
protected readonly page: Page;
protected readonly actionName: string;
protected readonly stepCounter?: StepCounter;
constructor(page: Page, actionName: string, stepCounter?: StepCounter) {
this.page = page;
this.actionName = actionName;
this.stepCounter = stepCounter;
}
protected beginAction(): void {
this.stepCounter?.nextMain();
}
protected async (: , : <>): <> {
{ prefix, hasTestContext } = .();
mainNo = .?. ?? ;
(. && mainNo === ) {
(
);
}
stepLabel = mainNo > ? : ;
.();
(hasTestContext) {
test.(name, fn);
} {
();
}
}
(): { : ; : } {
{
info = test.();
parts = info..();
(parts. === ) { : , : };
labels = parts.( .(p));
{ : , : };
} {
{ : , : };
}
}
(: ): {
candidates = [title.(), title.()].( i !== -);
(candidates. === ) title;
colonIdx = .(...candidates);
title.(, colonIdx).();
}
}
StepCounter.ts
import { test } from '@playwright/test';
export class StepCounter {
private mainNumber = 0;
private lastDescribeKey: string | null = null;
nextMain(): number {
const currentKey = this.getDescribeKey();
if (currentKey !== this.lastDescribeKey) {
this.mainNumber = 0;
this.lastDescribeKey = currentKey;
}
this.mainNumber++;
return this.mainNumber;
}
get currentMain(): number {
return this.mainNumber;
}
private getDescribeKey(): string | null {
try {
const info = test.();
parts = info.;
keyParts = parts.(, -);
keyParts. > ? keyParts.() : ;
} {
;
}
}
}
prefix の構成:
test.info().titlePath から自動導出([file, describe, test] の順)
- describe / test 名の
: 前半をラベルとして採用(例: 'Suite-A: フロー名...' → 'Suite-A')
: が無ければ名前全体を使用
- ASCII
: と全角 : の両方に対応(先に現れる方で分割)
出力例:
コンソール:
[Suite-A / Phase 1] Step 1: LoginAction - ログインページへ遷移
[Suite-A / Phase 1] Step 1: LoginAction - 認証情報入力
[Suite-A / Phase 1] Step 2: NavigationAction - メインメニューを開く
...
[Suite-A / Phase 2] Step 21: LoginAction - ログインページへ遷移 ← describe 内で連番継続
HTMLレポート(test.step() ネスト — Action 内部詳細はこちらで階層表示):
ログインページへ遷移
認証情報入力
送信ボタンクリック
...
番号の意味: 番号は「同一 describe 内の Action 呼び出し順」。並列 workers では各 worker が独立した StepCounter を持つため、異なる describe の番号同士は比較できない(グローバル順序は読み取れない)。
§7. .env.example
TEST_BASE_URL=
TEST_USER_EMAIL=
TEST_USER_PASSWORD=
§8. コーディング規約
package.json 必須devDependencies / scripts
{
"scripts": {
"gate": "bash scripts/gate.sh"
},
"devDependencies": {
"@playwright/test": "^1.50.0",
"dotenv": "^16.4.0",
"typescript": "^5.9.0",
"@types/node": "^22.0.0"
}
}
typescript は 5 系を維持する(gate の verify 内固定待機チェックが TypeScript 5 系の JS コンパイラ API を使用。7 系は API 非公開のためチェックがエラー停止する)。
typescript と @types/node がないと npx tsc --noEmit による型チェックが実行できない。
§1 Definition of Done の型チェック要件を満たすために必須。
gate script は必須(機械ゲート)。CI で gate を走らせる場合は、.github/workflows/gate.yml 等にプロジェクトのディレクトリを追加すること。
TypeScript設定(tsconfig.json)
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
コードフォーマット(Prettier)
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100
}
命名規則
| 種類 | 規則 | 例 |
|---|
| クラス | PascalCase | LoginPage, LoginAction |
| メソッド | camelCase | fillEmail(), clickButton() |
| 変数 | camelCase | emailInput, userName |
| 定数 | UPPER_SNAKE_CASE | MAX_RETRY, DEFAULT_TIMEOUT |
| インターフェース | PascalCase | TestEnvironment |
JSDocコメント規約
async execute(email: string, password: string): Promise<boolean> {
}
§9. Playwrightデフォルト構成から4層への変換手順
Playwrightデフォルト(tests/直下にspecファイル)から変換する場合:
src/ 配下に4層ディレクトリ作成(§2参照)
config/constants.ts と config/env.ts を作成(§4, §5参照)
- specファイル内のLocator →
pages/ のPage Objectに移動
- specファイル内のフロー操作 →
actions/ のActionに移動
- Fixture定義を作成(§3参照)し、testのimport元を切り替える
- specファイルには意図・期待結果のみ残す(Locator・ロジック禁止)
- ハードコード値 →
constants.ts に移動
- 認証情報 →
env.ts + .env に移動
npx playwright test で全テスト通過を確認
変換時の注意:
- 一度に全部変換しない。1テストずつ移行して確認
- 既存のテストが通る状態を常に維持する
- 新規Actionを作ったらFixtureに登録を忘れない
§10. プロジェクト固有設定チェックリスト
新プロジェクトに導入する際、以下を確認してCLAUDE.mdに記入する:
§11. トラブルシューティング
macOS + Chromium headed モードでテスト失敗後にターミナルがハングする
症状: npx playwright test --headed で失敗した後、ターミナルがコマンドを受け付けない。Ctrl+C も効かないことがある。
原因: chrome_crashpad_handler プロセスが残存し、親プロセスが解放されない。globalSetup(§5)は次回テスト実行前に残存をクリーンアップする予防策であって、現在のハング状態は解消しない。
対処: 別ターミナルタブから次のコマンドで Playwright の Chromium プロセスのみを kill する。
pkill -f 'ms-playwright'
ms-playwright でパスを絞り込むため、通常の Chrome / VS Code / 他アプリには影響しない。kill 後、ハングしていたターミナルが解放される。