Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Playwright testing guide covering E2E, component, API, visual, accessibility, and security tests.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
debugging/console-errors.md
1# Browser Console & JavaScript Error Handling23## Table of Contents451. [Capturing Console Messages](#capturing-console-messages)62. [Failing on Console Errors](#failing-on-console-errors)73. [JavaScript Error Detection](#javascript-error-detection)84. [Monitoring Warnings](#monitoring-warnings)95. [Console Fixtures](#console-fixtures)1011## Capturing Console Messages1213### Basic Console Capture1415```typescript16test("capture console logs", async ({ page }) => {17const logs: string[] = [];1819page.on("console", (msg) => {20logs.push(`${msg.type()}: ${msg.text()}`);21});2223await page.goto("/");2425// Check what was logged26console.log("Captured logs:", logs);27});28```2930### Capture by Type3132```typescript33test("capture specific console types", async ({ page }) => {34const errors: string[] = [];35const warnings: string[] = [];36const infos: string[] = [];3738page.on("console", (msg) => {39switch (msg.type()) {40case "error":41errors.push(msg.text());42break;43case "warning":44warnings.push(msg.text());45break;46case "info":47case "log":48infos.push(msg.text());49break;50}51});5253await page.goto("/dashboard");5455expect(errors).toHaveLength(0);56console.log("Warnings:", warnings);57});58```5960### Capture with Stack Trace6162```typescript63test("capture errors with location", async ({ page }) => {64const errors: { message: string; location?: string }[] = [];6566page.on("console", async (msg) => {67if (msg.type() === "error") {68const location = msg.location();69errors.push({70message: msg.text(),71location: location72? `${location.url}:${location.lineNumber}`73: undefined,74});75}76});7778await page.goto("/buggy-page");7980// Log errors with source location81errors.forEach((e) => {82console.log(`Error: ${e.message}`);83if (e.location) console.log(` at ${e.location}`);84});85});86```8788## Failing on Console Errors8990### Fail Test on Any Error9192```typescript93test("no console errors allowed", async ({ page }) => {94const errors: string[] = [];9596page.on("console", (msg) => {97if (msg.type() === "error") {98errors.push(msg.text());99}100});101102await page.goto("/");103await page.getByRole("button", { name: "Load Data" }).click();104105// Fail if any console errors106expect(errors, `Console errors found:\n${errors.join("\n")}`).toHaveLength(0);107});108```109110### Fail with Allowed Exceptions111112```typescript113test("no unexpected console errors", async ({ page }) => {114const allowedErrors = [115/Failed to load resource.*favicon/,116/ResizeObserver loop/,117];118119const unexpectedErrors: string[] = [];120121page.on("console", (msg) => {122if (msg.type() === "error") {123const text = msg.text();124const isAllowed = allowedErrors.some((pattern) => pattern.test(text));125if (!isAllowed) {126unexpectedErrors.push(text);127}128}129});130131await page.goto("/");132133expect(134unexpectedErrors,135`Unexpected console errors:\n${unexpectedErrors.join("\n")}`,136).toHaveLength(0);137});138```139140### Auto-Fail Fixture141142```typescript143// fixtures/console.fixture.ts144type ConsoleFixtures = {145failOnConsoleError: void;146};147148export const test = base.extend<ConsoleFixtures>({149failOnConsoleError: [150async ({ page }, use, testInfo) => {151const errors: string[] = [];152153page.on("console", (msg) => {154if (msg.type() === "error") {155errors.push(msg.text());156}157});158159await use();160161// After test, check for errors162if (errors.length > 0) {163testInfo.annotations.push({164type: "console-errors",165description: errors.join("\n"),166});167throw new Error(`Console errors detected:\n${errors.join("\n")}`);168}169},170{ auto: true }, // Runs for every test171],172});173```174175## JavaScript Error Detection176177### Catch Uncaught Exceptions178179```typescript180test("no uncaught exceptions", async ({ page }) => {181const pageErrors: Error[] = [];182183page.on("pageerror", (error) => {184pageErrors.push(error);185});186187await page.goto("/");188await page.getByRole("button", { name: "Trigger Action" }).click();189190expect(191pageErrors,192`Uncaught exceptions:\n${pageErrors.map((e) => e.message).join("\n")}`,193).toHaveLength(0);194});195```196197### Capture Error Details198199```typescript200test("capture JS error details", async ({ page }) => {201const errors: { message: string; stack?: string }[] = [];202203page.on("pageerror", (error) => {204errors.push({205message: error.message,206stack: error.stack,207});208});209210await page.goto("/error-page");211212if (errors.length > 0) {213console.log("JavaScript errors:");214errors.forEach((e) => {215console.log(` Message: ${e.message}`);216console.log(` Stack: ${e.stack}`);217});218}219});220```221222### Test Error Boundary Triggers223224```typescript225test("error boundary catches render error", async ({ page }) => {226let errorCaught = false;227228page.on("pageerror", () => {229// Note: React error boundaries catch errors before they become pageerrors230// This would only fire for unhandled errors231errorCaught = true;232});233234// Trigger component error via props235await page.route(236"**/api/data",237(route) => route.fulfill({ json: null }), // Will cause "cannot read property of null"238);239240await page.goto("/dashboard");241242// Error boundary should show fallback, not crash243await expect(page.getByText("Something went wrong")).toBeVisible();244expect(errorCaught).toBe(false); // Error was caught by boundary245});246```247248## Monitoring Warnings249250### Capture Deprecation Warnings251252```typescript253test("no deprecation warnings", async ({ page }) => {254const deprecations: string[] = [];255256page.on("console", (msg) => {257const text = msg.text();258if (259msg.type() === "warning" &&260(text.includes("deprecated") || text.includes("Deprecation"))261) {262deprecations.push(text);263}264});265266await page.goto("/");267268if (deprecations.length > 0) {269console.warn("Deprecation warnings found:");270deprecations.forEach((d) => console.warn(` - ${d}`));271}272273// Optionally fail274// expect(deprecations).toHaveLength(0);275});276```277278### React Development Warnings279280```typescript281test("no React warnings", async ({ page }) => {282const reactWarnings: string[] = [];283284page.on("console", (msg) => {285const text = msg.text();286if (287msg.type() === "warning" &&288(text.includes("Warning:") || text.includes("React"))289) {290reactWarnings.push(text);291}292});293294await page.goto("/");295296// Common React warnings to check297const criticalWarnings = reactWarnings.filter(298(w) =>299w.includes("Each child in a list should have a unique") ||300w.includes("Cannot update a component") ||301w.includes("Can't perform a React state update"),302);303304expect(305criticalWarnings,306`React warnings:\n${criticalWarnings.join("\n")}`,307).toHaveLength(0);308});309```310311## Console Fixtures312313### Comprehensive Console Fixture314315```typescript316// fixtures/console.fixture.ts317type ConsoleMessage = {318type: string;319text: string;320location?: { url: string; line: number };321timestamp: number;322};323324type ConsoleFixtures = {325consoleMessages: ConsoleMessage[];326getConsoleErrors: () => ConsoleMessage[];327getConsoleWarnings: () => ConsoleMessage[];328assertNoErrors: (allowedPatterns?: RegExp[]) => void;329};330331export const test = base.extend<ConsoleFixtures>({332consoleMessages: async ({ page }, use) => {333const messages: ConsoleMessage[] = [];334335page.on("console", (msg) => {336const location = msg.location();337messages.push({338type: msg.type(),339text: msg.text(),340location: location341? { url: location.url, line: location.lineNumber }342: undefined,343timestamp: Date.now(),344});345});346347await use(messages);348},349350getConsoleErrors: async ({ consoleMessages }, use) => {351await use(() => consoleMessages.filter((m) => m.type === "error"));352},353354getConsoleWarnings: async ({ consoleMessages }, use) => {355await use(() => consoleMessages.filter((m) => m.type === "warning"));356},357358assertNoErrors: async ({ getConsoleErrors }, use) => {359await use((allowedPatterns = []) => {360const errors = getConsoleErrors();361const unexpected = errors.filter(362(e) => !allowedPatterns.some((p) => p.test(e.text)),363);364365if (unexpected.length > 0) {366throw new Error(367`Unexpected console errors:\n${unexpected.map((e) => e.text).join("\n")}`,368);369}370});371},372});373374// Usage375test("page loads without errors", async ({ page, assertNoErrors }) => {376await page.goto("/dashboard");377await page.getByRole("button", { name: "Load" }).click();378379assertNoErrors([/favicon/]); // Allow favicon errors380});381```382383### Attach Console to Report384385```typescript386test("capture console for debugging", async ({ page }, testInfo) => {387const logs: string[] = [];388389page.on("console", (msg) => {390logs.push(`[${msg.type()}] ${msg.text()}`);391});392393page.on("pageerror", (error) => {394logs.push(`[EXCEPTION] ${error.message}`);395});396397await page.goto("/");398// ... test actions399400// Attach console log to test report401await testInfo.attach("console-log", {402body: logs.join("\n"),403contentType: "text/plain",404});405});406```407408## Anti-Patterns to Avoid409410| Anti-Pattern | Problem | Solution |411| -------------------------- | -------------------------- | --------------------------- |412| Ignoring console errors | Bugs go unnoticed | Check for errors in tests |413| Too strict error checking | Tests fail on minor issues | Allow known/expected errors |414| Not capturing stack traces | Hard to debug | Include location info |415| Checking only at end | Miss errors during actions | Capture continuously |416417## Related References418419- **Debugging**: See [debugging.md](debugging.md) for troubleshooting420- **Error Testing**: See [error-testing.md](error-testing.md) for error scenarios421