From 011b11a8e09bd9e25d085bc2dfeaace0713a4366 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 10 Mar 2026 18:11:07 -0700 Subject: [PATCH 1/6] feat(api): add filter option to Page.consoleMessages() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a `filter` option ('all' | 'sinceNavigation') to `Page.consoleMessages()`. When 'sinceNavigation' is passed, only messages logged after the last committed main-frame navigation are returned. The last pre-navigation message is marked with a symbol in the shared array — no separate array needed. --- docs/src/api/class-page.md | 8 ++++++++ packages/playwright-client/types/types.d.ts | 12 +++++++++--- packages/playwright-core/src/client/page.ts | 4 ++-- packages/playwright-core/src/protocol/validator.ts | 5 ++++- .../src/server/dispatchers/pageDispatcher.ts | 2 +- packages/playwright-core/src/server/page.ts | 11 +++++++++-- packages/playwright-core/types/types.d.ts | 12 +++++++++--- packages/protocol/src/channels.d.ts | 11 ++++++++--- packages/protocol/src/protocol.yml | 8 ++++++++ tests/page/page-event-console.spec.ts | 14 ++++++++++++++ 10 files changed, 72 insertions(+), 15 deletions(-) diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 1b3093a52ebc8..6f78b369301df 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -2760,6 +2760,14 @@ Clears all stored page errors from this page. Subsequent calls to [`method: Page Returns up to (currently) 200 last console messages from this page. See [`event: Page.console`] for more details. +### option: Page.consoleMessages.filter +* since: v1.59 +- `filter` <[ConsoleMessagesFilter]<"all"|"sinceNavigation">> + +Controls which messages are returned: +- `'all'` (default) — returns all stored console messages. +- `'sinceNavigation'` — returns only messages logged after the last committed main-frame navigation. + ## async method: Page.pageErrors * since: v1.56 diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index c7b0375a83056..086e09da732d6 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -2258,8 +2258,8 @@ export interface Page { /** * Clears all stored console messages from this page. Subsequent calls to - * [page.consoleMessages()](https://playwright.dev/docs/api/class-page#page-console-messages) will only return - * messages logged after the clear. + * [page.consoleMessages([options])](https://playwright.dev/docs/api/class-page#page-console-messages) will only + * return messages logged after the clear. */ clearConsoleMessages(): Promise; @@ -2395,8 +2395,14 @@ export interface Page { /** * Returns up to (currently) 200 last console messages from this page. See * [page.on('console')](https://playwright.dev/docs/api/class-page#page-event-console) for more details. + * @param options */ - consoleMessages(): Promise>; + consoleMessages(options?: { + /** + * Controls which messages are returned: + */ + filter?: "all"|"sinceNavigation"; + }): Promise>; /** * Gets the full HTML contents of the page, including the doctype. diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index ffc58e30b74d2..ee4be7f2f71b3 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -670,8 +670,8 @@ export class Page extends ChannelOwner implements api.Page await this._channel.clearConsoleMessages(); } - async consoleMessages(): Promise { - const { messages } = await this._channel.consoleMessages(); + async consoleMessages(options?: { filter?: 'all' | 'sinceNavigation' }): Promise { + const { messages } = await this._channel.consoleMessages({ filter: options?.filter }); return messages.map(message => new ConsoleMessage(this._platform, message, this, null)); } diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index b841a21e8923b..4e9d60ff7e63b 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -252,6 +252,7 @@ scheme.APIResponse = tObject({ headers: tArray(tType('NameValue')), }); scheme.LifecycleEvent = tEnum(['load', 'domcontentloaded', 'networkidle', 'commit']); +scheme.ConsoleMessagesFilter = tEnum(['all', 'sinceNavigation']); scheme.LocalUtilsInitializer = tObject({ deviceDescriptors: tArray(tObject({ name: tString, @@ -1249,7 +1250,9 @@ scheme.PageCloseParams = tObject({ scheme.PageCloseResult = tOptional(tObject({})); scheme.PageClearConsoleMessagesParams = tOptional(tObject({})); scheme.PageClearConsoleMessagesResult = tOptional(tObject({})); -scheme.PageConsoleMessagesParams = tOptional(tObject({})); +scheme.PageConsoleMessagesParams = tObject({ + filter: tOptional(tType('ConsoleMessagesFilter')), +}); scheme.PageConsoleMessagesResult = tObject({ messages: tArray(tObject({ type: tString, diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index 14bd1b0aa0412..034353759896c 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -290,7 +290,7 @@ export class PageDispatcher extends Dispatcher this.parentScope().serializeConsoleMessage(message, this)) }; + return { messages: this._page.consoleMessages(params.filter).map(message => this.parentScope().serializeConsoleMessage(message, this)) }; } async clearPageErrors(params: channels.PageClearPageErrorsParams, progress: Progress): Promise { diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 95cd244ebb996..44d014628ba09 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -149,6 +149,8 @@ export type PageEventMap = { [PageEvent.Worker]: [worker: Worker]; }; +const navigationMarkSymbol = Symbol('navigationMark'); + export class Page extends SdkObject { static Events = PageEvent; @@ -403,8 +405,11 @@ export class Page extends SdkObject { this._consoleMessages.length = 0; } - consoleMessages() { - return this._consoleMessages; + consoleMessages(filter?: 'all' | 'sinceNavigation') { + if (filter !== 'sinceNavigation') + return this._consoleMessages; + const marked = this._consoleMessages.findLastIndex(m => (m as any)[navigationMarkSymbol]); + return marked === -1 ? this._consoleMessages : this._consoleMessages.slice(marked + 1); } addPageError(pageError: Error) { @@ -842,6 +847,8 @@ export class Page extends SdkObject { const origin = frame.origin(); if (origin) this.browserContext.addVisitedOrigin(origin); + if (frame === this.mainFrame() && this._consoleMessages.length > 0) + (this._consoleMessages[this._consoleMessages.length - 1] as any)[navigationMarkSymbol] = true; } allInitScripts() { diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index c7b0375a83056..086e09da732d6 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -2258,8 +2258,8 @@ export interface Page { /** * Clears all stored console messages from this page. Subsequent calls to - * [page.consoleMessages()](https://playwright.dev/docs/api/class-page#page-console-messages) will only return - * messages logged after the clear. + * [page.consoleMessages([options])](https://playwright.dev/docs/api/class-page#page-console-messages) will only + * return messages logged after the clear. */ clearConsoleMessages(): Promise; @@ -2395,8 +2395,14 @@ export interface Page { /** * Returns up to (currently) 200 last console messages from this page. See * [page.on('console')](https://playwright.dev/docs/api/class-page#page-event-console) for more details. + * @param options */ - consoleMessages(): Promise>; + consoleMessages(options?: { + /** + * Controls which messages are returned: + */ + filter?: "all"|"sinceNavigation"; + }): Promise>; /** * Gets the full HTML contents of the page, including the doctype. diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 8222697e25f58..1a4df47b4e3e6 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -438,6 +438,7 @@ export type APIResponse = { }; export type LifecycleEvent = 'load' | 'domcontentloaded' | 'networkidle' | 'commit'; +export type ConsoleMessagesFilter = 'all' | 'sinceNavigation'; // ----------- LocalUtils ----------- export type LocalUtilsInitializer = { deviceDescriptors: { @@ -2108,7 +2109,7 @@ export interface PageChannel extends PageEventTarget, EventTargetChannel { addInitScript(params: PageAddInitScriptParams, progress?: Progress): Promise; close(params: PageCloseParams, progress?: Progress): Promise; clearConsoleMessages(params?: PageClearConsoleMessagesParams, progress?: Progress): Promise; - consoleMessages(params?: PageConsoleMessagesParams, progress?: Progress): Promise; + consoleMessages(params: PageConsoleMessagesParams, progress?: Progress): Promise; emulateMedia(params: PageEmulateMediaParams, progress?: Progress): Promise; exposeBinding(params: PageExposeBindingParams, progress?: Progress): Promise; goBack(params: PageGoBackParams, progress?: Progress): Promise; @@ -2220,8 +2221,12 @@ export type PageCloseResult = void; export type PageClearConsoleMessagesParams = {}; export type PageClearConsoleMessagesOptions = {}; export type PageClearConsoleMessagesResult = void; -export type PageConsoleMessagesParams = {}; -export type PageConsoleMessagesOptions = {}; +export type PageConsoleMessagesParams = { + filter?: ConsoleMessagesFilter, +}; +export type PageConsoleMessagesOptions = { + filter?: ConsoleMessagesFilter, +}; export type PageConsoleMessagesResult = { messages: { type: string, diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 5f94b100ddbd9..f196f2d54b0bb 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -429,6 +429,12 @@ LifecycleEvent: - networkidle - commit +ConsoleMessagesFilter: + type: enum + literals: + - all + - sinceNavigation + CommonScreenshotOptions: type: mixin properties: @@ -1615,6 +1621,8 @@ Page: consoleMessages: title: Get console messages group: getter + parameters: + filter: ConsoleMessagesFilter? returns: messages: type: array diff --git a/tests/page/page-event-console.spec.ts b/tests/page/page-event-console.spec.ts index e1f3ac4c03bc4..279f3d5a1b40d 100644 --- a/tests/page/page-event-console.spec.ts +++ b/tests/page/page-event-console.spec.ts @@ -300,3 +300,17 @@ it('clearConsoleMessages should work', async ({ page }) => { expect(messages.length).toBe(1); expect(messages[0].text()).toBe('message3'); }); + +it('consoleMessages sinceNavigation filter should work', async ({ page, server }) => { + await page.evaluate(() => console.log('before navigation')); + await page.goto(server.EMPTY_PAGE); + await page.evaluate(() => console.log('after navigation')); + + const all = await page.consoleMessages(); + expect(all.map(m => m.text())).toContain('before navigation'); + expect(all.map(m => m.text())).toContain('after navigation'); + + const sinceNav = await page.consoleMessages({ filter: 'sinceNavigation' }); + expect(sinceNav.map(m => m.text())).not.toContain('before navigation'); + expect(sinceNav.map(m => m.text())).toContain('after navigation'); +}); From 9172ff04520766de7b8d10ad01857ace2767e810 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 10 Mar 2026 18:31:25 -0700 Subject: [PATCH 2/6] clear errors only --- packages/playwright-core/src/tools/tab.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/playwright-core/src/tools/tab.ts b/packages/playwright-core/src/tools/tab.ts index 7ecb8981b17a1..a780f16966f78 100644 --- a/packages/playwright-core/src/tools/tab.ts +++ b/packages/playwright-core/src/tools/tab.ts @@ -298,7 +298,7 @@ export class Tab extends EventEmitter { async navigate(url: string) { await this._initializedPromise; - await this.clearConsoleMessages(); + await this.page.clearPageErrors(); this._clearCollectedArtifacts(); const { promise: downloadEvent, abort: abortDownloadEvent } = eventWaiter(this.page, 'download', 3000); @@ -327,7 +327,7 @@ export class Tab extends EventEmitter { async consoleMessageCount(): Promise<{ total: number, errors: number, warnings: number }> { await this._initializedPromise; - const messages = await this.page.consoleMessages(); + const messages = await this.page.consoleMessages({ filter: 'sinceNavigation' }); const pageErrors = await this.page.pageErrors(); let errors = pageErrors.length; let warnings = 0; @@ -343,7 +343,7 @@ export class Tab extends EventEmitter { async consoleMessages(level: ConsoleMessageLevel): Promise { await this._initializedPromise; const result: ConsoleMessage[] = []; - const messages = await this.page.consoleMessages(); + const messages = await this.page.consoleMessages({ filter: 'sinceNavigation' }); for (const message of messages) { const cm = messageToConsoleMessage(message); if (shouldIncludeMessage(level, cm.type)) From bc1792e70718b90847d6eed02c4cd35a23e168e0 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 10 Mar 2026 22:51:29 -0700 Subject: [PATCH 3/6] feat(api): make sinceNavigation the default filter for Page.consoleMessages() --- docs/src/api/class-page.md | 4 ++-- packages/playwright-core/src/server/page.ts | 2 +- tests/page/page-event-console.spec.ts | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 6f78b369301df..cb8bde7c70404 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -2765,8 +2765,8 @@ Returns up to (currently) 200 last console messages from this page. See [`event: - `filter` <[ConsoleMessagesFilter]<"all"|"sinceNavigation">> Controls which messages are returned: -- `'all'` (default) — returns all stored console messages. -- `'sinceNavigation'` — returns only messages logged after the last committed main-frame navigation. +- `'sinceNavigation'` (default) — returns only messages logged after the last committed main-frame navigation. +- `'all'` — returns all stored console messages. ## async method: Page.pageErrors diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 44d014628ba09..4da851af4a19a 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -406,7 +406,7 @@ export class Page extends SdkObject { } consoleMessages(filter?: 'all' | 'sinceNavigation') { - if (filter !== 'sinceNavigation') + if (filter === 'all') return this._consoleMessages; const marked = this._consoleMessages.findLastIndex(m => (m as any)[navigationMarkSymbol]); return marked === -1 ? this._consoleMessages : this._consoleMessages.slice(marked + 1); diff --git a/tests/page/page-event-console.spec.ts b/tests/page/page-event-console.spec.ts index 279f3d5a1b40d..1c80514b10a2f 100644 --- a/tests/page/page-event-console.spec.ts +++ b/tests/page/page-event-console.spec.ts @@ -306,11 +306,12 @@ it('consoleMessages sinceNavigation filter should work', async ({ page, server } await page.goto(server.EMPTY_PAGE); await page.evaluate(() => console.log('after navigation')); - const all = await page.consoleMessages(); + const all = await page.consoleMessages({ filter: 'all' }); expect(all.map(m => m.text())).toContain('before navigation'); expect(all.map(m => m.text())).toContain('after navigation'); - const sinceNav = await page.consoleMessages({ filter: 'sinceNavigation' }); + // sinceNavigation is the default + const sinceNav = await page.consoleMessages(); expect(sinceNav.map(m => m.text())).not.toContain('before navigation'); expect(sinceNav.map(m => m.text())).toContain('after navigation'); }); From bd21ad4f2b700dc92ff80293c77155a04f6aebbd Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 10 Mar 2026 22:53:23 -0700 Subject: [PATCH 4/6] feat(mcp): add 'all' option to browser_console_messages tool --- packages/playwright-core/src/tools/console.ts | 3 ++- packages/playwright-core/src/tools/tab.ts | 4 ++-- tests/mcp/console.spec.ts | 24 +++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/playwright-core/src/tools/console.ts b/packages/playwright-core/src/tools/console.ts index be6ed7cd6d488..be2471ae77442 100644 --- a/packages/playwright-core/src/tools/console.ts +++ b/packages/playwright-core/src/tools/console.ts @@ -25,6 +25,7 @@ const console = defineTabTool({ description: 'Returns all console messages', inputSchema: z.object({ level: z.enum(['error', 'warning', 'info', 'debug']).default('info').describe('Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".'), + all: z.boolean().optional().describe('Return all console messages since the beginning of the session, not just since the last navigation. Defaults to false.'), filename: z.string().optional().describe('Filename to save the console messages to. If not provided, messages are returned as text.'), }), type: 'readOnly', @@ -32,7 +33,7 @@ const console = defineTabTool({ handle: async (tab, params, response) => { const count = await tab.consoleMessageCount(); const header = [`Total messages: ${count.total} (Errors: ${count.errors}, Warnings: ${count.warnings})`]; - const messages = await tab.consoleMessages(params.level); + const messages = await tab.consoleMessages(params.level, params.all); if (messages.length !== count.total) header.push(`Returning ${messages.length} messages for level "${params.level}"`); const text = [...header, '', ...messages.map(message => message.toString())].join('\n'); diff --git a/packages/playwright-core/src/tools/tab.ts b/packages/playwright-core/src/tools/tab.ts index a780f16966f78..ddafafdf4efd4 100644 --- a/packages/playwright-core/src/tools/tab.ts +++ b/packages/playwright-core/src/tools/tab.ts @@ -340,10 +340,10 @@ export class Tab extends EventEmitter { return { total: messages.length + pageErrors.length, errors, warnings }; } - async consoleMessages(level: ConsoleMessageLevel): Promise { + async consoleMessages(level: ConsoleMessageLevel, all?: boolean): Promise { await this._initializedPromise; const result: ConsoleMessage[] = []; - const messages = await this.page.consoleMessages({ filter: 'sinceNavigation' }); + const messages = await this.page.consoleMessages({ filter: all ? 'all' : 'sinceNavigation' }); for (const message of messages) { const cm = messageToConsoleMessage(message); if (shouldIncludeMessage(level, cm.type)) diff --git a/tests/mcp/console.spec.ts b/tests/mcp/console.spec.ts index 1eea579f895f2..41791a6477530 100644 --- a/tests/mcp/console.spec.ts +++ b/tests/mcp/console.spec.ts @@ -406,6 +406,30 @@ test('console log file stores message type and content', async ({ startClient, s expect(logContent).toMatch(/@ http:\/\/localhost:\d+\/:\d/); }); +test('browser_console_messages all option', async ({ client, server }) => { + server.setContent('/page1', ` + + `, 'text/html'); + + server.setContent('/page2', ` + + `, 'text/html'); + + await client.callTool({ name: 'browser_navigate', arguments: { url: server.PREFIX + '/page1' } }); + await client.callTool({ name: 'browser_navigate', arguments: { url: server.PREFIX + '/page2' } }); + + const defaultResponse = parseResponse(await client.callTool({ name: 'browser_console_messages' })); + expect(defaultResponse.result).toContain('page2 message'); + expect(defaultResponse.result).not.toContain('page1 message'); + + const allResponse = parseResponse(await client.callTool({ + name: 'browser_console_messages', + arguments: { all: true }, + })); + expect(allResponse.result).toContain('page1 message'); + expect(allResponse.result).toContain('page2 message'); +}); + test('console log is updated without taking snapshots', async ({ startClient, server }, testInfo) => { const outputDir = testInfo.outputPath('output'); const { client } = await startClient({ From 7af693bf17aa7ed83034ecb086a37de377d045ef Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 10 Mar 2026 23:07:43 -0700 Subject: [PATCH 5/6] feat(api): add filter option to Page.pageErrors() and stop clearing page errors on navigate --- docs/src/api/class-page.md | 9 +++++++++ packages/playwright-client/types/types.d.ts | 12 +++++++++--- packages/playwright-core/src/client/page.ts | 4 ++-- .../playwright-core/src/protocol/validator.ts | 4 +++- .../src/server/dispatchers/pageDispatcher.ts | 2 +- packages/playwright-core/src/server/page.ts | 15 +++++++++++---- packages/playwright-core/src/tools/tab.ts | 5 ++--- packages/playwright-core/types/types.d.ts | 12 +++++++++--- packages/protocol/src/channels.d.ts | 10 +++++++--- packages/protocol/src/protocol.yml | 2 ++ tests/mcp/console.spec.ts | 19 +++++++++++++++++++ tests/page/page-event-console.spec.ts | 17 +++++++++++++++++ 12 files changed, 91 insertions(+), 20 deletions(-) diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index cb8bde7c70404..349cc110a5cc1 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -2783,6 +2783,15 @@ Returns up to (currently) 200 last page errors from this page. See [`event: Page Returns up to (currently) 200 last page errors from this page. See [`event: Page.pageError`] for more details. +### option: Page.pageErrors.filter +* since: v1.59 +* langs: js +- `filter` <[PageErrorsFilter]<"all"|"sinceNavigation">> + +Controls which errors are returned: +- `'sinceNavigation'` (default) — returns only errors thrown after the last committed main-frame navigation. +- `'all'` — returns all stored page errors. + ## method: Page.locator * since: v1.14 diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 086e09da732d6..952407ba8d1bb 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -2265,8 +2265,8 @@ export interface Page { /** * Clears all stored page errors from this page. Subsequent calls to - * [page.pageErrors()](https://playwright.dev/docs/api/class-page#page-page-errors) will only return errors thrown - * after the clear. + * [page.pageErrors([options])](https://playwright.dev/docs/api/class-page#page-page-errors) will only return errors + * thrown after the clear. */ clearPageErrors(): Promise; @@ -3745,8 +3745,14 @@ export interface Page { /** * Returns up to (currently) 200 last page errors from this page. See * [page.on('pageerror')](https://playwright.dev/docs/api/class-page#page-event-page-error) for more details. + * @param options */ - pageErrors(): Promise>; + pageErrors(options?: { + /** + * Controls which errors are returned: + */ + filter?: "all"|"sinceNavigation"; + }): Promise>; /** * Pauses script execution. Playwright will stop executing the script and wait for the user to either press the diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index ee4be7f2f71b3..c8243b82d925f 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -679,8 +679,8 @@ export class Page extends ChannelOwner implements api.Page await this._channel.clearPageErrors(); } - async pageErrors(): Promise { - const { errors } = await this._channel.pageErrors(); + async pageErrors(options?: { filter?: 'all' | 'sinceNavigation' }): Promise { + const { errors } = await this._channel.pageErrors({ filter: options?.filter }); return errors.map(error => parseError(error)); } diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 4e9d60ff7e63b..436a56228714d 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -1460,7 +1460,9 @@ scheme.PageTouchscreenTapParams = tObject({ scheme.PageTouchscreenTapResult = tOptional(tObject({})); scheme.PageClearPageErrorsParams = tOptional(tObject({})); scheme.PageClearPageErrorsResult = tOptional(tObject({})); -scheme.PagePageErrorsParams = tOptional(tObject({})); +scheme.PagePageErrorsParams = tObject({ + filter: tOptional(tType('ConsoleMessagesFilter')), +}); scheme.PagePageErrorsResult = tObject({ errors: tArray(tType('SerializedError')), }); diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index 034353759896c..027bbd93b94f9 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -298,7 +298,7 @@ export class PageDispatcher extends Dispatcher { - return { errors: this._page.pageErrors().map(error => serializeError(error)) }; + return { errors: this._page.pageErrors(params.filter).map(error => serializeError(error)) }; } async mouseMove(params: channels.PageMouseMoveParams, progress: Progress): Promise { diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 4da851af4a19a..3a2b754d8ffed 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -427,8 +427,11 @@ export class Page extends SdkObject { this._pageErrors.length = 0; } - pageErrors() { - return this._pageErrors; + pageErrors(filter?: 'all' | 'sinceNavigation') { + if (filter === 'all') + return this._pageErrors; + const marked = this._pageErrors.findLastIndex(e => (e as any)[navigationMarkSymbol]); + return marked === -1 ? this._pageErrors : this._pageErrors.slice(marked + 1); } async reload(progress: Progress, options: types.NavigateOptions): Promise { @@ -847,8 +850,12 @@ export class Page extends SdkObject { const origin = frame.origin(); if (origin) this.browserContext.addVisitedOrigin(origin); - if (frame === this.mainFrame() && this._consoleMessages.length > 0) - (this._consoleMessages[this._consoleMessages.length - 1] as any)[navigationMarkSymbol] = true; + if (frame === this.mainFrame()) { + if (this._consoleMessages.length > 0) + (this._consoleMessages[this._consoleMessages.length - 1] as any)[navigationMarkSymbol] = true; + if (this._pageErrors.length > 0) + (this._pageErrors[this._pageErrors.length - 1] as any)[navigationMarkSymbol] = true; + } } allInitScripts() { diff --git a/packages/playwright-core/src/tools/tab.ts b/packages/playwright-core/src/tools/tab.ts index ddafafdf4efd4..e299ffe9cf8f3 100644 --- a/packages/playwright-core/src/tools/tab.ts +++ b/packages/playwright-core/src/tools/tab.ts @@ -298,7 +298,6 @@ export class Tab extends EventEmitter { async navigate(url: string) { await this._initializedPromise; - await this.page.clearPageErrors(); this._clearCollectedArtifacts(); const { promise: downloadEvent, abort: abortDownloadEvent } = eventWaiter(this.page, 'download', 3000); @@ -328,7 +327,7 @@ export class Tab extends EventEmitter { async consoleMessageCount(): Promise<{ total: number, errors: number, warnings: number }> { await this._initializedPromise; const messages = await this.page.consoleMessages({ filter: 'sinceNavigation' }); - const pageErrors = await this.page.pageErrors(); + const pageErrors = await this.page.pageErrors({ filter: 'sinceNavigation' }); let errors = pageErrors.length; let warnings = 0; for (const message of messages) { @@ -350,7 +349,7 @@ export class Tab extends EventEmitter { result.push(cm); } if (shouldIncludeMessage(level, 'error')) { - const errors = await this.page.pageErrors(); + const errors = await this.page.pageErrors({ filter: all ? 'all' : 'sinceNavigation' }); for (const error of errors) result.push(pageErrorToConsoleMessage(error)); } diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 086e09da732d6..952407ba8d1bb 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -2265,8 +2265,8 @@ export interface Page { /** * Clears all stored page errors from this page. Subsequent calls to - * [page.pageErrors()](https://playwright.dev/docs/api/class-page#page-page-errors) will only return errors thrown - * after the clear. + * [page.pageErrors([options])](https://playwright.dev/docs/api/class-page#page-page-errors) will only return errors + * thrown after the clear. */ clearPageErrors(): Promise; @@ -3745,8 +3745,14 @@ export interface Page { /** * Returns up to (currently) 200 last page errors from this page. See * [page.on('pageerror')](https://playwright.dev/docs/api/class-page#page-event-page-error) for more details. + * @param options */ - pageErrors(): Promise>; + pageErrors(options?: { + /** + * Controls which errors are returned: + */ + filter?: "all"|"sinceNavigation"; + }): Promise>; /** * Pauses script execution. Playwright will stop executing the script and wait for the user to either press the diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 1a4df47b4e3e6..490324746bc3e 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -2137,7 +2137,7 @@ export interface PageChannel extends PageEventTarget, EventTargetChannel { mouseWheel(params: PageMouseWheelParams, progress?: Progress): Promise; touchscreenTap(params: PageTouchscreenTapParams, progress?: Progress): Promise; clearPageErrors(params?: PageClearPageErrorsParams, progress?: Progress): Promise; - pageErrors(params?: PagePageErrorsParams, progress?: Progress): Promise; + pageErrors(params: PagePageErrorsParams, progress?: Progress): Promise; pdf(params: PagePdfParams, progress?: Progress): Promise; requests(params?: PageRequestsParams, progress?: Progress): Promise; snapshotForAI(params: PageSnapshotForAIParams, progress?: Progress): Promise; @@ -2552,8 +2552,12 @@ export type PageTouchscreenTapResult = void; export type PageClearPageErrorsParams = {}; export type PageClearPageErrorsOptions = {}; export type PageClearPageErrorsResult = void; -export type PagePageErrorsParams = {}; -export type PagePageErrorsOptions = {}; +export type PagePageErrorsParams = { + filter?: ConsoleMessagesFilter, +}; +export type PagePageErrorsOptions = { + filter?: ConsoleMessagesFilter, +}; export type PagePageErrorsResult = { errors: SerializedError[], }; diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index f196f2d54b0bb..0b1dc1455c9b3 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -1967,6 +1967,8 @@ Page: pageErrors: title: Get page errors group: getter + parameters: + filter: ConsoleMessagesFilter? returns: errors: type: array diff --git a/tests/mcp/console.spec.ts b/tests/mcp/console.spec.ts index 41791a6477530..fe4a311c2a079 100644 --- a/tests/mcp/console.spec.ts +++ b/tests/mcp/console.spec.ts @@ -430,6 +430,25 @@ test('browser_console_messages all option', async ({ client, server }) => { expect(allResponse.result).toContain('page2 message'); }); +test('browser_console_messages all option for page errors', async ({ client, server }) => { + server.setContent('/page1', ``, 'text/html'); + server.setContent('/page2', ``, 'text/html'); + + await client.callTool({ name: 'browser_navigate', arguments: { url: server.PREFIX + '/page1' } }); + await client.callTool({ name: 'browser_navigate', arguments: { url: server.PREFIX + '/page2' } }); + + const defaultResponse = parseResponse(await client.callTool({ name: 'browser_console_messages' })); + expect(defaultResponse.result).toContain('page2 error'); + expect(defaultResponse.result).not.toContain('page1 error'); + + const allResponse = parseResponse(await client.callTool({ + name: 'browser_console_messages', + arguments: { all: true }, + })); + expect(allResponse.result).toContain('page1 error'); + expect(allResponse.result).toContain('page2 error'); +}); + test('console log is updated without taking snapshots', async ({ startClient, server }, testInfo) => { const outputDir = testInfo.outputPath('output'); const { client } = await startClient({ diff --git a/tests/page/page-event-console.spec.ts b/tests/page/page-event-console.spec.ts index 1c80514b10a2f..0f094e8ba0901 100644 --- a/tests/page/page-event-console.spec.ts +++ b/tests/page/page-event-console.spec.ts @@ -315,3 +315,20 @@ it('consoleMessages sinceNavigation filter should work', async ({ page, server } expect(sinceNav.map(m => m.text())).not.toContain('before navigation'); expect(sinceNav.map(m => m.text())).toContain('after navigation'); }); + +it('pageErrors sinceNavigation filter should work', async ({ page, server }) => { + server.setContent('/page1', ``, 'text/html'); + server.setContent('/page2', ``, 'text/html'); + + await page.goto(server.PREFIX + '/page1'); + await page.goto(server.PREFIX + '/page2'); + + const all = await page.pageErrors({ filter: 'all' }); + expect(all.map(e => e.message)).toContain('page1 error'); + expect(all.map(e => e.message)).toContain('page2 error'); + + // sinceNavigation is the default + const sinceNav = await page.pageErrors(); + expect(sinceNav.map(e => e.message)).not.toContain('page1 error'); + expect(sinceNav.map(e => e.message)).toContain('page2 error'); +}); From 857bfc20e06a02d4a04ca79ee000f0aed5ac8e80 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 11 Mar 2026 09:49:05 -0700 Subject: [PATCH 6/6] fix init-script in firefox --- tests/mcp/init-script.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/mcp/init-script.spec.ts b/tests/mcp/init-script.spec.ts index b0c001e5d58b1..9462eda5a2818 100644 --- a/tests/mcp/init-script.spec.ts +++ b/tests/mcp/init-script.spec.ts @@ -19,7 +19,7 @@ import fs from 'fs'; for (const context of ['isolated', 'persistent']) { - test(`--init-script option loads and executes script (${context})`, async ({ startClient, server }, testInfo) => { + test(`--init-script option loads and executes script (${context})`, async ({ startClient, server, mcpBrowser }, testInfo) => { // Create a temporary init script const initScriptPath = testInfo.outputPath('init-script1.js'); const initScriptContent1 = `window.testInitScriptExecuted = true;`; @@ -55,6 +55,9 @@ for (const context of ['isolated', 'persistent']) { expect(await client.callTool({ name: 'browser_console_messages', + // FIXME: in firefox commit event comes after console messages from the init script. + // See https://github.com/microsoft/playwright/issues/39624. + arguments: { all: mcpBrowser === 'firefox' } })).toHaveResponse({ result: expect.stringMatching(/Init script executed successfully.*Custom log/ms), });