feat(route): migrate from request interception w/ events to page.route (#809)
This commit is contained in:
parent
84edefd087
commit
8028fb052a
73
docs/api.md
73
docs/api.md
|
|
@ -470,6 +470,7 @@ page.removeListener('request', logRequest);
|
|||
- [page.opener()](#pageopener)
|
||||
- [page.pdf([options])](#pagepdfoptions)
|
||||
- [page.reload([options])](#pagereloadoptions)
|
||||
- [page.route(url, handler)](#pagerouteurl-handler)
|
||||
- [page.screenshot([options])](#pagescreenshotoptions)
|
||||
- [page.select(selector, value, options)](#pageselectselector-value-options)
|
||||
- [page.setCacheEnabled([enabled])](#pagesetcacheenabledenabled)
|
||||
|
|
@ -478,7 +479,6 @@ page.removeListener('request', logRequest);
|
|||
- [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout)
|
||||
- [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
|
||||
- [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled)
|
||||
- [page.setRequestInterception(enabled)](#pagesetrequestinterceptionenabled)
|
||||
- [page.setViewport(viewport)](#pagesetviewportviewport)
|
||||
- [page.title()](#pagetitle)
|
||||
- [page.tripleclick(selector[, options])](#pagetripleclickselector-options)
|
||||
|
|
@ -585,7 +585,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(true)`.
|
||||
In order to intercept and mutate requests, see `page.route()`.
|
||||
|
||||
#### event: 'requestfailed'
|
||||
- <[Request]>
|
||||
|
|
@ -1181,6 +1181,36 @@ The `format` options are:
|
|||
- `'networkidle2'` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
|
||||
- returns: <[Promise]<[Response]>> Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect.
|
||||
|
||||
#### page.route(url, handler)
|
||||
- `url` <[string]|[RegExp]|[Function]> A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
- `handler` <[Function]> handler function to router the request.
|
||||
- returns: <[Promise]<[void]>>.
|
||||
|
||||
Routing activates the request interception and enables `request.abort`, `request.continue` and
|
||||
`request.respond` methods on the request. This 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, responded or aborted.
|
||||
An example of a naïve request interceptor that aborts all image requests:
|
||||
|
||||
```js
|
||||
const page = await browser.newPage();
|
||||
await page.route('**/*.{png,jpg,jpeg}', request => request.abort());
|
||||
// await page.route(/\.(png|jpeg|jpg)$/, request => request.abort()); // <-- same thing
|
||||
await page.goto('https://example.com');
|
||||
await browser.close();
|
||||
```
|
||||
|
||||
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.goto('https://example.com');
|
||||
await browser.close();
|
||||
```
|
||||
|
||||
> **NOTE** Enabling request interception disables page caching.
|
||||
|
||||
#### page.screenshot([options])
|
||||
- `options` <[Object]> Options object which might have the following properties:
|
||||
- `path` <[string]> The file path to save the image to. The screenshot 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). If no path is provided, the image won't be saved to the disk.
|
||||
|
|
@ -1288,31 +1318,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(enabled)
|
||||
- `enabled` <[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 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.setViewport(viewport)
|
||||
- `viewport` <[Object]>
|
||||
- `width` <[number]> page width in pixels. **required**
|
||||
|
|
@ -1501,7 +1506,7 @@ Shortcut for [page.mainFrame().waitForLoadState([options])](#framewaitforloadsta
|
|||
#### page.waitForNavigation([options])
|
||||
- `options` <[Object]> Navigation parameters which might have the following properties:
|
||||
- `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
|
||||
- `url` <[string]|[RegExp]|[Function]> URL string, URL regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
- `url` <[string]|[RegExp]|[Function]> A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
- `waitUntil` <"load"|"domcontentloaded"|"networkidle0"|"networkidle2"|[Array]<"load"|"domcontentloaded"|"networkidle0"|"networkidle2">> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:
|
||||
- `'load'` - consider navigation to be finished when the `load` event is fired.
|
||||
- `'domcontentloaded'` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
|
||||
|
|
@ -2849,7 +2854,7 @@ If request gets a 'redirect' response, the request is successfully finished with
|
|||
- `failed` - A generic failure occurred.
|
||||
- returns: <[Promise]>
|
||||
|
||||
Aborts request. To use this, request interception should be enabled with `page.setRequestInterception`.
|
||||
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])
|
||||
|
|
@ -2859,12 +2864,11 @@ Exception is immediately thrown if the request interception is not enabled.
|
|||
- `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`.
|
||||
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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
// Override headers
|
||||
const headers = Object.assign({}, request.headers(), {
|
||||
foo: 'bar', // set "foo" header
|
||||
|
|
@ -2901,14 +2905,13 @@ page.on('requestfailed', request => {
|
|||
- returns: <[Promise]>
|
||||
|
||||
Fulfills request with given response. To use this, request interception should
|
||||
be enabled with `page.setRequestInterception`. Exception is thrown if
|
||||
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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
request.respond({
|
||||
status: 404,
|
||||
contentType: 'text/plain',
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ export class FrameManager {
|
|||
watcher._onNavigationRequest(frame, request);
|
||||
}
|
||||
if (!request._isFavicon)
|
||||
this._page.emit(Events.Page.Request, request);
|
||||
this._page._requestStarted(request);
|
||||
}
|
||||
|
||||
requestReceivedResponse(response: network.Response) {
|
||||
|
|
|
|||
|
|
@ -155,6 +155,62 @@ class Helper {
|
|||
clearTimeout(timeoutTimer);
|
||||
}
|
||||
}
|
||||
|
||||
static globToRegex(glob: string): RegExp {
|
||||
const tokens = ['^'];
|
||||
let inGroup;
|
||||
for (let i = 0; i < glob.length; ++i) {
|
||||
const c = glob[i];
|
||||
if (escapeGlobChars.has(c)) {
|
||||
tokens.push('\\' + c);
|
||||
continue;
|
||||
}
|
||||
if (c === '*') {
|
||||
const beforeDeep = glob[i - 1];
|
||||
let starCount = 1;
|
||||
while (glob[i + 1] === '*') {
|
||||
starCount++;
|
||||
i++;
|
||||
}
|
||||
const afterDeep = glob[i + 1];
|
||||
const isDeep = starCount > 1 &&
|
||||
(beforeDeep === '/' || beforeDeep === undefined) &&
|
||||
(afterDeep === '/' || afterDeep === undefined);
|
||||
if (isDeep) {
|
||||
tokens.push('((?:[^/]*(?:\/|$))*)');
|
||||
i++;
|
||||
} else {
|
||||
tokens.push('([^/]*)');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '?':
|
||||
tokens.push('.');
|
||||
break;
|
||||
case '{':
|
||||
inGroup = true;
|
||||
tokens.push('(');
|
||||
break;
|
||||
case '}':
|
||||
inGroup = false;
|
||||
tokens.push(')');
|
||||
break;
|
||||
case ',':
|
||||
if (inGroup) {
|
||||
tokens.push('|');
|
||||
break;
|
||||
}
|
||||
tokens.push('\\' + c);
|
||||
break;
|
||||
default:
|
||||
tokens.push(c);
|
||||
}
|
||||
}
|
||||
tokens.push('$');
|
||||
return new RegExp(tokens.join(''));
|
||||
}
|
||||
}
|
||||
|
||||
export function assert(value: any, message?: string) {
|
||||
|
|
@ -162,4 +218,6 @@ export function assert(value: any, message?: string) {
|
|||
throw new Error(message);
|
||||
}
|
||||
|
||||
const escapeGlobChars = new Set(['/', '$', '^', '+', '.', '(', ')', '=', '!', '|']);
|
||||
|
||||
export const helper = Helper;
|
||||
|
|
|
|||
|
|
@ -216,6 +216,10 @@ export class Request {
|
|||
assert(!this._interceptionHandled, 'Request is already handled!');
|
||||
await this._delegate!.continue(overrides);
|
||||
}
|
||||
|
||||
_isIntercepted(): boolean {
|
||||
return !!this._delegate;
|
||||
}
|
||||
}
|
||||
|
||||
type GetResponseBodyCallback = () => Promise<platform.BufferType>;
|
||||
|
|
|
|||
23
src/page.ts
23
src/page.ts
|
|
@ -111,6 +111,7 @@ export class Page extends platform.EventEmitter {
|
|||
private _workers = new Map<string, Worker>();
|
||||
readonly pdf: ((options?: types.PDFOptions) => Promise<platform.BufferType>) | undefined;
|
||||
readonly coverage: Coverage | undefined;
|
||||
readonly _requestHandlers: { url: types.URLMatch, handler: (request: network.Request) => void }[] = [];
|
||||
|
||||
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
||||
super();
|
||||
|
|
@ -416,11 +417,25 @@ export class Page extends platform.EventEmitter {
|
|||
await this._delegate.setCacheEnabled(enabled);
|
||||
}
|
||||
|
||||
async setRequestInterception(enabled: boolean) {
|
||||
if (this._state.interceptNetwork === enabled)
|
||||
async route(url: types.URLMatch, handler: (request: network.Request) => void) {
|
||||
if (!this._state.interceptNetwork) {
|
||||
this._state.interceptNetwork = true;
|
||||
await this._delegate.setRequestInterception(true);
|
||||
}
|
||||
this._requestHandlers.push({ url, handler });
|
||||
}
|
||||
|
||||
_requestStarted(request: network.Request) {
|
||||
this.emit(Events.Page.Request, request);
|
||||
if (!request._isIntercepted())
|
||||
return;
|
||||
this._state.interceptNetwork = enabled;
|
||||
await this._delegate.setRequestInterception(enabled);
|
||||
for (const { url, handler } of this._requestHandlers) {
|
||||
if (platform.urlMatches(request.url(), url)) {
|
||||
handler(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
request.continue();
|
||||
}
|
||||
|
||||
async setOfflineMode(enabled: boolean) {
|
||||
|
|
|
|||
|
|
@ -219,19 +219,20 @@ export function getMimeType(file: string): string | null {
|
|||
}
|
||||
|
||||
export function urlMatches(urlString: string, match: types.URLMatch | undefined): boolean {
|
||||
if (match === undefined)
|
||||
if (match === undefined || match === '')
|
||||
return true;
|
||||
if (typeof match === 'string')
|
||||
return match === urlString;
|
||||
if (helper.isString(match))
|
||||
match = helper.globToRegex(match);
|
||||
if (match instanceof RegExp)
|
||||
return match.test(urlString);
|
||||
assert(typeof match === 'function', 'url parameter should be string, RegExp or function');
|
||||
if (typeof match === 'string' && match === urlString)
|
||||
return true;
|
||||
const url = new URL(urlString);
|
||||
if (typeof match === 'string')
|
||||
return url.pathname === match;
|
||||
|
||||
try {
|
||||
return match(new URL(urlString));
|
||||
} catch (e) {
|
||||
}
|
||||
return false;
|
||||
assert(typeof match === 'function', 'url parameter should be string, RegExp or function');
|
||||
return match(url);
|
||||
}
|
||||
|
||||
export function pngToJpeg(buffer: Buffer): Buffer {
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
|
|||
});
|
||||
|
||||
describe('Chromium-Specific Page Tests', function() {
|
||||
it('Page.setRequestInterception should work with intervention headers', async({server, page}) => {
|
||||
it('Page.route should work with intervention headers', async({server, page}) => {
|
||||
server.setRoute('/intervention', (req, res) => res.end(`
|
||||
<script>
|
||||
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
|
||||
|
|
@ -237,8 +237,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
|
|||
res.end('console.log(1);');
|
||||
});
|
||||
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => request.continue());
|
||||
await page.route('*', request => request.continue());
|
||||
await page.goto(server.PREFIX + '/intervention');
|
||||
// Check for feature URL substring rather than https://www.chromestatus.com to
|
||||
// make it work with Edgium.
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
expect(page.frames().length).toBe(2);
|
||||
});
|
||||
it('should load oopif iframes with subresources and request interception', async function({browser, page, server, context}) {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => request.continue());
|
||||
await page.route('*', request => request.continue());
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(oopifs(browser).length).toBe(1);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { helper } = require('../lib/helper');
|
||||
const utils = require('./utils');
|
||||
|
||||
module.exports.describe = function({testRunner, expect, defaultBrowserOptions, playwright, FFOX, CHROMIUM, WEBKIT}) {
|
||||
|
|
@ -24,10 +25,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
const {it, fit, xit, dit} = testRunner;
|
||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||
|
||||
describe('Page.setRequestInterception', function() {
|
||||
describe('Page.route', function() {
|
||||
it('should intercept', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('/empty.html', request => {
|
||||
expect(request.url()).toContain('empty.html');
|
||||
expect(request.headers()['user-agent']).toBeTruthy();
|
||||
expect(request.method()).toBe('GET');
|
||||
|
|
@ -44,8 +44,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.setRequestInterception(true);
|
||||
page.on('request', request => request.continue());
|
||||
await page.route('**/*', request => request.continue());
|
||||
await page.setContent(`
|
||||
<form action='/rredirect' method='post'>
|
||||
<input type="hidden" id="foo" name="foo" value="FOOBAR">
|
||||
|
|
@ -59,8 +58,7 @@ 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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
const headers = Object.assign({}, request.headers(), {
|
||||
foo: 'bar'
|
||||
});
|
||||
|
|
@ -70,8 +68,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
});
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/4743
|
||||
it('should be able to remove headers', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
const headers = Object.assign({}, request.headers(), {
|
||||
foo: 'bar',
|
||||
origin: undefined, // remove "origin" header
|
||||
|
|
@ -87,9 +84,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
expect(serverRequest.headers.origin).toBe(undefined);
|
||||
});
|
||||
it('should contain referer header', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
const requests = [];
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
requests.push(request);
|
||||
request.continue();
|
||||
});
|
||||
|
|
@ -103,24 +99,15 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
await context.setCookies([{ url: server.EMPTY_PAGE, name: 'foo', value: 'bar'}]);
|
||||
|
||||
// Setup request interception.
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => request.continue());
|
||||
await page.route('**/*', request => request.continue());
|
||||
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.goto(server.EMPTY_PAGE);
|
||||
await page.setRequestInterception(false);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
});
|
||||
it('should show custom HTTP headers', async({page, server}) => {
|
||||
await page.setExtraHTTPHeaders({
|
||||
foo: 'bar'
|
||||
});
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
expect(request.headers()['foo']).toBe('bar');
|
||||
request.continue();
|
||||
});
|
||||
|
|
@ -131,8 +118,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.setRequestInterception(true);
|
||||
page.on('request', request => request.continue());
|
||||
await page.route('**/*', request => request.continue());
|
||||
const status = await page.evaluate(async() => {
|
||||
const request = new XMLHttpRequest();
|
||||
request.open('GET', '/logo.png', false); // `false` makes the request synchronous
|
||||
|
|
@ -143,8 +129,7 @@ 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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
expect(request.headers()['referer']).toBe(server.EMPTY_PAGE);
|
||||
request.continue();
|
||||
});
|
||||
|
|
@ -152,13 +137,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
expect(response.ok()).toBe(true);
|
||||
});
|
||||
it('should be abortable', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
if (request.url().endsWith('.css'))
|
||||
request.abort();
|
||||
else
|
||||
request.continue();
|
||||
});
|
||||
await page.route(/\.css$/, request => request.abort());
|
||||
let failedRequests = 0;
|
||||
page.on('requestfailed', event => ++failedRequests);
|
||||
const response = await page.goto(server.PREFIX + '/one-style.html');
|
||||
|
|
@ -167,10 +146,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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
request.abort('internetdisconnected');
|
||||
});
|
||||
await page.route('**/*', request => request.abort('internetdisconnected'));
|
||||
let failedRequest = null;
|
||||
page.on('requestfailed', request => failedRequest = request);
|
||||
await page.goto(server.EMPTY_PAGE).catch(e => {});
|
||||
|
|
@ -186,8 +162,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
await page.setExtraHTTPHeaders({
|
||||
referer: 'http://google.com/'
|
||||
});
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => request.continue());
|
||||
await page.route('**/*', request => request.continue());
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/grid.html'),
|
||||
page.goto(server.PREFIX + '/grid.html'),
|
||||
|
|
@ -195,8 +170,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.setRequestInterception(true);
|
||||
page.on('request', request => request.abort());
|
||||
await page.route('**/*', request => request.abort());
|
||||
let error = null;
|
||||
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
|
|
@ -208,9 +182,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
expect(error.message).toContain('net::ERR_FAILED');
|
||||
});
|
||||
it('should work with redirects', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
const requests = [];
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
request.continue();
|
||||
requests.push(request);
|
||||
});
|
||||
|
|
@ -235,9 +208,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
}
|
||||
});
|
||||
it('should work with redirects for subresources', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
const requests = [];
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
request.continue();
|
||||
requests.push(request);
|
||||
});
|
||||
|
|
@ -262,11 +234,10 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
await page.goto(server.EMPTY_PAGE);
|
||||
let responseCount = 1;
|
||||
server.setRoute('/zzz', (req, res) => res.end((responseCount++) * 11 + ''));
|
||||
await page.setRequestInterception(true);
|
||||
|
||||
let spinner = false;
|
||||
// Cancel 2nd request.
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
spinner ? request.abort() : request.continue();
|
||||
spinner = !spinner;
|
||||
});
|
||||
|
|
@ -278,9 +249,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
expect(results).toEqual(['11', 'FAILED', '22']);
|
||||
});
|
||||
it('should navigate to dataURL and not fire dataURL requests', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
const requests = [];
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
requests.push(request);
|
||||
request.continue();
|
||||
});
|
||||
|
|
@ -291,9 +261,8 @@ 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);
|
||||
await page.setRequestInterception(true);
|
||||
const requests = [];
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
requests.push(request);
|
||||
request.continue();
|
||||
});
|
||||
|
|
@ -303,9 +272,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
expect(requests.length).toBe(0);
|
||||
});
|
||||
it.skip(FFOX)('should navigate to URL with hash and and fire requests without hash', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
const requests = [];
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
requests.push(request);
|
||||
request.continue();
|
||||
});
|
||||
|
|
@ -318,24 +286,21 @@ 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.setRequestInterception(true);
|
||||
page.on('request', request => request.continue());
|
||||
await page.route('**/*', request => request.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}) => {
|
||||
await page.setRequestInterception(true);
|
||||
server.setRoute('/malformed?rnd=%911', (req, res) => res.end());
|
||||
page.on('request', request => request.continue());
|
||||
await page.route('**/*', request => request.continue());
|
||||
const response = await page.goto(server.PREFIX + '/malformed?rnd=%911');
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
it('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);
|
||||
const requests = [];
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
request.continue();
|
||||
requests.push(request);
|
||||
});
|
||||
|
|
@ -346,9 +311,8 @@ 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>');
|
||||
await page.setRequestInterception(true);
|
||||
let request = null;
|
||||
page.on('request', async r => request = r);
|
||||
await page.route('**/*', async r => request = r);
|
||||
page.$eval('iframe', (frame, url) => frame.src = url, server.EMPTY_PAGE),
|
||||
// Wait for request interception.
|
||||
await utils.waitEvent(page, 'request');
|
||||
|
|
@ -373,11 +337,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
});
|
||||
it('should intercept main resource during cross-process navigation', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setRequestInterception(true);
|
||||
let intercepted = false;
|
||||
page.on('request', request => {
|
||||
if (request.url().includes(server.CROSS_PROCESS_PREFIX + '/empty.html'))
|
||||
intercepted = true;
|
||||
await page.route(server.CROSS_PROCESS_PREFIX + '/empty.html', request => {
|
||||
intercepted = true;
|
||||
request.continue();
|
||||
});
|
||||
const response = await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
|
|
@ -385,11 +347,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
expect(intercepted).toBe(true);
|
||||
});
|
||||
it('should not throw when continued after navigation', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
if (request.url() !== server.PREFIX + '/one-style.css')
|
||||
request.continue();
|
||||
});
|
||||
await page.route(server.PREFIX + '/one-style.css', () => {});
|
||||
// For some reason, Firefox issues load event with one outstanding request.
|
||||
const failed = page.goto(server.PREFIX + '/one-style.html', { waitUntil: FFOX ? 'networkidle0' : 'load' }).catch(e => e);
|
||||
const request = await page.waitForRequest(server.PREFIX + '/one-style.css');
|
||||
|
|
@ -400,11 +358,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
expect(notAnError).toBe(null);
|
||||
});
|
||||
it('should not throw when continued after cross-process navigation', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
if (request.url() !== server.PREFIX + '/one-style.css')
|
||||
request.continue();
|
||||
});
|
||||
await page.route(server.PREFIX + '/one-style.css', () => {});
|
||||
// For some reason, Firefox issues load event with one outstanding request.
|
||||
const failed = page.goto(server.PREFIX + '/one-style.html', { waitUntil: FFOX ? 'networkidle0' : 'load' }).catch(e => e);
|
||||
const request = await page.waitForRequest(server.PREFIX + '/one-style.css');
|
||||
|
|
@ -418,13 +372,11 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
|
||||
describe('Request.continue', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => request.continue());
|
||||
await page.route('**/*', request => request.continue());
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
});
|
||||
it('should amend HTTP headers', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
const headers = Object.assign({}, request.headers());
|
||||
headers['FOO'] = 'bar';
|
||||
request.continue({ headers });
|
||||
|
|
@ -439,10 +391,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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
request.continue({ method: 'POST' });
|
||||
});
|
||||
await page.route('**/*', request => request.continue({ method: 'POST' }));
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/sleep.zzz'),
|
||||
page.evaluate(() => fetch('/sleep.zzz'))
|
||||
|
|
@ -452,17 +401,13 @@ 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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
request.continue({ method: 'POST' });
|
||||
});
|
||||
await page.route('**/*', request => request.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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
request.continue({ postData: 'doggo' });
|
||||
});
|
||||
const [serverRequest] = await Promise.all([
|
||||
|
|
@ -475,8 +420,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
|
||||
describe('Request.fulfill', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
request.fulfill({
|
||||
status: 201,
|
||||
headers: {
|
||||
|
|
@ -492,8 +436,7 @@ 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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
request.fulfill({
|
||||
status: 422,
|
||||
body: 'Yo, page!'
|
||||
|
|
@ -505,8 +448,7 @@ 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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
|
||||
request.fulfill({
|
||||
contentType: 'image/png',
|
||||
|
|
@ -523,8 +465,7 @@ 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.setRequestInterception(true);
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
request.fulfill({
|
||||
status: 200,
|
||||
headers: {
|
||||
|
|
@ -601,11 +542,10 @@ 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();
|
||||
page.on('request', request => {
|
||||
await page.route('**/*', request => {
|
||||
requests.set(request.url().split('/').pop(), request);
|
||||
request.continue();
|
||||
});
|
||||
await page.setRequestInterception(true);
|
||||
server.setRedirect('/rrredirect', '/frames/one-frame.html');
|
||||
await page.goto(server.PREFIX + '/rrredirect');
|
||||
expect(requests.get('rrredirect').isNavigationRequest()).toBe(true);
|
||||
|
|
@ -616,31 +556,34 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
});
|
||||
});
|
||||
|
||||
describe('Page.setCacheEnabled', function() {
|
||||
it('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.goto(server.PREFIX + '/cached/one-style.html');
|
||||
const [nonCachedRequest] = await Promise.all([
|
||||
server.waitForRequest('/cached/one-style.html'),
|
||||
page.reload(),
|
||||
]);
|
||||
expect(nonCachedRequest.headers['if-modified-since']).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ignoreHTTPSErrors', function() {
|
||||
it('should work with request interception', async({newPage, httpsServer}) => {
|
||||
const page = await newPage({ ignoreHTTPSErrors: true, interceptNetwork: true });
|
||||
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', request => request.continue());
|
||||
await page.route('**/*', request => request.continue());
|
||||
const response = await page.goto(httpsServer.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('glob', function() {
|
||||
it('should work with glob', async({newPage, httpsServer}) => {
|
||||
expect(helper.globToRegex('**/*.js').test('https://localhost:8080/foo.js')).toBeTruthy();
|
||||
expect(helper.globToRegex('**/*.css').test('https://localhost:8080/foo.js')).toBeFalsy();
|
||||
expect(helper.globToRegex('*.js').test('https://localhost:8080/foo.js')).toBeFalsy();
|
||||
expect(helper.globToRegex('https://**/*.js').test('https://localhost:8080/foo.js')).toBeTruthy();
|
||||
expect(helper.globToRegex('http://localhost:8080/simple/path.js').test('http://localhost:8080/simple/path.js')).toBeTruthy();
|
||||
expect(helper.globToRegex('http://localhost:8080/?imple/path.js').test('http://localhost:8080/Simple/path.js')).toBeTruthy();
|
||||
expect(helper.globToRegex('**/{a,b}.js').test('https://localhost:8080/a.js')).toBeTruthy();
|
||||
expect(helper.globToRegex('**/{a,b}.js').test('https://localhost:8080/b.js')).toBeTruthy();
|
||||
expect(helper.globToRegex('**/{a,b}.js').test('https://localhost:8080/c.js')).toBeFalsy();
|
||||
|
||||
expect(helper.globToRegex('**/*.{png,jpg,jpeg}').test('https://localhost:8080/c.jpg')).toBeTruthy();
|
||||
expect(helper.globToRegex('**/*.{png,jpg,jpeg}').test('https://localhost:8080/c.jpeg')).toBeTruthy();
|
||||
expect(helper.globToRegex('**/*.{png,jpg,jpeg}').test('https://localhost:8080/c.png')).toBeTruthy();
|
||||
expect(helper.globToRegex('**/*.{png,jpg,jpeg}').test('https://localhost:8080/c.css')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -35,27 +35,5 @@ module.exports.describe = function ({ testRunner, expect }) {
|
|||
expect(htmlReq.headers['foo']).toBe(undefined);
|
||||
expect(cssReq.headers['foo']).toBe('bar');
|
||||
});
|
||||
it('should continue load when interception gets disabled during provisional load', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setRequestInterception(true);
|
||||
expect(await page.evaluate(() => navigator.onLine)).toBe(true);
|
||||
let intercepted = null;
|
||||
const order = [];
|
||||
page.on('request', request => {
|
||||
intercepted = page.setRequestInterception(false).then(() => order.push('setRequestInterception'));
|
||||
});
|
||||
const response = await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html').then(response => {
|
||||
order.push('goto');
|
||||
return response;
|
||||
});
|
||||
// Should intercept a request.
|
||||
expect(intercepted).not.toBe(null);
|
||||
await intercepted;
|
||||
// Should continue on disabling and load successfully.
|
||||
expect(response.status()).toBe(200);
|
||||
// Should resolve setRequestInterception before goto.
|
||||
expect(order[0]).toBe('setRequestInterception');
|
||||
expect(order[1]).toBe('goto');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue