{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreiavqogt66ssymd7qyda7xdsakkrnbeuun4jmhgalkvgd3zteudgta",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mooq3777lf22"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreiesv254l24f7if45ysycpbkbk75pzdtrcfbljfnq5ipbeqew4u334"
},
"mimeType": "image/webp",
"size": 401608
},
"path": "/sshhfaiz/the-playwright-playbook-part-6-debugging-like-a-pro-trace-viewer-inspector-vs-code-32li",
"publishedAt": "2026-06-20T00:59:29.000Z",
"site": "https://dev.to",
"tags": [
"playwright",
"testing",
"typescript",
"automation",
"LinkedIn",
"@playwright",
"@smoke"
],
"textContent": "## The Playwright Playbook β Part 6: Debugging Like a Pro β Trace Viewer, Inspector & VS Code\n\n> _\"A failing test at 2am in CI tells you what broke. Trace Viewer tells you why.\"_\n\nIn Part 1, we wrote solid tests. In Parts 2 through 5, we built layer after layer β network interception, multi-user flows, API testing, visual regression.\n\nNow something fails.\n\nIt always does.\n\nThe question isn't whether your tests will fail β it's how fast you can understand why.\n\nMost engineers debug Playwright like this:\n\n\n\n // π΄ The classic debugging approach\n console.log('got here 1');\n await page.click(someLocator);\n console.log('got here 2');\n\n\nOr worse β they add `waitForTimeout(5000)` and hope the problem goes away.\n\nThere's a better way. Playwright ships with a debugging toolkit so powerful that once you start using it, you'll never go back to `console.log` again.\n\n**Trace Viewer. Playwright Inspector. VS Code integration.`page.pause()`.**\n\nThese tools show you every click, every network call, every DOM snapshot, every console error β in a visual timeline that reconstructs exactly what happened, step by step.\n\nLet's build the debugging layer into our framework. π―\n\n## ποΈ Where We Left Off\n\nAfter Part 5, our full project structure is:\n\n\n\n playwright-playbook/\n βββ tests/\n β βββ auth/\n β β βββ login.spec.ts β
Part 1\n β βββ tasks/\n β β βββ task-management.spec.ts β
Part 1\n β βββ network/ β
Part 2\n β β βββ api-mocking.spec.ts\n β β βββ error-simulation.spec.ts\n β β βββ network-assertions.spec.ts\n β βββ multi-user/ β
Part 3\n β β βββ role-permissions.spec.ts\n β β βββ realtime-collaboration.spec.ts\n β βββ multi-tab/ β
Part 3\n β β βββ multi-tab-flows.spec.ts\n β βββ api/ β
Part 4\n β β βββ tasks-api.spec.ts\n β β βββ auth-api.spec.ts\n β β βββ graphql-api.spec.ts\n β β βββ api-ui-chain.spec.ts\n β βββ visual/ β
Part 5\n β βββ dashboard-visual.spec.ts\n β βββ task-visual.spec.ts\n β βββ responsive-visual.spec.ts\n βββ pages/\n β βββ LoginPage.ts β
Part 1\n β βββ TaskPage.ts β
Part 1\n β βββ DashboardPage.ts β
Part 3\n βββ api/\n β βββ TaskApiClient.ts β
Part 4\n β βββ AuthApiClient.ts β
Part 4\n βββ fixtures/\n β βββ auth.fixture.ts β
Part 1\n β βββ tasks.json β
Part 2\n β βββ empty-tasks.json β
Part 2\n β βββ tasks-har.har β
Part 2\n β βββ multi-user.fixture.ts β
Part 3\n β βββ api.fixture.ts β
Part 4\n βββ scripts/\n β βββ record-har.ts β
Part 2\n βββ utils/\n β βββ schema-validator.ts β
Part 4\n β βββ visual-helpers.ts β
Part 5\n βββ snapshots/ β
Part 5\n βββ .auth/\n βββ global-setup.ts β
Part 1\n βββ playwright.config.ts β
Part 1 (updated Parts 3, 4 & 5)\n βββ .env\n\n\nBy the end of Part 6, we add:\n\n\n\n playwright-playbook/\n βββ tests/\n β βββ debug/ β NEW\n β βββ trace-examples.spec.ts\n βββ utils/\n β βββ debug-helpers.ts β NEW\n βββ .vscode/ β NEW\n β βββ extensions.json\n β βββ launch.json\n\n\nEvery file gets fully built below. π\n\n## π§ The Debugging Toolkit β Overview\n\nPlaywright gives you five distinct debugging tools. Each is for a different situation:\n\n\n\n Tool When to use\n βββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββββββ\n Trace Viewer Post-mortem β understanding a CI failure\n Playwright Inspector Live β step through a test interactively\n page.pause() Breakpoint β pause mid-test, inspect live DOM\n VS Code Extension IDE β run/debug individual tests without CLI\n Console + Network During development β lightweight live logging\n\n\nWe'll build proper support for all five into the framework. Let's start with the most important one. π\n\n## π¬ Trace Viewer β The Most Powerful Debugging Tool in Playwright\n\nTrace Viewer is a full timeline recording of your test β every action, every network request, every DOM snapshot, every console log β captured and viewable in a browser-based UI.\n\nWhen a test fails in CI, you download the trace file and open it locally. You see exactly what the browser saw at every step. No guessing. No re-running the test hoping to reproduce it.\n\n### Step 1 β Configure traces in `playwright.config.ts`\n\n\n // playwright.config.ts β updated use block\n use: {\n baseURL: process.env.BASE_URL || 'http://localhost:3000',\n\n // Screenshot on every failure β attached to HTML report\n screenshot: 'only-on-failure',\n\n // Video on first retry β see exactly what happened\n video: 'retain-on-failure',\n\n // Trace on first retry in CI, always on locally for debugging\n trace: process.env.CI ? 'on-first-retry' : 'on',\n\n extraHTTPHeaders: {\n 'Accept': 'application/json',\n 'Content-Type': 'application/json',\n },\n },\n\n\n**Trace modes explained:**\n\n\n\n 'off' β No traces recorded (fastest)\n 'on' β Trace recorded for every test (slowest, most info)\n 'retain-on-failure' β Trace only kept when test fails (good balance)\n 'on-first-retry' β Trace recorded on first retry (recommended for CI)\n\n\n`on-first-retry` is the sweet spot for CI β it only records when something is already failing, so you don't pay the overhead on every green run. π―\n\n### Step 2 β Open a trace locally\n\nAfter a test failure, Playwright saves a `.zip` trace file in `test-results/`. Open it:\n\n\n\n # Open the trace viewer in your browser\n npx playwright show-trace test-results/my-test/trace.zip\n\n # Or open the full HTML report which embeds trace links\n npx playwright show-report\n\n\n### Step 3 β What you see in Trace Viewer\n\nThe Trace Viewer UI has five panels:\n\n\n\n βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n β Timeline ββββββββββββββββββββββββββββββββββββββββββββ β\n β [action][action][action][network][action][FAIL] β\n βββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ€\n β β Before snapshot After snapshot β\n β Action list β βββββββββββββββ βββββββββββββββββββ β\n β β β β β β β\n β βΆ goto /tasks β β DOM beforeβ β DOM after β β\n β βΆ click button β β the actionβ β the action β β\n β βΆ fill input β β β β β β\n β β expect failsβ βββββββββββββββ βββββββββββββββββββ β\n βββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββ€\n β Network | Console | Source β\n βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\n\nEvery action shows:\n\n * The DOM **before** the action\n * The DOM **after** the action\n * All network calls that fired during the action\n * Console logs and errors at that moment\n * The exact line of test code that triggered it\n\n\n\nThis is how you debug a test that fails once in 50 runs in CI. You don't reproduce it β you read the trace. π₯\n\n## π Playwright Inspector β Interactive Step-Through Debugging\n\nInspector is for when you're writing a new test and want to figure out the right locator or understand why an action isn't working.\n\nIt opens a live browser alongside an inspector panel β you step through your test action by action, watching what happens in real time.\n\n### Launch modes\n\n\n # Run a specific test in debug mode β opens Inspector\n npx playwright test tests/tasks/task-management.spec.ts --debug\n\n # Run in headed mode only β see the browser, no Inspector panel\n npx playwright test --headed\n\n # Run with slow motion β slows each action by 1 second (great for watching)\n npx playwright test --headed --slow-mo=1000\n\n # Debug a specific test by name\n npx playwright test --debug -g \"user can create a new task\"\n\n\n### Using Inspector to find the right locator\n\nThis is the killer use case. When you're not sure what locator to use:\n\n 1. Run your test with `--debug`\n 2. Inspector opens β click **Record** or **Explore**\n 3. Hover over elements in the browser β Inspector shows you the recommended locator\n 4. Click the element β Inspector generates the `getByRole`, `getByLabel`, or `getByTestId` code\n 5. Copy it into your test\n\n\n\nNo more guessing at selectors. No more `page.$('div > div:nth-child(2) > span')`. π―\n\n### The Inspector toolbar\n\n\n βΆ Resume β Run to the next breakpoint or end of test\n β Step over β Execute the current action and pause on the next\n π Pick β Hover over elements to see their locators\n βΊ Record β Record new actions by clicking in the browser\n\n\n## βΈοΈ `page.pause()` β Your Interactive Breakpoint\n\n`page.pause()` is the equivalent of a debugger breakpoint inside your test. The test runs normally until it hits `page.pause()` β then it freezes and opens Inspector.\n\n\n\n test('debugging a flaky task creation flow', async ({ page }) => {\n const taskPage = new TaskPage(page);\n await taskPage.goto();\n\n // Test runs normally up to here\n await taskPage.newTaskButton.click();\n\n // βΈοΈ Test freezes here β Inspector opens\n // You can now interact with the page, inspect the DOM,\n // check network calls, try locators β all live\n await page.pause();\n\n // When you click Resume in Inspector, the test continues\n await taskPage.taskTitleInput.fill('Debugging this task');\n await taskPage.saveTaskButton.click();\n });\n\n\n**Important:** Remove `page.pause()` before committing. If it runs in CI with no terminal, the test will hang indefinitely.\n\nA safer pattern β only pause in local development:\n\n\n\n // Only pause when running locally β never in CI\n if (!process.env.CI) {\n await page.pause();\n }\n\n\n## π οΈ Building the Debug Helpers Utility\n\nThese helpers centralise common debugging patterns β collecting console errors, logging performance timing, and capturing network activity during a test.\n\n\n\n // utils/debug-helpers.ts\n import { Page, ConsoleMessage } from '@playwright/test';\n\n export interface ConsoleError {\n type: string;\n text: string;\n location: string;\n }\n\n export interface NetworkCall {\n method: string;\n url: string;\n status: number;\n duration: number;\n }\n\n /**\n * Attach a console error collector to the page.\n * Returns a function to get all collected errors at any point.\n *\n * Usage:\n * const getErrors = collectConsoleErrors(page);\n * // ... run your test ...\n * const errors = getErrors();\n * expect(errors).toHaveLength(0);\n */\n export function collectConsoleErrors(page: Page): () => ConsoleError[] {\n const errors: ConsoleError[] = [];\n\n page.on('console', (msg: ConsoleMessage) => {\n if (msg.type() === 'error') {\n errors.push({\n type: msg.type(),\n text: msg.text(),\n location: msg.location().url,\n });\n }\n });\n\n // Also capture uncaught page errors (runtime JS errors)\n page.on('pageerror', (error: Error) => {\n errors.push({\n type: 'pageerror',\n text: error.message,\n location: error.stack ?? '',\n });\n });\n\n return () => [...errors];\n }\n\n /**\n * Attach a network call logger to the page.\n * Returns a function to get all captured network calls.\n *\n * Usage:\n * const getCalls = collectNetworkCalls(page, '/api/');\n * // ... run your test ...\n * const calls = getCalls();\n * console.log(calls); // see every API call made\n */\n export function collectNetworkCalls(\n page: Page,\n urlFilter: string = ''\n ): () => NetworkCall[] {\n const calls: NetworkCall[] = [];\n\n page.on('requestfinished', async request => {\n if (urlFilter && !request.url().includes(urlFilter)) return;\n\n const response = await request.response();\n const timing = request.timing();\n\n calls.push({\n method: request.method(),\n url: request.url(),\n status: response?.status() ?? 0,\n duration: timing.responseEnd - timing.requestStart,\n });\n });\n\n return () => [...calls];\n }\n\n /**\n * Log all page console output during a test.\n * Useful during development β remove before committing.\n */\n export function attachConsoleLogger(page: Page): void {\n page.on('console', msg => {\n const type = msg.type().padEnd(7);\n console.log(`[PAGE ${type}] ${msg.text()}`);\n });\n\n page.on('pageerror', error => {\n console.error(`[PAGE ERROR] ${error.message}`);\n });\n }\n\n /**\n * Capture a named checkpoint screenshot during a test.\n * Useful for documenting test steps without full VRT.\n *\n * Usage:\n * await checkpoint(page, 'after-task-created');\n */\n export async function checkpoint(page: Page, name: string): Promise<void> {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n await page.screenshot({\n path: `test-results/checkpoints/${name}-${timestamp}.png`,\n fullPage: false,\n });\n console.log(`πΈ Checkpoint: ${name}`);\n }\n\n /**\n * Assert no console errors occurred during the test.\n * Add this at the end of any test to catch silent JS errors.\n */\n export function assertNoConsoleErrors(getErrors: () => ConsoleError[]): void {\n const errors = getErrors();\n if (errors.length > 0) {\n const errorMessages = errors.map(e => ` [${e.type}] ${e.text}`).join('\\n');\n throw new Error(`Console errors detected during test:\\n${errorMessages}`);\n }\n }\n\n /**\n * Log a summary of all network calls made during the test.\n * Helpful for understanding what API traffic a user action triggers.\n */\n export function logNetworkSummary(getCalls: () => NetworkCall[]): void {\n const calls = getCalls();\n if (calls.length === 0) {\n console.log('No API network calls recorded.');\n return;\n }\n\n console.log('\\nπ‘ Network calls during test:');\n calls.forEach(call => {\n const status = call.status >= 400 ? `β ${call.status}` : `β
${call.status}`;\n console.log(` ${status} ${call.method.padEnd(6)} ${call.url} (${Math.round(call.duration)}ms)`);\n });\n }\n\n\n## π§ͺ Trace Examples β Tests That Demonstrate the Debug Toolkit\n\nThese tests serve a dual purpose: they're real tests AND they demonstrate how to use each debugging tool in a real scenario.\n\n\n\n // tests/debug/trace-examples.spec.ts\n import { test, expect } from '@playwright/test';\n import { TaskPage } from '../../pages/TaskPage';\n import { DashboardPage } from '../../pages/DashboardPage';\n import {\n collectConsoleErrors,\n collectNetworkCalls,\n assertNoConsoleErrors,\n logNetworkSummary,\n checkpoint,\n } from '../../utils/debug-helpers';\n\n test.describe('Debug Helpers β Console Error Detection', () => {\n test('task page loads with no console errors', async ({ page }) => {\n // Attach error collector BEFORE navigating\n const getErrors = collectConsoleErrors(page);\n\n const taskPage = new TaskPage(page);\n await taskPage.goto();\n\n // Wait for page to fully settle\n await page.waitForLoadState('networkidle');\n\n // Assert no JS errors occurred during page load\n assertNoConsoleErrors(getErrors);\n });\n\n test('dashboard loads with no console errors', async ({ page }) => {\n const getErrors = collectConsoleErrors(page);\n\n const dashboard = new DashboardPage(page);\n await dashboard.goto();\n await page.waitForLoadState('networkidle');\n\n assertNoConsoleErrors(getErrors);\n });\n\n test('task creation flow produces no console errors', async ({ page }) => {\n const getErrors = collectConsoleErrors(page);\n\n const taskPage = new TaskPage(page);\n await taskPage.goto();\n await taskPage.createTask('Console error test task');\n\n await expect(taskPage.getTaskLocator('Console error test task')).toBeVisible();\n\n // Clean up\n await taskPage.deleteTask('Console error test task');\n\n // Assert no errors occurred during the entire flow\n assertNoConsoleErrors(getErrors);\n });\n });\n\n test.describe('Debug Helpers β Network Call Auditing', () => {\n test('task page load triggers expected API calls', async ({ page }) => {\n const getCalls = collectNetworkCalls(page, '/api/');\n\n const taskPage = new TaskPage(page);\n await taskPage.goto();\n await page.waitForLoadState('networkidle');\n\n const calls = getCalls();\n logNetworkSummary(getCalls);\n\n // Assert the expected API calls were made\n const tasksFetch = calls.find(\n c => c.url.includes('/api/tasks') && c.method === 'GET'\n );\n expect(tasksFetch).toBeDefined();\n expect(tasksFetch!.status).toBe(200);\n\n // Assert no unexpected 4xx or 5xx calls\n const failedCalls = calls.filter(c => c.status >= 400);\n expect(failedCalls).toHaveLength(0);\n });\n\n test('task creation triggers exactly one POST call', async ({ page }) => {\n const taskPage = new TaskPage(page);\n await taskPage.goto();\n\n // Start collecting AFTER initial page load β only capture creation calls\n const getCalls = collectNetworkCalls(page, '/api/tasks');\n\n await taskPage.createTask('Network audit test task');\n await expect(taskPage.getTaskLocator('Network audit test task')).toBeVisible();\n\n const calls = getCalls();\n const postCalls = calls.filter(c => c.method === 'POST');\n\n // Should be exactly one POST β not two, not zero\n expect(postCalls).toHaveLength(1);\n expect(postCalls[0].status).toBe(201);\n\n // Cleanup\n await taskPage.deleteTask('Network audit test task');\n });\n });\n\n test.describe('Debug Helpers β Checkpoint Screenshots', () => {\n test('document task creation flow with checkpoints', async ({ page }) => {\n const taskPage = new TaskPage(page);\n await taskPage.goto();\n\n // Checkpoint 1 β initial state\n await checkpoint(page, '01-task-page-initial');\n\n // Open new task modal\n await taskPage.newTaskButton.click();\n await checkpoint(page, '02-new-task-modal-open');\n\n // Fill in the task\n await taskPage.taskTitleInput.fill('Checkpoint documented task');\n await checkpoint(page, '03-task-title-filled');\n\n // Save\n await taskPage.saveTaskButton.click();\n await expect(taskPage.getTaskLocator('Checkpoint documented task')).toBeVisible();\n await checkpoint(page, '04-task-created-success');\n\n // Cleanup\n await taskPage.deleteTask('Checkpoint documented task');\n await checkpoint(page, '05-task-deleted');\n\n // Checkpoint PNGs are saved to test-results/checkpoints/\n // Useful for documenting a flow or building a test report with screenshots\n });\n });\n\n test.describe('Flaky Test Patterns β How to Debug Them', () => {\n test('wait for real condition β not arbitrary timeout', async ({ page }) => {\n const taskPage = new TaskPage(page);\n await taskPage.goto();\n\n // β
Correct pattern β wait for the network response AND the action together\n const [response] = await Promise.all([\n page.waitForResponse(\n resp => resp.url().includes('/api/tasks') && resp.request().method() === 'POST'\n ),\n taskPage.createTask('Condition-based wait task'),\n ]);\n\n // Assert on the actual condition that confirms completion\n expect(response.status()).toBe(201);\n await expect(taskPage.getTaskLocator('Condition-based wait task')).toBeVisible();\n\n await taskPage.deleteTask('Condition-based wait task');\n });\n\n test('identify what a \"random\" failure actually looks like', async ({ page }) => {\n const getErrors = collectConsoleErrors(page);\n const getCalls = collectNetworkCalls(page, '/api/');\n\n const taskPage = new TaskPage(page);\n await taskPage.goto();\n\n // Simulate a slow/unreliable API response\n await page.route('**/api/tasks', async route => {\n // Add a random delay between 0 and 500ms β simulates real-world jitter\n const delay = Math.random() * 500;\n await new Promise(resolve => setTimeout(resolve, delay));\n await route.continue();\n });\n\n await taskPage.createTask('Jitter test task');\n\n // This should STILL pass β because we're waiting on conditions, not timeouts\n await expect(taskPage.getTaskLocator('Jitter test task')).toBeVisible();\n\n // Log what actually happened\n logNetworkSummary(getCalls);\n assertNoConsoleErrors(getErrors);\n\n await taskPage.deleteTask('Jitter test task');\n });\n });\n\n\n## π» VS Code Integration β Run & Debug Tests From Your IDE\n\nIf you use VS Code, the official Playwright extension is the fastest way to run and debug individual tests without touching the terminal.\n\n### Step 1 β Install the extension\n\n\n // .vscode/extensions.json\n {\n \"recommendations\": [\n \"ms-playwright.playwright\",\n \"dbaeumer.vscode-eslint\",\n \"esbenp.prettier-vscode\"\n ]\n }\n\n\nCommit this file β when teammates open the repo, VS Code prompts them to install the recommended extensions automatically.\n\n### Step 2 β VS Code debug launch config\n\n\n // .vscode/launch.json\n {\n \"version\": \"0.2.0\",\n \"configurations\": [\n {\n \"name\": \"Debug Playwright Test\",\n \"type\": \"node\",\n \"request\": \"launch\",\n \"program\": \"${workspaceFolder}/node_modules/.bin/playwright\",\n \"args\": [\"test\", \"--debug\", \"${file}\"],\n \"cwd\": \"${workspaceFolder}\",\n \"env\": {\n \"PWDEBUG\": \"1\"\n },\n \"console\": \"integratedTerminal\"\n },\n {\n \"name\": \"Debug Current Test File\",\n \"type\": \"node\",\n \"request\": \"launch\",\n \"program\": \"${workspaceFolder}/node_modules/.bin/playwright\",\n \"args\": [\"test\", \"${file}\", \"--headed\", \"--project=admin\"],\n \"cwd\": \"${workspaceFolder}\",\n \"env\": {},\n \"console\": \"integratedTerminal\"\n },\n {\n \"name\": \"Debug Specific Test by Name\",\n \"type\": \"node\",\n \"request\": \"launch\",\n \"program\": \"${workspaceFolder}/node_modules/.bin/playwright\",\n \"args\": [\n \"test\",\n \"--debug\",\n \"--grep\",\n \"${input:testName}\"\n ],\n \"cwd\": \"${workspaceFolder}\",\n \"console\": \"integratedTerminal\"\n }\n ],\n \"inputs\": [\n {\n \"id\": \"testName\",\n \"type\": \"promptString\",\n \"description\": \"Enter the test name or partial match\"\n }\n ]\n }\n\n\n### What the VS Code extension gives you\n\nOnce installed and configured, you get:\n\n\n\n Testing sidebar (beaker icon):\n βββ See all tests in a tree view\n βββ βΆ Run individual test with one click\n βββ π Debug individual test with one click\n βββ π Set breakpoints in test code\n βββ π΄ Failing tests highlighted inline in the editor\n\n Editor gutter:\n βββ βΆ Run this test (appears next to each test() block)\n βββ π Debug this test\n\n\nYou can click the βΆ icon next to any `test()` in your editor and it runs immediately β no terminal, no command to remember. π―\n\n## π Reading CI Failures Like a Pro\n\nWhen a test fails in CI, you get three artifacts attached to the pipeline run:\n\n\n\n test-results/\n βββ my-failing-test/\n β βββ trace.zip β Full trace β every action, network call, DOM snapshot\n β βββ test-failed-1.png β Screenshot at the moment of failure\n β βββ video.webm β Video of the entire test run\n\n\n### The debugging workflow for CI failures\n\n\n # Step 1 β Download trace.zip from CI artifacts\n\n # Step 2 β Open it locally\n npx playwright show-trace path/to/trace.zip\n\n # Step 3 β In Trace Viewer:\n # Click through the action timeline to find where it went wrong\n # Check the \"Before\" vs \"After\" DOM snapshots\n # Look at the network tab β was there a 500? A missing response?\n # Check the console tab β was there a JS error?\n\n # Step 4 β Reproduce locally if needed\n npx playwright test --debug -g \"the failing test name\"\n\n\n### Reading the trace timeline β what to look for\n\n\n Common failure patterns and where to find them in the trace:\n\n Element not found:\n β Action shows \"waiting for locator\" β times out\n β Check \"Before\" snapshot β is the element there? Is the selector wrong?\n β Check network tab β did the API call that renders it fail?\n\n Wrong element clicked:\n β \"Before\" snapshot shows multiple matching elements\n β Your locator was too broad β make it more specific\n\n Flaky timing failure:\n β Network tab shows slow response (800ms+)\n β Action fired before the data loaded\n β Fix: wait for the response, not a timeout\n\n Authentication failure:\n β First network call returns 401\n β storageState expired or wasn't loaded\n β Fix: check global-setup.ts ran correctly\n\n Console error on failure:\n β Console tab shows red JS error\n β Often unrelated to your test β but worth checking\n β Add collectConsoleErrors() to catch these proactively\n\n\n## π§ Useful Debug Commands β Quick Reference\n\n\n # Run a single test file in debug mode\n npx playwright test tests/tasks/task-management.spec.ts --debug\n\n # Run a test by name pattern\n npx playwright test --debug -g \"user can create a new task\"\n\n # Run headed (see the browser) without Inspector\n npx playwright test --headed\n\n # Run with slow motion β 1 second between each action\n npx playwright test --headed --slow-mo=1000\n\n # Generate and open the HTML report after a run\n npx playwright test && npx playwright show-report\n\n # Open a specific trace file\n npx playwright show-trace test-results/my-test/trace.zip\n\n # Update visual snapshots after intentional UI change\n npx playwright test --project=visual --update-snapshots\n\n # Run only tests matching a tag\n npx playwright test --grep @smoke\n\n # List all tests without running them\n npx playwright test --list\n\n # Run with maximum verbosity\n npx playwright test --reporter=list --verbose\n\n\n## π Final Project Structure After Part 6\n\nEvery file listed below has been fully built across Parts 1 through 6:\n\n\n\n playwright-playbook/\n βββ tests/\n β βββ auth/\n β β βββ login.spec.ts β
Part 1\n β βββ tasks/\n β β βββ task-management.spec.ts β
Part 1\n β βββ network/ β
Part 2\n β β βββ api-mocking.spec.ts\n β β βββ error-simulation.spec.ts\n β β βββ network-assertions.spec.ts\n β βββ multi-user/ β
Part 3\n β β βββ role-permissions.spec.ts\n β β βββ realtime-collaboration.spec.ts\n β βββ multi-tab/ β
Part 3\n β β βββ multi-tab-flows.spec.ts\n β βββ api/ β
Part 4\n β β βββ tasks-api.spec.ts\n β β βββ auth-api.spec.ts\n β β βββ graphql-api.spec.ts\n β β βββ api-ui-chain.spec.ts\n β βββ visual/ β
Part 5\n β β βββ dashboard-visual.spec.ts\n β β βββ task-visual.spec.ts\n β β βββ responsive-visual.spec.ts\n β βββ debug/ β
Part 6\n β βββ trace-examples.spec.ts\n βββ pages/\n β βββ LoginPage.ts β
Part 1\n β βββ TaskPage.ts β
Part 1\n β βββ DashboardPage.ts β
Part 3\n βββ api/\n β βββ TaskApiClient.ts β
Part 4\n β βββ AuthApiClient.ts β
Part 4\n βββ fixtures/\n β βββ auth.fixture.ts β
Part 1\n β βββ tasks.json β
Part 2\n β βββ empty-tasks.json β
Part 2\n β βββ tasks-har.har β
Part 2\n β βββ multi-user.fixture.ts β
Part 3\n β βββ api.fixture.ts β
Part 4\n βββ scripts/\n β βββ record-har.ts β
Part 2\n βββ utils/\n β βββ schema-validator.ts β
Part 4\n β βββ visual-helpers.ts β
Part 5\n β βββ debug-helpers.ts β
Part 6\n βββ snapshots/ β
Part 5\n βββ .vscode/ β
Part 6\n β βββ extensions.json\n β βββ launch.json\n βββ .auth/ β git-ignored\n β βββ admin.json\n β βββ user.json\n βββ global-setup.ts β
Part 1\n βββ playwright.config.ts β
Part 1 (updated Parts 3, 4, 5 & 6)\n βββ .env β git-ignored\n βββ package.json\n\n\n## πΊοΈ What's Coming in This Series\n\n\n Part 1 β Stop Writing Tests Like a Beginner β
Done\n Part 2 β Network Interception: The Complete Guide β
Done\n Part 3 β Multi-User, Multi-Tab & Context Testing β
Done\n Part 4 β API Testing (The Underrated Superpower) β
Done\n Part 5 β Visual Regression Testing β
Done\n Part 6 β Debugging Like a Pro: Trace Viewer & Inspector β You are here\n Part 7 β The CI/CD Setup Nobody Shows You\n Part 8 β Playwright Meets AI: Agents, MCP & Self-Healing Tests\n\n\nIn **Part 7** , we take everything we've built and put it in a real CI/CD pipeline β GitHub Actions, test sharding across multiple machines, browser matrix, Docker for consistent environments, Slack failure notifications, and the HTML report published as a pipeline artifact.\n\n## π Before You Go\n\nSix parts in. The framework is deep.\n\nAnd now when something breaks β which it will β you're not flying blind.\n\nYou have Trace Viewer to reconstruct exactly what happened. Inspector to step through a test live. `page.pause()` for interactive breakpoints. VS Code integration to debug without leaving your editor. And `debug-helpers.ts` to collect console errors and network calls automatically.\n\nThe best QA engineers aren't the ones who write tests that never fail. They're the ones who understand failures the fastest. π―\n\n**Follow me** so you don't miss Part 7 β where we take this entire framework to CI/CD. Sharding, GitHub Actions, Docker, browser matrix, failure notifications β the setup that makes your suite production-ready.\n\nDrop a comment below π\n\n * What's your current debugging workflow when a Playwright test fails?\n * Have you used Trace Viewer before β or is that new?\n * What's the most mysterious flaky test you've ever had to debug?\n\n\n\nLet's talk in the comments. π\n\n_Faizal Shaikh | Senior Automation Engineer | Playwright & AI Testing_\n_Connect with me on LinkedIn_",
"title": "The Playwright Playbook β Part 6: Debugging Like a Pro β Trace Viewer, Inspector & VS Code"
}