Files

137 lines
3.5 KiB
Markdown
Raw Permalink Normal View History

# Debugging Guide
## First Moves
1. Reproduce in headed mode.
2. Capture a trace before rewriting selectors or waits.
3. Check whether the failure is selector drift, actionability, environment drift, shared state, or a real product regression.
## Inspector and Headed Runs
```bash
npx playwright test --debug
npx playwright test my-test.spec.ts --debug
npx playwright test --headed
```
```typescript
await page.pause();
```
## Trace Viewer
```bash
npx playwright test --trace on
npx playwright show-trace trace.zip
```
```typescript
use: {
trace: 'retain-on-failure',
}
```
Start with traces for CI and flaky failures. Use screenshots and videos as supporting evidence, not as the primary debugging tool.
## Common Errors
### Element Not Found
Use explicit waits and confirm the right frame or shadow boundary before rewriting selectors. If the locator is ambiguous, improve the locator instead of clicking the first match.
```typescript
await page.waitForSelector('.element');
const frame = page.frameLocator('iframe');
await frame.locator('.element').click();
await page.click('.element', { timeout: 60000 });
```
### Flaky Click
Check visibility, scrolling, overlays, and disabled state before forcing the click.
```typescript
await page.locator('.btn').waitFor({ state: 'visible' });
await page.locator('.btn').scrollIntoViewIfNeeded();
await page.locator('.btn').click();
```
Use `force: true` only after confirming that the overlay or disabled state is not the real bug.
If the click target keeps changing, inspect actionability conditions first: visible, stable, enabled, and actually receiving pointer events.
### Timeout in CI
Slow environments usually need better waits, traces, or fewer workers before they need bigger timeouts.
```typescript
export default defineConfig({
timeout: 60000,
expect: { timeout: 10000 },
});
await expect.poll(async () => {
return await page.locator('.items').count();
}, { timeout: 30000 }).toBeGreaterThan(5);
```
### Network Issues
```typescript
page.on('request', request => {
console.log('>>', request.method(), request.url());
});
page.on('response', response => {
console.log('<<', response.status(), response.url());
});
const responsePromise = page.waitForResponse('**/api/data');
await page.click('.load-data');
const response = await responsePromise;
```
## Failure Artifacts
```typescript
test.afterEach(async ({ page }, testInfo) => {
if (testInfo.status !== 'passed') {
await page.screenshot({
path: `screenshots/${testInfo.title}.png`,
fullPage: true,
});
}
});
```
## Console and Runtime Errors
```typescript
page.on('console', msg => {
console.log('PAGE LOG:', msg.text());
});
page.on('pageerror', error => {
console.log('PAGE ERROR:', error.message);
});
```
## Compare Local vs CI
| Check | Command |
|-------|---------|
| Viewport | `await page.viewportSize()` |
| User agent | `await page.evaluate(() => navigator.userAgent)` |
| Timezone | `await page.evaluate(() => Intl.DateTimeFormat().resolvedOptions().timeZone)` |
| Network | `page.on('request', ...)` |
| Shared auth/data | verify whether tests mutate the same account or fixtures |
## Debugging Checklist
1. Run with `--debug` or `--headed`.
2. Add `await page.pause()` before the failure point.
3. Capture trace, screenshot, and console output before changing selectors.
4. Check for iframes, shadow DOM, overlays, loading states, and shared auth or data collisions.
5. Compare viewport, network behavior, workers, and environment flags between local and CI.
6. Only then rewrite selectors, waits, fixtures, or test structure.