diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md
index 322c3a5072d9d..483b2b293bfeb 100644
--- a/docs/src/api/class-browsercontext.md
+++ b/docs/src/api/class-browsercontext.md
@@ -239,6 +239,13 @@ Use [`method: Page.waitForLoadState`] to wait until the page gets to a particula
cases).
:::
+## event: BrowserContext.pageError
+* since: v1.38
+- argument: <[PageError]>
+
+Emitted when unhandled exceptions occur on any pages created through this
+context. To only listen for `pageError` events from a particular page, use [`event: Page.pageError`].
+
## event: BrowserContext.request
* since: v1.12
- argument: <[Request]>
diff --git a/docs/src/api/class-pageerror.md b/docs/src/api/class-pageerror.md
new file mode 100644
index 0000000000000..d61829eae4b80
--- /dev/null
+++ b/docs/src/api/class-pageerror.md
@@ -0,0 +1,62 @@
+# class: PageError
+* since: v1.38
+
+[PageError] class represents objects created by context when there are unhandled
+execeptions thrown on the pages and dispatched via the [`event: BrowserContext.pageError`] event.
+
+```js
+// Log all uncaught errors to the terminal
+context.on('pageerror', pageerror => {
+ console.log(`Uncaught exception: "${pageerror.error()}"`);
+});
+
+// Navigate to a page with an exception.
+await page.goto('data:text/html,');
+```
+
+```java
+// Log all uncaught errors to the terminal
+context.onPageError(pagerror -> {
+ System.out.println("Uncaught exception: " + pagerror.error());
+});
+
+// Navigate to a page with an exception.
+page.navigate("data:text/html,");
+```
+
+```python async
+# Log all uncaught errors to the terminal
+context.on("pageerror", lambda pageerror: print(f"uncaught exception: {pageerror.error}"))
+
+# Navigate to a page with an exception.
+await page.goto("data:text/html,")
+```
+
+```python sync
+# Log all uncaught errors to the terminal
+context.on("pageerror", lambda pageerror: print(f"uncaught exception: {pageerror.error}"))
+
+# Navigate to a page with an exception.
+page.goto("data:text/html,")
+```
+
+```csharp
+// Log all uncaught errors to the terminal
+context.PageError += (_, pageerror) =>
+{
+ Console.WriteLine("Uncaught exception: " + pageerror.Error);
+};
+```
+
+## method: PageError.page
+* since: v1.38
+- returns: <[null]|[Page]>
+
+The page that produced this unhandled exception, if any.
+
+## method: PageError.error
+* since: v1.38
+- returns: <[Error]>
+
+Unhandled error that was thrown.
+
diff --git a/packages/playwright-core/src/client/api.ts b/packages/playwright-core/src/client/api.ts
index df14817104493..905c8543ce5e5 100644
--- a/packages/playwright-core/src/client/api.ts
+++ b/packages/playwright-core/src/client/api.ts
@@ -42,3 +42,4 @@ export { Video } from './video';
export { Worker } from './worker';
export { CDPSession } from './cdpSession';
export { Playwright } from './playwright';
+export { PageError } from './pageError';
diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts
index 30144a1aaff67..3fee5a1dc20ec 100644
--- a/packages/playwright-core/src/client/browserContext.ts
+++ b/packages/playwright-core/src/client/browserContext.ts
@@ -41,6 +41,8 @@ import { rewriteErrorMessage } from '../utils/stackTrace';
import { HarRouter } from './harRouter';
import { ConsoleMessage } from './consoleMessage';
import { Dialog } from './dialog';
+import { PageError } from './pageError';
+import { parseError } from '../protocol/serializers';
export class BrowserContext extends ChannelOwner implements api.BrowserContext {
_pages = new Set();
@@ -100,6 +102,13 @@ export class BrowserContext extends ChannelOwner
if (page)
page.emit(Events.Page.Console, consoleMessage);
});
+ this._channel.on('pageError', ({ error, page }) => {
+ const pageObject = Page.from(page);
+ const parsedError = parseError(error);
+ this.emit(Events.BrowserContext.PageError, new PageError(pageObject, parsedError));
+ if (pageObject)
+ pageObject.emit(Events.Page.PageError, parsedError);
+ });
this._channel.on('dialog', ({ dialog }) => {
const dialogObject = Dialog.from(dialog);
let hasListeners = this.emit(Events.BrowserContext.Dialog, dialogObject);
diff --git a/packages/playwright-core/src/client/events.ts b/packages/playwright-core/src/client/events.ts
index ceca0829d6e2f..72747e0f5115d 100644
--- a/packages/playwright-core/src/client/events.ts
+++ b/packages/playwright-core/src/client/events.ts
@@ -39,6 +39,9 @@ export const Events = {
Close: 'close',
Dialog: 'dialog',
Page: 'page',
+ // Can't use just 'error' due to node.js special treatment of error events.
+ // @see https://nodejs.org/api/events.html#events_error_events
+ PageError: 'pageerror',
BackgroundPage: 'backgroundpage',
ServiceWorker: 'serviceworker',
Request: 'request',
diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts
index 6373ebe779a4e..0d2a0e98e5ab6 100644
--- a/packages/playwright-core/src/client/page.ts
+++ b/packages/playwright-core/src/client/page.ts
@@ -23,7 +23,7 @@ import { isSafeCloseError, kBrowserOrContextClosedError } from '../common/errors
import { urlMatches } from '../utils/network';
import { TimeoutSettings } from '../common/timeoutSettings';
import type * as channels from '@protocol/channels';
-import { parseError, serializeError } from '../protocol/serializers';
+import { serializeError } from '../protocol/serializers';
import { assert, headersObjectToArray, isObject, isRegExp, isString, LongStandingScope, urlMatchesEqual } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import { Accessibility } from './accessibility';
@@ -130,7 +130,6 @@ export class Page extends ChannelOwner implements api.Page
this._channel.on('fileChooser', ({ element, isMultiple }) => this.emit(Events.Page.FileChooser, new FileChooser(this, ElementHandle.from(element), isMultiple)));
this._channel.on('frameAttached', ({ frame }) => this._onFrameAttached(Frame.from(frame)));
this._channel.on('frameDetached', ({ frame }) => this._onFrameDetached(Frame.from(frame)));
- this._channel.on('pageError', ({ error }) => this.emit(Events.Page.PageError, parseError(error)));
this._channel.on('route', ({ route }) => this._onRoute(Route.from(route)));
this._channel.on('video', ({ artifact }) => {
const artifactObject = Artifact.from(artifact);
diff --git a/packages/playwright-core/src/client/pageError.ts b/packages/playwright-core/src/client/pageError.ts
new file mode 100644
index 0000000000000..71b0b329c512a
--- /dev/null
+++ b/packages/playwright-core/src/client/pageError.ts
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type * as api from '../../types/types';
+import type { Page } from './page';
+
+export class PageError implements api.PageError {
+ private _page: Page | null;
+ private _error: Error;
+
+ constructor(page: Page | null, error: Error) {
+ this._page = page;
+ this._error = error;
+ }
+
+ page() {
+ return this._page;
+ }
+
+ error() {
+ return this._error;
+ }
+}
diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts
index 273dab728464f..c40db1e2e36f3 100644
--- a/packages/playwright-core/src/protocol/validator.ts
+++ b/packages/playwright-core/src/protocol/validator.ts
@@ -770,6 +770,10 @@ scheme.BrowserContextDialogEvent = tObject({
scheme.BrowserContextPageEvent = tObject({
page: tChannel(['Page']),
});
+scheme.BrowserContextPageErrorEvent = tObject({
+ error: tType('SerializedError'),
+ page: tChannel(['Page']),
+});
scheme.BrowserContextRouteEvent = tObject({
route: tChannel(['Route']),
});
@@ -957,9 +961,6 @@ scheme.PageFrameAttachedEvent = tObject({
scheme.PageFrameDetachedEvent = tObject({
frame: tChannel(['Frame']),
});
-scheme.PagePageErrorEvent = tObject({
- error: tType('SerializedError'),
-});
scheme.PageRouteEvent = tObject({
route: tChannel(['Route']),
});
diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts
index ac6b418098bcc..a5a30904689e6 100644
--- a/packages/playwright-core/src/server/browserContext.ts
+++ b/packages/playwright-core/src/server/browserContext.ts
@@ -48,6 +48,9 @@ export abstract class BrowserContext extends SdkObject {
Close: 'close',
Dialog: 'dialog',
Page: 'page',
+ // Can't use just 'error' due to node.js special treatment of error events.
+ // @see https://nodejs.org/api/events.html#events_error_events
+ PageError: 'pageerror',
Request: 'request',
Response: 'response',
RequestFailed: 'requestfailed',
diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts
index 254b42dc3c319..54f5e4459d909 100644
--- a/packages/playwright-core/src/server/chromium/crPage.ts
+++ b/packages/playwright-core/src/server/chromium/crPage.ts
@@ -756,7 +756,7 @@ class FrameSession {
const args = event.args.map(o => worker._existingExecutionContext!.createHandle(o));
this._page._addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace));
});
- session.on('Runtime.exceptionThrown', exception => this._page.emit(Page.Events.PageError, exceptionToError(exception.exceptionDetails)));
+ session.on('Runtime.exceptionThrown', exception => this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, exceptionToError(exception.exceptionDetails), this._page));
// TODO: attribute workers to the right frame.
this._networkManager.instrumentNetworkEvents({ session, workerFrame: this._page._frameManager.frame(this._targetId) ?? undefined });
}
@@ -859,7 +859,7 @@ class FrameSession {
}
_handleException(exceptionDetails: Protocol.Runtime.ExceptionDetails) {
- this._page.firePageError(exceptionToError(exceptionDetails));
+ this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, exceptionToError(exceptionDetails), this._page);
}
async _onTargetCrashed() {
diff --git a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts
index 69e5ed355e61b..3bd83d81dcc44 100644
--- a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts
@@ -38,6 +38,7 @@ import { DialogDispatcher } from './dialogDispatcher';
import type { Page } from '../page';
import type { Dialog } from '../dialog';
import type { ConsoleMessage } from '../console';
+import { serializeError } from '../../protocol/serializers';
export class BrowserContextDispatcher extends Dispatcher implements channels.BrowserContextChannel {
_type_EventTarget = true;
@@ -84,6 +85,9 @@ export class BrowserContextDispatcher extends Dispatcher {
+ this._dispatchEvent('pageError', { error: serializeError(error), page: PageDispatcher.from(this, page) });
+ });
this.addObjectListener(BrowserContext.Events.Console, (message: ConsoleMessage) => {
if (this._shouldDispatchEvent(message.page(), 'console'))
this._dispatchEvent('console', { message: new ConsoleMessageDispatcher(PageDispatcher.from(this, message.page()), message) });
diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts
index 63ecc51b288de..2a40a27d18e06 100644
--- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts
@@ -19,7 +19,7 @@ import type { Frame } from '../frames';
import { Page, Worker } from '../page';
import type * as channels from '@protocol/channels';
import { Dispatcher, existingDispatcher } from './dispatcher';
-import { parseError, serializeError } from '../../protocol/serializers';
+import { parseError } from '../../protocol/serializers';
import { FrameDispatcher } from './frameDispatcher';
import { RequestDispatcher } from './networkDispatchers';
import { ResponseDispatcher } from './networkDispatchers';
@@ -85,7 +85,6 @@ export class PageDispatcher extends Dispatcher this._onFrameAttached(frame));
this.addObjectListener(Page.Events.FrameDetached, frame => this._onFrameDetached(frame));
- this.addObjectListener(Page.Events.PageError, error => this._dispatchEvent('pageError', { error: serializeError(error) }));
this.addObjectListener(Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this, webSocket) }));
this.addObjectListener(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this, worker) }));
this.addObjectListener(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: ArtifactDispatcher.from(parentScope, artifact) }));
diff --git a/packages/playwright-core/src/server/firefox/ffPage.ts b/packages/playwright-core/src/server/firefox/ffPage.ts
index 378d7c8683fa0..c599d0e3e33ce 100644
--- a/packages/playwright-core/src/server/firefox/ffPage.ts
+++ b/packages/playwright-core/src/server/firefox/ffPage.ts
@@ -246,7 +246,7 @@ export class FFPage implements PageDelegate {
const error = new Error(message);
error.stack = params.message + '\n' + params.stack.split('\n').filter(Boolean).map(a => a.replace(/([^@]*)@(.*)/, ' at $1 ($2)')).join('\n');
error.name = name;
- this._page.firePageError(error);
+ this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, error, this._page);
}
_onConsole(payload: Protocol.Runtime.consolePayload) {
diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts
index 588e77b2bd2cd..a0b98a9d3f4e5 100644
--- a/packages/playwright-core/src/server/page.ts
+++ b/packages/playwright-core/src/server/page.ts
@@ -125,9 +125,6 @@ export class Page extends SdkObject {
Crash: 'crash',
Download: 'download',
FileChooser: 'filechooser',
- // Can't use just 'error' due to node.js special treatment of error events.
- // @see https://nodejs.org/api/events.html#events_error_events
- PageError: 'pageerror',
FrameAttached: 'frameattached',
FrameDetached: 'framedetached',
InternalFrameNavigatedToNewDocument: 'internalframenavigatedtonewdocument',
@@ -696,10 +693,6 @@ export class Page extends SdkObject {
this._frameThrottler.recharge();
}
- firePageError(error: Error) {
- this.emit(Page.Events.PageError, error);
- }
-
async hideHighlight() {
await Promise.all(this.frames().map(frame => frame.hideHighlight().catch(() => {})));
}
diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts
index 5d0bb4e241ca7..a2b24c0677774 100644
--- a/packages/playwright-core/src/server/webkit/wkPage.ts
+++ b/packages/playwright-core/src/server/webkit/wkPage.ts
@@ -555,7 +555,7 @@ export class WKPage implements PageDelegate {
error.stack = stack;
error.name = name;
- this._page.firePageError(error);
+ this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, error, this._page);
return;
}
diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts
index bf75aa146b0e3..aea8dca052af9 100644
--- a/packages/playwright-core/types/types.d.ts
+++ b/packages/playwright-core/types/types.d.ts
@@ -7636,6 +7636,13 @@ export interface BrowserContext {
*/
on(event: 'page', listener: (page: Page) => void): this;
+ /**
+ * Emitted when unhandled exceptions occur on any pages created through this context. To only listen for `pageError`
+ * events from a particular page, use
+ * [page.on('pageerror')](https://playwright.dev/docs/api/class-page#page-event-page-error).
+ */
+ on(event: 'pageerror', listener: (pageError: PageError) => void): this;
+
/**
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To
* only listen for requests from a particular page, use
@@ -7706,6 +7713,11 @@ export interface BrowserContext {
*/
once(event: 'page', listener: (page: Page) => void): this;
+ /**
+ * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event.
+ */
+ once(event: 'pageerror', listener: (pageError: PageError) => void): this;
+
/**
* Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event.
*/
@@ -7817,6 +7829,13 @@ export interface BrowserContext {
*/
addListener(event: 'page', listener: (page: Page) => void): this;
+ /**
+ * Emitted when unhandled exceptions occur on any pages created through this context. To only listen for `pageError`
+ * events from a particular page, use
+ * [page.on('pageerror')](https://playwright.dev/docs/api/class-page#page-event-page-error).
+ */
+ addListener(event: 'pageerror', listener: (pageError: PageError) => void): this;
+
/**
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To
* only listen for requests from a particular page, use
@@ -7887,6 +7906,11 @@ export interface BrowserContext {
*/
removeListener(event: 'page', listener: (page: Page) => void): this;
+ /**
+ * Removes an event listener added by `on` or `addListener`.
+ */
+ removeListener(event: 'pageerror', listener: (pageError: PageError) => void): this;
+
/**
* Removes an event listener added by `on` or `addListener`.
*/
@@ -7937,6 +7961,11 @@ export interface BrowserContext {
*/
off(event: 'page', listener: (page: Page) => void): this;
+ /**
+ * Removes an event listener added by `on` or `addListener`.
+ */
+ off(event: 'pageerror', listener: (pageError: PageError) => void): this;
+
/**
* Removes an event listener added by `on` or `addListener`.
*/
@@ -8048,6 +8077,13 @@ export interface BrowserContext {
*/
prependListener(event: 'page', listener: (page: Page) => void): this;
+ /**
+ * Emitted when unhandled exceptions occur on any pages created through this context. To only listen for `pageError`
+ * events from a particular page, use
+ * [page.on('pageerror')](https://playwright.dev/docs/api/class-page#page-event-page-error).
+ */
+ prependListener(event: 'pageerror', listener: (pageError: PageError) => void): this;
+
/**
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To
* only listen for requests from a particular page, use
@@ -8634,6 +8670,13 @@ export interface BrowserContext {
*/
waitForEvent(event: 'page', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise, timeout?: number } | ((page: Page) => boolean | Promise)): Promise;
+ /**
+ * Emitted when unhandled exceptions occur on any pages created through this context. To only listen for `pageError`
+ * events from a particular page, use
+ * [page.on('pageerror')](https://playwright.dev/docs/api/class-page#page-event-page-error).
+ */
+ waitForEvent(event: 'pageerror', optionsOrPredicate?: { predicate?: (pageError: PageError) => boolean | Promise, timeout?: number } | ((pageError: PageError) => boolean | Promise)): Promise;
+
/**
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To
* only listen for requests from a particular page, use
@@ -17900,6 +17943,35 @@ export interface Mouse {
wheel(deltaX: number, deltaY: number): Promise;
}
+/**
+ * {@link PageError} class represents objects created by context when there are unhandled execeptions thrown on the
+ * pages and dispatched via the
+ * [browserContext.on('pageerror')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-page-error)
+ * event.
+ *
+ * ```js
+ * // Log all uncaught errors to the terminal
+ * context.on('pageerror', pageerror => {
+ * console.log(`Uncaught exception: "${pageerror.error()}"`);
+ * });
+ *
+ * // Navigate to a page with an exception.
+ * await page.goto('data:text/html,');
+ * ```
+ *
+ */
+export interface PageError {
+ /**
+ * Unhandled error that was thrown.
+ */
+ error(): Error;
+
+ /**
+ * The page that produced this unhandled exception, if any.
+ */
+ page(): null|Page;
+}
+
/**
* This object can be used to launch or connect to Chromium, returning instances of {@link Browser}.
*/
diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts
index 3ffa80dc60f4f..ee616ab07bd72 100644
--- a/packages/protocol/src/channels.ts
+++ b/packages/protocol/src/channels.ts
@@ -1404,6 +1404,7 @@ export interface BrowserContextEventTarget {
on(event: 'close', callback: (params: BrowserContextCloseEvent) => void): this;
on(event: 'dialog', callback: (params: BrowserContextDialogEvent) => void): this;
on(event: 'page', callback: (params: BrowserContextPageEvent) => void): this;
+ on(event: 'pageError', callback: (params: BrowserContextPageErrorEvent) => void): this;
on(event: 'route', callback: (params: BrowserContextRouteEvent) => void): this;
on(event: 'video', callback: (params: BrowserContextVideoEvent) => void): this;
on(event: 'backgroundPage', callback: (params: BrowserContextBackgroundPageEvent) => void): this;
@@ -1453,6 +1454,10 @@ export type BrowserContextDialogEvent = {
export type BrowserContextPageEvent = {
page: PageChannel,
};
+export type BrowserContextPageErrorEvent = {
+ error: SerializedError,
+ page: PageChannel,
+};
export type BrowserContextRouteEvent = {
route: RouteChannel,
};
@@ -1697,6 +1702,7 @@ export interface BrowserContextEvents {
'close': BrowserContextCloseEvent;
'dialog': BrowserContextDialogEvent;
'page': BrowserContextPageEvent;
+ 'pageError': BrowserContextPageErrorEvent;
'route': BrowserContextRouteEvent;
'video': BrowserContextVideoEvent;
'backgroundPage': BrowserContextBackgroundPageEvent;
@@ -1725,7 +1731,6 @@ export interface PageEventTarget {
on(event: 'fileChooser', callback: (params: PageFileChooserEvent) => void): this;
on(event: 'frameAttached', callback: (params: PageFrameAttachedEvent) => void): this;
on(event: 'frameDetached', callback: (params: PageFrameDetachedEvent) => void): this;
- on(event: 'pageError', callback: (params: PagePageErrorEvent) => void): this;
on(event: 'route', callback: (params: PageRouteEvent) => void): this;
on(event: 'video', callback: (params: PageVideoEvent) => void): this;
on(event: 'webSocket', callback: (params: PageWebSocketEvent) => void): this;
@@ -1787,9 +1792,6 @@ export type PageFrameAttachedEvent = {
export type PageFrameDetachedEvent = {
frame: FrameChannel,
};
-export type PagePageErrorEvent = {
- error: SerializedError,
-};
export type PageRouteEvent = {
route: RouteChannel,
};
@@ -2220,7 +2222,6 @@ export interface PageEvents {
'fileChooser': PageFileChooserEvent;
'frameAttached': PageFrameAttachedEvent;
'frameDetached': PageFrameDetachedEvent;
- 'pageError': PagePageErrorEvent;
'route': PageRouteEvent;
'video': PageVideoEvent;
'webSocket': PageWebSocketEvent;
diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml
index bbc4009c4b831..ed7dd92e6913f 100644
--- a/packages/protocol/src/protocol.yml
+++ b/packages/protocol/src/protocol.yml
@@ -1188,6 +1188,11 @@ BrowserContext:
parameters:
page: Page
+ pageError:
+ parameters:
+ error: SerializedError
+ page: Page
+
route:
parameters:
route: Route
@@ -1635,10 +1640,6 @@ Page:
parameters:
frame: Frame
- pageError:
- parameters:
- error: SerializedError
-
route:
parameters:
route: Route
diff --git a/tests/library/browsercontext-events.spec.ts b/tests/library/browsercontext-events.spec.ts
index b6f84aafc72e3..966d1d0852d7f 100644
--- a/tests/library/browsercontext-events.spec.ts
+++ b/tests/library/browsercontext-events.spec.ts
@@ -160,3 +160,12 @@ test('dialog event should work with inline script tag', async ({ page, server })
await promise;
await expect.poll(() => popup.evaluate('window.result')).toBe('hello');
});
+
+test('pageError event should work', async ({ page }) => {
+ const [pageerror] = await Promise.all([
+ page.context().waitForEvent('pageerror'),
+ page.setContent(''),
+ ]);
+ expect(pageerror.page()).toBe(page);
+ expect(pageerror.error().stack).toContain('boom');
+});