mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-12 17:22:38 +02:00
15 KiB
15 KiB
Debugging & Troubleshooting
Table of Contents
- Debug Tools
- Trace Viewer
- Identifying Flaky Tests
- Debugging Network Issues
- Debugging in CI
- Debugging Authentication
- Debugging Screenshots
- Common Issues
- Logging
Debug Tools
Playwright Inspector
# 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
# 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.
// playwright.config.ts
export default defineConfig({
use: {
launchOptions: {
slowMo: 500,
},
},
});
UI Mode
# Interactive test runner
npx playwright test --ui
Features:
- Watch mode
- Test timeline
- DOM snapshots
- Network logs
- Console logs
Debug in Code
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
// 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
# 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
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 |
| Fails only with multiple workers | Flaky - parallelism/isolation | flaky-tests.md |
| Fails only in CI | Environment difference | CI Debugging below |
| Always fails | Bug in test or app | Debug with tools above |
| Always passes locally, always fails CI | CI-specific issue | ci-cd.md |
For flaky test detection commands, root cause analysis, and fixing strategies, see flaky-tests.md.
Debugging Network Issues
Monitor All Requests
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.
// 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.
Debug Slow Requests
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
# 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
// 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
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
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
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
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
// 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
// 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
// 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
// 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
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.
Custom Test Attachments
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, 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 |
| Flaky tests | Race conditions, shared state, timing dependencies | See comprehensive flaky test guide | 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, flaky-tests.md |
| Slow test execution | Not parallelized, heavy network calls, unnecessary waits | Enable parallelization, mock APIs, optimize waits | 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 |
| Test fails on retry | Non-deterministic data, external dependencies | Use test data fixtures, mock external services | fixtures-hooks.md |
Step-by-Step Debugging Process
-
Reproduce the issue
# Run with trace enabled npx playwright test tests/failing.spec.ts --trace on # If intermittent, run multiple times npx playwright test --repeat-each=10 -
Inspect the failure
# 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 -
Isolate the problem
// 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" }); -
Check related areas
- Network requests: Are API calls completing? (see Debugging Network Issues)
- Timing: Is auto-waiting working correctly?
- State: Is the test isolated? (see flaky-tests.md)
- Environment: Does it work locally but fail in CI? (see Debugging in CI)
-
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 for comprehensive flaky test guide
- Locator issues: See locators.md for selector strategies
- Waiting problems: See assertions-waiting.md for waiting patterns
- Test isolation: See fixtures-hooks.md for fixtures and isolation
- CI issues: See ci-cd.md for CI configuration