diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md index c478049fc9..1a04c554df 100644 --- a/docs/src/api/class-browsercontext.md +++ b/docs/src/api/class-browsercontext.md @@ -143,6 +143,41 @@ Use [`method: Page.waitForLoadState`] to wait until the page gets to a particula cases). ::: +## event: BrowserContext.request +- argument: <[Request]> + +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 [`event: Page.request`]. + +In order to intercept and mutate requests, see [`method: BrowserContext.route`] +or [`method: Page.route`]. + +## event: BrowserContext.requestFailed +- argument: <[Request]> + +Emitted when a request fails, for example by timing out. To only listen for +failed requests from a particular page, use [`event: Page.requestFailed`]. + +:::note +HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will complete +with [`event: BrowserContext.requestFinished`] event and not with [`event: BrowserContext.requestFailed`]. +::: + +## event: BrowserContext.requestFinished +- argument: <[Request]> + +Emitted when a request finishes successfully after downloading the response body. For a successful response, the +sequence of events is `request`, `response` and `requestfinished`. To listen for +successful requests from a particular page, use [`event: Page.requestFinished`]. + +## event: BrowserContext.response +- argument: <[Response]> + +Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events +is `request`, `response` and `requestfinished`. To listen for response events +from a particular page, use [`event: Page.response`]. + ## event: BrowserContext.serviceWorker * langs: js, python - argument: <[Worker]> diff --git a/src/client/browserContext.ts b/src/client/browserContext.ts index 0df343192b..f3ac2d227e 100644 --- a/src/client/browserContext.ts +++ b/src/client/browserContext.ts @@ -86,6 +86,10 @@ export class BrowserContext extends ChannelOwner this._onRequest(network.Request.from(request), Page.fromNullable(page))); + this._channel.on('requestFailed', ({ request, failureText, responseEndTiming, page }) => this._onRequestFailed(network.Request.from(request), responseEndTiming, failureText, Page.fromNullable(page))); + this._channel.on('requestFinished', ({ request, responseEndTiming, page }) => this._onRequestFinished(network.Request.from(request), responseEndTiming, Page.fromNullable(page))); + this._channel.on('response', ({ response, page }) => this._onResponse(network.Response.from(response), Page.fromNullable(page))); this._closedPromise = new Promise(f => this.once(Events.BrowserContext.Close, f)); } @@ -96,6 +100,35 @@ export class BrowserContext extends ChannelOwner this._onFrameDetached(Frame.from(frame))); this._channel.on('load', () => this.emit(Events.Page.Load, this)); this._channel.on('pageError', ({ error }) => this.emit(Events.Page.PageError, parseError(error))); - this._channel.on('request', ({ request }) => this.emit(Events.Page.Request, Request.from(request))); - this._channel.on('requestFailed', ({ request, failureText, responseEndTiming }) => this._onRequestFailed(Request.from(request), responseEndTiming, failureText)); - this._channel.on('requestFinished', ({ request, responseEndTiming }) => this._onRequestFinished(Request.from(request), responseEndTiming)); - this._channel.on('response', ({ response }) => this.emit(Events.Page.Response, Response.from(response))); this._channel.on('route', ({ route, request }) => this._onRoute(Route.from(route), Request.from(request))); this._channel.on('video', ({ artifact }) => { const artifactObject = Artifact.from(artifact); @@ -152,19 +148,6 @@ export class Page extends ChannelOwner implements channels.BrowserContextChannel { private _context: BrowserContext; @@ -63,6 +64,27 @@ export class BrowserContextDispatcher extends Dispatcher this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker) })); } + context.on(BrowserContext.Events.Request, (request: Request) => { + return this._dispatchEvent('request', { + request: RequestDispatcher.from(this._scope, request), + page: PageDispatcher.fromNullable(this._scope, request.frame()._page.initializedOrUndefined()) + }); + }); + context.on(BrowserContext.Events.Response, (response: Response) => this._dispatchEvent('response', { + response: ResponseDispatcher.from(this._scope, response), + page: PageDispatcher.fromNullable(this._scope, response.frame()._page.initializedOrUndefined()) + })); + context.on(BrowserContext.Events.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', { + request: RequestDispatcher.from(this._scope, request), + failureText: request._failureText, + responseEndTiming: request._responseEndTiming, + page: PageDispatcher.fromNullable(this._scope, request.frame()._page.initializedOrUndefined()) + })); + context.on(BrowserContext.Events.RequestFinished, (request: Request) => this._dispatchEvent('requestFinished', { + request: RequestDispatcher.from(scope, request), + responseEndTiming: request._responseEndTiming, + page: PageDispatcher.fromNullable(this._scope, request.frame()._page.initializedOrUndefined()) + })); } async setDefaultNavigationTimeoutNoReply(params: channels.BrowserContextSetDefaultNavigationTimeoutNoReplyParams) { diff --git a/src/dispatchers/pageDispatcher.ts b/src/dispatchers/pageDispatcher.ts index bdc98e4e05..c14bb6015f 100644 --- a/src/dispatchers/pageDispatcher.ts +++ b/src/dispatchers/pageDispatcher.ts @@ -16,7 +16,6 @@ import { BrowserContext } from '../server/browserContext'; import { Frame } from '../server/frames'; -import { Request } from '../server/network'; import { Page, Worker } from '../server/page'; import * as channels from '../protocol/channels'; import { Dispatcher, DispatcherScope, existingDispatcher, lookupDispatcher, lookupNullableDispatcher } from './dispatcher'; @@ -39,7 +38,7 @@ import { createGuid } from '../utils/utils'; export class PageDispatcher extends Dispatcher implements channels.PageChannel { private _page: Page; - private static fromNullable(scope: DispatcherScope, page: Page | undefined): PageDispatcher | undefined { + static fromNullable(scope: DispatcherScope, page: Page | undefined): PageDispatcher | undefined { if (!page) return undefined; const result = existingDispatcher(page); @@ -75,17 +74,6 @@ export class PageDispatcher extends Dispatcher i page.on(Page.Events.FrameDetached, frame => this._onFrameDetached(frame)); page.on(Page.Events.Load, () => this._dispatchEvent('load')); page.on(Page.Events.PageError, error => this._dispatchEvent('pageError', { error: serializeError(error) })); - page.on(Page.Events.Request, request => this._dispatchEvent('request', { request: RequestDispatcher.from(this._scope, request) })); - page.on(Page.Events.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', { - request: RequestDispatcher.from(this._scope, request), - failureText: request._failureText, - responseEndTiming: request._responseEndTiming - })); - page.on(Page.Events.RequestFinished, (request: Request) => this._dispatchEvent('requestFinished', { - request: RequestDispatcher.from(scope, request), - responseEndTiming: request._responseEndTiming - })); - page.on(Page.Events.Response, response => this._dispatchEvent('response', { response: ResponseDispatcher.from(this._scope, response) })); page.on(Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this._scope, webSocket) })); page.on(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this._scope, worker) })); page.on(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: existingDispatcher(artifact) })); diff --git a/src/protocol/channels.ts b/src/protocol/channels.ts index fa1c2edf28..7ea19290f5 100644 --- a/src/protocol/channels.ts +++ b/src/protocol/channels.ts @@ -590,6 +590,10 @@ export interface BrowserContextChannel extends Channel { on(event: 'video', callback: (params: BrowserContextVideoEvent) => void): this; on(event: 'backgroundPage', callback: (params: BrowserContextBackgroundPageEvent) => void): this; on(event: 'serviceWorker', callback: (params: BrowserContextServiceWorkerEvent) => void): this; + on(event: 'request', callback: (params: BrowserContextRequestEvent) => void): this; + on(event: 'requestFailed', callback: (params: BrowserContextRequestFailedEvent) => void): this; + on(event: 'requestFinished', callback: (params: BrowserContextRequestFinishedEvent) => void): this; + on(event: 'response', callback: (params: BrowserContextResponseEvent) => void): this; addCookies(params: BrowserContextAddCookiesParams, metadata?: Metadata): Promise; addInitScript(params: BrowserContextAddInitScriptParams, metadata?: Metadata): Promise; clearCookies(params?: BrowserContextClearCookiesParams, metadata?: Metadata): Promise; @@ -634,6 +638,25 @@ export type BrowserContextBackgroundPageEvent = { export type BrowserContextServiceWorkerEvent = { worker: WorkerChannel, }; +export type BrowserContextRequestEvent = { + request: RequestChannel, + page?: PageChannel, +}; +export type BrowserContextRequestFailedEvent = { + request: RequestChannel, + failureText?: string, + responseEndTiming: number, + page?: PageChannel, +}; +export type BrowserContextRequestFinishedEvent = { + request: RequestChannel, + responseEndTiming: number, + page?: PageChannel, +}; +export type BrowserContextResponseEvent = { + response: ResponseChannel, + page?: PageChannel, +}; export type BrowserContextAddCookiesParams = { cookies: SetNetworkCookie[], }; @@ -832,10 +855,6 @@ export interface PageChannel extends Channel { on(event: 'frameDetached', callback: (params: PageFrameDetachedEvent) => void): this; on(event: 'load', callback: (params: PageLoadEvent) => void): this; on(event: 'pageError', callback: (params: PagePageErrorEvent) => void): this; - on(event: 'request', callback: (params: PageRequestEvent) => void): this; - on(event: 'requestFailed', callback: (params: PageRequestFailedEvent) => void): this; - on(event: 'requestFinished', callback: (params: PageRequestFinishedEvent) => void): this; - on(event: 'response', callback: (params: PageResponseEvent) => 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; @@ -903,21 +922,6 @@ export type PageLoadEvent = {}; export type PagePageErrorEvent = { error: SerializedError, }; -export type PageRequestEvent = { - request: RequestChannel, -}; -export type PageRequestFailedEvent = { - request: RequestChannel, - failureText?: string, - responseEndTiming: number, -}; -export type PageRequestFinishedEvent = { - request: RequestChannel, - responseEndTiming: number, -}; -export type PageResponseEvent = { - response: ResponseChannel, -}; export type PageRouteEvent = { route: RouteChannel, request: RequestChannel, diff --git a/src/protocol/protocol.yml b/src/protocol/protocol.yml index 347d23d694..ca19786919 100644 --- a/src/protocol/protocol.yml +++ b/src/protocol/protocol.yml @@ -642,6 +642,29 @@ BrowserContext: parameters: worker: Worker + request: + parameters: + request: Request + page: Page? + + requestFailed: + parameters: + request: Request + failureText: string? + responseEndTiming: number + page: Page? + + requestFinished: + parameters: + request: Request + responseEndTiming: number + page: Page? + + response: + parameters: + response: Response + page: Page? + Page: type: interface @@ -964,25 +987,6 @@ Page: parameters: error: SerializedError - request: - parameters: - request: Request - - requestFailed: - parameters: - request: Request - failureText: string? - responseEndTiming: number - - requestFinished: - parameters: - request: Request - responseEndTiming: number - - response: - parameters: - response: Response - route: parameters: route: Route diff --git a/src/server/browserContext.ts b/src/server/browserContext.ts index 6c7a8bb8b3..d8b40ff055 100644 --- a/src/server/browserContext.ts +++ b/src/server/browserContext.ts @@ -39,6 +39,10 @@ export abstract class BrowserContext extends SdkObject { static Events = { Close: 'close', Page: 'page', + Request: 'request', + Response: 'response', + RequestFailed: 'requestfailed', + RequestFinished: 'requestfinished', BeforeClose: 'beforeclose', VideoStarted: 'videostarted', }; diff --git a/src/server/chromium/crNetworkManager.ts b/src/server/chromium/crNetworkManager.ts index 303b92c8ef..7c008a22cd 100644 --- a/src/server/chromium/crNetworkManager.ts +++ b/src/server/chromium/crNetworkManager.ts @@ -207,10 +207,10 @@ export class CRNetworkManager { frame = this._page._frameManager.frame(requestPausedEvent.frameId); // Check if it's main resource request interception (targetId === main frame id). - if (!frame && requestPausedEvent && requestWillBeSentEvent.frameId === (this._page._delegate as CRPage)._targetId) { + if (!frame && requestWillBeSentEvent.frameId === (this._page._delegate as CRPage)._targetId) { // Main resource request for the page is being intercepted so the Frame is not created // yet. Precreate it here for the purposes of request interception. It will be updated - // later as soon as the request contnues and we receive frame tree from the page. + // later as soon as the request continues and we receive frame tree from the page. frame = this._page._frameManager.frameAttached(requestWillBeSentEvent.frameId, null); } diff --git a/src/server/frames.ts b/src/server/frames.ts index 00b19d69dc..5e91dfd96b 100644 --- a/src/server/frames.ts +++ b/src/server/frames.ts @@ -263,6 +263,7 @@ export class FrameManager { route.continue(); return; } + this._page._browserContext.emit(BrowserContext.Events.Request, request); this._page._requestStarted(request); } @@ -271,12 +272,15 @@ export class FrameManager { return; this._responses.push(response); this._page.emit(Page.Events.Response, response); + this._page._browserContext.emit(BrowserContext.Events.Response, response); } requestFinished(request: network.Request) { this._inflightRequestFinished(request); - if (!request._isFavicon) - this._page.emit(Page.Events.RequestFinished, request); + if (request._isFavicon) + return; + this._page._browserContext.emit(BrowserContext.Events.RequestFinished, request); + this._page.emit(Page.Events.RequestFinished, request); } requestFailed(request: network.Request, canceled: boolean) { @@ -288,8 +292,10 @@ export class FrameManager { errorText += '; maybe frame was detached?'; this.frameAbortedNavigation(frame._id, errorText, frame.pendingDocument()!.documentId); } - if (!request._isFavicon) - this._page.emit(Page.Events.RequestFailed, request); + if (request._isFavicon) + return; + this._page._browserContext.emit(BrowserContext.Events.RequestFailed, request); + this._page.emit(Page.Events.RequestFailed, request); } removeChildFramesRecursively(frame: Frame) { diff --git a/src/server/page.ts b/src/server/page.ts index 57dc55306a..8095ed7d42 100644 --- a/src/server/page.ts +++ b/src/server/page.ts @@ -122,6 +122,7 @@ export class Page extends SdkObject { private _closedCallback: () => void; private _closedPromise: Promise; private _disconnected = false; + private _initialized = false; private _disconnectedCallback: (e: Error) => void; readonly _disconnectedPromise: Promise; private _crashedCallback: (e: Error) => void; @@ -195,6 +196,7 @@ export class Page extends SdkObject { return; this._setIsError(error); } + this._initialized = true; this._browserContext.emit(BrowserContext.Events.Page, this); // I may happen that page iniatialization finishes after Close event has already been sent, // in that case we fire another Close event to ensure that each reported Page will have @@ -203,6 +205,10 @@ export class Page extends SdkObject { this.emit(Page.Events.Close); } + initializedOrUndefined() { + return this._initialized ? this : undefined; + } + async _doSlowMo() { const slowMo = this._browserContext._browser.options.slowMo; if (!slowMo) diff --git a/tests/browsercontext-network-event.spec.ts b/tests/browsercontext-network-event.spec.ts new file mode 100644 index 0000000000..770f1d17d8 --- /dev/null +++ b/tests/browsercontext-network-event.spec.ts @@ -0,0 +1,109 @@ +/** + * Copyright 2018 Google Inc. All rights reserved. + * Modifications 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 { browserTest as it, expect } from './config/browserTest'; + +it('BrowserContext.Events.Request', async ({browser, server}) => { + const context = await browser.newContext(); + const page = await context.newPage(); + const requests = []; + context.on('request', request => requests.push(request)); + await page.goto(server.EMPTY_PAGE); + await page.setContent('yo'); + const [page1] = await Promise.all([ + context.waitForEvent('page'), + page.click('a'), + ]); + await page1.waitForLoadState(); + const urls = requests.map(r => r.url()); + expect(urls).toEqual([ + server.EMPTY_PAGE, + `${server.PREFIX}/one-style.html`, + `${server.PREFIX}/one-style.css` + ]); +}); + +it('BrowserContext.Events.Response', async ({browser, server}) => { + const context = await browser.newContext(); + const page = await context.newPage(); + const responses = []; + context.on('response', response => responses.push(response)); + await page.goto(server.EMPTY_PAGE); + await page.setContent('yo'); + const [page1] = await Promise.all([ + context.waitForEvent('page'), + page.click('a'), + ]); + await page1.waitForLoadState(); + const urls = responses.map(r => r.url()); + expect(urls).toEqual([ + server.EMPTY_PAGE, + `${server.PREFIX}/one-style.html`, + `${server.PREFIX}/one-style.css` + ]); +}); + +it('BrowserContext.Events.RequestFailed', async ({browser, server}) => { + server.setRoute('/one-style.css', (_, res) => { + res.setHeader('Content-Type', 'text/css'); + res.connection.destroy(); + }); + const context = await browser.newContext(); + const page = await context.newPage(); + const failedRequests = []; + context.on('requestfailed', request => failedRequests.push(request)); + await page.goto(server.PREFIX + '/one-style.html'); + expect(failedRequests.length).toBe(1); + expect(failedRequests[0].url()).toContain('one-style.css'); + expect(await failedRequests[0].response()).toBe(null); + expect(failedRequests[0].resourceType()).toBe('stylesheet'); + expect(failedRequests[0].frame()).toBeTruthy(); +}); + + +it('BrowserContext.Events.RequestFinished', async ({browser, server}) => { + const context = await browser.newContext(); + const page = await context.newPage(); + const [response] = await Promise.all([ + page.goto(server.EMPTY_PAGE), + context.waitForEvent('requestfinished') + ]); + const request = response.request(); + expect(request.url()).toBe(server.EMPTY_PAGE); + expect(await request.response()).toBeTruthy(); + expect(request.frame() === page.mainFrame()).toBe(true); + expect(request.frame().url()).toBe(server.EMPTY_PAGE); + expect(request.failure()).toBe(null); +}); + +it('should fire events in proper order', async ({browser, server}) => { + const context = await browser.newContext(); + const page = await context.newPage(); + const events = []; + context.on('request', () => events.push('request')); + context.on('response', () => events.push('response')); + context.on('requestfinished', () => events.push('requestfinished')); + await Promise.all([ + page.goto(server.EMPTY_PAGE), + context.waitForEvent('requestfinished') + ]); + expect(events).toEqual([ + 'request', + 'response', + 'requestfinished', + ]); +}); diff --git a/tests/channels.spec.ts b/tests/channels.spec.ts index ba4e3d3fec..0746850d46 100644 --- a/tests/channels.spec.ts +++ b/tests/channels.spec.ts @@ -49,10 +49,9 @@ it('should scope context handles', async ({browserType, browserOptions, server}) { _guid: 'browser', objects: [ { _guid: 'browser-context', objects: [ { _guid: 'frame', objects: [] }, - { _guid: 'page', objects: [ - { _guid: 'request', objects: [] }, - { _guid: 'response', objects: [] }, - ]}, + { _guid: 'page', objects: []}, + { _guid: 'request', objects: [] }, + { _guid: 'response', objects: [] }, ]}, ] }, ] }, diff --git a/types/types.d.ts b/types/types.d.ts index c274a9e4cd..c2bb2bd366 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -4838,6 +4838,43 @@ export interface BrowserContext { */ on(event: 'page', listener: (page: Page) => 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 + * [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest). + * + * In order to intercept and mutate requests, see + * [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler) + * or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler). + */ + on(event: 'request', listener: (request: Request) => void): this; + + /** + * Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use + * [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed). + * + * > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + * complete with + * [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished) + * event and not with + * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed). + */ + on(event: 'requestfailed', listener: (request: Request) => void): this; + + /** + * Emitted when a request finishes successfully after downloading the response body. For a successful response, the + * sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular + * page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished). + */ + on(event: 'requestfinished', listener: (request: Request) => void): this; + + /** + * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + * is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + * [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse). + */ + on(event: 'response', listener: (response: Response) => void): this; + /** * > NOTE: Service workers are only supported on Chromium-based browsers. * @@ -4888,6 +4925,43 @@ export interface BrowserContext { */ once(event: 'page', listener: (page: Page) => 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 + * [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest). + * + * In order to intercept and mutate requests, see + * [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler) + * or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler). + */ + once(event: 'request', listener: (request: Request) => void): this; + + /** + * Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use + * [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed). + * + * > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + * complete with + * [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished) + * event and not with + * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed). + */ + once(event: 'requestfailed', listener: (request: Request) => void): this; + + /** + * Emitted when a request finishes successfully after downloading the response body. For a successful response, the + * sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular + * page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished). + */ + once(event: 'requestfinished', listener: (request: Request) => void): this; + + /** + * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + * is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + * [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse). + */ + once(event: 'response', listener: (response: Response) => void): this; + /** * > NOTE: Service workers are only supported on Chromium-based browsers. * @@ -4938,6 +5012,43 @@ export interface BrowserContext { */ addListener(event: 'page', listener: (page: Page) => 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 + * [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest). + * + * In order to intercept and mutate requests, see + * [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler) + * or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler). + */ + addListener(event: 'request', listener: (request: Request) => void): this; + + /** + * Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use + * [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed). + * + * > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + * complete with + * [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished) + * event and not with + * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed). + */ + addListener(event: 'requestfailed', listener: (request: Request) => void): this; + + /** + * Emitted when a request finishes successfully after downloading the response body. For a successful response, the + * sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular + * page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished). + */ + addListener(event: 'requestfinished', listener: (request: Request) => void): this; + + /** + * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + * is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + * [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse). + */ + addListener(event: 'response', listener: (response: Response) => void): this; + /** * > NOTE: Service workers are only supported on Chromium-based browsers. * @@ -4988,6 +5099,43 @@ export interface BrowserContext { */ removeListener(event: 'page', listener: (page: Page) => 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 + * [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest). + * + * In order to intercept and mutate requests, see + * [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler) + * or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler). + */ + removeListener(event: 'request', listener: (request: Request) => void): this; + + /** + * Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use + * [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed). + * + * > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + * complete with + * [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished) + * event and not with + * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed). + */ + removeListener(event: 'requestfailed', listener: (request: Request) => void): this; + + /** + * Emitted when a request finishes successfully after downloading the response body. For a successful response, the + * sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular + * page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished). + */ + removeListener(event: 'requestfinished', listener: (request: Request) => void): this; + + /** + * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + * is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + * [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse). + */ + removeListener(event: 'response', listener: (response: Response) => void): this; + /** * > NOTE: Service workers are only supported on Chromium-based browsers. * @@ -5038,6 +5186,43 @@ export interface BrowserContext { */ off(event: 'page', listener: (page: Page) => 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 + * [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest). + * + * In order to intercept and mutate requests, see + * [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler) + * or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler). + */ + off(event: 'request', listener: (request: Request) => void): this; + + /** + * Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use + * [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed). + * + * > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + * complete with + * [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished) + * event and not with + * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed). + */ + off(event: 'requestfailed', listener: (request: Request) => void): this; + + /** + * Emitted when a request finishes successfully after downloading the response body. For a successful response, the + * sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular + * page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished). + */ + off(event: 'requestfinished', listener: (request: Request) => void): this; + + /** + * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + * is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + * [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse). + */ + off(event: 'response', listener: (response: Response) => void): this; + /** * > NOTE: Service workers are only supported on Chromium-based browsers. * @@ -5509,6 +5694,43 @@ export interface BrowserContext { */ waitForEvent(event: 'page', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise, timeout?: number } | ((page: Page) => 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 + * [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest). + * + * In order to intercept and mutate requests, see + * [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler) + * or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler). + */ + waitForEvent(event: 'request', optionsOrPredicate?: { predicate?: (request: Request) => boolean | Promise, timeout?: number } | ((request: Request) => boolean | Promise)): Promise; + + /** + * Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use + * [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed). + * + * > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + * complete with + * [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished) + * event and not with + * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed). + */ + waitForEvent(event: 'requestfailed', optionsOrPredicate?: { predicate?: (request: Request) => boolean | Promise, timeout?: number } | ((request: Request) => boolean | Promise)): Promise; + + /** + * Emitted when a request finishes successfully after downloading the response body. For a successful response, the + * sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular + * page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished). + */ + waitForEvent(event: 'requestfinished', optionsOrPredicate?: { predicate?: (request: Request) => boolean | Promise, timeout?: number } | ((request: Request) => boolean | Promise)): Promise; + + /** + * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + * is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + * [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse). + */ + waitForEvent(event: 'response', optionsOrPredicate?: { predicate?: (response: Response) => boolean | Promise, timeout?: number } | ((response: Response) => boolean | Promise)): Promise; + /** * > NOTE: Service workers are only supported on Chromium-based browsers. *