mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-02 00:53:32 +03:00
api(route): pass Route object instead of Request to route handlers (#1385)
References #1348.
This commit is contained in:
parent
26479119b6
commit
69be12ae12
@ -101,7 +101,7 @@ const { firefox } = require('playwright');
|
||||
|
||||
#### Intercept network requests
|
||||
|
||||
This code snippet sets up network interception for a WebKit page to log all network requests.
|
||||
This code snippet sets up request routing for a WebKit page to log all network requests.
|
||||
|
||||
```js
|
||||
const { webkit } = require('playwright');
|
||||
@ -112,9 +112,9 @@ const { webkit } = require('playwright');
|
||||
const page = await context.newPage();
|
||||
|
||||
// Log and continue all network requests
|
||||
page.route('**', request => {
|
||||
console.log(request.url());
|
||||
request.continue();
|
||||
page.route('**', route => {
|
||||
console.log(route.request().url());
|
||||
route.continue();
|
||||
});
|
||||
|
||||
await page.goto('http://todomvc.com');
|
||||
|
201
docs/api.md
201
docs/api.md
@ -20,6 +20,7 @@
|
||||
- [class: Request](#class-request)
|
||||
- [class: Response](#class-response)
|
||||
- [class: Selectors](#class-selectors)
|
||||
- [class: Route](#class-route)
|
||||
- [class: TimeoutError](#class-timeouterror)
|
||||
- [class: Accessibility](#class-accessibility)
|
||||
- [class: Worker](#class-worker)
|
||||
@ -456,17 +457,17 @@ Creates a new page in the browser context.
|
||||
|
||||
#### browserContext.route(url, handler)
|
||||
- `url` <[string]|[RegExp]|[function]\([string]\):[boolean]> A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
- `handler` <[function]\([Request]\)> handler function to route the request.
|
||||
- returns: <[Promise]>.
|
||||
- `handler` <[function]\([Route], [Request]\)> handler function to route the request.
|
||||
- returns: <[Promise]>
|
||||
|
||||
Routing activates the request interception and enables `request.abort`, `request.continue` and `request.fulfill` methods on the request. This provides the capability to modify network requests that are made by any page in the browser context.
|
||||
Routing provides the capability to modify network requests that are made by any page in the browser context.
|
||||
Once route is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
|
||||
Once request interception is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
An example of a naïve request interceptor that aborts all image requests:
|
||||
An example of a naïve handler that aborts all image requests:
|
||||
|
||||
```js
|
||||
const context = await browser.newContext();
|
||||
await context.route('**/*.{png,jpg,jpeg}', request => request.abort());
|
||||
await context.route('**/*.{png,jpg,jpeg}', route => route.abort());
|
||||
const page = await context.newPage();
|
||||
await page.goto('https://example.com');
|
||||
await browser.close();
|
||||
@ -476,7 +477,7 @@ or the same snippet using a regex pattern instead:
|
||||
|
||||
```js
|
||||
const context = await browser.newContext();
|
||||
await context.route(/(\.png$)|(\.jpg$)/, request => request.abort());
|
||||
await context.route(/(\.png$)|(\.jpg$)/, route => route.abort());
|
||||
const page = await context.newPage();
|
||||
await page.goto('https://example.com');
|
||||
await browser.close();
|
||||
@ -484,7 +485,7 @@ await browser.close();
|
||||
|
||||
Page routes (set up with [page.route(url, handler)](#pagerouteurl-handler)) take precedence over browser context routes when request matches both handlers.
|
||||
|
||||
> **NOTE** Enabling request interception disables http cache.
|
||||
> **NOTE** Enabling routing disables http cache.
|
||||
|
||||
#### browserContext.setDefaultNavigationTimeout(timeout)
|
||||
- `timeout` <[number]> Maximum navigation time in milliseconds
|
||||
@ -783,7 +784,7 @@ const popup = await event.page();
|
||||
- <[Request]>
|
||||
|
||||
Emitted when a page issues a request. The [request] object is read-only.
|
||||
In order to intercept and mutate requests, see `page.route()`.
|
||||
In order to intercept and mutate requests, see !!!`page.route()` or `brows.
|
||||
|
||||
#### event: 'requestfailed'
|
||||
- <[Request]>
|
||||
@ -1423,18 +1424,18 @@ If `key` is a single character and no modifier keys besides `Shift` are being he
|
||||
|
||||
#### page.route(url, handler)
|
||||
- `url` <[string]|[RegExp]|[function]\([string]\):[boolean]> A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
- `handler` <[function]\([Request]\)> handler function to route the request.
|
||||
- `handler` <[function]\([Route], [Request]\)> handler function to route the request.
|
||||
- returns: <[Promise]>.
|
||||
|
||||
Routing activates the request interception and enables `request.abort`, `request.continue` and
|
||||
`request.fulfill` methods on the request. This provides the capability to modify network requests that are made by a page.
|
||||
Routing provides the capability to modify network requests that are made by a page.
|
||||
|
||||
Once request interception is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
An example of a naïve request interceptor that aborts all image requests:
|
||||
Once routing is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
|
||||
An example of a naïve handler that aborts all image requests:
|
||||
|
||||
```js
|
||||
const page = await browser.newPage();
|
||||
await page.route('**/*.{png,jpg,jpeg}', request => request.abort());
|
||||
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
|
||||
await page.goto('https://example.com');
|
||||
await browser.close();
|
||||
```
|
||||
@ -1443,14 +1444,14 @@ or the same snippet using a regex pattern instead:
|
||||
|
||||
```js
|
||||
const page = await browser.newPage();
|
||||
await page.route(/(\.png$)|(\.jpg$)/, request => request.abort());
|
||||
await page.route(/(\.png$)|(\.jpg$)/, route => route.abort());
|
||||
await page.goto('https://example.com');
|
||||
await browser.close();
|
||||
```
|
||||
|
||||
Page routes take precedence over browser context routes (set up with [browserContext.route(url, handler)](#browsercontextrouteurl-handler)) when request matches both handlers.
|
||||
|
||||
> **NOTE** Enabling request interception disables http cache.
|
||||
> **NOTE** Enabling rouing disables http cache.
|
||||
|
||||
#### page.screenshot([options])
|
||||
- `options` <[Object]> Options object which might have the following properties:
|
||||
@ -3131,11 +3132,8 @@ 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.
|
||||
|
||||
<!-- GEN:toc -->
|
||||
- [request.abort([errorCode])](#requestaborterrorcode)
|
||||
- [request.continue([overrides])](#requestcontinueoverrides)
|
||||
- [request.failure()](#requestfailure)
|
||||
- [request.frame()](#requestframe)
|
||||
- [request.fulfill(response)](#requestfulfillresponse)
|
||||
- [request.headers()](#requestheaders)
|
||||
- [request.isNavigationRequest()](#requestisnavigationrequest)
|
||||
- [request.method()](#requestmethod)
|
||||
@ -3146,50 +3144,6 @@ If request gets a 'redirect' response, the request is successfully finished with
|
||||
- [request.url()](#requesturl)
|
||||
<!-- GEN:stop -->
|
||||
|
||||
#### 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.route`.
|
||||
Exception is immediately thrown if the request interception is not enabled.
|
||||
|
||||
#### request.continue([overrides])
|
||||
- `overrides` <[Object]> Optional request overrides, which can be one of the following:
|
||||
- `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.route`.
|
||||
Exception is immediately thrown if the request interception is not enabled.
|
||||
|
||||
```js
|
||||
await page.route('**/*', 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]> Object describing request failure, if any
|
||||
- `errorText` <[string]> Human-readable error message, e.g. `'net::ERR_FAILED'`.
|
||||
@ -3208,37 +3162,6 @@ page.on('requestfailed', request => {
|
||||
#### request.frame()
|
||||
- returns: <[Frame]> A [Frame] that initiated this request.
|
||||
|
||||
#### request.fulfill(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.
|
||||
- `path` <[string]> Optional file path to respond with. The content type will be inferred from file extension. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
|
||||
- returns: <[Promise]>
|
||||
|
||||
Fulfills request with given response. To use this, request interception should
|
||||
be enabled with `page.route`. Exception is thrown if
|
||||
request interception is not enabled.
|
||||
|
||||
An example of fulfilling all requests with 404 responses:
|
||||
|
||||
```js
|
||||
await page.route('**/*', request => {
|
||||
request.fulfill({
|
||||
status: 404,
|
||||
contentType: 'text/plain',
|
||||
body: 'Not Found!'
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
An example of serving static file:
|
||||
|
||||
```js
|
||||
await page.route('**/xhr_endpoint', request => request.fulfill({ path: 'mock_data.json' }));
|
||||
```
|
||||
|
||||
#### request.headers()
|
||||
- returns: <[Object]> An object with HTTP headers associated with the request. All header names are lower-case.
|
||||
|
||||
@ -3410,6 +3333,93 @@ const { selectors, firefox } = require('playwright'); // Or 'chromium' or 'webk
|
||||
})();
|
||||
```
|
||||
|
||||
|
||||
### class: Route
|
||||
|
||||
Whenever a network route is set up with [page.route(url, handler)](#pagerouteurl-handler) or [browserContext.route(url, handler)](#browsercontextrouteurl-handler), the `Route` object allows to handle the route.
|
||||
|
||||
<!-- GEN:toc -->
|
||||
- [route.abort([errorCode])](#routeaborterrorcode)
|
||||
- [route.continue([overrides])](#routecontinueoverrides)
|
||||
- [route.fulfill(response)](#routefulfillresponse)
|
||||
- [route.request()](#routerequest)
|
||||
<!-- GEN:stop -->
|
||||
|
||||
#### route.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 the route's request.
|
||||
|
||||
#### route.continue([overrides])
|
||||
- `overrides` <[Object]> Optional request overrides, which can be one of the following:
|
||||
- `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 route's request with optional overrides.
|
||||
|
||||
```js
|
||||
await page.route('**/*', (route, request) => {
|
||||
// Override headers
|
||||
const headers = Object.assign({}, request.headers(), {
|
||||
foo: 'bar', // set "foo" header
|
||||
origin: undefined, // remove "origin" header
|
||||
});
|
||||
route.continue({headers});
|
||||
});
|
||||
```
|
||||
|
||||
#### route.fulfill(response)
|
||||
- `response` <[Object]> Response that will fulfill this route's 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.
|
||||
- `path` <[string]> Optional file path to respond with. The content type will be inferred from file extension. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
|
||||
- returns: <[Promise]>
|
||||
|
||||
Fulfills route's request with given response.
|
||||
|
||||
An example of fulfilling all requests with 404 responses:
|
||||
|
||||
```js
|
||||
await page.route('**/*', route => {
|
||||
route.fulfill({
|
||||
status: 404,
|
||||
contentType: 'text/plain',
|
||||
body: 'Not Found!'
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
An example of serving static file:
|
||||
|
||||
```js
|
||||
await page.route('**/xhr_endpoint', route => route.fulfill({ path: 'mock_data.json' }));
|
||||
```
|
||||
|
||||
#### route.request()
|
||||
- returns: <[Request]> A request to be routed.
|
||||
|
||||
|
||||
### class: TimeoutError
|
||||
|
||||
* extends: [Error]
|
||||
@ -4052,6 +4062,7 @@ const { chromium } = require('playwright');
|
||||
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
|
||||
[Request]: #class-request "Request"
|
||||
[Response]: #class-response "Response"
|
||||
[Route]: #class-route "Route"
|
||||
[Selectors]: #class-selectors "Selectors"
|
||||
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
|
||||
[TimeoutError]: #class-timeouterror "TimeoutError"
|
||||
|
@ -24,7 +24,7 @@ export { TimeoutError } from './errors';
|
||||
export { Frame } from './frames';
|
||||
export { Keyboard, Mouse } from './input';
|
||||
export { JSHandle } from './javascript';
|
||||
export { Request, Response } from './network';
|
||||
export { Request, Response, Route } from './network';
|
||||
export { FileChooser, Page, PageEvent, Worker } from './page';
|
||||
export { Selectors } from './selectors';
|
||||
|
||||
|
@ -63,7 +63,7 @@ export abstract class BrowserContextBase extends platform.EventEmitter implement
|
||||
readonly _timeoutSettings = new TimeoutSettings();
|
||||
readonly _pageBindings = new Map<string, PageBinding>();
|
||||
readonly _options: BrowserContextOptions;
|
||||
readonly _routes: { url: types.URLMatch, handler: (request: network.Request) => any }[] = [];
|
||||
readonly _routes: { url: types.URLMatch, handler: network.RouteHandler }[] = [];
|
||||
_closed = false;
|
||||
private readonly _closePromise: Promise<Error>;
|
||||
private _closePromiseFulfill: ((error: Error) => void) | undefined;
|
||||
|
@ -241,7 +241,7 @@ export class CRNetworkManager {
|
||||
}
|
||||
}
|
||||
|
||||
class InterceptableRequest implements network.RequestDelegate {
|
||||
class InterceptableRequest implements network.RouteDelegate {
|
||||
readonly request: network.Request;
|
||||
_requestId: string;
|
||||
_interceptionId: string | null;
|
||||
|
@ -141,7 +141,7 @@ const causeToResourceType: {[key: string]: string} = {
|
||||
TYPE_WEB_MANIFEST: 'manifest',
|
||||
};
|
||||
|
||||
class InterceptableRequest implements network.RequestDelegate {
|
||||
class InterceptableRequest implements network.RouteDelegate {
|
||||
readonly request: network.Request;
|
||||
_id: string;
|
||||
private _session: FFSession;
|
||||
|
@ -41,8 +41,6 @@ export type SetNetworkCookieParam = {
|
||||
sameSite?: 'Strict' | 'Lax' | 'None'
|
||||
};
|
||||
|
||||
export type RouteHandler = (request: Request) => void;
|
||||
|
||||
export function filterCookies(cookies: NetworkCookie[], urls: string | string[] = []): NetworkCookie[] {
|
||||
if (!Array.isArray(urls))
|
||||
urls = [ urls ];
|
||||
@ -95,7 +93,7 @@ function stripFragmentFromUrl(url: string): string {
|
||||
export type Headers = { [key: string]: string };
|
||||
|
||||
export class Request {
|
||||
private _delegate: RequestDelegate | null;
|
||||
readonly _routeDelegate: RouteDelegate | null;
|
||||
private _response: Response | null = null;
|
||||
_redirectChain: Request[];
|
||||
_finalRequest: Request;
|
||||
@ -110,12 +108,11 @@ export class Request {
|
||||
private _frame: frames.Frame;
|
||||
private _waitForResponsePromise: Promise<Response | null>;
|
||||
private _waitForResponsePromiseCallback: (value: Response | null) => void = () => {};
|
||||
private _interceptionHandled = false;
|
||||
|
||||
constructor(delegate: RequestDelegate | null, frame: frames.Frame, redirectChain: Request[], documentId: string | undefined,
|
||||
constructor(routeDelegate: RouteDelegate | null, frame: frames.Frame, redirectChain: Request[], documentId: string | undefined,
|
||||
url: string, resourceType: string, method: string, postData: string | null, headers: Headers) {
|
||||
assert(!url.startsWith('data:'), 'Data urls should not fire requests');
|
||||
this._delegate = delegate;
|
||||
this._routeDelegate = routeDelegate;
|
||||
this._frame = frame;
|
||||
this._redirectChain = redirectChain;
|
||||
this._finalRequest = this;
|
||||
@ -189,17 +186,36 @@ export class Request {
|
||||
};
|
||||
}
|
||||
|
||||
_route(): Route | null {
|
||||
if (!this._routeDelegate)
|
||||
return null;
|
||||
return new Route(this, this._routeDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
export class Route {
|
||||
private readonly _request: Request;
|
||||
private readonly _delegate: RouteDelegate;
|
||||
private _handled = false;
|
||||
|
||||
constructor(request: Request, delegate: RouteDelegate) {
|
||||
this._request = request;
|
||||
this._delegate = delegate;
|
||||
}
|
||||
|
||||
request(): Request {
|
||||
return this._request;
|
||||
}
|
||||
|
||||
async abort(errorCode: string = 'failed') {
|
||||
assert(this._delegate, 'Request Interception is not enabled!');
|
||||
assert(!this._interceptionHandled, 'Request is already handled!');
|
||||
this._interceptionHandled = true;
|
||||
assert(!this._handled, 'Route is already handled!');
|
||||
this._handled = true;
|
||||
await this._delegate.abort(errorCode);
|
||||
}
|
||||
|
||||
async fulfill(response: FulfillResponse & { path?: string }) {
|
||||
assert(this._delegate, 'Request Interception is not enabled!');
|
||||
assert(!this._interceptionHandled, 'Request is already handled!');
|
||||
this._interceptionHandled = true;
|
||||
assert(!this._handled, 'Route is already handled!');
|
||||
this._handled = true;
|
||||
if (response.path) {
|
||||
response = {
|
||||
status: response.status,
|
||||
@ -212,16 +228,13 @@ export class Request {
|
||||
}
|
||||
|
||||
async continue(overrides: { method?: string; headers?: Headers; postData?: string } = {}) {
|
||||
assert(this._delegate, 'Request Interception is not enabled!');
|
||||
assert(!this._interceptionHandled, 'Request is already handled!');
|
||||
assert(!this._handled, 'Route is already handled!');
|
||||
await this._delegate.continue(overrides);
|
||||
}
|
||||
|
||||
_isIntercepted(): boolean {
|
||||
return !!this._delegate;
|
||||
}
|
||||
}
|
||||
|
||||
export type RouteHandler = (route: Route, request: Request) => void;
|
||||
|
||||
type GetResponseBodyCallback = () => Promise<platform.BufferType>;
|
||||
|
||||
export class Response {
|
||||
@ -313,7 +326,7 @@ export type FulfillResponse = {
|
||||
body?: string | platform.BufferType,
|
||||
};
|
||||
|
||||
export interface RequestDelegate {
|
||||
export interface RouteDelegate {
|
||||
abort(errorCode: string): Promise<void>;
|
||||
fulfill(response: FulfillResponse): Promise<void>;
|
||||
continue(overrides: { method?: string; headers?: Headers; postData?: string; }): Promise<void>;
|
||||
|
11
src/page.ts
11
src/page.ts
@ -150,7 +150,7 @@ export class Page extends platform.EventEmitter {
|
||||
private _workers = new Map<string, Worker>();
|
||||
readonly pdf: ((options?: types.PDFOptions) => Promise<platform.BufferType>) | undefined;
|
||||
readonly coverage: any;
|
||||
readonly _routes: { url: types.URLMatch, handler: (request: network.Request) => any }[] = [];
|
||||
readonly _routes: { url: types.URLMatch, handler: network.RouteHandler }[] = [];
|
||||
_ownedContext: BrowserContext | undefined;
|
||||
|
||||
constructor(delegate: PageDelegate, browserContext: BrowserContextBase) {
|
||||
@ -419,21 +419,22 @@ export class Page extends platform.EventEmitter {
|
||||
|
||||
_requestStarted(request: network.Request) {
|
||||
this.emit(Events.Page.Request, request);
|
||||
if (!request._isIntercepted())
|
||||
const route = request._route();
|
||||
if (!route)
|
||||
return;
|
||||
for (const { url, handler } of this._routes) {
|
||||
if (platform.urlMatches(request.url(), url)) {
|
||||
handler(request);
|
||||
handler(route, request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const { url, handler } of this._browserContext._routes) {
|
||||
if (platform.urlMatches(request.url(), url)) {
|
||||
handler(request);
|
||||
handler(route, request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
request.continue();
|
||||
route.continue();
|
||||
}
|
||||
|
||||
async screenshot(options?: types.ScreenshotOptions): Promise<platform.BufferType> {
|
||||
|
@ -39,7 +39,7 @@ const errorReasons: { [reason: string]: string } = {
|
||||
'failed': 'General',
|
||||
};
|
||||
|
||||
export class WKInterceptableRequest implements network.RequestDelegate {
|
||||
export class WKInterceptableRequest implements network.RouteDelegate {
|
||||
private readonly _session: WKSession;
|
||||
readonly request: network.Request;
|
||||
readonly _requestId: string;
|
||||
|
@ -371,8 +371,9 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, FF
|
||||
it('should intercept', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
let intercepted = false;
|
||||
await context.route('**/empty.html', request => {
|
||||
await context.route('**/empty.html', route => {
|
||||
intercepted = true;
|
||||
const request = route.request();
|
||||
expect(request.url()).toContain('empty.html');
|
||||
expect(request.headers()['user-agent']).toBeTruthy();
|
||||
expect(request.method()).toBe('GET');
|
||||
@ -381,7 +382,7 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, FF
|
||||
expect(request.resourceType()).toBe('document');
|
||||
expect(request.frame() === page.mainFrame()).toBe(true);
|
||||
expect(request.frame().url()).toBe('about:blank');
|
||||
request.continue();
|
||||
route.continue();
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
@ -391,12 +392,12 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, FF
|
||||
});
|
||||
it('should yield to page.route', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.route('**/empty.html', request => {
|
||||
request.fulfill({ status: 200, body: 'context' });
|
||||
await context.route('**/empty.html', route => {
|
||||
route.fulfill({ status: 200, body: 'context' });
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.route('**/empty.html', request => {
|
||||
request.fulfill({ status: 200, body: 'page' });
|
||||
await page.route('**/empty.html', route => {
|
||||
route.fulfill({ status: 200, body: 'page' });
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.ok()).toBe(true);
|
||||
|
@ -57,7 +57,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
|
||||
res.end('console.log(1);');
|
||||
});
|
||||
|
||||
await page.route('*', request => request.continue());
|
||||
await page.route('*', route => route.continue());
|
||||
await page.goto(server.PREFIX + '/intervention');
|
||||
// Check for feature URL substring rather than https://www.chromestatus.com to
|
||||
// make it work with Edgium.
|
||||
|
@ -51,7 +51,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, b
|
||||
expect(page.frames().length).toBe(2);
|
||||
});
|
||||
it('should load oopif iframes with subresources and request interception', async function({browser, page, server, context}) {
|
||||
await page.route('**/*', request => request.continue());
|
||||
await page.route('**/*', route => route.continue());
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
});
|
||||
@ -68,8 +68,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, b
|
||||
const browser = await browserType.launch(headfulOptions);
|
||||
const page = await browser.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.route('**/*', request => {
|
||||
request.fulfill({body: 'YO, GOOGLE.COM'});
|
||||
await page.route('**/*', route => {
|
||||
route.fulfill({body: 'YO, GOOGLE.COM'});
|
||||
});
|
||||
await page.evaluate(() => {
|
||||
const frame = document.createElement('iframe');
|
||||
|
@ -32,7 +32,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
describe('Page.route', function() {
|
||||
it('should intercept', async({page, server}) => {
|
||||
let intercepted = false;
|
||||
await page.route('**/empty.html', request => {
|
||||
await page.route('**/empty.html', (route, request) => {
|
||||
expect(route.request()).toBe(request);
|
||||
expect(request.url()).toContain('empty.html');
|
||||
expect(request.headers()['user-agent']).toBeTruthy();
|
||||
expect(request.method()).toBe('GET');
|
||||
@ -41,7 +42,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
expect(request.resourceType()).toBe('document');
|
||||
expect(request.frame() === page.mainFrame()).toBe(true);
|
||||
expect(request.frame().url()).toBe('about:blank');
|
||||
request.continue();
|
||||
route.continue();
|
||||
intercepted = true;
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
@ -51,7 +52,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
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.route('**/*', request => request.continue());
|
||||
await page.route('**/*', route => route.continue());
|
||||
await page.setContent(`
|
||||
<form action='/rredirect' method='post'>
|
||||
<input type="hidden" id="foo" name="foo" value="FOOBAR">
|
||||
@ -65,22 +66,22 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
// @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.route('**/*', request => {
|
||||
const headers = Object.assign({}, request.headers(), {
|
||||
await page.route('**/*', route => {
|
||||
const headers = Object.assign({}, route.request().headers(), {
|
||||
foo: 'bar'
|
||||
});
|
||||
request.continue({ headers });
|
||||
route.continue({ 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.route('**/*', request => {
|
||||
const headers = Object.assign({}, request.headers(), {
|
||||
await page.route('**/*', route => {
|
||||
const headers = Object.assign({}, route.request().headers(), {
|
||||
foo: 'bar',
|
||||
origin: undefined, // remove "origin" header
|
||||
});
|
||||
request.continue({ headers });
|
||||
route.continue({ headers });
|
||||
});
|
||||
|
||||
const [serverRequest] = await Promise.all([
|
||||
@ -92,9 +93,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
});
|
||||
it('should contain referer header', async({page, server}) => {
|
||||
const requests = [];
|
||||
await page.route('**/*', request => {
|
||||
requests.push(request);
|
||||
request.continue();
|
||||
await page.route('**/*', route => {
|
||||
requests.push(route.request());
|
||||
route.continue();
|
||||
});
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
expect(requests[1].url()).toContain('/one-style.css');
|
||||
@ -106,7 +107,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
await context.addCookies([{ url: server.EMPTY_PAGE, name: 'foo', value: 'bar'}]);
|
||||
|
||||
// Setup request interception.
|
||||
await page.route('**/*', request => request.continue());
|
||||
await page.route('**/*', route => route.continue());
|
||||
const response = await page.reload();
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
@ -114,9 +115,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
await page.setExtraHTTPHeaders({
|
||||
foo: 'bar'
|
||||
});
|
||||
await page.route('**/*', request => {
|
||||
expect(request.headers()['foo']).toBe('bar');
|
||||
request.continue();
|
||||
await page.route('**/*', route => {
|
||||
expect(route.request().headers()['foo']).toBe('bar');
|
||||
route.continue();
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.ok()).toBe(true);
|
||||
@ -125,7 +126,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
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.route('**/*', request => request.continue());
|
||||
await page.route('**/*', route => route.continue());
|
||||
const status = await page.evaluate(async() => {
|
||||
const request = new XMLHttpRequest();
|
||||
request.open('GET', '/logo.png', false); // `false` makes the request synchronous
|
||||
@ -136,15 +137,15 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
});
|
||||
it('should work with custom referer headers', async({page, server}) => {
|
||||
await page.setExtraHTTPHeaders({ 'referer': server.EMPTY_PAGE });
|
||||
await page.route('**/*', request => {
|
||||
expect(request.headers()['referer']).toBe(server.EMPTY_PAGE);
|
||||
request.continue();
|
||||
await page.route('**/*', route => {
|
||||
expect(route.request().headers()['referer']).toBe(server.EMPTY_PAGE);
|
||||
route.continue();
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.ok()).toBe(true);
|
||||
});
|
||||
it('should be abortable', async({page, server}) => {
|
||||
await page.route(/\.css$/, request => request.abort());
|
||||
await page.route(/\.css$/, route => route.abort());
|
||||
let failedRequests = 0;
|
||||
page.on('requestfailed', event => ++failedRequests);
|
||||
const response = await page.goto(server.PREFIX + '/one-style.html');
|
||||
@ -153,7 +154,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
expect(failedRequests).toBe(1);
|
||||
});
|
||||
it('should be abortable with custom error codes', async({page, server}) => {
|
||||
await page.route('**/*', request => request.abort('internetdisconnected'));
|
||||
await page.route('**/*', route => route.abort('internetdisconnected'));
|
||||
let failedRequest = null;
|
||||
page.on('requestfailed', request => failedRequest = request);
|
||||
await page.goto(server.EMPTY_PAGE).catch(e => {});
|
||||
@ -169,7 +170,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
await page.setExtraHTTPHeaders({
|
||||
referer: 'http://google.com/'
|
||||
});
|
||||
await page.route('**/*', request => request.continue());
|
||||
await page.route('**/*', route => route.continue());
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/grid.html'),
|
||||
page.goto(server.PREFIX + '/grid.html'),
|
||||
@ -177,7 +178,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
expect(request.headers['referer']).toBe('http://google.com/');
|
||||
});
|
||||
it('should fail navigation when aborting main resource', async({page, server}) => {
|
||||
await page.route('**/*', request => request.abort());
|
||||
await page.route('**/*', route => route.abort());
|
||||
let error = null;
|
||||
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
@ -190,9 +191,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
});
|
||||
it('should work with redirects', async({page, server}) => {
|
||||
const requests = [];
|
||||
await page.route('**/*', request => {
|
||||
request.continue();
|
||||
requests.push(request);
|
||||
await page.route('**/*', route => {
|
||||
route.continue();
|
||||
requests.push(route.request());
|
||||
});
|
||||
server.setRedirect('/non-existing-page.html', '/non-existing-page-2.html');
|
||||
server.setRedirect('/non-existing-page-2.html', '/non-existing-page-3.html');
|
||||
@ -216,9 +217,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
});
|
||||
it('should work with redirects for subresources', async({page, server}) => {
|
||||
const requests = [];
|
||||
await page.route('**/*', request => {
|
||||
request.continue();
|
||||
requests.push(request);
|
||||
await page.route('**/*', route => {
|
||||
route.continue();
|
||||
requests.push(route.request());
|
||||
});
|
||||
server.setRedirect('/one-style.css', '/two-style.css');
|
||||
server.setRedirect('/two-style.css', '/three-style.css');
|
||||
@ -244,8 +245,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
|
||||
let spinner = false;
|
||||
// Cancel 2nd request.
|
||||
await page.route('**/*', request => {
|
||||
spinner ? request.abort() : request.continue();
|
||||
await page.route('**/*', route => {
|
||||
spinner ? route.abort() : route.continue();
|
||||
spinner = !spinner;
|
||||
});
|
||||
const results = await page.evaluate(() => Promise.all([
|
||||
@ -257,9 +258,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
});
|
||||
it('should navigate to dataURL and not fire dataURL requests', async({page, server}) => {
|
||||
const requests = [];
|
||||
await page.route('**/*', request => {
|
||||
requests.push(request);
|
||||
request.continue();
|
||||
await page.route('**/*', route => {
|
||||
requests.push(route.request());
|
||||
route.continue();
|
||||
});
|
||||
const dataURL = 'data:text/html,<div>yo</div>';
|
||||
const response = await page.goto(dataURL);
|
||||
@ -269,9 +270,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
it('should be able to fetch dataURL and not fire dataURL requests', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const requests = [];
|
||||
await page.route('**/*', request => {
|
||||
requests.push(request);
|
||||
request.continue();
|
||||
await page.route('**/*', route => {
|
||||
requests.push(route.request());
|
||||
route.continue();
|
||||
});
|
||||
const dataURL = 'data:text/html,<div>yo</div>';
|
||||
const text = await page.evaluate(url => fetch(url).then(r => r.text()), dataURL);
|
||||
@ -280,9 +281,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
});
|
||||
it('should navigate to URL with hash and and fire requests without hash', async({page, server}) => {
|
||||
const requests = [];
|
||||
await page.route('**/*', request => {
|
||||
requests.push(request);
|
||||
request.continue();
|
||||
await page.route('**/*', route => {
|
||||
requests.push(route.request());
|
||||
route.continue();
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE + '#hash');
|
||||
expect(response.status()).toBe(200);
|
||||
@ -293,13 +294,13 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
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.route('**/*', request => request.continue());
|
||||
await page.route('**/*', route => route.continue());
|
||||
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}) => {
|
||||
server.setRoute('/malformed?rnd=%911', (req, res) => res.end());
|
||||
await page.route('**/*', request => request.continue());
|
||||
await page.route('**/*', route => route.continue());
|
||||
const response = await page.goto(server.PREFIX + '/malformed?rnd=%911');
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
@ -307,9 +308,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
// The requestWillBeSent will report URL as-is, whereas interception will
|
||||
// report encoded URL for stylesheet. @see crbug.com/759388
|
||||
const requests = [];
|
||||
await page.route('**/*', request => {
|
||||
request.continue();
|
||||
requests.push(request);
|
||||
await page.route('**/*', route => {
|
||||
route.continue();
|
||||
requests.push(route.request());
|
||||
});
|
||||
const response = await page.goto(`data:text/html,<link rel="stylesheet" href="${server.PREFIX}/fonts?helvetica|arial"/>`);
|
||||
expect(response).toBe(null);
|
||||
@ -318,75 +319,40 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
});
|
||||
it('should not throw "Invalid Interception Id" if the request was cancelled', async({page, server}) => {
|
||||
await page.setContent('<iframe></iframe>');
|
||||
let request = null;
|
||||
await page.route('**/*', async r => request = r);
|
||||
let route = null;
|
||||
await page.route('**/*', async r => route = r);
|
||||
page.$eval('iframe', (frame, url) => frame.src = url, server.EMPTY_PAGE),
|
||||
// Wait for request interception.
|
||||
await utils.waitEvent(page, 'request');
|
||||
// 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 route.continue().catch(e => error = e);
|
||||
expect(error).toBe(null);
|
||||
});
|
||||
it('should throw if interception is not enabled', async({browser, server}) => {
|
||||
let error = null;
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
page.on('request', async request => {
|
||||
try {
|
||||
await request.continue();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(error.message).toContain('Request Interception is not enabled');
|
||||
await context.close();
|
||||
});
|
||||
it('should intercept main resource during cross-process navigation', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let intercepted = false;
|
||||
await page.route(server.CROSS_PROCESS_PREFIX + '/empty.html', request => {
|
||||
await page.route(server.CROSS_PROCESS_PREFIX + '/empty.html', route => {
|
||||
intercepted = true;
|
||||
request.continue();
|
||||
route.continue();
|
||||
});
|
||||
const response = await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expect(response.ok()).toBe(true);
|
||||
expect(intercepted).toBe(true);
|
||||
});
|
||||
it('should not throw when continued after navigation', async({page, server}) => {
|
||||
await page.route(server.PREFIX + '/one-style.css', () => {});
|
||||
// For some reason, Firefox issues load event with one outstanding request.
|
||||
const firstNavigation = page.goto(server.PREFIX + '/one-style.html', { waitUntil: FFOX ? 'networkidle0' : 'load' }).catch(e => e);
|
||||
const request = await page.waitForRequest(server.PREFIX + '/one-style.css');
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
await firstNavigation;
|
||||
const notAnError = await request.continue().then(() => null).catch(e => e);
|
||||
expect(notAnError).toBe(null);
|
||||
});
|
||||
it('should not throw when continued after cross-process navigation', async({page, server}) => {
|
||||
await page.route(server.PREFIX + '/one-style.css', () => {});
|
||||
// For some reason, Firefox issues load event with one outstanding request.
|
||||
const firstNavigation = page.goto(server.PREFIX + '/one-style.html', { waitUntil: FFOX ? 'networkidle0' : 'load' }).catch(e => e);
|
||||
const request = await page.waitForRequest(server.PREFIX + '/one-style.css');
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
await firstNavigation;
|
||||
const notAnError = await request.continue().then(() => null).catch(e => e);
|
||||
expect(notAnError).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Request.continue', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.route('**/*', request => request.continue());
|
||||
await page.route('**/*', route => route.continue());
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
});
|
||||
it('should amend HTTP headers', async({page, server}) => {
|
||||
await page.route('**/*', request => {
|
||||
const headers = Object.assign({}, request.headers());
|
||||
await page.route('**/*', route => {
|
||||
const headers = Object.assign({}, route.request().headers());
|
||||
headers['FOO'] = 'bar';
|
||||
request.continue({ headers });
|
||||
route.continue({ headers });
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [request] = await Promise.all([
|
||||
@ -398,7 +364,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
it('should amend method', async({page, server}) => {
|
||||
const sRequest = server.waitForRequest('/sleep.zzz');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.route('**/*', request => request.continue({ method: 'POST' }));
|
||||
await page.route('**/*', route => route.continue({ method: 'POST' }));
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/sleep.zzz'),
|
||||
page.evaluate(() => fetch('/sleep.zzz'))
|
||||
@ -408,14 +374,14 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
});
|
||||
it('should amend method on main request', async({page, server}) => {
|
||||
const request = server.waitForRequest('/empty.html');
|
||||
await page.route('**/*', request => request.continue({ method: 'POST' }));
|
||||
await page.route('**/*', route => route.continue({ method: 'POST' }));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect((await request).method).toBe('POST');
|
||||
});
|
||||
it('should amend post data', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.route('**/*', request => {
|
||||
request.continue({ postData: 'doggo' });
|
||||
await page.route('**/*', route => {
|
||||
route.continue({ postData: 'doggo' });
|
||||
});
|
||||
const [serverRequest] = await Promise.all([
|
||||
server.waitForRequest('/sleep.zzz'),
|
||||
@ -427,8 +393,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
|
||||
describe('Request.fulfill', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.route('**/*', request => {
|
||||
request.fulfill({
|
||||
await page.route('**/*', route => {
|
||||
route.fulfill({
|
||||
status: 201,
|
||||
headers: {
|
||||
foo: 'bar'
|
||||
@ -443,8 +409,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
|
||||
});
|
||||
it('should work with status code 422', async({page, server}) => {
|
||||
await page.route('**/*', request => {
|
||||
request.fulfill({
|
||||
await page.route('**/*', route => {
|
||||
route.fulfill({
|
||||
status: 422,
|
||||
body: 'Yo, page!'
|
||||
});
|
||||
@ -455,9 +421,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
|
||||
});
|
||||
it('should allow mocking binary responses', async({page, server}) => {
|
||||
await page.route('**/*', request => {
|
||||
await page.route('**/*', route => {
|
||||
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
|
||||
request.fulfill({
|
||||
route.fulfill({
|
||||
contentType: 'image/png',
|
||||
body: imageBuffer
|
||||
});
|
||||
@ -472,7 +438,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
expect(await img.screenshot()).toBeGolden('mock-binary-response.png');
|
||||
});
|
||||
it('should work with file path', async({page, server}) => {
|
||||
await page.route('**/*', request => request.fulfill({ contentType: 'shouldBeIgnored', path: path.join(__dirname, 'assets', 'pptr.png') }));
|
||||
await page.route('**/*', route => route.fulfill({ contentType: 'shouldBeIgnored', path: path.join(__dirname, 'assets', 'pptr.png') }));
|
||||
await page.evaluate(PREFIX => {
|
||||
const img = document.createElement('img');
|
||||
img.src = PREFIX + '/does-not-exist.png';
|
||||
@ -483,8 +449,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
expect(await img.screenshot()).toBeGolden('mock-binary-response.png');
|
||||
});
|
||||
it('should stringify intercepted request response headers', async({page, server}) => {
|
||||
await page.route('**/*', request => {
|
||||
request.fulfill({
|
||||
await page.route('**/*', route => {
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
headers: {
|
||||
'foo': true
|
||||
@ -503,9 +469,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
describe('Interception vs isNavigationRequest', () => {
|
||||
it('should work with request interception', async({page, server}) => {
|
||||
const requests = new Map();
|
||||
await page.route('**/*', request => {
|
||||
requests.set(request.url().split('/').pop(), request);
|
||||
request.continue();
|
||||
await page.route('**/*', route => {
|
||||
requests.set(route.request().url().split('/').pop(), route.request());
|
||||
route.continue();
|
||||
});
|
||||
server.setRedirect('/rrredirect', '/frames/one-frame.html');
|
||||
await page.goto(server.PREFIX + '/rrredirect');
|
||||
@ -522,7 +488,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
const page = await context.newPage();
|
||||
|
||||
await page.route('**/*', request => request.continue());
|
||||
await page.route('**/*', route => route.continue());
|
||||
const response = await page.goto(httpsServer.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
await context.close();
|
||||
@ -538,10 +504,10 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
const swResponse = await page.evaluate(() => fetchDummy('foo'));
|
||||
expect(swResponse).toBe('responseFromServiceWorker:foo');
|
||||
|
||||
await page.route('**/foo', request => {
|
||||
const slash = request.url().lastIndexOf('/');
|
||||
const name = request.url().substring(slash + 1);
|
||||
request.fulfill({
|
||||
await page.route('**/foo', route => {
|
||||
const slash = route.request().url().lastIndexOf('/');
|
||||
const name = route.request().url().substring(slash + 1);
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'text/css',
|
||||
body: 'responseFromInterception:' + name
|
||||
@ -581,8 +547,10 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
it('should work with regular expression passed from a different context', async({page, server}) => {
|
||||
const ctx = vm.createContext();
|
||||
const regexp = vm.runInContext('new RegExp("empty\\.html")', ctx);
|
||||
let intercepted = false;
|
||||
|
||||
await page.route(regexp, request => {
|
||||
await page.route(regexp, (route, request) => {
|
||||
expect(route.request()).toBe(request);
|
||||
expect(request.url()).toContain('empty.html');
|
||||
expect(request.headers()['user-agent']).toBeTruthy();
|
||||
expect(request.method()).toBe('GET');
|
||||
@ -591,11 +559,13 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||
expect(request.resourceType()).toBe('document');
|
||||
expect(request.frame() === page.mainFrame()).toBe(true);
|
||||
expect(request.frame().url()).toBe('about:blank');
|
||||
request.continue();
|
||||
route.continue();
|
||||
intercepted = true;
|
||||
});
|
||||
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.ok()).toBe(true);
|
||||
expect(intercepted).toBe(true);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -44,8 +44,8 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="empty.html">link</a>');
|
||||
let intercepted = false;
|
||||
await context.route('**/empty.html', request => {
|
||||
request.continue();
|
||||
await context.route('**/empty.html', route => {
|
||||
route.continue();
|
||||
intercepted = true;
|
||||
});
|
||||
const [popup] = await Promise.all([
|
||||
@ -143,8 +143,8 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let intercepted = false;
|
||||
await context.route('**/empty.html', request => {
|
||||
request.continue();
|
||||
await context.route('**/empty.html', route => {
|
||||
route.continue();
|
||||
intercepted = true;
|
||||
});
|
||||
await page.evaluate(url => window.__popup = window.open(url), server.EMPTY_PAGE);
|
||||
|
Loading…
Reference in New Issue
Block a user