fix: move offline/cache/interception switches to BrowserContext

This commit is contained in:
Yury Semikhatsky 2020-01-29 09:17:16 -08:00
parent 492304be41
commit 178fc0ba83
13 changed files with 247 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 page cache in the context. By defaul caching 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 cache.
- returns: <[Promise]>
Toggles ignoring cache for each request based on the enabled state. By default, caching 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 page.
- 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 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 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 page caching.
### 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,8 @@ 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));
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,30 @@ 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);
});
});
};

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