diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md index 27ded1efe6796..f3292865d2157 100644 --- a/docs/src/api/class-browsercontext.md +++ b/docs/src/api/class-browsercontext.md @@ -198,6 +198,30 @@ Context.Dialog += async (_, dialog) => When no [`event: Page.dialog`] or [`event: BrowserContext.dialog`] listeners are present, all dialogs are automatically dismissed. ::: +## event: BrowserContext.download +* since: v1.60 +- argument: <[Download]> + +Emitted when attachment download started in any page belonging to this context. User can access basic file operations on downloaded content via the passed [Download] instance. See also [`event: Page.download`] to receive events about a specific page. + +## event: BrowserContext.frameAttached +* since: v1.60 +- argument: <[Frame]> + +Emitted when a frame is attached in any page belonging to this context. See also [`event: Page.frameAttached`] to receive events about a specific page. + +## event: BrowserContext.frameDetached +* since: v1.60 +- argument: <[Frame]> + +Emitted when a frame is detached in any page belonging to this context. See also [`event: Page.frameDetached`] to receive events about a specific page. + +## event: BrowserContext.frameNavigated +* since: v1.60 +- argument: <[Frame]> + +Emitted when a frame is navigated to a new url in any page belonging to this context. See also [`event: Page.frameNavigated`] to receive events about navigations in a specific page. + ## event: BrowserContext.page * since: v1.8 - argument: <[Page]> @@ -250,6 +274,18 @@ Use [`method: Page.waitForLoadState`] to wait until the page gets to a particula cases). ::: +## event: BrowserContext.pageClose +* since: v1.60 +- argument: <[Page]> + +Emitted when a page in this context is closed. See also [`event: Page.close`] to receive events about a specific page. + +## event: BrowserContext.pageLoad +* since: v1.60 +- argument: <[Page]> + +Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched in any page belonging to this context. See also [`event: Page.load`] to receive events about a specific page. + ## event: BrowserContext.webError * since: v1.38 - argument: <[WebError]> diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 65bf9f237b1b2..b0a45b471e42a 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -8294,6 +8294,35 @@ export interface BrowserContext { */ on(event: 'dialog', listener: (dialog: Dialog) => any): this; + /** + * Emitted when attachment download started in any page belonging to this context. User can access basic file + * operations on downloaded content via the passed [Download](https://playwright.dev/docs/api/class-download) + * instance. See also [page.on('download')](https://playwright.dev/docs/api/class-page#page-event-download) to receive + * events about a specific page. + */ + on(event: 'download', listener: (download: Download) => any): this; + + /** + * Emitted when a frame is attached in any page belonging to this context. See also + * [page.on('frameattached')](https://playwright.dev/docs/api/class-page#page-event-frame-attached) to receive events + * about a specific page. + */ + on(event: 'frameattached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is detached in any page belonging to this context. See also + * [page.on('framedetached')](https://playwright.dev/docs/api/class-page#page-event-frame-detached) to receive events + * about a specific page. + */ + on(event: 'framedetached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is navigated to a new url in any page belonging to this context. See also + * [page.on('framenavigated')](https://playwright.dev/docs/api/class-page#page-event-frame-navigated) to receive + * events about navigations in a specific page. + */ + on(event: 'framenavigated', listener: (frame: Frame) => any): this; + /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event * will also fire for popup pages. See also @@ -8323,6 +8352,21 @@ export interface BrowserContext { */ on(event: 'page', listener: (page: Page) => any): this; + /** + * Emitted when a page in this context is closed. See also + * [page.on('close')](https://playwright.dev/docs/api/class-page#page-event-close) to receive events about a specific + * page. + */ + on(event: 'pageclose', listener: (page: Page) => any): this; + + /** + * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched + * in any page belonging to this context. See also + * [page.on('load')](https://playwright.dev/docs/api/class-page#page-event-load) to receive events about a specific + * page. + */ + on(event: 'pageload', listener: (page: Page) => any): this; + /** * Emitted when a client calls [page.pickLocator()](https://playwright.dev/docs/api/class-page#page-pick-locator) on a * page in this context. The event is dispatched to all clients connected to the context, including the one that @@ -8402,11 +8446,41 @@ export interface BrowserContext { */ once(event: 'dialog', listener: (dialog: Dialog) => any): 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: 'download', listener: (download: Download) => any): 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: 'frameattached', listener: (frame: Frame) => any): 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: 'framedetached', listener: (frame: Frame) => any): 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: 'framenavigated', listener: (frame: Frame) => any): 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: 'page', listener: (page: Page) => any): 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: 'pageclose', listener: (page: Page) => any): 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: 'pageload', listener: (page: Page) => any): this; + /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ @@ -8498,6 +8572,35 @@ export interface BrowserContext { */ addListener(event: 'dialog', listener: (dialog: Dialog) => any): this; + /** + * Emitted when attachment download started in any page belonging to this context. User can access basic file + * operations on downloaded content via the passed [Download](https://playwright.dev/docs/api/class-download) + * instance. See also [page.on('download')](https://playwright.dev/docs/api/class-page#page-event-download) to receive + * events about a specific page. + */ + addListener(event: 'download', listener: (download: Download) => any): this; + + /** + * Emitted when a frame is attached in any page belonging to this context. See also + * [page.on('frameattached')](https://playwright.dev/docs/api/class-page#page-event-frame-attached) to receive events + * about a specific page. + */ + addListener(event: 'frameattached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is detached in any page belonging to this context. See also + * [page.on('framedetached')](https://playwright.dev/docs/api/class-page#page-event-frame-detached) to receive events + * about a specific page. + */ + addListener(event: 'framedetached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is navigated to a new url in any page belonging to this context. See also + * [page.on('framenavigated')](https://playwright.dev/docs/api/class-page#page-event-frame-navigated) to receive + * events about navigations in a specific page. + */ + addListener(event: 'framenavigated', listener: (frame: Frame) => any): this; + /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event * will also fire for popup pages. See also @@ -8527,6 +8630,21 @@ export interface BrowserContext { */ addListener(event: 'page', listener: (page: Page) => any): this; + /** + * Emitted when a page in this context is closed. See also + * [page.on('close')](https://playwright.dev/docs/api/class-page#page-event-close) to receive events about a specific + * page. + */ + addListener(event: 'pageclose', listener: (page: Page) => any): this; + + /** + * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched + * in any page belonging to this context. See also + * [page.on('load')](https://playwright.dev/docs/api/class-page#page-event-load) to receive events about a specific + * page. + */ + addListener(event: 'pageload', listener: (page: Page) => any): this; + /** * Emitted when a client calls [page.pickLocator()](https://playwright.dev/docs/api/class-page#page-pick-locator) on a * page in this context. The event is dispatched to all clients connected to the context, including the one that @@ -8606,11 +8724,41 @@ export interface BrowserContext { */ removeListener(event: 'dialog', listener: (dialog: Dialog) => any): this; + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'download', listener: (download: Download) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'frameattached', listener: (frame: Frame) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'framedetached', listener: (frame: Frame) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'framenavigated', listener: (frame: Frame) => any): this; + /** * Removes an event listener added by `on` or `addListener`. */ removeListener(event: 'page', listener: (page: Page) => any): this; + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'pageclose', listener: (page: Page) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'pageload', listener: (page: Page) => any): this; + /** * Removes an event listener added by `on` or `addListener`. */ @@ -8666,11 +8814,41 @@ export interface BrowserContext { */ off(event: 'dialog', listener: (dialog: Dialog) => any): this; + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'download', listener: (download: Download) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'frameattached', listener: (frame: Frame) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'framedetached', listener: (frame: Frame) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'framenavigated', listener: (frame: Frame) => any): this; + /** * Removes an event listener added by `on` or `addListener`. */ off(event: 'page', listener: (page: Page) => any): this; + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'pageclose', listener: (page: Page) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'pageload', listener: (page: Page) => any): this; + /** * Removes an event listener added by `on` or `addListener`. */ @@ -8762,6 +8940,35 @@ export interface BrowserContext { */ prependListener(event: 'dialog', listener: (dialog: Dialog) => any): this; + /** + * Emitted when attachment download started in any page belonging to this context. User can access basic file + * operations on downloaded content via the passed [Download](https://playwright.dev/docs/api/class-download) + * instance. See also [page.on('download')](https://playwright.dev/docs/api/class-page#page-event-download) to receive + * events about a specific page. + */ + prependListener(event: 'download', listener: (download: Download) => any): this; + + /** + * Emitted when a frame is attached in any page belonging to this context. See also + * [page.on('frameattached')](https://playwright.dev/docs/api/class-page#page-event-frame-attached) to receive events + * about a specific page. + */ + prependListener(event: 'frameattached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is detached in any page belonging to this context. See also + * [page.on('framedetached')](https://playwright.dev/docs/api/class-page#page-event-frame-detached) to receive events + * about a specific page. + */ + prependListener(event: 'framedetached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is navigated to a new url in any page belonging to this context. See also + * [page.on('framenavigated')](https://playwright.dev/docs/api/class-page#page-event-frame-navigated) to receive + * events about navigations in a specific page. + */ + prependListener(event: 'framenavigated', listener: (frame: Frame) => any): this; + /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event * will also fire for popup pages. See also @@ -8791,6 +8998,21 @@ export interface BrowserContext { */ prependListener(event: 'page', listener: (page: Page) => any): this; + /** + * Emitted when a page in this context is closed. See also + * [page.on('close')](https://playwright.dev/docs/api/class-page#page-event-close) to receive events about a specific + * page. + */ + prependListener(event: 'pageclose', listener: (page: Page) => any): this; + + /** + * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched + * in any page belonging to this context. See also + * [page.on('load')](https://playwright.dev/docs/api/class-page#page-event-load) to receive events about a specific + * page. + */ + prependListener(event: 'pageload', listener: (page: Page) => any): this; + /** * Emitted when a client calls [page.pickLocator()](https://playwright.dev/docs/api/class-page#page-pick-locator) on a * page in this context. The event is dispatched to all clients connected to the context, including the one that @@ -9558,6 +9780,35 @@ export interface BrowserContext { */ waitForEvent(event: 'dialog', optionsOrPredicate?: { predicate?: (dialog: Dialog) => boolean | Promise, timeout?: number } | ((dialog: Dialog) => boolean | Promise)): Promise; + /** + * Emitted when attachment download started in any page belonging to this context. User can access basic file + * operations on downloaded content via the passed [Download](https://playwright.dev/docs/api/class-download) + * instance. See also [page.on('download')](https://playwright.dev/docs/api/class-page#page-event-download) to receive + * events about a specific page. + */ + waitForEvent(event: 'download', optionsOrPredicate?: { predicate?: (download: Download) => boolean | Promise, timeout?: number } | ((download: Download) => boolean | Promise)): Promise; + + /** + * Emitted when a frame is attached in any page belonging to this context. See also + * [page.on('frameattached')](https://playwright.dev/docs/api/class-page#page-event-frame-attached) to receive events + * about a specific page. + */ + waitForEvent(event: 'frameattached', optionsOrPredicate?: { predicate?: (frame: Frame) => boolean | Promise, timeout?: number } | ((frame: Frame) => boolean | Promise)): Promise; + + /** + * Emitted when a frame is detached in any page belonging to this context. See also + * [page.on('framedetached')](https://playwright.dev/docs/api/class-page#page-event-frame-detached) to receive events + * about a specific page. + */ + waitForEvent(event: 'framedetached', optionsOrPredicate?: { predicate?: (frame: Frame) => boolean | Promise, timeout?: number } | ((frame: Frame) => boolean | Promise)): Promise; + + /** + * Emitted when a frame is navigated to a new url in any page belonging to this context. See also + * [page.on('framenavigated')](https://playwright.dev/docs/api/class-page#page-event-frame-navigated) to receive + * events about navigations in a specific page. + */ + waitForEvent(event: 'framenavigated', optionsOrPredicate?: { predicate?: (frame: Frame) => boolean | Promise, timeout?: number } | ((frame: Frame) => boolean | Promise)): Promise; + /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event * will also fire for popup pages. See also @@ -9587,6 +9838,21 @@ export interface BrowserContext { */ waitForEvent(event: 'page', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise, timeout?: number } | ((page: Page) => boolean | Promise)): Promise; + /** + * Emitted when a page in this context is closed. See also + * [page.on('close')](https://playwright.dev/docs/api/class-page#page-event-close) to receive events about a specific + * page. + */ + waitForEvent(event: 'pageclose', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise, timeout?: number } | ((page: Page) => boolean | Promise)): Promise; + + /** + * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched + * in any page belonging to this context. See also + * [page.on('load')](https://playwright.dev/docs/api/class-page#page-event-load) to receive events about a specific + * page. + */ + waitForEvent(event: 'pageload', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise, timeout?: number } | ((page: Page) => boolean | Promise)): Promise; + /** * Emitted when a client calls [page.pickLocator()](https://playwright.dev/docs/api/class-page#page-pick-locator) on a * page in this context. The event is dispatched to all clients connected to the context, including the one that diff --git a/packages/playwright-core/src/client/events.ts b/packages/playwright-core/src/client/events.ts index 5cb6cae7e6908..f244da4dd75d3 100644 --- a/packages/playwright-core/src/client/events.ts +++ b/packages/playwright-core/src/client/events.ts @@ -43,7 +43,13 @@ export const Events = { Console: 'console', Close: 'close', Dialog: 'dialog', + Download: 'download', + FrameAttached: 'frameattached', + FrameDetached: 'framedetached', + FrameNavigated: 'framenavigated', Page: 'page', + PageClose: 'pageclose', + PageLoad: 'pageload', // Can't use just 'error' due to node.js special treatment of error events. // @see https://nodejs.org/api/events.html#events_error_events WebError: 'weberror', diff --git a/packages/playwright-core/src/client/frame.ts b/packages/playwright-core/src/client/frame.ts index 0a03ab019d163..b4d6bd3c368a8 100644 --- a/packages/playwright-core/src/client/frame.ts +++ b/packages/playwright-core/src/client/frame.ts @@ -80,8 +80,10 @@ export class Frame extends ChannelOwner implements api.Fr } if (event.remove) this._loadStates.delete(event.remove); - if (!this._parentFrame && event.add === 'load' && this._page) + if (!this._parentFrame && event.add === 'load' && this._page) { this._page.emit(Events.Page.Load, this._page); + this._page.context().emit(Events.BrowserContext.PageLoad, this._page); + } if (!this._parentFrame && event.add === 'domcontentloaded' && this._page) this._page.emit(Events.Page.DOMContentLoaded, this._page); }); @@ -89,8 +91,10 @@ export class Frame extends ChannelOwner implements api.Fr this._url = event.url; this._name = event.name; this._eventEmitter.emit('navigated', event); - if (!event.error && this._page) + if (!event.error && this._page) { this._page.emit(Events.Page.FrameNavigated, this); + this._page.context().emit(Events.BrowserContext.FrameNavigated, this); + } }); } diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 2a549895b755d..2d519d8095c62 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -143,7 +143,9 @@ export class Page extends ChannelOwner implements api.Page this._channel.on('crash', () => this._onCrash()); this._channel.on('download', ({ url, suggestedFilename, artifact }) => { const artifactObject = Artifact.from(artifact); - this.emit(Events.Page.Download, new Download(this, url, suggestedFilename, artifactObject)); + const download = new Download(this, url, suggestedFilename, artifactObject); + this.emit(Events.Page.Download, download); + this._browserContext.emit(Events.BrowserContext.Download, download); }); 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))); @@ -177,6 +179,7 @@ export class Page extends ChannelOwner implements api.Page if (frame._parentFrame) frame._parentFrame._childFrames.add(frame); this.emit(Events.Page.FrameAttached, frame); + this._browserContext.emit(Events.BrowserContext.FrameAttached, frame); } private _onFrameDetached(frame: Frame) { @@ -185,6 +188,7 @@ export class Page extends ChannelOwner implements api.Page if (frame._parentFrame) frame._parentFrame._childFrames.delete(frame); this.emit(Events.Page.FrameDetached, frame); + this._browserContext.emit(Events.BrowserContext.FrameDetached, frame); } private async _onRoute(route: Route) { @@ -239,6 +243,7 @@ export class Page extends ChannelOwner implements api.Page this._browserContext._pages.delete(this); this._disposeHarRouters(); this.emit(Events.Page.Close, this); + this._browserContext.emit(Events.BrowserContext.PageClose, this); } private _onCrash() { diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 65bf9f237b1b2..b0a45b471e42a 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -8294,6 +8294,35 @@ export interface BrowserContext { */ on(event: 'dialog', listener: (dialog: Dialog) => any): this; + /** + * Emitted when attachment download started in any page belonging to this context. User can access basic file + * operations on downloaded content via the passed [Download](https://playwright.dev/docs/api/class-download) + * instance. See also [page.on('download')](https://playwright.dev/docs/api/class-page#page-event-download) to receive + * events about a specific page. + */ + on(event: 'download', listener: (download: Download) => any): this; + + /** + * Emitted when a frame is attached in any page belonging to this context. See also + * [page.on('frameattached')](https://playwright.dev/docs/api/class-page#page-event-frame-attached) to receive events + * about a specific page. + */ + on(event: 'frameattached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is detached in any page belonging to this context. See also + * [page.on('framedetached')](https://playwright.dev/docs/api/class-page#page-event-frame-detached) to receive events + * about a specific page. + */ + on(event: 'framedetached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is navigated to a new url in any page belonging to this context. See also + * [page.on('framenavigated')](https://playwright.dev/docs/api/class-page#page-event-frame-navigated) to receive + * events about navigations in a specific page. + */ + on(event: 'framenavigated', listener: (frame: Frame) => any): this; + /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event * will also fire for popup pages. See also @@ -8323,6 +8352,21 @@ export interface BrowserContext { */ on(event: 'page', listener: (page: Page) => any): this; + /** + * Emitted when a page in this context is closed. See also + * [page.on('close')](https://playwright.dev/docs/api/class-page#page-event-close) to receive events about a specific + * page. + */ + on(event: 'pageclose', listener: (page: Page) => any): this; + + /** + * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched + * in any page belonging to this context. See also + * [page.on('load')](https://playwright.dev/docs/api/class-page#page-event-load) to receive events about a specific + * page. + */ + on(event: 'pageload', listener: (page: Page) => any): this; + /** * Emitted when a client calls [page.pickLocator()](https://playwright.dev/docs/api/class-page#page-pick-locator) on a * page in this context. The event is dispatched to all clients connected to the context, including the one that @@ -8402,11 +8446,41 @@ export interface BrowserContext { */ once(event: 'dialog', listener: (dialog: Dialog) => any): 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: 'download', listener: (download: Download) => any): 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: 'frameattached', listener: (frame: Frame) => any): 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: 'framedetached', listener: (frame: Frame) => any): 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: 'framenavigated', listener: (frame: Frame) => any): 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: 'page', listener: (page: Page) => any): 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: 'pageclose', listener: (page: Page) => any): 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: 'pageload', listener: (page: Page) => any): this; + /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ @@ -8498,6 +8572,35 @@ export interface BrowserContext { */ addListener(event: 'dialog', listener: (dialog: Dialog) => any): this; + /** + * Emitted when attachment download started in any page belonging to this context. User can access basic file + * operations on downloaded content via the passed [Download](https://playwright.dev/docs/api/class-download) + * instance. See also [page.on('download')](https://playwright.dev/docs/api/class-page#page-event-download) to receive + * events about a specific page. + */ + addListener(event: 'download', listener: (download: Download) => any): this; + + /** + * Emitted when a frame is attached in any page belonging to this context. See also + * [page.on('frameattached')](https://playwright.dev/docs/api/class-page#page-event-frame-attached) to receive events + * about a specific page. + */ + addListener(event: 'frameattached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is detached in any page belonging to this context. See also + * [page.on('framedetached')](https://playwright.dev/docs/api/class-page#page-event-frame-detached) to receive events + * about a specific page. + */ + addListener(event: 'framedetached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is navigated to a new url in any page belonging to this context. See also + * [page.on('framenavigated')](https://playwright.dev/docs/api/class-page#page-event-frame-navigated) to receive + * events about navigations in a specific page. + */ + addListener(event: 'framenavigated', listener: (frame: Frame) => any): this; + /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event * will also fire for popup pages. See also @@ -8527,6 +8630,21 @@ export interface BrowserContext { */ addListener(event: 'page', listener: (page: Page) => any): this; + /** + * Emitted when a page in this context is closed. See also + * [page.on('close')](https://playwright.dev/docs/api/class-page#page-event-close) to receive events about a specific + * page. + */ + addListener(event: 'pageclose', listener: (page: Page) => any): this; + + /** + * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched + * in any page belonging to this context. See also + * [page.on('load')](https://playwright.dev/docs/api/class-page#page-event-load) to receive events about a specific + * page. + */ + addListener(event: 'pageload', listener: (page: Page) => any): this; + /** * Emitted when a client calls [page.pickLocator()](https://playwright.dev/docs/api/class-page#page-pick-locator) on a * page in this context. The event is dispatched to all clients connected to the context, including the one that @@ -8606,11 +8724,41 @@ export interface BrowserContext { */ removeListener(event: 'dialog', listener: (dialog: Dialog) => any): this; + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'download', listener: (download: Download) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'frameattached', listener: (frame: Frame) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'framedetached', listener: (frame: Frame) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'framenavigated', listener: (frame: Frame) => any): this; + /** * Removes an event listener added by `on` or `addListener`. */ removeListener(event: 'page', listener: (page: Page) => any): this; + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'pageclose', listener: (page: Page) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'pageload', listener: (page: Page) => any): this; + /** * Removes an event listener added by `on` or `addListener`. */ @@ -8666,11 +8814,41 @@ export interface BrowserContext { */ off(event: 'dialog', listener: (dialog: Dialog) => any): this; + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'download', listener: (download: Download) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'frameattached', listener: (frame: Frame) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'framedetached', listener: (frame: Frame) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'framenavigated', listener: (frame: Frame) => any): this; + /** * Removes an event listener added by `on` or `addListener`. */ off(event: 'page', listener: (page: Page) => any): this; + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'pageclose', listener: (page: Page) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'pageload', listener: (page: Page) => any): this; + /** * Removes an event listener added by `on` or `addListener`. */ @@ -8762,6 +8940,35 @@ export interface BrowserContext { */ prependListener(event: 'dialog', listener: (dialog: Dialog) => any): this; + /** + * Emitted when attachment download started in any page belonging to this context. User can access basic file + * operations on downloaded content via the passed [Download](https://playwright.dev/docs/api/class-download) + * instance. See also [page.on('download')](https://playwright.dev/docs/api/class-page#page-event-download) to receive + * events about a specific page. + */ + prependListener(event: 'download', listener: (download: Download) => any): this; + + /** + * Emitted when a frame is attached in any page belonging to this context. See also + * [page.on('frameattached')](https://playwright.dev/docs/api/class-page#page-event-frame-attached) to receive events + * about a specific page. + */ + prependListener(event: 'frameattached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is detached in any page belonging to this context. See also + * [page.on('framedetached')](https://playwright.dev/docs/api/class-page#page-event-frame-detached) to receive events + * about a specific page. + */ + prependListener(event: 'framedetached', listener: (frame: Frame) => any): this; + + /** + * Emitted when a frame is navigated to a new url in any page belonging to this context. See also + * [page.on('framenavigated')](https://playwright.dev/docs/api/class-page#page-event-frame-navigated) to receive + * events about navigations in a specific page. + */ + prependListener(event: 'framenavigated', listener: (frame: Frame) => any): this; + /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event * will also fire for popup pages. See also @@ -8791,6 +8998,21 @@ export interface BrowserContext { */ prependListener(event: 'page', listener: (page: Page) => any): this; + /** + * Emitted when a page in this context is closed. See also + * [page.on('close')](https://playwright.dev/docs/api/class-page#page-event-close) to receive events about a specific + * page. + */ + prependListener(event: 'pageclose', listener: (page: Page) => any): this; + + /** + * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched + * in any page belonging to this context. See also + * [page.on('load')](https://playwright.dev/docs/api/class-page#page-event-load) to receive events about a specific + * page. + */ + prependListener(event: 'pageload', listener: (page: Page) => any): this; + /** * Emitted when a client calls [page.pickLocator()](https://playwright.dev/docs/api/class-page#page-pick-locator) on a * page in this context. The event is dispatched to all clients connected to the context, including the one that @@ -9558,6 +9780,35 @@ export interface BrowserContext { */ waitForEvent(event: 'dialog', optionsOrPredicate?: { predicate?: (dialog: Dialog) => boolean | Promise, timeout?: number } | ((dialog: Dialog) => boolean | Promise)): Promise; + /** + * Emitted when attachment download started in any page belonging to this context. User can access basic file + * operations on downloaded content via the passed [Download](https://playwright.dev/docs/api/class-download) + * instance. See also [page.on('download')](https://playwright.dev/docs/api/class-page#page-event-download) to receive + * events about a specific page. + */ + waitForEvent(event: 'download', optionsOrPredicate?: { predicate?: (download: Download) => boolean | Promise, timeout?: number } | ((download: Download) => boolean | Promise)): Promise; + + /** + * Emitted when a frame is attached in any page belonging to this context. See also + * [page.on('frameattached')](https://playwright.dev/docs/api/class-page#page-event-frame-attached) to receive events + * about a specific page. + */ + waitForEvent(event: 'frameattached', optionsOrPredicate?: { predicate?: (frame: Frame) => boolean | Promise, timeout?: number } | ((frame: Frame) => boolean | Promise)): Promise; + + /** + * Emitted when a frame is detached in any page belonging to this context. See also + * [page.on('framedetached')](https://playwright.dev/docs/api/class-page#page-event-frame-detached) to receive events + * about a specific page. + */ + waitForEvent(event: 'framedetached', optionsOrPredicate?: { predicate?: (frame: Frame) => boolean | Promise, timeout?: number } | ((frame: Frame) => boolean | Promise)): Promise; + + /** + * Emitted when a frame is navigated to a new url in any page belonging to this context. See also + * [page.on('framenavigated')](https://playwright.dev/docs/api/class-page#page-event-frame-navigated) to receive + * events about navigations in a specific page. + */ + waitForEvent(event: 'framenavigated', optionsOrPredicate?: { predicate?: (frame: Frame) => boolean | Promise, timeout?: number } | ((frame: Frame) => boolean | Promise)): Promise; + /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event * will also fire for popup pages. See also @@ -9587,6 +9838,21 @@ export interface BrowserContext { */ waitForEvent(event: 'page', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise, timeout?: number } | ((page: Page) => boolean | Promise)): Promise; + /** + * Emitted when a page in this context is closed. See also + * [page.on('close')](https://playwright.dev/docs/api/class-page#page-event-close) to receive events about a specific + * page. + */ + waitForEvent(event: 'pageclose', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise, timeout?: number } | ((page: Page) => boolean | Promise)): Promise; + + /** + * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched + * in any page belonging to this context. See also + * [page.on('load')](https://playwright.dev/docs/api/class-page#page-event-load) to receive events about a specific + * page. + */ + waitForEvent(event: 'pageload', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise, timeout?: number } | ((page: Page) => boolean | Promise)): Promise; + /** * Emitted when a client calls [page.pickLocator()](https://playwright.dev/docs/api/class-page#page-pick-locator) on a * page in this context. The event is dispatched to all clients connected to the context, including the one that diff --git a/tests/library/browsercontext-events.spec.ts b/tests/library/browsercontext-events.spec.ts index 16dbd6ffd2ea4..84aef8f923283 100644 --- a/tests/library/browsercontext-events.spec.ts +++ b/tests/library/browsercontext-events.spec.ts @@ -181,3 +181,73 @@ test('weberror event should work', async ({ page }) => { expect(webError.page()).toBe(page); expect(webError.error().stack).toContain('boom'); }); + +test('pageload event should work @smoke', async ({ page, server }) => { + const [eventPage] = await Promise.all([ + page.context().waitForEvent('pageload'), + page.goto(server.EMPTY_PAGE), + ]); + expect(eventPage).toBe(page); +}); + +test('framenavigated event should work @smoke', async ({ page, server }) => { + const [frame] = await Promise.all([ + page.context().waitForEvent('framenavigated'), + page.goto(server.EMPTY_PAGE), + ]); + expect(frame).toBe(page.mainFrame()); + expect(frame.url()).toBe(server.EMPTY_PAGE); +}); + +test('pageclose event should work @smoke', async ({ context }) => { + const page = await context.newPage(); + const [closed] = await Promise.all([ + context.waitForEvent('pageclose'), + page.close(), + ]); + expect(closed).toBe(page); +}); + +test('frameattached event should work @smoke', async ({ page, server }) => { + await page.goto(server.EMPTY_PAGE); + const [frame] = await Promise.all([ + page.context().waitForEvent('frameattached'), + page.evaluate(() => { + const iframe = document.createElement('iframe'); + iframe.src = 'about:blank'; + document.body.appendChild(iframe); + }), + ]); + expect(frame.parentFrame()).toBe(page.mainFrame()); +}); + +test('framedetached event should work @smoke', async ({ page, server }) => { + await page.goto(server.EMPTY_PAGE); + await page.evaluate(() => { + const iframe = document.createElement('iframe'); + iframe.id = 'x'; + iframe.src = 'about:blank'; + document.body.appendChild(iframe); + }); + await page.waitForSelector('iframe'); + const [frame] = await Promise.all([ + page.context().waitForEvent('framedetached'), + page.evaluate(() => document.getElementById('x')!.remove()), + ]); + expect(frame.parentFrame()).toBe(page.mainFrame()); +}); + +test('download event should work @smoke', async ({ page, server }) => { + server.setRoute('/download', (req, res) => { + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Disposition', 'attachment; filename=file.txt'); + res.end('Hello world'); + }); + await page.setContent(`download`); + const [download] = await Promise.all([ + page.context().waitForEvent('download'), + page.click('a'), + ]); + expect(download.suggestedFilename()).toBe('file.txt'); + expect(download.page()).toBe(page); +});