diff --git a/docs/api.md b/docs/api.md index 7baa695299..2148a17c95 100644 --- a/docs/api.md +++ b/docs/api.md @@ -116,6 +116,7 @@ * [page.goForward([options])](#pagegoforwardoptions) * [page.goto(url[, options])](#pagegotourl-options) * [page.hover(selector[, options])](#pagehoverselector-options) + * [page.interception](#pageinterception) * [page.isClosed()](#pageisclosed) * [page.keyboard](#pagekeyboard) * [page.mainFrame()](#pagemainframe) @@ -133,7 +134,6 @@ * [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders) * [page.setJavaScriptEnabled(enabled)](#pagesetjavascriptenabledenabled) * [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled) - * [page.setRequestInterception(value)](#pagesetrequestinterceptionvalue) * [page.setUserAgent(userAgent)](#pagesetuseragentuseragent) * [page.setViewport(viewport)](#pagesetviewportviewport) * [page.target()](#pagetarget) @@ -233,6 +233,12 @@ * [executionContext.evaluate(pageFunction[, ...args])](#executioncontextevaluatepagefunction-args) * [executionContext.evaluateHandle(pageFunction[, ...args])](#executioncontextevaluatehandlepagefunction-args) * [executionContext.frame()](#executioncontextframe) +- [class: Interception](#class-interception) + * [interception.abort(request, [errorCode])](#interceptionabortrequest-errorcode) + * [interception.continue(request, [overrides])](#interceptioncontinuerequest-overrides) + * [interception.disable()](#interceptiondisable) + * [interception.enable()](#interceptionenable) + * [interception.fulfill(request, response)](#interceptionfulfillrequest-response) - [class: JSHandle](#class-jshandle) * [jsHandle.asElement()](#jshandleaselement) * [jsHandle.dispose()](#jshandledispose) @@ -272,8 +278,6 @@ * [elementHandle.type(text[, options])](#elementhandletypetext-options) * [elementHandle.uploadFile(...filePaths)](#elementhandleuploadfilefilepaths) - [class: Request](#class-request) - * [request.abort([errorCode])](#requestaborterrorcode) - * [request.continue([overrides])](#requestcontinueoverrides) * [request.failure()](#requestfailure) * [request.frame()](#requestframe) * [request.headers()](#requestheaders) @@ -282,7 +286,6 @@ * [request.postData()](#requestpostdata) * [request.redirectChain()](#requestredirectchain) * [request.resourceType()](#requestresourcetype) - * [request.respond(response)](#requestrespondresponse) * [request.response()](#requestresponse) * [request.url()](#requesturl) - [class: Response](#class-response) @@ -1035,7 +1038,7 @@ const [popup] = await Promise.all([ - <[Request]> Emitted when a page issues a request. The [request] object is read-only. -In order to intercept and mutate requests, see `page.setRequestInterception`. +In order to intercept and mutate requests, see `page.interception.enable()`. #### event: 'requestfailed' - <[Request]> @@ -1604,6 +1607,9 @@ If there's no element matching `selector`, the method throws an error. Shortcut for [page.mainFrame().hover(selector)](#framehoverselector). +#### page.interception +- returns: <[Interception]> + #### page.isClosed() - returns: <[boolean]> @@ -1763,36 +1769,6 @@ The extra HTTP headers will be sent with every request the page initiates. - `enabled` <[boolean]> When `true`, enables offline mode for the page. - returns: <[Promise]> -#### page.setRequestInterception(value) -- `value` <[boolean]> Whether to enable request interception. -- returns: <[Promise]> - -Activating request interception enables `request.abort`, `request.continue` and -`request.respond` methods. This provides the capability to modify network requests that are made by a page. - -Once request interception is enabled, every request will stall unless it's continued, responded or aborted. -An example of a naïve request interceptor that aborts all image requests: - -```js -const playwright = require('playwright'); - -(async () => { - const browser = await playwright.launch(); - const page = await browser.newPage(); - await page.setRequestInterception(true); - page.on('request', interceptedRequest => { - if (interceptedRequest.url().endsWith('.png') || interceptedRequest.url().endsWith('.jpg')) - interceptedRequest.abort(); - else - interceptedRequest.continue(); - }); - await page.goto('https://example.com'); - await browser.close(); -})(); -``` - -> **NOTE** Enabling request interception disables page caching. - #### page.setUserAgent(userAgent) - `userAgent` <[string]> Specific user agent to use in this page - returns: <[Promise]> Promise which resolves when the user agent is set. @@ -3150,6 +3126,115 @@ await resultHandle.dispose(); > **NOTE** Not every execution context is associated with a frame. For example, workers and extensions have execution contexts that are not associated with frames. +### class: Interception + +#### interception.abort(request, [errorCode]) +- `request` <[Request]> +- `errorCode` <[string]> Optional error code. Defaults to `failed`, could be + one of the following: + - `aborted` - An operation was aborted (due to user action) + - `accessdenied` - Permission to access a resource, other than the network, was denied + - `addressunreachable` - The IP address is unreachable. This usually means + that there is no route to the specified host or network. + - `blockedbyclient` - The client chose to block the request. + - `blockedbyresponse` - The request failed because the response was delivered along with requirements which are not met ('X-Frame-Options' and 'Content-Security-Policy' ancestor checks, for instance). + - `connectionaborted` - A connection timed out as a result of not receiving an ACK for data sent. + - `connectionclosed` - A connection was closed (corresponding to a TCP FIN). + - `connectionfailed` - A connection attempt failed. + - `connectionrefused` - A connection attempt was refused. + - `connectionreset` - A connection was reset (corresponding to a TCP RST). + - `internetdisconnected` - The Internet connection has been lost. + - `namenotresolved` - The host name could not be resolved. + - `timedout` - An operation timed out. + - `failed` - A generic failure occurred. +- returns: <[Promise]> + +Aborts request. To use this, request interception should be enabled with `page.interception.enable()`. +Exception is immediately thrown if the request interception is not enabled. + +#### interception.continue(request, [overrides]) +- `request` <[Request]> +- `overrides` <[Object]> Optional request overwrites, which can be one of the following: + - `url` <[string]> If set, the request url will be changed. This is not a redirect. The request will be silently forwarded to the new url. For example, the address bar will show the original url. + - `method` <[string]> If set changes the request method (e.g. `GET` or `POST`) + - `postData` <[string]> If set changes the post data of request + - `headers` <[Object]> If set changes the request HTTP headers. Header values will be converted to a string. +- returns: <[Promise]> + +Continues request with optional request overrides. To use this, request interception should be enabled with `page.interception.enable()`. +Exception is immediately thrown if the request interception is not enabled. + +```js +await page.interception.enable(); +page.on('request', request => { + // Override headers + const headers = Object.assign({}, request.headers(), { + foo: 'bar', // set "foo" header + origin: undefined, // remove "origin" header + }); + page.interception.continue(request, {headers}); +}); +``` + +#### interception.disable() + +Disables network request interception. + +#### interception.enable() + +Once request interception is enabled, every request will stall unless it's continued, responded or aborted. +An example of a naïve request interceptor that aborts all image requests: + +```js +const playwright = require('playwright'); + +(async () => { + const browser = await playwright.launch(); + const page = await browser.newPage(); + await page.interception.enable(); + page.on('request', interceptedRequest => { + if (interceptedRequest.url().endsWith('.png') || interceptedRequest.url().endsWith('.jpg')) + page.interception.abort(interceptedRequest); + else + page.interception.continue(interceptedRequest); + }); + await page.goto('https://example.com'); + await browser.close(); +})(); +``` + +> **NOTE** Enabling request interception disables page caching. + +#### interception.fulfill(request, response) +- `request` <[Request]> +- `response` <[Object]> Response that will fulfill this request + - `status` <[number]> Response status code, defaults to `200`. + - `headers` <[Object]> Optional response headers. Header values will be converted to a string. + - `contentType` <[string]> If set, equals to setting `Content-Type` response header + - `body` <[string]|[Buffer]> Optional response body +- returns: <[Promise]> + +Fulfills request with given response. To use this, request interception should +be enabled with `page.interception.enable()`. Exception is thrown if +request interception is not enabled. + +An example of fulfilling all requests with 404 responses: + +```js +await page.interception.enable(); +page.on('request', request => { + page.interception.respond(request, { + status: 404, + contentType: 'text/plain', + body: 'Not Found!' + }); +}); +``` + +> **NOTE** Mocking responses for dataURL requests is not supported. +> Calling `request.respond` for a dataURL request is a noop. + + ### class: JSHandle JSHandle represents an in-page JavaScript object. JSHandles can be created with the [page.evaluateHandle](#pageevaluatehandlepagefunction-args) method. @@ -3544,52 +3629,6 @@ If request fails at some point, then instead of `'requestfinished'` event (and p If request gets a 'redirect' response, the request is successfully finished with the 'requestfinished' event, and a new request is issued to a redirected url. -#### request.abort([errorCode]) -- `errorCode` <[string]> Optional error code. Defaults to `failed`, could be - one of the following: - - `aborted` - An operation was aborted (due to user action) - - `accessdenied` - Permission to access a resource, other than the network, was denied - - `addressunreachable` - The IP address is unreachable. This usually means - that there is no route to the specified host or network. - - `blockedbyclient` - The client chose to block the request. - - `blockedbyresponse` - The request failed because the response was delivered along with requirements which are not met ('X-Frame-Options' and 'Content-Security-Policy' ancestor checks, for instance). - - `connectionaborted` - A connection timed out as a result of not receiving an ACK for data sent. - - `connectionclosed` - A connection was closed (corresponding to a TCP FIN). - - `connectionfailed` - A connection attempt failed. - - `connectionrefused` - A connection attempt was refused. - - `connectionreset` - A connection was reset (corresponding to a TCP RST). - - `internetdisconnected` - The Internet connection has been lost. - - `namenotresolved` - The host name could not be resolved. - - `timedout` - An operation timed out. - - `failed` - A generic failure occurred. -- returns: <[Promise]> - -Aborts request. To use this, request interception should be enabled with `page.setRequestInterception`. -Exception is immediately thrown if the request interception is not enabled. - -#### request.continue([overrides]) -- `overrides` <[Object]> Optional request overwrites, which can be one of the following: - - `url` <[string]> If set, the request url will be changed. This is not a redirect. The request will be silently forwarded to the new url. For example, the address bar will show the original url. - - `method` <[string]> If set changes the request method (e.g. `GET` or `POST`) - - `postData` <[string]> If set changes the post data of request - - `headers` <[Object]> If set changes the request HTTP headers. Header values will be converted to a string. -- returns: <[Promise]> - -Continues request with optional request overrides. To use this, request interception should be enabled with `page.setRequestInterception`. -Exception is immediately thrown if the request interception is not enabled. - -```js -await page.setRequestInterception(true); -page.on('request', request => { - // Override headers - const headers = Object.assign({}, request.headers(), { - foo: 'bar', // set "foo" header - origin: undefined, // remove "origin" header - }); - request.continue({headers}); -}); -``` - #### request.failure() - returns: Object describing request failure, if any - `errorText` <[string]> Human-readable error message, e.g. `'net::ERR_FAILED'`. @@ -3655,34 +3694,6 @@ console.log(chain.length); // 0 Contains the request's resource type as it was perceived by the rendering engine. ResourceType will be one of the following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`, `manifest`, `other`. -#### request.respond(response) -- `response` <[Object]> Response that will fulfill this request - - `status` <[number]> Response status code, defaults to `200`. - - `headers` <[Object]> Optional response headers. Header values will be converted to a string. - - `contentType` <[string]> If set, equals to setting `Content-Type` response header - - `body` <[string]|[Buffer]> Optional response body -- returns: <[Promise]> - -Fulfills request with given response. To use this, request interception should -be enabled with `page.setRequestInterception`. Exception is thrown if -request interception is not enabled. - -An example of fulfilling all requests with 404 responses: - -```js -await page.setRequestInterception(true); -page.on('request', request => { - request.respond({ - status: 404, - contentType: 'text/plain', - body: 'Not Found!' - }); -}); -``` - -> **NOTE** Mocking responses for dataURL requests is not supported. -> Calling `request.respond` for a dataURL request is a noop. - #### request.response() - returns: A matching [Response] object, or `null` if the response has not been received yet. diff --git a/examples/block-images.js b/examples/block-images.js index 6331c1dc15..474c001c7a 100644 --- a/examples/block-images.js +++ b/examples/block-images.js @@ -21,7 +21,7 @@ const playwright = require('playwright'); (async() => { const browser = await playwright.launch(); const page = await browser.newPage(); - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { if (request.resourceType() === 'image') request.abort(); diff --git a/src/chromium/NetworkManager.ts b/src/chromium/NetworkManager.ts index 5c87f2676b..7d742d8ae4 100644 --- a/src/chromium/NetworkManager.ts +++ b/src/chromium/NetworkManager.ts @@ -334,7 +334,7 @@ export class Request { }; } - async continue(overrides: { url?: string; method?: string; postData?: string; headers?: {[key: string]: string}; } = {}) { + async _continue(overrides: { url?: string; method?: string; postData?: string; headers?: {[key: string]: string}; } = {}) { // Request interception is not supported for data: urls. if (this._url.startsWith('data:')) return; @@ -360,7 +360,7 @@ export class Request { }); } - async respond(response: { status: number; headers: {[key: string]: string}; contentType: string; body: (string | Buffer); }) { + async _fulfill(response: { status: number; headers: {[key: string]: string}; contentType: string; body: (string | Buffer); }) { // Mocking responses for dataURL requests is not currently supported. if (this._url.startsWith('data:')) return; @@ -393,7 +393,7 @@ export class Request { }); } - async abort(errorCode: string = 'failed') { + async _abort(errorCode: string = 'failed') { // Request interception is not supported for data: urls. if (this._url.startsWith('data:')) return; diff --git a/src/chromium/Page.ts b/src/chromium/Page.ts index 545178a3cb..bacfddb976 100644 --- a/src/chromium/Page.ts +++ b/src/chromium/Page.ts @@ -42,6 +42,7 @@ import { TaskQueue } from './TaskQueue'; import { Geolocation } from './features/geolocation'; import { Tracing } from './features/tracing'; import { Workers } from './features/workers'; +import { Interception } from './features/interception'; const writeFileAsync = helper.promisify(fs.writeFile); @@ -66,6 +67,7 @@ export class Page extends EventEmitter { readonly accessibility: Accessibility; readonly coverage: Coverage; readonly geolocation: Geolocation; + readonly interception: Interception; readonly pdf: PDF; readonly workers: Workers; readonly tracing: Tracing; @@ -100,6 +102,7 @@ export class Page extends EventEmitter { this.pdf = new PDF(client); this.workers = new Workers(client, this._addConsoleMessage.bind(this), this._handleException.bind(this)); this.geolocation = new Geolocation(client); + this.interception = new Interception(this._frameManager.networkManager()); this._screenshotTaskQueue = screenshotTaskQueue; @@ -213,10 +216,6 @@ export class Page extends EventEmitter { return this._frameManager.frames(); } - async setRequestInterception(value: boolean) { - return this._frameManager.networkManager().setRequestInterception(value); - } - setOfflineMode(enabled: boolean) { return this._frameManager.networkManager().setOfflineMode(enabled); } diff --git a/src/chromium/api.ts b/src/chromium/api.ts index 9e8d62c9c6..9372c16ce7 100644 --- a/src/chromium/api.ts +++ b/src/chromium/api.ts @@ -11,6 +11,7 @@ export { ExecutionContext } from './ExecutionContext'; export { Accessibility } from './features/accessibility'; export { Coverage } from './features/coverage'; export { Geolocation } from './features/geolocation'; +export { Interception } from './features/interception'; export { PDF } from './features/pdf'; export { Permissions } from './features/permissions'; export { Tracing } from './features/tracing'; @@ -22,3 +23,4 @@ export { Request, Response } from './NetworkManager'; export { ConsoleMessage, FileChooser, Page } from './Page'; export { Playwright } from './Playwright'; export { Target } from './Target'; + diff --git a/src/chromium/features/interception.ts b/src/chromium/features/interception.ts new file mode 100644 index 0000000000..ed932ee888 --- /dev/null +++ b/src/chromium/features/interception.ts @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { NetworkManager, Request } from '../NetworkManager'; + +export class Interception { + private _networkManager: NetworkManager; + + constructor(networkManager: NetworkManager) { + this._networkManager = networkManager; + } + + enable() { + this._networkManager.setRequestInterception(true); + } + + disable() { + this._networkManager.setRequestInterception(false); + } + + async continue(request: Request, overrides: { url?: string; method?: string; postData?: string; headers?: {[key: string]: string}; } = {}) { + return request._continue(overrides); + } + + async fulfill(request: Request, response: { status: number; headers: {[key: string]: string}; contentType: string; body: (string | Buffer); }) { + return request._fulfill(response); + } + + async abort(request: Request, errorCode: string = 'failed') { + return request._abort(errorCode); + } +} diff --git a/src/firefox/NetworkManager.ts b/src/firefox/NetworkManager.ts index 13fb242e2d..aab635dc6f 100644 --- a/src/firefox/NetworkManager.ts +++ b/src/firefox/NetworkManager.ts @@ -169,7 +169,7 @@ export class Request { return this._errorText ? {errorText: this._errorText} : null; } - async continue(overrides: any = {}) { + async _continue(overrides: any = {}) { assert(!overrides.url, 'Playwright-Firefox does not support overriding URL'); assert(!overrides.method, 'Playwright-Firefox does not support overriding method'); assert(!overrides.postData, 'Playwright-Firefox does not support overriding postData'); @@ -187,7 +187,7 @@ export class Request { }); } - async abort() { + async _abort() { assert(this._suspended, 'Request Interception is not enabled!'); assert(!this._interceptionHandled, 'Request is already handled!'); this._interceptionHandled = true; diff --git a/src/firefox/Page.ts b/src/firefox/Page.ts index 6da8f3d595..5fd33a151c 100644 --- a/src/firefox/Page.ts +++ b/src/firefox/Page.ts @@ -9,6 +9,7 @@ import { Connection, JugglerSession, JugglerSessionEvents } from './Connection'; import { Dialog } from './Dialog'; import { Events } from './events'; import { Accessibility } from './features/accessibility'; +import { Interception } from './features/interception'; import { FrameManager, FrameManagerEvents, normalizeWaitUntil } from './FrameManager'; import { Keyboard, Mouse } from './Input'; import { createHandle, ElementHandle, JSHandle } from './JSHandle'; @@ -25,6 +26,7 @@ export class Page extends EventEmitter { private _keyboard: Keyboard; private _mouse: Mouse; readonly accessibility: Accessibility; + readonly interception: Interception; private _closed: boolean; private _pageBindings: Map; private _networkManager: NetworkManager; @@ -59,6 +61,7 @@ export class Page extends EventEmitter { this._networkManager = new NetworkManager(session); this._frameManager = new FrameManager(session, this, this._networkManager, this._timeoutSettings); this._networkManager.setFrameManager(this._frameManager); + this.interception = new Interception(this._networkManager); this._eventListeners = [ helper.addEventListener(this._session, 'Page.uncaughtError', this._onUncaughtError.bind(this)), helper.addEventListener(this._session, 'Runtime.console', this._onConsole.bind(this)), @@ -137,10 +140,6 @@ export class Page extends EventEmitter { } } - async setRequestInterception(enabled) { - await this._networkManager.setRequestInterception(enabled); - } - async setExtraHTTPHeaders(headers) { await this._networkManager.setExtraHTTPHeaders(headers); } diff --git a/src/firefox/api.ts b/src/firefox/api.ts index 0266967ece..a7cfabc52a 100644 --- a/src/firefox/api.ts +++ b/src/firefox/api.ts @@ -7,6 +7,7 @@ export { BrowserFetcher } from './BrowserFetcher'; export { Dialog } from './Dialog'; export { ExecutionContext } from './ExecutionContext'; export { Accessibility } from './features/accessibility'; +export { Interception } from './features/interception'; export { Permissions } from './features/permissions'; export { Frame } from './FrameManager'; export { Keyboard, Mouse } from './Input'; @@ -14,3 +15,4 @@ export { ElementHandle, JSHandle } from './JSHandle'; export { Request, Response } from './NetworkManager'; export { ConsoleMessage, Page } from './Page'; export { Playwright } from './Playwright'; + diff --git a/src/firefox/features/interception.ts b/src/firefox/features/interception.ts new file mode 100644 index 0000000000..ffcdab1277 --- /dev/null +++ b/src/firefox/features/interception.ts @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { NetworkManager, Request } from '../NetworkManager'; + +export class Interception { + private _networkManager: NetworkManager; + + constructor(networkManager: NetworkManager) { + this._networkManager = networkManager; + } + + enable() { + this._networkManager.setRequestInterception(true); + } + + disable() { + this._networkManager.setRequestInterception(false); + } + + async continue(request: Request, overrides: { url?: string; method?: string; postData?: string; headers?: {[key: string]: string}; } = {}) { + return request._continue(overrides); + } + + async fulfill(request: Request, response: { status: number; headers: {[key: string]: string}; contentType: string; body: (string | Buffer); }) { + throw new Error('Not implemented'); + } + + async abort(request: Request, errorCode: string = 'failed') { + return request._abort(); + } +} diff --git a/test/chromiumonly.spec.js b/test/chromiumonly.spec.js index 8d80b4a097..92111a1e1c 100644 --- a/test/chromiumonly.spec.js +++ b/test/chromiumonly.spec.js @@ -132,8 +132,8 @@ module.exports.addPageTests = function({testRunner, expect}) { res.end('console.log(1);'); }); - await page.setRequestInterception(true); - page.on('request', request => request.continue()); + await page.interception.enable(); + page.on('request', request => page.interception.continue(request)); await page.goto(server.PREFIX + '/intervention'); // Check for feature URL substring rather than https://www.chromestatus.com to // make it work with Edgium. diff --git a/test/headful.spec.js b/test/headful.spec.js index 61ab37c602..a091199766 100644 --- a/test/headful.spec.js +++ b/test/headful.spec.js @@ -93,7 +93,7 @@ module.exports.addTests = function({testRunner, expect, playwright, defaultBrows const browser = await playwright.launch(headfulOptions); const page = await browser.newPage(); await page.goto(server.EMPTY_PAGE); - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', r => r.respond({body: 'YO, GOOGLE.COM'})); await page.evaluate(() => { const frame = document.createElement('iframe'); diff --git a/test/ignorehttpserrors.spec.js b/test/ignorehttpserrors.spec.js index 0dec0f178f..7a37e4e069 100644 --- a/test/ignorehttpserrors.spec.js +++ b/test/ignorehttpserrors.spec.js @@ -43,7 +43,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p expect(response.ok()).toBe(true); }); it('should work with request interception', async({page, server, httpsServer}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => request.continue()); const response = await page.goto(httpsServer.EMPTY_PAGE); expect(response.status()).toBe(200); diff --git a/test/network.spec.js b/test/network.spec.js index 5eb6813e74..fbd86a09e3 100644 --- a/test/network.spec.js +++ b/test/network.spec.js @@ -235,7 +235,7 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { // FIXME: requires request interception. it.skip(WEBKIT)('Page.Events.RequestFailed', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { if (request.url().endsWith('css')) request.abort(); @@ -318,7 +318,7 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { requests.set(request.url().split('/').pop(), request); request.continue(); }); - await page.setRequestInterception(true); + await page.interception.enable(); server.setRedirect('/rrredirect', '/frames/one-frame.html'); await page.goto(server.PREFIX + '/rrredirect'); expect(requests.get('rrredirect').isNavigationRequest()).toBe(true); diff --git a/test/oopif.spec.js b/test/oopif.spec.js index 0bc5496799..372b9b425f 100644 --- a/test/oopif.spec.js +++ b/test/oopif.spec.js @@ -44,7 +44,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p expect(page.frames().length).toBe(2); }); it('should load oopif iframes with subresources and request interception', async function({page, server, context}) { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => request.continue()); await page.goto(server.PREFIX + '/dynamic-oopif.html'); expect(oopifs(context).length).toBe(1); diff --git a/test/page.spec.js b/test/page.spec.js index fb01ffd104..4a800a634f 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -864,8 +864,8 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF }); it.skip(WEBKIT)('should stay disabled when toggling request interception on/off', async({page, server}) => { await page.setCacheEnabled(false); - await page.setRequestInterception(true); - await page.setRequestInterception(false); + await page.interception.enable(); + await page.interception.disable(); await page.goto(server.PREFIX + '/cached/one-style.html'); const [nonCachedRequest] = await Promise.all([ diff --git a/test/requestinterception.spec.js b/test/requestinterception.spec.js index 77bf3923de..22c10e3f4c 100644 --- a/test/requestinterception.spec.js +++ b/test/requestinterception.spec.js @@ -23,12 +23,12 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - describe.skip(WEBKIT)('Page.setRequestInterception', function() { + describe.skip(WEBKIT)('Interception.enable', function() { it('should intercept', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { if (utils.isFavicon(request)) { - request.continue(); + page.interception.continue(request); return; } expect(request.url()).toContain('empty.html'); @@ -39,7 +39,7 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(request.resourceType()).toBe('document'); expect(request.frame() === page.mainFrame()).toBe(true); expect(request.frame().url()).toBe('about:blank'); - request.continue(); + page.interception.continue(request); }); const response = await page.goto(server.EMPTY_PAGE); expect(response.ok()).toBe(true); @@ -48,8 +48,8 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { it('should work when POST is redirected with 302', async({page, server}) => { server.setRedirect('/rredirect', '/empty.html'); await page.goto(server.EMPTY_PAGE); - await page.setRequestInterception(true); - page.on('request', request => request.continue()); + await page.interception.enable(); + page.on('request', request => page.interception.continue(request)); await page.setContent(`
@@ -63,24 +63,24 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { // @see https://github.com/GoogleChrome/puppeteer/issues/3973 it('should work when header manipulation headers with redirect', async({page, server}) => { server.setRedirect('/rrredirect', '/empty.html'); - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { const headers = Object.assign({}, request.headers(), { foo: 'bar' }); - request.continue({ headers }); + page.interception.continue(request, { headers }); }); await page.goto(server.PREFIX + '/rrredirect'); }); // @see https://github.com/GoogleChrome/puppeteer/issues/4743 it('should be able to remove headers', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { const headers = Object.assign({}, request.headers(), { foo: 'bar', origin: undefined, // remove "origin" header }); - request.continue({ headers }); + page.interception.continue(request, { headers }); }); const [serverRequest] = await Promise.all([ @@ -91,12 +91,12 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(serverRequest.headers.origin).toBe(undefined); }); it('should contain referer header', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); const requests = []; page.on('request', request => { if (!utils.isFavicon(request)) requests.push(request); - request.continue(); + page.interception.continue(request); }); await page.goto(server.PREFIX + '/one-style.html'); expect(requests[1].url()).toContain('/one-style.css'); @@ -108,26 +108,26 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { await page.setCookie({ name: 'foo', value: 'bar'}); // Setup request interception. - await page.setRequestInterception(true); - page.on('request', request => request.continue()); + await page.interception.enable(); + page.on('request', request => page.interception.continue(request)); const response = await page.reload(); expect(response.status()).toBe(200); }); it('should stop intercepting', async({page, server}) => { - await page.setRequestInterception(true); - page.once('request', request => request.continue()); + await page.interception.enable(); + page.once('request', request => page.interception.continue(request)); await page.goto(server.EMPTY_PAGE); - await page.setRequestInterception(false); + await page.interception.disable(); await page.goto(server.EMPTY_PAGE); }); it('should show custom HTTP headers', async({page, server}) => { await page.setExtraHTTPHeaders({ foo: 'bar' }); - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { expect(request.headers()['foo']).toBe('bar'); - request.continue(); + page.interception.continue(request); }); const response = await page.goto(server.EMPTY_PAGE); expect(response.ok()).toBe(true); @@ -136,8 +136,8 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { it('should work with redirect inside sync XHR', async({page, server}) => { await page.goto(server.EMPTY_PAGE); server.setRedirect('/logo.png', '/pptr.png'); - await page.setRequestInterception(true); - page.on('request', request => request.continue()); + await page.interception.enable(); + page.on('request', request => page.interception.continue(request)); const status = await page.evaluate(async() => { const request = new XMLHttpRequest(); request.open('GET', '/logo.png', false); // `false` makes the request synchronous @@ -148,21 +148,21 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { }); it('should work with custom referer headers', async({page, server}) => { await page.setExtraHTTPHeaders({ 'referer': server.EMPTY_PAGE }); - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { expect(request.headers()['referer']).toBe(server.EMPTY_PAGE); - request.continue(); + page.interception.continue(request); }); const response = await page.goto(server.EMPTY_PAGE); expect(response.ok()).toBe(true); }); it('should be abortable', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { if (request.url().endsWith('.css')) - request.abort(); + page.interception.abort(request); else - request.continue(); + page.interception.continue(request); }); let failedRequests = 0; page.on('requestfailed', event => ++failedRequests); @@ -172,9 +172,9 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(failedRequests).toBe(1); }); it.skip(FFOX)('should be abortable with custom error codes', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { - request.abort('internetdisconnected'); + page.interception.abort(request, 'internetdisconnected'); }); let failedRequest = null; page.on('requestfailed', request => failedRequest = request); @@ -186,8 +186,8 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { await page.setExtraHTTPHeaders({ referer: 'http://google.com/' }); - await page.setRequestInterception(true); - page.on('request', request => request.continue()); + await page.interception.enable(); + page.on('request', request => page.interception.continue(request)); const [request] = await Promise.all([ server.waitForRequest('/grid.html'), page.goto(server.PREFIX + '/grid.html'), @@ -195,8 +195,8 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(request.headers['referer']).toBe('http://google.com/'); }); it('should fail navigation when aborting main resource', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => request.abort()); + await page.interception.enable(); + page.on('request', request => page.interception.abort(request)); let error = null; await page.goto(server.EMPTY_PAGE).catch(e => error = e); expect(error).toBeTruthy(); @@ -206,10 +206,10 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(error.message).toContain('NS_ERROR_FAILURE'); }); it('should work with redirects', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); const requests = []; page.on('request', request => { - request.continue(); + page.interception.continue(request); requests.push(request); }); server.setRedirect('/non-existing-page.html', '/non-existing-page-2.html'); @@ -233,10 +233,10 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { } }); it('should work with redirects for subresources', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); const requests = []; page.on('request', request => { - request.continue(); + page.interception.continue(request); if (!utils.isFavicon(request)) requests.push(request); }); @@ -258,14 +258,14 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(redirectChain[2].url()).toContain('/three-style.css'); }); it('should be able to abort redirects', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); server.setRedirect('/non-existing.json', '/non-existing-2.json'); server.setRedirect('/non-existing-2.json', '/simple.html'); page.on('request', request => { if (request.url().includes('non-existing-2')) - request.abort(); + page.interception.abort(request); else - request.continue(); + page.interception.continue(request); }); await page.goto(server.EMPTY_PAGE); const result = await page.evaluate(async() => { @@ -284,16 +284,16 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { await page.goto(server.EMPTY_PAGE); let responseCount = 1; server.setRoute('/zzz', (req, res) => res.end((responseCount++) * 11 + '')); - await page.setRequestInterception(true); + await page.interception.enable(); let spinner = false; // Cancel 2nd request. page.on('request', request => { if (utils.isFavicon(request)) { - request.continue(); + page.interception.continue(request); return; } - spinner ? request.abort() : request.continue(); + spinner ? page.interception.abort(request) : page.interception.continue(request); spinner = !spinner; }); const results = await page.evaluate(() => Promise.all([ @@ -304,11 +304,11 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(results).toEqual(['11', 'FAILED', '22']); }); it.skip(FFOX)('should navigate to dataURL and fire dataURL requests', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); const requests = []; page.on('request', request => { requests.push(request); - request.continue(); + page.interception.continue(request); }); const dataURL = 'data:text/html,
yo
'; const response = await page.goto(dataURL); @@ -318,11 +318,11 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { }); it.skip(FFOX)('should be able to fetch dataURL and fire dataURL requests', async({page, server}) => { await page.goto(server.EMPTY_PAGE); - await page.setRequestInterception(true); + await page.interception.enable(); const requests = []; page.on('request', request => { requests.push(request); - request.continue(); + page.interception.continue(request); }); const dataURL = 'data:text/html,
yo
'; const text = await page.evaluate(url => fetch(url).then(r => r.text()), dataURL); @@ -331,11 +331,11 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(requests[0].url()).toBe(dataURL); }); it.skip(FFOX)('should navigate to URL with hash and and fire requests without hash', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); const requests = []; page.on('request', request => { requests.push(request); - request.continue(); + page.interception.continue(request); }); const response = await page.goto(server.EMPTY_PAGE + '#hash'); expect(response.status()).toBe(200); @@ -346,25 +346,25 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { it('should work with encoded server', async({page, server}) => { // The requestWillBeSent will report encoded URL, whereas interception will // report URL as-is. @see crbug.com/759388 - await page.setRequestInterception(true); - page.on('request', request => request.continue()); + await page.interception.enable(); + page.on('request', request => page.interception.continue(request)); const response = await page.goto(server.PREFIX + '/some nonexisting page'); expect(response.status()).toBe(404); }); it('should work with badly encoded server', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); server.setRoute('/malformed?rnd=%911', (req, res) => res.end()); - page.on('request', request => request.continue()); + page.on('request', request => page.interception.continue(request)); const response = await page.goto(server.PREFIX + '/malformed?rnd=%911'); expect(response.status()).toBe(200); }); it.skip(FFOX)('should work with encoded server - 2', async({page, server}) => { // The requestWillBeSent will report URL as-is, whereas interception will // report encoded URL for stylesheet. @see crbug.com/759388 - await page.setRequestInterception(true); + await page.interception.enable(); const requests = []; page.on('request', request => { - request.continue(); + page.interception.continue(request); requests.push(request); }); const response = await page.goto(`data:text/html,`); @@ -374,7 +374,7 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { }); it.skip(FFOX)('should not throw "Invalid Interception Id" if the request was cancelled', async({page, server}) => { await page.setContent(''); - await page.setRequestInterception(true); + await page.interception.enable(); let request = null; page.on('request', async r => request = r); page.$eval('iframe', (frame, url) => frame.src = url, server.EMPTY_PAGE), @@ -383,14 +383,14 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { // Delete frame to cause request to be canceled. await page.$eval('iframe', frame => frame.remove()); let error = null; - await request.continue().catch(e => error = e); + await page.interception.continue(request).catch(e => error = e); expect(error).toBe(null); }); it('should throw if interception is not enabled', async({page, server}) => { let error = null; page.on('request', async request => { try { - await request.continue(); + await page.interception.continue(request); } catch (e) { error = e; } @@ -399,11 +399,11 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(error.message).toContain('Request Interception is not enabled'); }); it.skip(FFOX)('should work with file URLs', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); const urls = new Set(); page.on('request', request => { urls.add(request.url().split('/').pop()); - request.continue(); + page.interception.continue(request); }); await page.goto(pathToFileURL(path.join(__dirname, 'assets', 'one-style.html'))); expect(urls.size).toBe(2); @@ -412,18 +412,18 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { }); }); - describe.skip(WEBKIT)('Request.continue', function() { + describe.skip(WEBKIT)('Interception.continue', function() { it('should work', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => request.continue()); + await page.interception.enable(); + page.on('request', request => page.interception.continue(request)); await page.goto(server.EMPTY_PAGE); }); it('should amend HTTP headers', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { const headers = Object.assign({}, request.headers()); headers['FOO'] = 'bar'; - request.continue({ headers }); + page.interception.continue(request, { headers }); }); await page.goto(server.EMPTY_PAGE); const [request] = await Promise.all([ @@ -433,10 +433,10 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(request.headers['foo']).toBe('bar'); }); it.skip(FFOX)('should redirect in a way non-observable to page', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { const redirectURL = request.url().includes('/empty.html') ? server.PREFIX + '/consolelog.html' : undefined; - request.continue({ url: redirectURL }); + page.interception.continue(request, { url: redirectURL }); }); let consoleMessage = null; page.on('console', msg => consoleMessage = msg); @@ -447,9 +447,9 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { it.skip(FFOX)('should amend method', async({page, server}) => { await page.goto(server.EMPTY_PAGE); - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { - request.continue({ method: 'POST' }); + page.interception.continue(request, { method: 'POST' }); }); const [request] = await Promise.all([ server.waitForRequest('/sleep.zzz'), @@ -460,9 +460,9 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { it.skip(FFOX)('should amend post data', async({page, server}) => { await page.goto(server.EMPTY_PAGE); - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { - request.continue({ postData: 'doggo' }); + page.interception.continue(request, { postData: 'doggo' }); }); const [serverRequest] = await Promise.all([ server.waitForRequest('/sleep.zzz'), @@ -471,9 +471,9 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(await serverRequest.postBody).toBe('doggo'); }); it.skip(FFOX)('should amend both post data and method on navigation', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { - request.continue({ method: 'POST', postData: 'doggo' }); + page.interception.continue(request, { method: 'POST', postData: 'doggo' }); }); const [serverRequest] = await Promise.all([ server.waitForRequest('/empty.html'), @@ -484,11 +484,11 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { }); }); - describe.skip(FFOX || WEBKIT)('Request.respond', function() { + describe.skip(FFOX || WEBKIT)('interception.fulfill', function() { it('should work', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { - request.respond({ + page.interception.fulfill(request, { status: 201, headers: { foo: 'bar' @@ -502,9 +502,9 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); }); it('should work with status code 422', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { - request.respond({ + page.interception.fulfill(request, { status: 422, body: 'Yo, page!' }); @@ -515,13 +515,13 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); }); it('should redirect', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { if (!request.url().includes('rrredirect')) { - request.continue(); + page.interception.continue(request); return; } - request.respond({ + page.interception.fulfill(request, { status: 302, headers: { location: server.EMPTY_PAGE, @@ -534,10 +534,10 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(response.url()).toBe(server.EMPTY_PAGE); }); it('should allow mocking binary responses', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); - request.respond({ + page.interception.fulfill(request, { contentType: 'image/png', body: imageBuffer }); @@ -552,9 +552,9 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); }); it('should stringify intercepted request response headers', async({page, server}) => { - await page.setRequestInterception(true); + await page.interception.enable(); page.on('request', request => { - request.respond({ + page.interception.fulfill(request, { status: 200, headers: { 'foo': true diff --git a/utils/doclint/generate_types/test/test.ts b/utils/doclint/generate_types/test/test.ts index 26da42d1d0..6ad3f30e03 100644 --- a/utils/doclint/generate_types/test/test.ts +++ b/utils/doclint/generate_types/test/test.ts @@ -99,7 +99,7 @@ playwright.launch().then(async browser => { await page.emulateMedia("screen"); await page.pdf({ path: "page.pdf" }); - await page.setRequestInterception(true); + await page.interception.enable(); page.on("request", interceptedRequest => { if ( interceptedRequest.url().endsWith(".png") ||