6.1 KiB
6.1 KiB
Playwright Test Debugging Checklist
Initial Assessment
- Read the error message carefully
- Note the failing test name and location
- Check if test fails consistently or intermittently
- Identify which browser(s) are failing
- Check if failure is local only or also in CI
Error Type Identification
Timeout Errors
- Check if element exists with correct data-testid
- Verify page has loaded completely
- Add explicit wait before interaction
- Check if network requests are pending
- Increase timeout if operation is legitimately slow
Selector Errors
- Verify data-testid in HTML matches test code
- Check if multiple elements have the same testid
- Ensure element is not in an iframe
- Confirm element is added to DOM (not removed)
- Use Playwright Inspector to test selector
Assertion Errors
- Verify expected value is correct
- Check if element exists and is visible
- Add wait before assertion
- Check if content is populated asynchronously
- Increase assertion timeout if needed
Navigation Errors
- Check if URL is correct
- Verify server is running
- Check for network issues
- Ensure proper wait after navigation
- Look for redirects or authentication requirements
Element Investigation
-
Count elements matching selector (should be 1)
const count = await page.locator('[data-testid="element"]').count(); console.log('Element count:', count); -
Check element visibility
const isVisible = await page.locator('[data-testid="element"]').isVisible(); console.log('Is visible:', isVisible); -
Check element state
const isEnabled = await page.locator('[data-testid="element"]').isEnabled(); console.log('Is enabled:', isEnabled); -
Get element attributes
const testId = await page.locator('[data-testid="element"]').getAttribute('data-testid'); console.log('data-testid:', testId);
Timing Investigation
-
Add waits before interactions
await page.locator('[data-testid="element"]').waitFor({ state: 'visible' }); -
Wait for page load
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('networkidle'); -
Wait for specific API calls
await page.waitForResponse('**/api/endpoint'); -
Check if animations/transitions are in progress
await page.waitForTimeout(500); // Only for testing, remove later
Interactive Debugging
-
Run test in headed mode
npx playwright test --headed -
Use debug mode
npx playwright test --debug -
Add
page.pause()in testawait page.pause(); // Pauses execution for manual inspection -
Enable slow motion
// In playwright.config.ts use: { launchOptions: { slowMo: 1000, // Slow down by 1 second }, } -
Take screenshots for inspection
await page.screenshot({ path: 'debug-screenshot.png', fullPage: true });
Trace Analysis
-
Enable trace recording
// In playwright.config.ts use: { trace: 'on-first-retry', } -
Open trace viewer
npx playwright show-trace trace.zip -
Check trace for:
- Network requests
- Console logs
- Screenshots at each step
- DOM snapshots
- Action timeline
Test Isolation Check
- Run test in isolation (comment out other tests)
- Check if test passes when run alone
- Verify no shared state between tests
- Ensure proper cleanup in
afterEach - Check for global state mutations
Environment Differences
Local vs CI
-
Run test in headless mode locally
npx playwright test --headed=false -
Check viewport size matches CI
await page.setViewportSize({ width: 1920, height: 1080 }); -
Compare timeouts (CI may need longer)
-
Check if CI has different environment variables
Browser Differences
-
Test in all configured browsers
npx playwright test --project=chromium npx playwright test --project=firefox npx playwright test --project=webkit -
Check for browser-specific issues
-
Verify CSS compatibility
-
Test JavaScript feature support
Common Quick Fixes
Fix #1: Add Explicit Wait
// Before
await page.locator('[data-testid="element"]').click();
// After
await page.locator('[data-testid="element"]').waitFor({ state: 'visible' });
await page.locator('[data-testid="element"]').click();
Fix #2: Wait for Network
// Before
await page.locator('[data-testid="submit"]').click();
await expect(page.locator('[data-testid="result"]')).toBeVisible();
// After
await page.locator('[data-testid="submit"]').click();
await page.waitForLoadState('networkidle');
await expect(page.locator('[data-testid="result"]')).toBeVisible();
Fix #3: Handle Multiple Elements
// Before
await page.locator('[data-testid="item"]').click(); // Error if multiple
// After
await page.locator('[data-testid="item"]').first().click();
Fix #4: Increase Timeout
// Before
await expect(page.locator('[data-testid="slow-element"]')).toBeVisible();
// After
await expect(page.locator('[data-testid="slow-element"]')).toBeVisible({
timeout: 15000
});
Fix #5: Check Element State
// Before
await page.locator('[data-testid="button"]').click();
// After
await expect(page.locator('[data-testid="button"]')).toBeEnabled();
await page.locator('[data-testid="button"]').click();
Verification
- Run test 5+ times to verify consistency
- Test in all browsers
- Run full test suite to check for regressions
- Test in CI environment
- Document the fix for future reference
Prevention
- Add explicit waits where needed
- Use only data-testid locators
- Ensure test isolation
- Add retry logic in config
- Review and refactor flaky tests regularly
- Keep tests simple and focused
- Maintain good test hygiene (cleanup, fixtures)