SurfSense/.cursor/skills/playwright-testing/debugging/debugging.md

505 lines
15 KiB
Markdown
Raw Normal View History

2026-05-10 04:19:55 +05:30
# Debugging & Troubleshooting
## Table of Contents
1. [Debug Tools](#debug-tools)
2. [Trace Viewer](#trace-viewer)
3. [Identifying Flaky Tests](#identifying-flaky-tests)
4. [Debugging Network Issues](#debugging-network-issues)
5. [Debugging in CI](#debugging-in-ci)
6. [Debugging Authentication](#debugging-authentication)
7. [Debugging Screenshots](#debugging-screenshots)
8. [Common Issues](#common-issues)
9. [Logging](#logging)
## Debug Tools
### Playwright Inspector
```bash
# Run with inspector
PWDEBUG=1 npx playwright test
# Or specific test
PWDEBUG=1 npx playwright test login.spec.ts
```
Features:
- Step through test actions
- Pick locators visually
- Inspect DOM state
- Edit and re-run
### Headed Mode
```bash
# Run with visible browser
npx playwright test --headed
# Interactive debugging (headed, paused, step-through)
npx playwright test --debug
```
You can also set `slowMo` to add an `N` ms delay per action, making test execution easier to follow while debugging.
```typescript
// playwright.config.ts
export default defineConfig({
use: {
launchOptions: {
slowMo: 500,
},
},
});
```
### UI Mode
```bash
# Interactive test runner
npx playwright test --ui
```
Features:
- Watch mode
- Test timeline
- DOM snapshots
- Network logs
- Console logs
### Debug in Code
```typescript
test("debug example", async ({ page }) => {
await page.goto("/");
// Pause and open inspector
await page.pause();
// Continue test...
await page.click("button");
});
```
## Trace Viewer
### Enable Traces
```typescript
// playwright.config.ts
export default defineConfig({
use: {
trace: "on-first-retry", // Record on retry
// trace: 'on', // Always record
// trace: 'retain-on-failure', // Keep only failures
},
});
```
### View Traces
```bash
# Open trace file
npx playwright show-trace trace.zip
# From test-results
npx playwright show-trace test-results/test-name/trace.zip
```
### Trace Contents
- Screenshots at each action
- DOM snapshots
- Network requests/responses
- Console logs
- Action timeline
- Source code
### Programmatic Traces
```typescript
test("manual trace", async ({ page, context }) => {
await context.tracing.start({ screenshots: true, snapshots: true });
await page.goto("/");
await page.click("button");
await context.tracing.stop({ path: "trace.zip" });
});
```
## Identifying Flaky Tests
If a test fails intermittently, it's likely flaky. Quick checks:
| Behavior | Likely Cause | Next Step |
| -------------------------------------- | ----------------------------- | -------------------------------------- |
| Fails sometimes, passes other times | Flaky - timing/race condition | [flaky-tests.md](flaky-tests.md) |
| Fails only with multiple workers | Flaky - parallelism/isolation | [flaky-tests.md](flaky-tests.md) |
| Fails only in CI | Environment difference | [CI Debugging](#debugging-in-ci) below |
| Always fails | Bug in test or app | Debug with tools above |
| Always passes locally, always fails CI | CI-specific issue | [ci-cd.md](../infrastructure-ci-cd/ci-cd.md) |
> **For flaky test detection commands, root cause analysis, and fixing strategies**, see [flaky-tests.md](flaky-tests.md).
## Debugging Network Issues
### Monitor All Requests
```typescript
test("debug network", async ({ page }) => {
const requests: string[] = [];
const failures: string[] = [];
page.on("request", (req) => requests.push(`>> ${req.method()} ${req.url()}`));
page.on("requestfinished", (req) => {
const resp = req.response();
requests.push(`<< ${resp?.status()} ${req.url()}`);
});
page.on("requestfailed", (req) => {
failures.push(`FAILED: ${req.url()} - ${req.failure()?.errorText}`);
});
await page.goto("/dashboard");
// Log summary
console.log("Requests:", requests.length);
if (failures.length) console.log("Failures:", failures);
});
```
### Wait for Specific API Response
When debugging network-dependent issues, wait for specific API responses instead of arbitrary timeouts.
```typescript
// Start waiting BEFORE triggering the request
const responsePromise = page.waitForResponse(
(resp) => resp.url().includes("/api/data") && resp.status() === 200,
);
await page.getByRole("button", { name: "Load" }).click();
const response = await responsePromise;
console.log("Status:", response.status());
```
> **For comprehensive waiting patterns** (navigation, element state, network, polling), see [assertions-waiting.md](../core/assertions-waiting.md#waiting-strategies).
### Debug Slow Requests
```typescript
test("find slow requests", async ({ page }) => {
page.on("requestfinished", (request) => {
const timing = request.timing();
const total = timing.responseEnd - timing.requestStart;
if (total > 1000) {
console.log(`SLOW (${total}ms): ${request.url()}`);
}
});
await page.goto("/");
});
```
## Debugging in CI
### Simulate CI Locally
```bash
# Run in headless mode like CI
CI=true npx playwright test
# Match CI browser versions
npx playwright install --with-deps
# Run in Docker (same as CI)
docker run --rm -v $(pwd):/work -w /work \
mcr.microsoft.com/playwright:v1.40.0-jammy \
npx playwright test
```
### CI-Specific Configuration
```typescript
// playwright.config.ts
export default defineConfig({
// More artifacts in CI for debugging
use: {
trace: process.env.CI ? "on-first-retry" : "off",
video: process.env.CI ? "retain-on-failure" : "off",
screenshot: process.env.CI ? "only-on-failure" : "off",
},
// More retries in CI (but investigate failures!)
retries: process.env.CI ? 2 : 0,
});
```
### Debug CI Environment
```typescript
test("CI environment check", async ({ page }, testInfo) => {
console.log("CI:", process.env.CI);
console.log("Project:", testInfo.project.name);
console.log("Worker:", testInfo.workerIndex);
console.log("Retry:", testInfo.retry);
console.log("Base URL:", testInfo.project.use.baseURL);
// Check viewport
const viewport = page.viewportSize();
console.log("Viewport:", viewport);
});
```
## Debugging Authentication
```typescript
test("debug auth", async ({ page, context }) => {
// Inspect current storage state
const storage = await context.storageState();
console.log(
"Cookies:",
storage.cookies.map((c) => c.name),
);
// Check if auth cookies are present
const cookies = await context.cookies();
const authCookie = cookies.find((c) => c.name.includes("session"));
console.log("Auth cookie:", authCookie ? "present" : "MISSING");
await page.goto("/protected");
// Check if redirected to login (auth failed)
if (page.url().includes("/login")) {
console.error("Auth failed - redirected to login");
// Save state for inspection
await context.storageState({ path: "debug-auth.json" });
}
});
```
## Debugging Screenshots
### Compare Visual State
```typescript
test("visual debug", async ({ page }, testInfo) => {
await page.goto("/");
// Screenshot before action
await page.screenshot({
path: testInfo.outputPath("before.png"),
fullPage: true,
});
await page.getByRole("button", { name: "Open Menu" }).click();
// Screenshot after action
await page.screenshot({
path: testInfo.outputPath("after.png"),
fullPage: true,
});
// Attach to report
await testInfo.attach("before", {
path: testInfo.outputPath("before.png"),
contentType: "image/png",
});
});
```
### Screenshot Specific Element
```typescript
test("element screenshot", async ({ page }) => {
await page.goto("/");
const element = page.getByTestId("problem-area");
// Screenshot just the element
await element.screenshot({ path: "element-debug.png" });
// Highlight element in full page screenshot
await element.evaluate((el) => (el.style.border = "3px solid red"));
await page.screenshot({ path: "highlighted.png" });
});
```
## Common Issues
### Element Not Found
```typescript
// Debug: Check if element exists
console.log(await page.getByRole("button").count());
// Debug: Log all buttons
const buttons = await page.getByRole("button").all();
for (const button of buttons) {
console.log(await button.textContent());
}
// Debug: Screenshot before action
await page.screenshot({ path: "debug.png" });
await page.getByRole("button").click();
```
### Timeout Issues
```typescript
// Increase timeout for slow operations
await expect(page.getByText("Loaded")).toBeVisible({ timeout: 30000 });
// Global timeout increase
test.setTimeout(60000);
// Check what's blocking
test("debug timeout", async ({ page }) => {
await page.goto("/slow-page");
// Log network activity
page.on("request", (request) => console.log(">>", request.url()));
page.on("response", (response) =>
console.log("<<", response.url(), response.status()),
);
});
```
### Selector Issues
```typescript
// Debug: Highlight element
await page.getByRole("button").highlight();
// Debug: Evaluate selector in browser console
// Run in Inspector console:
// playwright.locator('button').first().highlight()
// Debug: Get element info
const element = page.getByRole("button");
console.log("Count:", await element.count());
console.log("Visible:", await element.isVisible());
console.log("Enabled:", await element.isEnabled());
```
### Frame Issues
```typescript
// Debug: List all frames
for (const frame of page.frames()) {
console.log("Frame:", frame.url());
}
// Debug: Check if element is in iframe
const frame = page.frameLocator("iframe").first();
console.log(await frame.getByRole("button").count());
```
## Logging
### Capture Browser Console
```typescript
test("with logging", async ({ page }) => {
page.on("console", (msg) => console.log("Browser:", msg.text()));
page.on("pageerror", (error) => console.log("Page error:", error.message));
await page.goto("/");
});
```
> **For comprehensive console error handling** (fail on errors, allowed patterns, fixtures), see [console-errors.md](console-errors.md).
### Custom Test Attachments
```typescript
test("with attachments", async ({ page }, testInfo) => {
// Attach screenshot to report
const screenshot = await page.screenshot();
await testInfo.attach("screenshot", {
body: screenshot,
contentType: "image/png",
});
// Attach logs or data
await testInfo.attach("logs", {
body: "Custom log data",
contentType: "text/plain",
});
// Use testInfo for output paths
const outputPath = testInfo.outputPath("debug-file.json");
});
```
## Troubleshooting Checklist
### By Symptom
| Symptom | Common Causes | Quick Fixes | Reference |
| --------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| **Element not found** | Wrong selector, element not visible, in iframe, timing issue | Check locator with Inspector, wait for visibility, use frameLocator | [locators.md](../core/locators.md), [assertions-waiting.md](../core/assertions-waiting.md) |
| **Timeout errors** | Slow network, heavy page load, waiting for wrong condition | Increase timeout, wait for specific response, check network tab | [assertions-waiting.md](../core/assertions-waiting.md) |
| **Flaky tests** | Race conditions, shared state, timing dependencies | See comprehensive flaky test guide | [flaky-tests.md](flaky-tests.md) |
| **Tests pass locally, fail in CI** | Environment differences, missing dependencies, timing | Simulate CI locally, check CI logs, verify environment vars | [ci-cd.md](../infrastructure-ci-cd/ci-cd.md), [flaky-tests.md](flaky-tests.md) |
| **Slow test execution** | Not parallelized, heavy network calls, unnecessary waits | Enable parallelization, mock APIs, optimize waits | [performance.md](../infrastructure-ci-cd/performance.md) |
| **Selector works in browser but not in test** | Element not attached, wrong context, dynamic content | Use auto-waiting, check iframe, verify element state | [locators.md](../core/locators.md) |
| **Test fails on retry** | Non-deterministic data, external dependencies | Use test data fixtures, mock external services | [fixtures-hooks.md](../core/fixtures-hooks.md) |
### Step-by-Step Debugging Process
1. **Reproduce the issue**
```bash
# Run with trace enabled
npx playwright test tests/failing.spec.ts --trace on
# If intermittent, run multiple times
npx playwright test --repeat-each=10
```
2. **Inspect the failure**
```bash
# View trace
npx playwright show-trace test-results/path-to-trace.zip
# Run in headed mode to watch
npx playwright test --headed
# Use inspector for step-by-step
PWDEBUG=1 npx playwright test
```
3. **Isolate the problem**
```typescript
// Add debugging points
await page.pause();
// Log element state
console.log("Element count:", await page.getByRole("button").count());
console.log("Element visible:", await page.getByRole("button").isVisible());
// Take screenshot at failure point
await page.screenshot({ path: "debug.png" });
```
4. **Check related areas**
- Network requests: Are API calls completing? (see [Debugging Network Issues](#debugging-network-issues))
- Timing: Is auto-waiting working correctly?
- State: Is the test isolated? (see [flaky-tests.md](flaky-tests.md))
- Environment: Does it work locally but fail in CI? (see [Debugging in CI](#debugging-in-ci))
5. **Apply fix and verify**
- Fix the root cause (not just symptoms)
- Run multiple times to confirm stability: `--repeat-each=10`
- Check related tests aren't affected
## Related References
- **Flaky tests**: See [flaky-tests.md](flaky-tests.md) for comprehensive flaky test guide
- **Locator issues**: See [locators.md](../core/locators.md) for selector strategies
- **Waiting problems**: See [assertions-waiting.md](../core/assertions-waiting.md) for waiting patterns
- **Test isolation**: See [fixtures-hooks.md](../core/fixtures-hooks.md) for fixtures and isolation
- **CI issues**: See [ci-cd.md](../infrastructure-ci-cd/ci-cd.md) for CI configuration