name: debug-e2e-test description: Debug and fix failing Playwright E2E tests. Use when tests fail, when asked to fix failing tests, or when investigating test failures. Analyzes test output, screenshots, error context, and uses Playwright MCP to identify root causes.
Skill: Debug E2E Test
This skill provides a systematic workflow for debugging and fixing failing Playwright E2E tests in the mock-app.
When to Use This Skill
Use this skill when:
- A Playwright E2E test is failing
- Asked to fix a failing test
- Investigating why tests are broken
- Tests pass locally but fail in CI
- Need to understand what a test failure means
Prerequisites
- Dev server must be running (
npm run dev) - Playwright MCP tools available
- Failed test has generated artifacts in
test-results/
Debugging Workflow
Step 1: Run the Specific Failed Test
Run ONLY the specific failing test using the -g flag to get focused output:
npm run test:e2e -- <test-file>.spec.ts -g "test name"
Why: Running a single test is faster and produces cleaner output than running the entire suite.
Step 2: Analyze the Test Output
Read the terminal output carefully and extract:
- Test location: File path and line number
- Failure type: What assertion failed
- Expected vs Actual: What the test expected vs what it got
- Error message: The specific error
- Artifact paths: Screenshot and error-context.md locations
Example output:
Error: expect(received).toBe(expected)
Expected: 0
Received: 13
177 | const inactiveBadges = page.locator('text=Inactive');
178 | const count = await inactiveBadges.count();
> 179 | expect(count).toBe(0);
Step 3: Load the Screenshot
Use the read_media_file tool to load the screenshot from the test results:
test-results/<test-name>/test-failed-1.png
This shows the visual state when the test failed.
Step 4: Read the Error Context
Read the error-context.md file from the test results directory:
test-results/<test-name>/error-context.md
This file contains:
- DOM snapshot: Complete page structure at failure time
- Console logs: Browser warnings/errors
- Network errors: Failed requests (if any)
Key things to look for:
- Are the elements the test is looking for present?
- Are there unexpected elements?
- Are there console errors indicating broken functionality?
- Is the page structure what the test expects?
Step 5: Read the Test Code
Read the failing test code to understand:
- What is it trying to test?
- What user actions does it simulate?
- What assertions does it make?
- Are the selectors specific enough?
Step 6: Compare Test Expectations with Implementation
Use grep or semantic_search to find the actual component/page being tested:
# Find where specific text/elements are defined
grep -r "text the test looks for" mock-app/src/
Check:
- Does the route match? (
/usersvs/organization/users) - Does the text match exactly? (case, spacing, punctuation)
- Are the element types correct? (
<button>vs<a>)
Step 7: Use Playwright MCP (If Needed)
If the issue isn't clear from the screenshot and error context, use Playwright MCP to interact with the live app:
⚠️ Only use MCP if necessary - it's slower than analyzing artifacts
- Navigate to the page:
playwright_navigate - Take a screenshot:
playwright_screenshot - Get HTML:
playwright_get_visible_html - Click elements:
playwright_click - Inspect what happens:
playwright_get_visible_text
Use MCP to:
- Verify filters/buttons actually work
- See what elements exist vs what test expects
- Understand the actual vs expected behavior
- Test different selectors
Step 8: Fix the Issue
Based on your findings, fix either the test, the code, or both.
Step 9: Verify the Fix
Run the specific test again to verify it passes:
npm run test:e2e -- <test-file>.spec.ts -g "test name"
If it passes, commit your changes with a clear message explaining what was fixed.
Tips for Effective Debugging
- Always run the single test first - Don't debug from full suite output
- Start with artifacts - Screenshot + error-context tell you 90% of issues
- Use MCP sparingly - Only when artifacts don't show the problem
- Check obvious things first - Routes, text, element types
- One test at a time - Fix and verify before moving to next failure
- Read the DOM snapshot - Often more useful than screenshot for structural issues
🔄 Intermittent Failures - Check for Non-Deterministic Data
If tests pass sometimes and fail other times, the issue is likely non-deterministic mock data.
Common Causes
Sample generators using
Date.now()ornew Date()- Check
/model/*.sample.tsfiles - Look for timestamps, dates, or "recent" calculations
- These create different data on each test run!
- Check
Missing seeds in mock handlers
- Check
mock-app/src/mocks/handlers.ts - Ensure all
createXxxSample()calls pass aseedparameter - Example:
createJobSample({ seed: i })notcreateJobSample()
- Check
Storybook stories without seeds
- Check
*.stories.tsxfiles - Use seeds for consistent component props
- Example:
createJobSample({ seed: 1 })notcreateJobSample()
- Check
How to Diagnose
# Run the same test multiple times
npx playwright test <file>.spec.ts -g "test name"
npx playwright test <file>.spec.ts -g "test name"
npx playwright test <file>.spec.ts -g "test name"
# If results differ, you have non-deterministic data
How to Fix
- Update sample generators - See generate-sample-data skill
- Add seeds everywhere - Never call sample functions without seeds
- Verify fix:
// Should produce identical output const data1 = createJobSample({ seed: 42 }); const data2 = createJobSample({ seed: 42 }); expect(data1).toEqual(data2); // Should pass
Example: Full Debugging Session
Scenario: Test "displays user list with all users" fails
1. Run the test:
npm run test:e2e -- multi-user-account-management.spec.ts -g "displays user list"
2. Output shows:
Error: expect(locator).toBeVisible() failed
Locator: locator('th:has-text("Name")')
3. Read error-context.md - See table headers are: User, Role, Status, Last Login, Actions
4. Read test code - Test looks for separate "Name" and "Email" headers
5. Compare - UI has combined "User" header, test expects separate headers
6. Diagnosis: Test is outdated, UI correctly shows combined User column
7. Fix: Update test selectors to match actual UI:
await expect(page.locator('th:has-text("User")')).toBeVisible();
// Remove old Name/Email checks
8. Verify:
npm run test:e2e -- multi-user-account-management.spec.ts -g "displays user list"
9. Result: ✅ Test passes!
Related Skills
- write-e2e-test: Create new E2E tests
- responsive-design: Verify responsive behavior in tests
- implement-feature: Ensure tests are created alongside features