From a5a57df105fd28f4ad15776c7cef536ef69b0075 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 15 Jul 2025 15:46:09 -0700 Subject: [PATCH] chore: include page errors in console messages (#671) Fixes https://github.com/microsoft/playwright-mcp/issues/669 --- src/tab.ts | 38 ++++++++++++++++++++++++++++++++++---- src/tools/console.ts | 2 +- tests/console.spec.ts | 28 ++++++++++++++++++++++++++-- tests/core.spec.ts | 15 +++++++-------- 4 files changed, 68 insertions(+), 15 deletions(-) diff --git a/src/tab.ts b/src/tab.ts index d80313d..f91a969 100644 --- a/src/tab.ts +++ b/src/tab.ts @@ -17,14 +17,14 @@ import * as playwright from 'playwright'; import { PageSnapshot } from './pageSnapshot.js'; +import { callOnPageNoTrace } from './tools/utils.js'; import type { Context } from './context.js'; -import { callOnPageNoTrace } from './tools/utils.js'; export class Tab { readonly context: Context; readonly page: playwright.Page; - private _consoleMessages: playwright.ConsoleMessage[] = []; + private _consoleMessages: ConsoleMessage[] = []; private _requests: Map = new Map(); private _snapshot: PageSnapshot | undefined; private _onPageClose: (tab: Tab) => void; @@ -33,7 +33,8 @@ export class Tab { this.context = context; this.page = page; this._onPageClose = onPageClose; - page.on('console', event => this._consoleMessages.push(event)); + page.on('console', event => this._consoleMessages.push(messageToConsoleMessage(event))); + page.on('pageerror', error => this._consoleMessages.push(pageErrorToConsoleMessage(error))); page.on('request', request => this._requests.set(request, null)); page.on('response', response => this._requests.set(response.request(), response)); page.on('close', () => this._onClose()); @@ -106,7 +107,7 @@ export class Tab { return this._snapshot; } - consoleMessages(): playwright.ConsoleMessage[] { + consoleMessages(): ConsoleMessage[] { return this._consoleMessages; } @@ -118,3 +119,32 @@ export class Tab { this._snapshot = await PageSnapshot.create(this.page); } } + +export type ConsoleMessage = { + type: ReturnType | undefined; + text: string; + toString(): string; +}; + +function messageToConsoleMessage(message: playwright.ConsoleMessage): ConsoleMessage { + return { + type: message.type(), + text: message.text(), + toString: () => `[${message.type().toUpperCase()}] ${message.text()} @ ${message.location().url}:${message.location().lineNumber}`, + }; +} + +function pageErrorToConsoleMessage(errorOrValue: Error | any): ConsoleMessage { + if (errorOrValue instanceof Error) { + return { + type: undefined, + text: errorOrValue.message, + toString: () => errorOrValue.stack || errorOrValue.message, + }; + } + return { + type: undefined, + text: String(errorOrValue), + toString: () => String(errorOrValue), + }; +} diff --git a/src/tools/console.ts b/src/tools/console.ts index 45bf3d7..704fa05 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -28,7 +28,7 @@ const console = defineTool({ }, handle: async context => { const messages = context.currentTabOrDie().consoleMessages(); - const log = messages.map(message => `[${message.type().toUpperCase()}] ${message.text()}`).join('\n'); + const log = messages.map(message => message.toString()).join('\n'); return { code: [`// `], action: async () => { diff --git a/tests/console.spec.ts b/tests/console.spec.ts index 8a9f60a..113f397 100644 --- a/tests/console.spec.ts +++ b/tests/console.spec.ts @@ -38,7 +38,31 @@ test('browser_console_messages', async ({ client, server }) => { name: 'browser_console_messages', }); expect(resource).toHaveTextContent([ - '[LOG] Hello, world!', - '[ERROR] Error', + `[LOG] Hello, world! @ ${server.PREFIX}:4`, + `[ERROR] Error @ ${server.PREFIX}:5`, ].join('\n')); }); + +test('browser_console_messages (page error)', async ({ client, server }) => { + server.setContent('/', ` + + + + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + + const resource = await client.callTool({ + name: 'browser_console_messages', + }); + expect(resource).toHaveTextContent(/Error: Error in script/); + expect(resource).toHaveTextContent(new RegExp(server.PREFIX)); +}); diff --git a/tests/core.spec.ts b/tests/core.spec.ts index f3f406d..a3463fa 100644 --- a/tests/core.spec.ts +++ b/tests/core.spec.ts @@ -214,7 +214,7 @@ test('browser_type', async ({ client, server }) => { }); expect(await client.callTool({ name: 'browser_console_messages', - })).toHaveTextContent('[LOG] Key pressed: Enter , Text: Hi!'); + })).toHaveTextContent(/\[LOG\] Key pressed: Enter , Text: Hi!/); }); test('browser_type (slowly)', async ({ client, server }) => { @@ -238,14 +238,13 @@ test('browser_type (slowly)', async ({ client, server }) => { slowly: true, }, }); - expect(await client.callTool({ + const response = await client.callTool({ name: 'browser_console_messages', - })).toHaveTextContent([ - '[LOG] Key pressed: H Text: ', - '[LOG] Key pressed: i Text: H', - '[LOG] Key pressed: ! Text: Hi', - '[LOG] Key pressed: Enter Text: Hi!', - ].join('\n')); + }); + expect(response).toHaveTextContent(/\[LOG\] Key pressed: H Text: /); + expect(response).toHaveTextContent(/\[LOG\] Key pressed: i Text: H/); + expect(response).toHaveTextContent(/\[LOG\] Key pressed: ! Text: Hi/); + expect(response).toHaveTextContent(/\[LOG\] Key pressed: Enter Text: Hi!/); }); test('browser_resize', async ({ client, server }) => {