name: generate-e2e-tests description: Use Playwright MCP to explore a feature, analyze existing tests, and generate/update E2E tests. Prioritizes user journey scenarios over individual element tests. user-invocable: true allowed-tools: - Read - Write - Edit - Grep - Glob - Bash - mcp__playwright__browser_navigate - mcp__playwright__browser_snapshot - mcp__playwright__browser_click - mcp__playwright__browser_type - mcp__playwright__browser_select_option - mcp__playwright__browser_close
Generate E2E Tests
Convert manual Playwright MCP verification into automated Playwright tests. This skill explores the UI, analyzes existing test coverage, and proposes scenario-based tests.
Philosophy
Prioritize scenarios over individual element tests:
- Good: "user can navigate from sidebar to resource list and view details"
- Bad: "sidebar button is visible", "table header exists", "row count is correct"
Tests should represent user journeys - sequences of actions a user would take to accomplish a goal. Multiple assertions within a single test scenario are preferred over separate tests for each assertion.
Process
Phase 1: Context Gathering
Understand the feature from conversation context or ask the user:
- What feature was just implemented?
- What page/route is it on?
- What are the key user interactions?
Start mock server and dev server if not running:
docker compose -f e2e/docker-compose.yml up -d # Dev server should be started with mock config: # DOT_AI_MCP_URL=http://localhost:3001 DOT_AI_AUTH_TOKEN=mock-token ./scripts/dev-start.sh
Phase 2: Exploration with Playwright MCP
Navigate to the feature using
mcp__playwright__browser_navigateCapture snapshots using
mcp__playwright__browser_snapshotto understand:- Page structure and available elements
- Interactive elements (buttons, links, inputs)
- Data displayed from mock server
Interact with the feature using:
mcp__playwright__browser_clickfor buttons/linksmcp__playwright__browser_typefor inputsmcp__playwright__browser_select_optionfor dropdowns
Document the user journey as you explore:
- Starting state
- Actions taken
- Expected outcomes after each action
- Final state
Phase 3: Analyze Existing Tests
Read existing test files in
e2e/directory:e2e/*.spec.tsIdentify coverage gaps:
- What scenarios are already tested?
- What new scenarios does this feature introduce?
- Can existing tests be extended rather than creating new ones?
Phase 4: Propose Test Plan
- Present the plan to the user in this format:
## E2E Test Plan for [Feature Name]
### Existing Coverage
- [List what's already tested that relates to this feature]
### Proposed Changes
#### Option A: Extend Existing Test (Preferred if applicable)
**File:** `e2e/[existing].spec.ts`
**Test:** `[existing test name]`
**Add assertions for:**
- [New assertion 1]
- [New assertion 2]
#### Option B: New Test Scenario
**File:** `e2e/[new-or-existing].spec.ts`
**Scenario:** `[descriptive scenario name]`
**User Journey:**
1. [Action 1] → [Expected outcome]
2. [Action 2] → [Expected outcome]
3. [Action 3] → [Expected outcome]
### Recommendation
[Which option and why]
- Wait for user approval before proceeding
Phase 5: Implement Tests
- Write or update tests following these patterns:
// GOOD: Scenario-based test with multiple assertions
test('user can browse resources and view details', async ({ page }) => {
// Navigate to starting point
await page.goto('/dashboard')
// Action 1: Select a resource type
await page.getByRole('button', { name: 'Pod' }).click()
await expect(page).toHaveURL(/kind=Pod/)
// Action 2: Verify list loads with expected data
await expect(page.getByRole('table')).toBeVisible()
// Use exact count when mock data is deterministic, or verify non-empty
await expect(page.getByRole('row')).toHaveCount(5)
// Action 3: Click on a specific resource
await page.getByRole('link', { name: 'nginx-pod' }).click()
// Action 4: Verify details page
await expect(page.getByRole('heading', { name: 'nginx-pod' })).toBeVisible()
})
// BAD: Separate tests for each element
test('sidebar has Pod button', ...)
test('clicking Pod updates URL', ...)
test('table is visible', ...)
test('rows exist', ...)
Use
beforeEachfor common setup (login, navigation to starting point)Prefer extending existing
test.describeblocks when the feature fits
Phase 6: Run and Verify
- Run the tests:
npx playwright test e2e/[file].spec.ts --reporter=line
- If tests fail, analyze the error and fix:
- Check if selectors need adjustment
- Verify mock data matches expected values
- Ensure proper wait conditions
- Report results to the user:
- Tests passed/failed
- Coverage added
- Any issues encountered
Test Writing Guidelines
Selectors (in order of preference)
getByRole()- Most reliable, semanticgetByLabel()- For form fieldsgetByText()- For static textgetByTestId()- Last resort, requires code changes
Assertions
- Use
toBeVisible()for elements that should be seen - Use
toBeAttached()for elements in DOM but possibly hidden - Use
toHaveURL()for navigation verification - Use
toHaveCount()for lists/tables - Use
toContainText()for partial text matching
Handling Async
- Playwright auto-waits, but use explicit waits when needed:
await expect(page.getByRole('table')).toBeVisible() // Now table is definitely visible before next action
Mock Data Awareness
- Tests run against mock server data
- Don't assert on specific counts unless mock data is deterministic
- Prefer assertions like "at least one" over exact counts when possible
Example Invocation
User: "I just finished implementing the resource detail page. Can you generate tests for it?"
Claude would then:
- Use Playwright MCP to navigate to the dashboard
- Click through to a resource detail page
- Capture the page structure
- Check existing tests in
e2e/dashboard.spec.ts - Propose extending the "clicking resource kind shows resource list" test to include clicking through to details
- Wait for approval
- Implement and run the tests