feat(route): migrate from request interception w/ events to page.route (#809)

This commit is contained in:
Pavel Feldman 2020-02-03 14:23:24 -08:00 committed by GitHub
parent 84edefd087
commit 8028fb052a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 192 additions and 192 deletions

View file

@ -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',

View file

@ -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) {

View file

@ -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;

View file

@ -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>;

View file

@ -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) {

View file

@ -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 {

View file

@ -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.

View file

@ -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);
});

View file

@ -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();
});
});
};
/**

View file

@ -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');
});
});
};