fix: move offline/cache/interception switches to BrowserContext (#748)

This commit is contained in:
Yury Semikhatsky 2020-01-29 12:51:14 -08:00 committed by GitHub
parent 9a126da6f3
commit 6faf74bc95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 259 additions and 202 deletions

View file

@ -212,6 +212,9 @@ Indicates that the browser is connected.
- `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
- `userAgent` <?[string]> Specific user agent to use in this context.
- `cacheEnabled` <?[boolean]> Toggles HTTP cache in the context. By default HTTP cache is enabled.
- `interceptNetwork` <?[boolean]> Toggles network interception in the context. Defaults to false.
- `offlineMode` <?[boolean]> Toggles offline network mode in the context. Defaults to false.
- `javaScriptEnabled` <?[boolean]> Whether or not to enable or disable JavaScript in the context. Defaults to true.
- `timezoneId` <?[string]> Changes the timezone of the context. See [ICUs `metaZones.txt`](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1) for a list of supported timezone IDs.
- `geolocation` <[Object]>
@ -261,9 +264,12 @@ await context.close();
- [browserContext.cookies([...urls])](#browsercontextcookiesurls)
- [browserContext.newPage(url)](#browsercontextnewpageurl)
- [browserContext.pages()](#browsercontextpages)
- [browserContext.setCacheEnabled([enabled])](#browsercontextsetcacheenabledenabled)
- [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
- [browserContext.setGeolocation(geolocation)](#browsercontextsetgeolocationgeolocation)
- [browserContext.setOfflineMode(enabled)](#browsercontextsetofflinemodeenabled)
- [browserContext.setPermissions(origin, permissions[])](#browsercontextsetpermissionsorigin-permissions)
- [browserContext.setRequestInterception(enabled)](#browsercontextsetrequestinterceptionenabled)
<!-- GEN:stop -->
#### browserContext.clearCookies()
@ -321,6 +327,12 @@ Creates a new page in the browser context and optionally navigates it to the spe
An array of all pages inside the browser context.
#### browserContext.setCacheEnabled([enabled])
- `enabled` <[boolean]> sets the `enabled` state of the HTTP cache.
- returns: <[Promise]>
Toggles ignoring HTTP cache for each request based on the enabled state. By default, HTTP cache is enabled.
#### browserContext.setCookies(cookies)
- `cookies` <[Array]<[Object]>>
- `name` <[string]> **required**
@ -353,6 +365,10 @@ await browserContext.setGeolocation({latitude: 59.95, longitude: 30.31667});
> **NOTE** Consider using [browserContext.setPermissions](#browsercontextsetpermissions-permissions) to grant permissions for the page to read its geolocation.
#### browserContext.setOfflineMode(enabled)
- `enabled` <[boolean]> When `true`, enables offline mode for the context.
- returns: <[Promise]>
#### browserContext.setPermissions(origin, permissions[])
- `origin` <[string]> The [origin] to grant permissions to, e.g. "https://example.com".
- `permissions` <[Array]<[string]>> An array of permissions to grant. All permissions that are not listed here will be automatically denied. Permissions can be one of the following values:
@ -380,6 +396,32 @@ const context = browser.defaultContext();
await context.setPermissions('https://html5demos.com', ['geolocation']);
```
#### browserContext.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 all pages in the context.
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 context = await browser.newContext();
const page = await context.newPage();
page.on('request', interceptedRequest => {
if (interceptedRequest.url().endsWith('.png') || interceptedRequest.url().endsWith('.jpg'))
interceptedRequest.abort();
else
interceptedRequest.continue();
});
await context.setRequestInterception(true);
await page.goto('https://example.com');
await browser.close();
```
> **NOTE** Enabling request interception disables HTTP cache.
### class: Page
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
@ -471,13 +513,10 @@ page.removeListener('request', logRequest);
- [page.reload([options])](#pagereloadoptions)
- [page.screenshot([options])](#pagescreenshotoptions)
- [page.select(selector, value, options)](#pageselectselector-value-options)
- [page.setCacheEnabled([enabled])](#pagesetcacheenabledenabled)
- [page.setContent(html[, options])](#pagesetcontenthtml-options)
- [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout)
- [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)
@ -1222,12 +1261,6 @@ page.select('select#colors', { value: 'blue' }, { index: 2 }, 'red');
Shortcut for [page.mainFrame().select()](#frameselectselector-values)
#### page.setCacheEnabled([enabled])
- `enabled` <[boolean]> sets the `enabled` state of the cache.
- returns: <[Promise]>
Toggles ignoring cache for each request based on the enabled state. By default, caching is enabled.
#### page.setContent(html[, options])
- `html` <[string]> HTML markup to assign to the page.
- `options` <[Object]> Parameters which might have the following properties:
@ -1279,35 +1312,6 @@ The extra HTTP headers will be sent with every request the page initiates.
> **NOTE** page.setExtraHTTPHeaders does not guarantee the order of headers in the outgoing requests.
#### page.setOfflineMode(enabled)
- `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**

View file

@ -42,6 +42,9 @@ export type BrowserContextOptions = {
javaScriptEnabled?: boolean,
bypassCSP?: boolean,
userAgent?: string,
cacheEnabled?: boolean;
interceptNetwork?: boolean;
offlineMode?: boolean;
timezoneId?: string,
geolocation?: types.Geolocation,
permissions?: { [key: string]: string[] };
@ -112,6 +115,27 @@ export class BrowserContext {
await this._delegate.setGeolocation(geolocation);
}
async setCacheEnabled(enabled: boolean = true) {
if (this._options.cacheEnabled === enabled)
return;
this._options.cacheEnabled = enabled;
await Promise.all(this._existingPages().map(page => page._delegate.setCacheEnabled(enabled)));
}
async setRequestInterception(enabled: boolean) {
if (this._options.interceptNetwork === enabled)
return;
this._options.interceptNetwork = enabled;
await Promise.all(this._existingPages().map(page => page._delegate.setRequestInterception(enabled)));
}
async setOfflineMode(enabled: boolean) {
if (this._options.offlineMode === enabled)
return;
this._options.offlineMode = enabled;
await Promise.all(this._existingPages().map(page => page._delegate.setOfflineMode(enabled)));
}
async close() {
if (this._closed)
return;

View file

@ -29,12 +29,9 @@ export class CRNetworkManager {
private _page: Page;
private _requestIdToRequest = new Map<string, InterceptableRequest>();
private _requestIdToRequestWillBeSentEvent = new Map<string, Protocol.Network.requestWillBeSentPayload>();
private _offline = false;
private _credentials: {username: string, password: string} | null = null;
private _attemptedAuthentications = new Set<string>();
private _userRequestInterceptionEnabled = false;
private _protocolRequestInterceptionEnabled = false;
private _userCacheDisabled = false;
private _requestIdToInterceptionId = new Map<string, string>();
private _eventListeners: RegisteredListener[];
@ -63,7 +60,17 @@ export class CRNetworkManager {
}
async initialize() {
await this._client.send('Network.enable');
const promises: Promise<any>[] = [
this._client.send('Network.enable')
];
const options = this._page.browserContext()._options;
if (options.offlineMode)
promises.push(this.setOfflineMode(options.offlineMode));
if (this._userRequestInterceptionEnabled())
promises.push(this._updateProtocolRequestInterception());
else if (options.cacheEnabled === false)
promises.push(this._updateProtocolCacheDisabled());
await Promise.all(promises);
}
dispose() {
@ -75,10 +82,9 @@ export class CRNetworkManager {
await this._updateProtocolRequestInterception();
}
async setOfflineMode(value: boolean) {
this._offline = value;
async setOfflineMode(offline: boolean) {
await this._client.send('Network.emulateNetworkConditions', {
offline: this._offline,
offline,
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
@ -91,17 +97,19 @@ export class CRNetworkManager {
}
async setCacheEnabled(enabled: boolean) {
this._userCacheDisabled = !enabled;
await this._updateProtocolCacheDisabled();
}
async setRequestInterception(value: boolean) {
this._userRequestInterceptionEnabled = value;
await this._updateProtocolRequestInterception();
}
async _updateProtocolRequestInterception() {
const enabled = this._userRequestInterceptionEnabled || !!this._credentials;
private _userRequestInterceptionEnabled() : boolean {
return !!this._page.browserContext()._options.interceptNetwork;
}
private async _updateProtocolRequestInterception() {
const enabled = this._userRequestInterceptionEnabled() || !!this._credentials;
if (enabled === this._protocolRequestInterceptionEnabled)
return;
this._protocolRequestInterceptionEnabled = enabled;
@ -121,13 +129,15 @@ export class CRNetworkManager {
}
}
async _updateProtocolCacheDisabled() {
private async _updateProtocolCacheDisabled() {
const options = this._page.browserContext()._options;
const cacheDisabled = options.cacheEnabled === false;
await this._client.send('Network.setCacheDisabled', {
cacheDisabled: this._userCacheDisabled || this._protocolRequestInterceptionEnabled
cacheDisabled: cacheDisabled || this._protocolRequestInterceptionEnabled
});
}
_onRequestWillBeSent(event: Protocol.Network.requestWillBeSentPayload) {
private _onRequestWillBeSent(event: Protocol.Network.requestWillBeSentPayload) {
// Request interception doesn't happen for data URLs with Network Service.
if (this._protocolRequestInterceptionEnabled && !event.request.url.startsWith('data:')) {
const requestId = event.requestId;
@ -143,7 +153,7 @@ export class CRNetworkManager {
this._onRequest(event, null);
}
_onAuthRequired(event: Protocol.Fetch.authRequiredPayload) {
private _onAuthRequired(event: Protocol.Fetch.authRequiredPayload) {
let response: 'Default' | 'CancelAuth' | 'ProvideCredentials' = 'Default';
if (this._attemptedAuthentications.has(event.requestId)) {
response = 'CancelAuth';
@ -158,8 +168,8 @@ export class CRNetworkManager {
}).catch(debugError);
}
_onRequestPaused(event: Protocol.Fetch.requestPausedPayload) {
if (!this._userRequestInterceptionEnabled && this._protocolRequestInterceptionEnabled) {
private _onRequestPaused(event: Protocol.Fetch.requestPausedPayload) {
if (!this._userRequestInterceptionEnabled() && this._protocolRequestInterceptionEnabled) {
this._client.send('Fetch.continueRequest', {
requestId: event.requestId
}).catch(debugError);
@ -178,7 +188,7 @@ export class CRNetworkManager {
}
}
_onRequest(event: Protocol.Network.requestWillBeSentPayload, interceptionId: string | null) {
private _onRequest(event: Protocol.Network.requestWillBeSentPayload, interceptionId: string | null) {
if (event.request.url.startsWith('data:'))
return;
let redirectChain: network.Request[] = [];
@ -194,12 +204,12 @@ export class CRNetworkManager {
const frame = event.frameId ? this._page._frameManager.frame(event.frameId) : null;
const isNavigationRequest = event.requestId === event.loaderId && event.type === 'Document';
const documentId = isNavigationRequest ? event.loaderId : undefined;
const request = new InterceptableRequest(this._client, frame, interceptionId, documentId, this._userRequestInterceptionEnabled, event, redirectChain);
const request = new InterceptableRequest(this._client, frame, interceptionId, documentId, this._userRequestInterceptionEnabled(), event, redirectChain);
this._requestIdToRequest.set(event.requestId, request);
this._page._frameManager.requestStarted(request.request);
}
_createResponse(request: InterceptableRequest, responsePayload: Protocol.Network.Response): network.Response {
private _createResponse(request: InterceptableRequest, responsePayload: Protocol.Network.Response): network.Response {
const getResponseBody = async () => {
const response = await this._client.send('Network.getResponseBody', { requestId: request._requestId });
return platform.Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
@ -207,7 +217,7 @@ export class CRNetworkManager {
return new network.Response(request.request, responsePayload.status, responsePayload.statusText, headersObject(responsePayload.headers), getResponseBody);
}
_handleRequestRedirect(request: InterceptableRequest, responsePayload: Protocol.Network.Response) {
private _handleRequestRedirect(request: InterceptableRequest, responsePayload: Protocol.Network.Response) {
const response = this._createResponse(request, responsePayload);
request.request._redirectChain.push(request.request);
response._requestFinished(new Error('Response body is unavailable for redirect responses'));
@ -218,7 +228,7 @@ export class CRNetworkManager {
this._page._frameManager.requestFinished(request.request);
}
_onResponseReceived(event: Protocol.Network.responseReceivedPayload) {
private _onResponseReceived(event: Protocol.Network.responseReceivedPayload) {
const request = this._requestIdToRequest.get(event.requestId);
// FileUpload sends a response without a matching request.
if (!request)
@ -227,7 +237,7 @@ export class CRNetworkManager {
this._page._frameManager.requestReceivedResponse(response);
}
_onLoadingFinished(event: Protocol.Network.loadingFinishedPayload) {
private _onLoadingFinished(event: Protocol.Network.loadingFinishedPayload) {
const request = this._requestIdToRequest.get(event.requestId);
// For certain requestIds we never receive requestWillBeSent event.
// @see https://crbug.com/750469
@ -245,7 +255,7 @@ export class CRNetworkManager {
this._page._frameManager.requestFinished(request.request);
}
_onLoadingFailed(event: Protocol.Network.loadingFailedPayload) {
private _onLoadingFailed(event: Protocol.Network.loadingFailedPayload) {
const request = this._requestIdToRequest.get(event.requestId);
// For certain requestIds we never receive requestWillBeSent event.
// @see https://crbug.com/750469

View file

@ -90,6 +90,10 @@ export class FFPage implements PageDelegate {
promises.push(this._session.send('Page.setJavascriptEnabled', { enabled: false }));
if (options.userAgent)
promises.push(this._session.send('Page.setUserAgent', { userAgent: options.userAgent }));
if (options.cacheEnabled === false)
promises.push(this.setCacheEnabled(false));
if (options.interceptNetwork)
promises.push(this.setRequestInterception(true));
await Promise.all(promises);
}

View file

@ -76,9 +76,6 @@ type PageState = {
mediaType: types.MediaType | null;
colorScheme: types.ColorScheme | null;
extraHTTPHeaders: network.Headers | null;
cacheEnabled: boolean | null;
interceptNetwork: boolean | null;
offlineMode: boolean | null;
credentials: types.Credentials | null;
hasTouch: boolean | null;
};
@ -122,9 +119,6 @@ export class Page extends platform.EventEmitter {
mediaType: null,
colorScheme: null,
extraHTTPHeaders: null,
cacheEnabled: null,
interceptNetwork: null,
offlineMode: null,
credentials: null,
hasTouch: null
};
@ -402,27 +396,6 @@ export class Page extends platform.EventEmitter {
await this._delegate.evaluateOnNewDocument(source);
}
async setCacheEnabled(enabled: boolean = true) {
if (this._state.cacheEnabled === enabled)
return;
this._state.cacheEnabled = enabled;
await this._delegate.setCacheEnabled(enabled);
}
async setRequestInterception(enabled: boolean) {
if (this._state.interceptNetwork === enabled)
return;
this._state.interceptNetwork = enabled;
await this._delegate.setRequestInterception(enabled);
}
async setOfflineMode(enabled: boolean) {
if (this._state.offlineMode === enabled)
return;
this._state.offlineMode = enabled;
await this._delegate.setOfflineMode(enabled);
}
async authenticate(credentials: types.Credentials | null) {
this._state.credentials = credentials;
await this._delegate.authenticate(credentials);

View file

@ -108,14 +108,13 @@ export class WKPage implements PageDelegate {
this._workers.initializeSession(session)
];
if (this._page._state.interceptNetwork)
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true, interceptRequests: true }));
if (this._page._state.offlineMode)
promises.push(session.send('Network.setEmulateOfflineState', { offline: true }));
if (this._page._state.cacheEnabled === false)
promises.push(session.send('Network.setResourceCachingDisabled', { disabled: true }));
const contextOptions = this._page.browserContext()._options;
if (contextOptions.interceptNetwork)
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true, interceptRequests: true }));
if (contextOptions.offlineMode)
promises.push(session.send('Network.setEmulateOfflineState', { offline: true }));
if (contextOptions.cacheEnabled === false)
promises.push(session.send('Network.setResourceCachingDisabled', { disabled: true }));
if (contextOptions.userAgent)
promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent }));
if (this._page._state.mediaType || this._page._state.colorScheme)
@ -586,7 +585,7 @@ export class WKPage implements PageDelegate {
// TODO(einbinder) this will fail if we are an XHR document request
const isNavigationRequest = event.type === 'Document';
const documentId = isNavigationRequest ? event.loaderId : undefined;
const request = new WKInterceptableRequest(session, !!this._page._state.interceptNetwork, frame, event, redirectChain, documentId);
const request = new WKInterceptableRequest(session, !!this._page.browserContext()._options.interceptNetwork, frame, event, redirectChain, documentId);
this._requestIdToRequest.set(event.requestId, request);
this._page._frameManager.requestStarted(request.request);
}

View file

@ -17,7 +17,7 @@
const utils = require('./utils');
module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WEBKIT}) {
module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WEBKIT, FFOX}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit, dit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
@ -313,4 +313,40 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
await page.goto(server.EMPTY_PAGE);
});
});
describe.skip(FFOX)('BrowserContext({offlineMode})', function() {
it('should create offline pages', async({newPage, server}) => {
const page = await newPage({ offlineMode: true });
expect(await page.evaluate(() => window.navigator.onLine)).toBe(false);
let error = null;
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
expect(error).toBeTruthy();
});
});
describe('BrowserContext({cacheEnabled})', function() {
it('should create pages with disabled cache', async({newPage, server}) => {
const page = await newPage({ cacheEnabled: false });
await page.goto(server.PREFIX + '/cached/one-style.html');
// WebKit does r.setCachePolicy(ResourceRequestCachePolicy::ReloadIgnoringCacheData);
// when navigating to the same url, load empty.html to avoid that.
await page.goto(server.EMPTY_PAGE);
const [cachedRequest] = await Promise.all([
server.waitForRequest('/cached/one-style.html'),
page.goto(server.PREFIX + '/cached/one-style.html'),
]);
// Rely on "if-modified-since" caching in our test server.
expect(cachedRequest.headers['if-modified-since']).toBe(undefined);
});
});
describe('BrowserContext({interceptNetwork})', function() {
it('should enable request interception', async({newPage, httpsServer}) => {
const page = await newPage({ ignoreHTTPSErrors: true, interceptNetwork: true });
page.on('request', request => request.abort());
let error;
await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
expect(error).toBeTruthy();
});
});
};

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('BrowserContext.setRequestInterception should work with intervention headers', async({context, server, page}) => {
server.setRoute('/intervention', (req, res) => res.end(`
<script>
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
@ -237,7 +237,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
res.end('console.log(1);');
});
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', request => request.continue());
await page.goto(server.PREFIX + '/intervention');
// Check for feature URL substring rather than https://www.chromestatus.com to

View file

@ -44,7 +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);
await context.setRequestInterception(true);
page.on('request', request => request.continue());
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(oopifs(browser).length).toBe(1);

View file

@ -24,9 +24,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() {
it('should intercept', async({page, server}) => {
await page.setRequestInterception(true);
describe('BrowserContext.setRequestInterception', function() {
it('should intercept', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
expect(request.url()).toContain('empty.html');
expect(request.headers()['user-agent']).toBeTruthy();
@ -41,10 +41,10 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const response = await page.goto(server.EMPTY_PAGE);
expect(response.ok()).toBe(true);
});
it('should work when POST is redirected with 302', async({page, server}) => {
it('should work when POST is redirected with 302', async({context, page, server}) => {
server.setRedirect('/rredirect', '/empty.html');
await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', request => request.continue());
await page.setContent(`
<form action='/rredirect' method='post'>
@ -57,9 +57,9 @@ 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}) => {
it('should work when header manipulation headers with redirect', async({context, page, server}) => {
server.setRedirect('/rrredirect', '/empty.html');
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', request => {
const headers = Object.assign({}, request.headers(), {
foo: 'bar'
@ -69,8 +69,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.goto(server.PREFIX + '/rrredirect');
});
// @see https://github.com/GoogleChrome/puppeteer/issues/4743
it('should be able to remove headers', async({page, server}) => {
await page.setRequestInterception(true);
it('should be able to remove headers', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
const headers = Object.assign({}, request.headers(), {
foo: 'bar',
@ -86,8 +86,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);
it('should contain referer header', async({context, page, server}) => {
await context.setRequestInterception(true);
const requests = [];
page.on('request', request => {
requests.push(request);
@ -103,23 +103,23 @@ 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);
await context.setRequestInterception(true);
page.on('request', request => request.continue());
const response = await page.reload();
expect(response.status()).toBe(200);
});
it('should stop intercepting', async({page, server}) => {
await page.setRequestInterception(true);
it('should stop intercepting', async({context, page, server}) => {
await context.setRequestInterception(true);
page.once('request', request => request.continue());
await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(false);
await context.setRequestInterception(false);
await page.goto(server.EMPTY_PAGE);
});
it('should show custom HTTP headers', async({page, server}) => {
it('should show custom HTTP headers', async({context, page, server}) => {
await page.setExtraHTTPHeaders({
foo: 'bar'
});
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', request => {
expect(request.headers()['foo']).toBe('bar');
request.continue();
@ -128,10 +128,10 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(response.ok()).toBe(true);
});
// @see https://github.com/GoogleChrome/puppeteer/issues/4337
it('should work with redirect inside sync XHR', async({page, server}) => {
it('should work with redirect inside sync XHR', async({context, page, server}) => {
await page.goto(server.EMPTY_PAGE);
server.setRedirect('/logo.png', '/pptr.png');
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', request => request.continue());
const status = await page.evaluate(async() => {
const request = new XMLHttpRequest();
@ -141,9 +141,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
});
expect(status).toBe(200);
});
it('should work with custom referer headers', async({page, server}) => {
it('should work with custom referer headers', async({context, page, server}) => {
await page.setExtraHTTPHeaders({ 'referer': server.EMPTY_PAGE });
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', request => {
expect(request.headers()['referer']).toBe(server.EMPTY_PAGE);
request.continue();
@ -151,8 +151,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const response = await page.goto(server.EMPTY_PAGE);
expect(response.ok()).toBe(true);
});
it('should be abortable', async({page, server}) => {
await page.setRequestInterception(true);
it('should be abortable', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
if (request.url().endsWith('.css'))
request.abort();
@ -166,8 +166,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(response.request().failure()).toBe(null);
expect(failedRequests).toBe(1);
});
it('should be abortable with custom error codes', async({page, server}) => {
await page.setRequestInterception(true);
it('should be abortable with custom error codes', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
request.abort('internetdisconnected');
});
@ -182,11 +182,11 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
else
expect(failedRequest.failure().errorText).toBe('net::ERR_INTERNET_DISCONNECTED');
});
it('should send referer', async({page, server}) => {
it('should send referer', async({context, page, server}) => {
await page.setExtraHTTPHeaders({
referer: 'http://google.com/'
});
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', request => request.continue());
const [request] = await Promise.all([
server.waitForRequest('/grid.html'),
@ -194,8 +194,8 @@ 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);
it('should fail navigation when aborting main resource', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => request.abort());
let error = null;
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
@ -207,8 +207,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
else
expect(error.message).toContain('net::ERR_FAILED');
});
it('should work with redirects', async({page, server}) => {
await page.setRequestInterception(true);
it('should work with redirects', async({context, page, server}) => {
await context.setRequestInterception(true);
const requests = [];
page.on('request', request => {
request.continue();
@ -234,8 +234,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(request.redirectChain().indexOf(request)).toBe(i);
}
});
it('should work with redirects for subresources', async({page, server}) => {
await page.setRequestInterception(true);
it('should work with redirects for subresources', async({context, page, server}) => {
await context.setRequestInterception(true);
const requests = [];
page.on('request', request => {
request.continue();
@ -258,11 +258,11 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(redirectChain[0].url()).toContain('/one-style.css');
expect(redirectChain[2].url()).toContain('/three-style.css');
});
it('should work with equal requests', async({page, server}) => {
it('should work with equal requests', async({context, page, server}) => {
await page.goto(server.EMPTY_PAGE);
let responseCount = 1;
server.setRoute('/zzz', (req, res) => res.end((responseCount++) * 11 + ''));
await page.setRequestInterception(true);
await context.setRequestInterception(true);
let spinner = false;
// Cancel 2nd request.
@ -277,8 +277,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);
it('should navigate to dataURL and not fire dataURL requests', async({context, page, server}) => {
await context.setRequestInterception(true);
const requests = [];
page.on('request', request => {
requests.push(request);
@ -289,9 +289,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(response).toBe(null);
expect(requests.length).toBe(0);
});
it('should be able to fetch dataURL and not fire dataURL requests', async({page, server}) => {
it('should be able to fetch dataURL and not fire dataURL requests', async({context, page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true);
await context.setRequestInterception(true);
const requests = [];
page.on('request', request => {
requests.push(request);
@ -302,8 +302,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(text).toBe('<div>yo</div>');
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);
it.skip(FFOX)('should navigate to URL with hash and and fire requests without hash', async({context, page, server}) => {
await context.setRequestInterception(true);
const requests = [];
page.on('request', request => {
requests.push(request);
@ -315,25 +315,25 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(server.EMPTY_PAGE);
});
it('should work with encoded server', async({page, server}) => {
it('should work with encoded server', async({context, page, server}) => {
// The requestWillBeSent will report encoded URL, whereas interception will
// report URL as-is. @see crbug.com/759388
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', 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);
it('should work with badly encoded server', async({context, page, server}) => {
await context.setRequestInterception(true);
server.setRoute('/malformed?rnd=%911', (req, res) => res.end());
page.on('request', 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}) => {
it('should work with encoded server - 2', async({context, page, server}) => {
// The requestWillBeSent will report URL as-is, whereas interception will
// report encoded URL for stylesheet. @see crbug.com/759388
await page.setRequestInterception(true);
await context.setRequestInterception(true);
const requests = [];
page.on('request', request => {
request.continue();
@ -344,9 +344,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(requests.length).toBe(1);
expect(requests[0].response().status()).toBe(404);
});
it('should not throw "Invalid Interception Id" if the request was cancelled', async({page, server}) => {
it('should not throw "Invalid Interception Id" if the request was cancelled', async({context, page, server}) => {
await page.setContent('<iframe></iframe>');
await page.setRequestInterception(true);
await context.setRequestInterception(true);
let request = null;
page.on('request', async r => request = r);
page.$eval('iframe', (frame, url) => frame.src = url, server.EMPTY_PAGE),
@ -371,9 +371,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.goto(server.EMPTY_PAGE);
expect(error.message).toContain('Request Interception is not enabled');
});
it('should intercept main resource during cross-process navigation', async({page, server}) => {
it('should intercept main resource during cross-process navigation', async({context, page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true);
await context.setRequestInterception(true);
let intercepted = false;
page.on('request', request => {
if (request.url().includes(server.CROSS_PROCESS_PREFIX + '/empty.html'))
@ -384,8 +384,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(response.ok()).toBe(true);
expect(intercepted).toBe(true);
});
it('should not throw when continued after navigation', async({page, server}) => {
await page.setRequestInterception(true);
it('should not throw when continued after navigation', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
if (request.url() !== server.PREFIX + '/one-style.css')
request.continue();
@ -399,8 +399,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
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.setRequestInterception(true);
it('should not throw when continued after cross-process navigation', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
if (request.url() !== server.PREFIX + '/one-style.css')
request.continue();
@ -417,13 +417,13 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
});
describe('Request.continue', function() {
it('should work', async({page, server}) => {
await page.setRequestInterception(true);
it('should work', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => request.continue());
await page.goto(server.EMPTY_PAGE);
});
it('should amend HTTP headers', async({page, server}) => {
await page.setRequestInterception(true);
it('should amend HTTP headers', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
const headers = Object.assign({}, request.headers());
headers['FOO'] = 'bar';
@ -436,10 +436,10 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
]);
expect(request.headers['foo']).toBe('bar');
});
it('should amend method', async({page, server}) => {
it('should amend method', async({context, page, server}) => {
const sRequest = server.waitForRequest('/sleep.zzz');
await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', request => {
request.continue({ method: 'POST' });
});
@ -450,18 +450,18 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(request.method).toBe('POST');
expect((await sRequest).method).toBe('POST');
});
it('should amend method on main request', async({page, server}) => {
it('should amend method on main request', async({context, page, server}) => {
const request = server.waitForRequest('/empty.html');
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', 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}) => {
it('should amend post data', async({context, page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true);
await context.setRequestInterception(true);
page.on('request', request => {
request.continue({ postData: 'doggo' });
});
@ -474,8 +474,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
});
describe('Request.fulfill', function() {
it('should work', async({page, server}) => {
await page.setRequestInterception(true);
it('should work', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
request.fulfill({
status: 201,
@ -491,8 +491,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(response.headers().foo).toBe('bar');
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
});
it('should work with status code 422', async({page, server}) => {
await page.setRequestInterception(true);
it('should work with status code 422', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
request.fulfill({
status: 422,
@ -504,8 +504,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
expect(response.statusText()).toBe('Unprocessable Entity');
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
});
it('should allow mocking binary responses', async({page, server}) => {
await page.setRequestInterception(true);
it('should allow mocking binary responses', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
request.fulfill({
@ -522,8 +522,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const img = await page.$('img');
expect(await img.screenshot()).toBeGolden('mock-binary-response.png');
});
it('should stringify intercepted request response headers', async({page, server}) => {
await page.setRequestInterception(true);
it('should stringify intercepted request response headers', async({context, page, server}) => {
await context.setRequestInterception(true);
page.on('request', request => {
request.fulfill({
status: 200,
@ -542,7 +542,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
});
describe('Page.authenticate', function() {
it('should work', async({page, server}) => {
it('should work', async({context, page, server}) => {
server.setAuth('/empty.html', 'user', 'pass');
let response = await page.goto(server.EMPTY_PAGE);
expect(response.status()).toBe(401);
@ -553,7 +553,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
response = await page.reload();
expect(response.status()).toBe(200);
});
it('should fail if wrong credentials', async({page, server}) => {
it('should fail if wrong credentials', async({context, page, server}) => {
// Use unique user/password since Chromium caches credentials per origin.
server.setAuth('/empty.html', 'user2', 'pass2');
await page.authenticate({
@ -563,7 +563,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const response = await page.goto(server.EMPTY_PAGE);
expect(response.status()).toBe(401);
});
it('should allow disable authentication', async({page, server}) => {
it('should allow disable authentication', async({context, page, server}) => {
// Use unique user/password since Chromium caches credentials per origin.
server.setAuth('/empty.html', 'user3', 'pass3');
await page.authenticate({
@ -580,32 +580,41 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
});
describe.skip(FFOX)('Interception.setOfflineMode', function() {
it('should work', async({page, server}) => {
await page.setOfflineMode(true);
it('should work', async({context, page, server}) => {
await context.setOfflineMode(true);
let error = null;
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
expect(error).toBeTruthy();
await page.setOfflineMode(false);
await context.setOfflineMode(false);
const response = await page.goto(server.EMPTY_PAGE);
expect(response.status()).toBe(200);
});
it('should emulate navigator.onLine', async({page, server}) => {
it('should emulate navigator.onLine', async({context, page}) => {
expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
await page.setOfflineMode(true);
await context.setOfflineMode(true);
expect(await page.evaluate(() => window.navigator.onLine)).toBe(false);
await page.setOfflineMode(false);
await context.setOfflineMode(false);
expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
});
it('should propagate to new pages', async({context, page, server}) => {
await context.setOfflineMode(true);
expect(await page.evaluate(() => window.navigator.onLine)).toBe(false);
const newPage = await context.newPage();
expect(await newPage.evaluate(() => window.navigator.onLine)).toBe(false);
let error = null;
await newPage.goto(server.EMPTY_PAGE).catch(e => error = e);
expect(error).toBeTruthy();
});
});
describe('Interception vs isNavigationRequest', () => {
it('should work with request interception', async({page, server}) => {
it('should work with request interception', async({context, page, server}) => {
const requests = new Map();
page.on('request', request => {
requests.set(request.url().split('/').pop(), request);
request.continue();
});
await page.setRequestInterception(true);
await context.setRequestInterception(true);
server.setRedirect('/rrredirect', '/frames/one-frame.html');
await page.goto(server.PREFIX + '/rrredirect');
expect(requests.get('rrredirect').isNavigationRequest()).toBe(true);
@ -616,11 +625,11 @@ 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);
describe('Context.setCacheEnabled', function() {
it('should stay disabled when toggling request interception on/off', async({context, page, server}) => {
await context.setCacheEnabled(false);
await context.setRequestInterception(true);
await context.setRequestInterception(false);
await page.goto(server.PREFIX + '/cached/one-style.html');
const [nonCachedRequest] = await Promise.all([
@ -634,8 +643,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
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());
const response = await page.goto(httpsServer.EMPTY_PAGE);
expect(response.status()).toBe(200);

View file

@ -831,9 +831,9 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
const error = await navigationPromise;
expect(error.message).toContain('frame was detached');
});
it('should return matching responses', async({page, server}) => {
it('should return matching responses', async({context, page, server}) => {
// Disable cache: otherwise, chromium will cache similar requests.
await page.setCacheEnabled(false);
await context.setCacheEnabled(false);
await page.goto(server.EMPTY_PAGE);
// Attach three frames.
const frames = [

View file

@ -779,8 +779,8 @@ module.exports.describe = function({testRunner, expect, headless, playwright, FF
});
});
describe('Page.setCacheEnabled', function() {
it('should enable or disable the cache based on the state passed', async({page, server}) => {
describe('Context.setCacheEnabled', function() {
it('should enable or disable the cache based on the state passed', async({context, page, server}) => {
await page.goto(server.PREFIX + '/cached/one-style.html');
// WebKit does r.setCachePolicy(ResourceRequestCachePolicy::ReloadIgnoringCacheData);
// when navigating to the same url, load empty.html to avoid that.
@ -792,7 +792,7 @@ module.exports.describe = function({testRunner, expect, headless, playwright, FF
// Rely on "if-modified-since" caching in our test server.
expect(cachedRequest.headers['if-modified-since']).not.toBe(undefined);
await page.setCacheEnabled(false);
await context.setCacheEnabled(false);
await page.goto(server.EMPTY_PAGE);
const [nonCachedRequest] = await Promise.all([
server.waitForRequest('/cached/one-style.html'),

View file

@ -19,8 +19,8 @@ module.exports.describe = function ({ testRunner, expect }) {
const {it, fit, xit, dit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('provisional page', function() {
it('extraHttpHeaders should be pushed to provisional page', async({page, server}) => {
describe('Provisional page', function() {
it('should receive extraHttpHeaders', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const pagePath = '/one-style.html';
server.setRoute(pagePath, async (req, res) => {
@ -35,14 +35,14 @@ 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}) => {
it('should continue loading when interception gets disabled', async({context, page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true);
await context.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'));
intercepted = context.setRequestInterception(false).then(() => order.push('setRequestInterception'));
});
const response = await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html').then(response => {
order.push('goto');