From 011ef2e84764892df175017c24ae3a519a0ffaed Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 12 Feb 2026 14:27:35 -0800 Subject: [PATCH] chore(mcp): use page.consoleMessages() API instead of local array --- docs/src/api/class-page.md | 5 +++ packages/playwright-client/types/types.d.ts | 7 ++++ packages/playwright-core/src/client/page.ts | 4 +++ .../playwright-core/src/protocol/validator.ts | 2 ++ .../src/server/dispatchers/pageDispatcher.ts | 4 +++ packages/playwright-core/src/server/page.ts | 4 +++ .../src/utils/isomorphic/protocolMetainfo.ts | 1 + packages/playwright-core/types/types.d.ts | 7 ++++ packages/playwright/src/mcp/browser/tab.ts | 36 +++++++++++++------ packages/protocol/src/channels.d.ts | 4 +++ packages/protocol/src/protocol.yml | 3 ++ tests/page/page-event-pageerror.spec.ts | 26 ++++++++++++++ 12 files changed, 93 insertions(+), 10 deletions(-) diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 311736f6399aa..56ef466cba5d3 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -2727,6 +2727,11 @@ Returns whether the element is [visible](../actionability.md#visible). [`param: Clears all stored console messages from this page. Subsequent calls to [`method: Page.consoleMessages`] will only return messages logged after the clear. +## async method: Page.clearPageErrors +* since: v1.59 + +Clears all stored page errors from this page. Subsequent calls to [`method: Page.pageErrors`] will only return errors thrown after the clear. + ## async method: Page.consoleMessages * since: v1.56 - returns: <[Array]<[ConsoleMessage]>> diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 39a29ac015edc..604208ad14b25 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -2263,6 +2263,13 @@ export interface Page { */ clearConsoleMessages(): Promise; + /** + * 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. + */ + clearPageErrors(): Promise; + /** * **NOTE** Use locator-based [locator.click([options])](https://playwright.dev/docs/api/class-locator#locator-click) instead. * Read more about [locators](https://playwright.dev/docs/locators). diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 9375390fe9fdb..e556bde327526 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -664,6 +664,10 @@ export class Page extends ChannelOwner implements api.Page return messages.map(message => new ConsoleMessage(this._platform, message, this, null)); } + async clearPageErrors(): Promise { + await this._channel.clearPageErrors(); + } + async pageErrors(): Promise { const { errors } = await this._channel.pageErrors(); 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 6064116b89fa3..bc93e70939146 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -1427,6 +1427,8 @@ scheme.PageTouchscreenTapParams = tObject({ y: tFloat, }); scheme.PageTouchscreenTapResult = tOptional(tObject({})); +scheme.PageClearPageErrorsParams = tOptional(tObject({})); +scheme.PageClearPageErrorsResult = tOptional(tObject({})); scheme.PagePageErrorsParams = tOptional(tObject({})); 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 18cc7f413ec28..8c29a14b04dcd 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -284,6 +284,10 @@ export class PageDispatcher extends Dispatcher this.parentScope().serializeConsoleMessage(message, this)) }; } + async clearPageErrors(params: channels.PageClearPageErrorsParams, progress: Progress): Promise { + this._page.clearPageErrors(); + } + async pageErrors(params: channels.PagePageErrorsParams, progress: Progress): Promise { return { errors: this._page.pageErrors().map(error => serializeError(error)) }; } diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 5039267d282ca..ec708bce58ec3 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -419,6 +419,10 @@ export class Page extends SdkObject { this.emitOnContext(BrowserContext.Events.PageError, pageError, this); } + clearPageErrors() { + this._pageErrors.length = 0; + } + pageErrors() { return this._pageErrors; } diff --git a/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts b/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts index 55042c86d955c..974e31bc335ed 100644 --- a/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts +++ b/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts @@ -133,6 +133,7 @@ export const methodMetainfo = new Map; + /** + * 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. + */ + clearPageErrors(): Promise; + /** * **NOTE** Use locator-based [locator.click([options])](https://playwright.dev/docs/api/class-locator#locator-click) instead. * Read more about [locators](https://playwright.dev/docs/locators). diff --git a/packages/playwright/src/mcp/browser/tab.ts b/packages/playwright/src/mcp/browser/tab.ts index b9a9cf993611b..7472db257d333 100644 --- a/packages/playwright/src/mcp/browser/tab.ts +++ b/packages/playwright/src/mcp/browser/tab.ts @@ -91,7 +91,6 @@ export class Tab extends EventEmitter { readonly context: Context; readonly page: Page; private _lastHeader: TabHeader = { title: 'about:blank', url: 'about:blank', current: false, console: { total: 0, warnings: 0, errors: 0 } }; - private _consoleMessages: ConsoleMessage[] = []; private _downloads: Download[] = []; private _requests: playwright.Request[] = []; private _onPageClose: (tab: Tab) => void; @@ -201,7 +200,6 @@ export class Tab extends EventEmitter { } private _clearCollectedArtifacts() { - this._consoleMessages.length = 0; this._downloads.length = 0; this._requests.length = 0; this._recentEventEntries.length = 0; @@ -236,7 +234,6 @@ export class Tab extends EventEmitter { } private _handleConsoleMessage(message: ConsoleMessage) { - this._consoleMessages.push(message); const wallTime = message.timestamp; this._addLogEntry({ type: 'console', wallTime, message }); const level = consoleLevelForMessageType(message.type); @@ -283,6 +280,8 @@ export class Tab extends EventEmitter { async navigate(url: string) { await this._initializedPromise; + + await this.clearConsoleMessages(); this._clearCollectedArtifacts(); const { promise: downloadEvent, abort: abortDownloadEvent } = eventWaiter(this.page, 'download', 3000); @@ -311,25 +310,42 @@ export class Tab extends EventEmitter { async consoleMessageCount(): Promise<{ total: number, errors: number, warnings: number }> { await this._initializedPromise; - let errors = 0; + const messages = await this.page.consoleMessages(); + const pageErrors = await this.page.pageErrors(); + let errors = pageErrors.length; let warnings = 0; - for (const message of this._consoleMessages) { - if (message.type === 'error') + for (const message of messages) { + if (message.type() === 'error') errors++; - else if (message.type === 'warning') + else if (message.type() === 'warning') warnings++; } - return { total: this._consoleMessages.length, errors, warnings }; + return { total: messages.length + pageErrors.length, errors, warnings }; } async consoleMessages(level: ConsoleMessageLevel): Promise { await this._initializedPromise; - return this._consoleMessages.filter(message => shouldIncludeMessage(level, message.type)); + const result: ConsoleMessage[] = []; + const messages = await this.page.consoleMessages(); + for (const message of messages) { + const cm = messageToConsoleMessage(message); + if (shouldIncludeMessage(level, cm.type)) + result.push(cm); + } + if (shouldIncludeMessage(level, 'error')) { + const errors = await this.page.pageErrors(); + for (const error of errors) + result.push(pageErrorToConsoleMessage(error)); + } + return result; } async clearConsoleMessages() { await this._initializedPromise; - this._consoleMessages.length = 0; + await Promise.all([ + this.page.clearConsoleMessages(), + this.page.clearPageErrors() + ]); } async requests(): Promise { diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 40cd7dd77e9cb..053c690d2b743 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -2098,6 +2098,7 @@ export interface PageChannel extends PageEventTarget, EventTargetChannel { mouseClick(params: PageMouseClickParams, progress?: Progress): Promise; mouseWheel(params: PageMouseWheelParams, progress?: Progress): Promise; touchscreenTap(params: PageTouchscreenTapParams, progress?: Progress): Promise; + clearPageErrors(params?: PageClearPageErrorsParams, progress?: Progress): Promise; pageErrors(params?: PagePageErrorsParams, progress?: Progress): Promise; pdf(params: PagePdfParams, progress?: Progress): Promise; requests(params?: PageRequestsParams, progress?: Progress): Promise; @@ -2495,6 +2496,9 @@ export type PageTouchscreenTapOptions = { }; export type PageTouchscreenTapResult = void; +export type PageClearPageErrorsParams = {}; +export type PageClearPageErrorsOptions = {}; +export type PageClearPageErrorsResult = void; export type PagePageErrorsParams = {}; export type PagePageErrorsOptions = {}; export type PagePageErrorsResult = { diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index feeadf0d14842..8ae8ed0a32f3d 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -1920,6 +1920,9 @@ Page: snapshot: true pausesBeforeAction: true + clearPageErrors: + title: Clear page errors + pageErrors: title: Get page errors group: getter diff --git a/tests/page/page-event-pageerror.spec.ts b/tests/page/page-event-pageerror.spec.ts index 2f31f554c3443..116805b63f515 100644 --- a/tests/page/page-event-pageerror.spec.ts +++ b/tests/page/page-event-pageerror.spec.ts @@ -169,6 +169,32 @@ it('pageErrors should work', async ({ page }) => { expect(messages.slice(messages.length - expected.length), 'should return last errors').toEqual(expected); }); +it('clearPageErrors should work', async ({ page }) => { + await page.evaluate(() => { + window.builtins.setTimeout(() => { throw new Error('error1'); }, 0); + window.builtins.setTimeout(() => { throw new Error('error2'); }, 0); + }); + await page.waitForTimeout(1000); + + let errors = await page.pageErrors(); + expect(errors.map(e => e.message)).toContain('error1'); + expect(errors.map(e => e.message)).toContain('error2'); + + await page.clearPageErrors(); + + errors = await page.pageErrors(); + expect(errors).toEqual([]); + + await page.evaluate(() => { + window.builtins.setTimeout(() => { throw new Error('error3'); }, 0); + }); + await page.waitForTimeout(1000); + + errors = await page.pageErrors(); + expect(errors.length).toBe(1); + expect(errors[0].message).toContain('error3'); +}); + it('should fire illegal character error', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/38388' }, }, async ({ page, server, browserName, isWindows }) => {