ワンクリックで
test-e2e
Run E2E Playwright testing workflow for web applications
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Run E2E Playwright testing workflow for web applications
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Enable or disable the workflow usage status line
Interactive live testing of a running web application via Playwright MCP browser automation
Autonomous driver — outer loop that wakes on a schedule, resumes or advances the in-flight task for a repo, then pulls and launches the next queued task. Reconstructs all state from disk and the GitHub issue on every wake; never relies on session memory.
Assign or archive unscoped legacy workflow state files (created before repo-scoping) so they stop leaking into every repo's session.
Run verification loop on current changes
| name | test-e2e |
| description | Run E2E Playwright testing workflow for web applications |
| user_invocable | true |
| usage | /workflow:test-e2e <url> [--framework=<framework>] [--auth=<strategy>] [--depth=<n>] [--output=<dir>] [--config-only] [--format=<format>] |
| arguments | [{"name":"url","required":true,"description":"Base URL of the web application to test (e.g., http://localhost:8080)"},{"name":"framework","required":false,"description":"Framework type: symfony, laravel, vue, react, next, generic (auto-detected if omitted)"},{"name":"auth","required":false,"description":"Auth strategy: none, form, token, cookie (default: none)"},{"name":"depth","required":false,"description":"Exploration depth limit (default: 3)"},{"name":"output","required":false,"description":"Test output directory (default: tests/e2e or e2e)"},{"name":"config-only","required":false,"description":"Only generate config files, skip exploration and test generation"},{"name":"format","required":false,"description":"State file format: org, md (default: org)"}] |
Orchestrate end-to-end test generation for web applications using browser automation.
E2E agents select a browser driver at runtime per resources/e2e/browser-driver.md. The selection order is:
mcp__playwright__browser_*mcp__chrome-devtools__*mcp__tpmcp-ux_capture__*ignoreHTTPSErrors: true (used when no MCP is present or when self-signed TLS blocks the MCP)The workflow adapts to whatever driver is available. The Playwright MCP is no longer hard-required for live exploration.
Generated test specs are always Playwright (@playwright/test, run via npx playwright test) — that is the artifact format. @playwright/test as a dev dependency is still installed so specs can be executed, but the live MCP browser is not mandatory.
Beyond this standalone /workflow:test-e2e command, e2e_validation is a mandatory gate in swarm and epic dev workflows whenever the change is FE-facing. FE-facing detection is performed by lib/fe-detect-cli.js and triggers when the change set touches routes, components, templates, styles, assets, or FE config files. On a non-FE change the gate status is set to skipped. The gate is enforced by the stop-guard and task-completed-gate hooks via the phase order, so an FE-facing workflow cannot reach completion without passing e2e_validation.
This workflow runs in fully autonomous agentic mode. Do NOT ask for permission on non-destructive operations.
REQUIRED: The project MUST have
Bash(*)in its permissions allow list. Without this, bash commands will prompt for permission and break autonomous execution.
~ in Tool CallsThe Write, Read, Glob, and Edit tools do NOT expand ~. You MUST run echo $HOME first and use the absolute path in ALL tool calls.
DO WITHOUT ASKING:
npm install --save-dev @playwright/test)npx playwright install chromium)npx playwright test)ASK BEFORE:
git commit - User reviews and commitsgit push - User pushes when readyrm) - Confirm before removalALWAYS BLOCKED:
rm -rf on directoriesgit push --forceProceed autonomously through all phases. Only pause for:
/workflow:test-e2e <url> [options]
# Test local Symfony app
/workflow:test-e2e http://localhost:8080 --framework=symfony --auth=form
# Test React app with default settings
/workflow:test-e2e http://localhost:3000
# Just generate config, no tests
/workflow:test-e2e http://localhost:8080 --config-only
# Deep exploration
/workflow:test-e2e http://localhost:8080 --depth=5
# Custom output directory
/workflow:test-e2e http://localhost:4200 --output=e2e/specs --framework=vue
Setup --> Exploration --> Generation --> Validation --> Quality Gate --> Completion
| | | | | |
| e2e-explorer e2e-generator e2e-reviewer quality-gate completion-guard
| (driver- (app map -> (run tests, (lint, type (final check)
| agnostic) test specs) check quality) check)
v
Install deps,
detect framework,
generate config
You are the supervisor agent for the E2E testing workflow. You coordinate all phases, spawn subagents, and maintain state.
Follow these steps in order:
echo $HOME
Store the result (e.g., /home/user). Use this for ALL file paths.
Ensure workflow directories exist (use ABSOLUTE paths):
$HOME_PATH/.claude-workflows/active/.gitkeep
$HOME_PATH/.claude-workflows/completed/.gitkeep
--framework=<val> (default: auto-detect)--auth=<val> (default: none)--depth=<n> (default: 3)--output=<dir> (default: auto-detect based on framework)--config-only (boolean flag)--format=<val> (default: org)Before anything else, validate and test the target URL:
URL scheme validation (MANDATORY):
Only http:// and https:// URLs are accepted. Reject file://, ftp://, javascript:, data:, and any other scheme.
# Validate URL scheme
URL_SCHEME=$(echo "<url>" | grep -oP '^[a-z]+(?=://)')
if [ "$URL_SCHEME" != "http" ] && [ "$URL_SCHEME" != "https" ]; then
echo "ERROR: Only http:// and https:// URLs are supported"
# Stop and inform user
fi
# Check reachability
curl -sS -o /dev/null -w "%{http_code}" --max-time 10 <url>
Check the project directory for framework indicators:
| File/Pattern | Framework |
|---|---|
symfony.lock or config/packages/ | symfony |
artisan or app/Http/Kernel.php | laravel |
vue.config.js or vite.config.ts with vue plugin | vue |
next.config.* | next |
package.json scripts with react-scripts | react |
| None of the above | generic |
If auto-detected, confirm with user: "Detected framework: {framework}. Correct?"
Default test directory based on framework:
| Framework | Default Output Dir |
|---|---|
| symfony | tests/E2e |
| laravel | tests/E2e |
| vue | e2e |
| react | e2e |
| next | e2e |
| generic | tests/e2e |
Generate workflow ID: YYYYMMDD-<random> (e.g., 20260213-abc123)
Create state file using the template from the plugin:
<PLUGIN_DIR>/templates/e2e-testing.<format>{{WORKFLOW_ID}} -> generated ID{{TITLE}} -> URL + framework description{{DESCRIPTION}} -> full task description{{DATE}} -> current date{{TIMESTAMP}} -> ISO timestamp{{BRANCH}} -> current branch{{BASE_BRANCH}} -> main/master{{STATE_FILE}} -> path to .state.json{{TESTS_ENABLED}} -> "true"{{BASE_URL}} -> target URL{{FRAMEWORK}} -> detected/specified framework{{AUTH_STRATEGY}} -> auth flag value{{DEPTH}} -> depth limit{{OUTPUT_DIR}} -> resolved output directoryPLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/workflow}"
ACTIVE_DIR=$(node "$PLUGIN_ROOT/lib/active-dir-cli.js")
REPO_KEY=$(node "$PLUGIN_ROOT/lib/repo-key-cli.js")
REPO_ROOT=$(node -e "console.log(require('$PLUGIN_ROOT/lib/repo-key').getRepoRoot())")
$ACTIVE_DIR/<id>.<format> (inside the repo bucket — never the flat active/ root)Create JSON state sidecar:
{
"$schema": "1.0.0",
"workflow_id": "<id>",
"repo_key": "<REPO_KEY>",
"repo_root": "<REPO_ROOT>",
"org_file": "<ACTIVE_DIR>/<id>.<format>",
"workflow": {
"type": "e2e-testing",
"description": "<description>",
"branch": "<branch>"
},
"config": {
"tests_enabled": true,
"base_url": "<url>",
"framework": "<framework>",
"auth_strategy": "<auth>",
"depth": <depth>,
"output_dir": "<output_dir>"
},
"phase": {
"current": "setup",
"completed": [],
"remaining": ["e2e_exploration", "e2e_generation", "e2e_validation", "quality_gate", "completion_guard"]
},
"gates": {
"setup": { "status": "pending", "iteration": 0 },
"e2e_exploration": { "status": "pending", "iteration": 0 },
"e2e_generation": { "status": "pending", "iteration": 0 },
"e2e_validation": { "status": "pending", "iteration": 0 },
"quality_gate": { "status": "pending", "iteration": 0 },
"completion_guard": { "status": "pending", "iteration": 0 }
},
"agent_log": [],
"updated_at": "<timestamp>"
}
Write to: $ACTIVE_DIR/<id>.state.json (inside the repo bucket — never the flat active/ root)
Verify both files by reading them back.
After creating the state files, bind this session to the workflow so hooks only affect this workflow.
First, get the OS temp directory:
node -e "console.log(require('os').tmpdir())"
Store this as $TMPDIR_PATH.
$TMPDIR_PATH/workflow-session-marker-*.json and read the most recent file to get the session_id$TMPDIR_PATH/workflow-binding-{session_id}.json with:
{
"session_id": "<session_id>",
"workflow_path": "<ACTIVE_DIR>/<id>.state.json",
"workflow_id": "<generated-id>",
"bound_at": "<ISO timestamp>"
}
If no session marker is found, skip this step (backward compatible — hooks will fall back to most recent workflow).
Show summary:
E2E Testing Workflow
URL: <url>
Framework: <framework>
Auth: <auth>
Depth: <depth>
Output: <output_dir>
State: ~/.claude-workflows/active/<id>.<format>
Ready to begin Phase 0: Setup?
The supervisor handles setup directly. This phase installs dependencies and configures Playwright.
# Check in priority order
if [ -f pnpm-lock.yaml ]; then echo "pnpm"
elif [ -f yarn.lock ]; then echo "yarn"
elif [ -f package-lock.json ] || [ -f package.json ]; then echo "npm"
else echo "npm" # default
fi
# Check if already installed
npx playwright --version 2>/dev/null
# If not installed:
<pkg_manager> install --save-dev @playwright/test
npx playwright install chromium
Only install chromium by default. Users can add firefox/webkit later.
Read the config template from the plugin:
<PLUGIN_DIR>/resources/e2e/playwright.config.ts.template
Replace placeholders:
{{OUTPUT_DIR}} -> resolved output directory{{BASE_URL}} -> target URL{{WEBSERVER_CONFIG}} -> look up framework in <PLUGIN_DIR>/resources/e2e/webserver-configs.jsonIf webserver-configs.json has a config for the framework, add:
webServer: {
command: '<command>',
url: '<url>',
reuseExistingServer: true,
},
If framework config is null (generic), omit the webServer block.
Port validation: If substituting {{PORT}} in webserver configs, validate that the value is a numeric integer between 1024 and 65535. Reject any non-numeric port values.
Write playwright.config.ts to the project root.
mkdir -p <output_dir>
MANDATORY: Update .gitignore to prevent accidental commit of auth state and test artifacts:
Check if .gitignore exists. If so, append these entries (if not already present):
# Playwright E2E
.auth/
auth.json
test-results/
playwright-report/
blob-report/
If .gitignore does not exist, create it with these entries.
Select the browser driver per resources/e2e/browser-driver.md:
mcp__playwright__browser_*)mcp__chrome-devtools__*)mcp__tpmcp-ux_capture__*)ignoreHTTPSErrors: trueState the chosen driver (e.g. DRIVER: playwright-mcp) in the setup summary. Attempt a navigate to <url> using the selected driver. If the URL is unreachable, report the connection error. A missing Playwright MCP is not a blocking error — the workflow continues with the next available driver.
If auth strategy is specified:
For --auth=form:
<PLUGIN_DIR>/resources/e2e/global-setup.ts.template{{LOGIN_URL}} -> URL where the login form is{{EMAIL_LABEL}} -> detected email/username label{{PASSWORD_LABEL}} -> detected password label{{SUBMIT_TEXT}} -> detected submit button text{{POST_LOGIN_URL}} -> detected redirect URL after login{{AUTH_STATE_FILE}} -> <output_dir>/.auth/state.jsonglobal-setup.ts to the output directoryauth-fixture.ts.template to the output directory./.creds file first (it's gitignored — test creds usually live there; never commit it or echo its contents into logs/commits/PRs). Only ask the user if .creds is missing or lacks the needed account. Expose them as E2E_USER / E2E_PASS env vars — never hardcode credentials in test files.For --auth=token:
./.creds first; only ask the user if it's absent. Token env var name default: E2E_TOKEN.For --auth=cookie:
E2E_SESSION_COOKIE)If --config-only flag was passed:
Mark setup phase as complete. Update state file and JSON sidecar.
Spawn the e2e-explorer agent to map the application.
Task(
subagent_type="workflow:e2e-explorer",
prompt="""
## Task
Explore the web application at {base_url} and generate an app map.
## Context
Workflow ID: {workflow_id}
Output path: {output_dir}/app-map.json
Max depth: {depth}
Auth strategy: {auth_strategy}
Framework: {framework}
## Instructions
### 1. Connection Check
- Select a browser driver per resources/e2e/browser-driver.md; state which driver is used
- Navigate to {base_url} using that driver
- If connection refused/timeout: report error and stop
- Capture initial accessibility snapshot
### 2. Auth Handling
{auth_instructions_based_on_strategy}
### 3. Exploration
- Use BFS traversal up to depth {depth}
- For each page: navigate then take accessibility snapshot (using the selected driver's action mapping)
- Record all interactive elements (links, buttons, forms, inputs)
- Track which pages require authentication
- Detect SPA routing (URL changes from clicks vs full navigations)
### 4. Output
Write app map JSON to: {output_dir}/app-map.json
App map structure:
{
"base_url": "...",
"framework": "...",
"explored_at": "ISO timestamp",
"pages": [
{
"url": "/path",
"title": "Page Title",
"requires_auth": false,
"links": [{"text": "...", "href": "..."}],
"forms": [{"action": "...", "fields": [...]}],
"buttons": [{"text": "...", "role": "..."}],
"navigation": [{"text": "...", "href": "..."}],
"headings": ["..."],
"dynamic_content": ["..."]
}
],
"navigation_graph": {
"/": ["/about", "/login"],
"/about": ["/", "/contact"]
},
"auth": {
"login_url": "/login",
"login_fields": ["email", "password"],
"protected_urls": ["/dashboard", "/profile"]
},
"summary": {
"total_pages": N,
"total_forms": N,
"total_buttons": N,
"auth_required_pages": N
}
}
Report completion with summary statistics.
"""
)
After explorer completes:
Spawn the e2e-generator agent to create test files from the app map.
Task(
subagent_type="workflow:e2e-generator",
prompt="""
## Task
Generate Playwright E2E test suite from app map.
## Context
Workflow ID: {workflow_id}
App map: {output_dir}/app-map.json
Test directory: {output_dir}
Framework: {framework}
Auth strategy: {auth_strategy}
## Instructions
### 1. Read App Map
Read and parse {output_dir}/app-map.json
### 2. Plan Test Suites
Group tests by feature:
- navigation.spec.ts - Navigation structure and routing
- auth.spec.ts - Login/logout (if auth detected)
- <page-name>.spec.ts - Per-page feature tests
- forms.spec.ts - Form validation and submission
- accessibility.spec.ts - A11y checks
### 3. Selector Priority (MANDATORY)
Use this priority for all selectors:
1. getByRole('button', { name: '...' })
2. getByLabel('...')
3. getByPlaceholder('...')
4. getByText('...')
5. getByTestId('...')
NEVER use:
- CSS selectors (.class, #id)
- XPath
- Auto-generated class names
- page.locator() with CSS unless no accessible alternative exists
### 4. Test Patterns
Each test file must:
- Import from @playwright/test (or auth fixture if auth needed)
- Use test.describe() for grouping
- Use meaningful test names: test('should <action> when <condition>')
- Include at least one assertion per test
- Use page.waitForURL() instead of hard waits
- Use expect(...).toBeVisible() for element presence
### 5. Auth Tests
If auth is configured:
- Import { test, expect } from the auth fixture
- Tests that need auth use the authenticated fixture
- Tests for public pages use standard @playwright/test import
### 6. Write Files
Write all test files to {output_dir}/
Each file must compile with tsc --noEmit.
### 7. Report
List all generated files with line counts and features covered.
"""
)
After generator completes:
npx tsc --noEmit on test files (quick syntax check)This is the main review gate. The e2e-reviewer runs tests and checks quality.
iteration = 0
max_iterations = 3
while iteration < max_iterations:
Task(
subagent_type="workflow:e2e-reviewer",
prompt="""
## Task
Review E2E Playwright tests in {output_dir}
## Context
Workflow ID: {workflow_id}
Review iteration: {iteration + 1} of {max_iterations}
Previous issues: {previous_issues if iteration > 0 else "None (first review)"}
Base URL: {base_url}
## Instructions
### 1. Run Tests
Execute: npx playwright test --reporter=list
### 2. Static Analysis
For each test file:
- Check selector quality (getByRole preferred)
- Check for anti-patterns (waitForTimeout, sleep, CSS selectors)
- Check test isolation (no shared state between tests)
- Check assertions are meaningful
- Check error handling
### 3. Flakiness Check
Run tests 2 more times and compare results for determinism.
### 4. Verdict
Return structured verdict:
VERDICT: PASS or FAIL
If FAIL, list issues:
- [ISSUE-1] <severity>: <description> in <file>:<line>
- [ISSUE-2] ...
If PASS:
- Total tests: N
- All passing: yes/no
- Flakiness detected: yes/no
- Selector quality: good/acceptable/poor
"""
)
if verdict == "PASS":
# Update state: e2e_validation passed
break
else:
iteration += 1
if iteration < max_iterations:
# Send issues back to generator for fixes
Task(
subagent_type="workflow:e2e-generator",
prompt="""
## Review Issues to Fix (MANDATORY)
{numbered_issues_from_review}
## Fix Protocol
1. Address EVERY issue by ID
2. Read the file, understand the problem, apply the fix
3. Self-verify by re-reading the fixed code
4. Report: [ISSUE-N] FIXED: <what changed>
5. If false positive: [ISSUE-N] DISPUTE: <justification>
"""
)
else:
# Max iterations reached
Report: "BLOCKING: Test validation failed after {max_iterations} iterations."
Ask user for intervention
PAUSE
After validation passes, run the quality gate.
Task(
subagent_type="workflow:quality-gate",
prompt="""
QUALITY GATE CHECK - E2E Testing
Project: {project_path}
Test directory: {output_dir}
Run checks:
1. TypeScript compilation: npx tsc --noEmit (on test files)
2. Lint: npx eslint {output_dir} (if eslint configured)
3. All E2E tests pass: npx playwright test
4. No security issues in test code (no hardcoded credentials, no eval)
Auto-fix up to 3 iterations.
Report: PASS or FAIL with details.
"""
)
If quality gate made changes, run a targeted review (e2e-reviewer on changed files only, max 2 iterations).
Final verification before marking the workflow complete.
Task(
subagent_type="workflow:completion-guard",
prompt="""
COMPLETION GUARD - E2E Testing Workflow
Original task: Generate E2E tests for {base_url}
Workflow ID: {workflow_id}
App map: {output_dir}/app-map.json
Verify:
1. App map exists and is valid JSON
2. Test files exist for discovered pages
3. All tests pass: npx playwright test
4. No TODO/FIXME in test files
5. playwright.config.ts is valid
6. Auth setup works (if configured)
7. Coverage: compare test files against app map pages
Return: APPROVED or REJECTED with specific issues.
"""
)
If rejected: fix issues -> re-run quality gate -> re-run completion guard (max 3 cycles).
When all phases pass:
Update state files:
Report to user:
E2E Testing Complete!
URL: <url>
Tests generated: N files, M test cases
All tests passing: yes
Coverage: N/M pages covered
Generated files:
- playwright.config.ts
- <output_dir>/navigation.spec.ts
- <output_dir>/auth.spec.ts
- ...
Run tests: npx playwright test
View report: npx playwright show-report
Ask about commit: "Should I commit these test files?"
Clean up temp files:
TMPDIR_PATH=$(node -e "console.log(require('os').tmpdir())")
rm -f "$TMPDIR_PATH/workflow-session-marker-${SESSION_ID}.json"
rm -f "$TMPDIR_PATH/workflow-binding-${SESSION_ID}.json"
rm -f "$TMPDIR_PATH/workflow-stop-${SESSION_ID}."*
rm -f "$TMPDIR_PATH/workflow-deny-${SESSION_ID}.json"
rm -f "$TMPDIR_PATH/workflow-complete-${SESSION_ID}-"*
${SESSION_ID} is the session_id from Step 5.5Archive state:
active/ to completed/| Error | Action |
|---|---|
| URL unreachable | Warn user, ask to proceed or abort |
| No browser driver available | Report which drivers were checked; local Playwright fallback is last resort |
| npm install fails | Report error, suggest manual install |
| Browser install fails | Report error, suggest npx playwright install --with-deps |
| Explorer finds no pages | Report, ask user to check URL and try again |
| Tests won't compile | Send back to generator with errors |
| All tests fail on first run | Check if app is running, then fix tests |
| Auth setup fails | Re-check ./.creds; if absent or insufficient, ask the user, then retry |
MANDATORY: Update the state file before and after each phase:
Templates and resources are in the plugin cache:
<PLUGIN_DIR>/templates/e2e-testing.org
<PLUGIN_DIR>/templates/e2e-testing.md
<PLUGIN_DIR>/resources/e2e/playwright.config.ts.template
<PLUGIN_DIR>/resources/e2e/global-setup.ts.template
<PLUGIN_DIR>/resources/e2e/auth-fixture.ts.template
<PLUGIN_DIR>/resources/e2e/webserver-configs.json
Where <PLUGIN_DIR> is the workflow plugin cache directory (find it via the plugin system).