api(popups): expose BrowserContext.route() (#1295)
This commit is contained in:
parent
adee9a9bd3
commit
ea6978a3d8
41
docs/api.md
41
docs/api.md
|
|
@ -290,6 +290,7 @@ await context.close();
|
||||||
- [browserContext.exposeFunction(name, playwrightFunction)](#browsercontextexposefunctionname-playwrightfunction)
|
- [browserContext.exposeFunction(name, playwrightFunction)](#browsercontextexposefunctionname-playwrightfunction)
|
||||||
- [browserContext.newPage()](#browsercontextnewpage)
|
- [browserContext.newPage()](#browsercontextnewpage)
|
||||||
- [browserContext.pages()](#browsercontextpages)
|
- [browserContext.pages()](#browsercontextpages)
|
||||||
|
- [browserContext.route(url, handler)](#browsercontextrouteurl-handler)
|
||||||
- [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
|
- [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
|
||||||
- [browserContext.setDefaultNavigationTimeout(timeout)](#browsercontextsetdefaultnavigationtimeouttimeout)
|
- [browserContext.setDefaultNavigationTimeout(timeout)](#browsercontextsetdefaultnavigationtimeouttimeout)
|
||||||
- [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout)
|
- [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout)
|
||||||
|
|
@ -466,6 +467,38 @@ Creates a new page in the browser context.
|
||||||
|
|
||||||
An array of all pages inside the browser context.
|
An array of all pages inside the browser context.
|
||||||
|
|
||||||
|
#### browserContext.route(url, handler)
|
||||||
|
- `url` <[string]|[RegExp]|[function]\([string]\):[boolean]> A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||||
|
- `handler` <[function]\([Request]\)> handler function to route the request.
|
||||||
|
- returns: <[Promise]>.
|
||||||
|
|
||||||
|
Routing activates the request interception and enables `request.abort`, `request.continue` and `request.fulfill` methods on the request. This provides the capability to modify network requests that are made by any page in the browser context.
|
||||||
|
|
||||||
|
Once request interception is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||||
|
An example of a naïve request interceptor that aborts all image requests:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const context = await browser.newContext();
|
||||||
|
await context.route('**/*.{png,jpg,jpeg}', request => request.abort());
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.goto('https://example.com');
|
||||||
|
await browser.close();
|
||||||
|
```
|
||||||
|
|
||||||
|
or the same snippet using a regex pattern instead:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const context = await browser.newContext();
|
||||||
|
await context.route(/(\.png$)|(\.jpg$)/, request => request.abort());
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.goto('https://example.com');
|
||||||
|
await browser.close();
|
||||||
|
```
|
||||||
|
|
||||||
|
Page routes (set up with [page.route(url, handler)](#pagerouteurl-handler)) take precedence over browser context routes when request matches both handlers.
|
||||||
|
|
||||||
|
> **NOTE** Enabling request interception disables http cache.
|
||||||
|
|
||||||
#### browserContext.setCookies(cookies)
|
#### browserContext.setCookies(cookies)
|
||||||
- `cookies` <[Array]<[Object]>>
|
- `cookies` <[Array]<[Object]>>
|
||||||
- `name` <[string]> **required**
|
- `name` <[string]> **required**
|
||||||
|
|
@ -1433,7 +1466,7 @@ If `key` is a single character and no modifier keys besides `Shift` are being he
|
||||||
|
|
||||||
#### page.route(url, handler)
|
#### page.route(url, handler)
|
||||||
- `url` <[string]|[RegExp]|[function]\([string]\):[boolean]> A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
- `url` <[string]|[RegExp]|[function]\([string]\):[boolean]> A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||||
- `handler` <[function]\([Request]\)> handler function to router the request.
|
- `handler` <[function]\([Request]\)> handler function to route the request.
|
||||||
- returns: <[Promise]>.
|
- returns: <[Promise]>.
|
||||||
|
|
||||||
Routing activates the request interception and enables `request.abort`, `request.continue` and
|
Routing activates the request interception and enables `request.abort`, `request.continue` and
|
||||||
|
|
@ -1445,7 +1478,6 @@ An example of a naïve request interceptor that aborts all image requests:
|
||||||
```js
|
```js
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
await page.route('**/*.{png,jpg,jpeg}', request => request.abort());
|
await page.route('**/*.{png,jpg,jpeg}', request => request.abort());
|
||||||
// await page.route(/\.(png|jpeg|jpg)$/, request => request.abort()); // <-- same thing
|
|
||||||
await page.goto('https://example.com');
|
await page.goto('https://example.com');
|
||||||
await browser.close();
|
await browser.close();
|
||||||
```
|
```
|
||||||
|
|
@ -1459,7 +1491,9 @@ await page.goto('https://example.com');
|
||||||
await browser.close();
|
await browser.close();
|
||||||
```
|
```
|
||||||
|
|
||||||
> **NOTE** Enabling request interception disables page caching.
|
Page routes take precedence over browser context routes (set up with [browserContext.route(url, handler)](#browsercontextrouteurl-handler)) when request matches both handlers.
|
||||||
|
|
||||||
|
> **NOTE** Enabling request interception disables http cache.
|
||||||
|
|
||||||
#### page.screenshot([options])
|
#### page.screenshot([options])
|
||||||
- `options` <[Object]> Options object which might have the following properties:
|
- `options` <[Object]> Options object which might have the following properties:
|
||||||
|
|
@ -3987,6 +4021,7 @@ const backgroundPage = await backroundPageTarget.page();
|
||||||
- [browserContext.exposeFunction(name, playwrightFunction)](#browsercontextexposefunctionname-playwrightfunction)
|
- [browserContext.exposeFunction(name, playwrightFunction)](#browsercontextexposefunctionname-playwrightfunction)
|
||||||
- [browserContext.newPage()](#browsercontextnewpage)
|
- [browserContext.newPage()](#browsercontextnewpage)
|
||||||
- [browserContext.pages()](#browsercontextpages)
|
- [browserContext.pages()](#browsercontextpages)
|
||||||
|
- [browserContext.route(url, handler)](#browsercontextrouteurl-handler)
|
||||||
- [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
|
- [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
|
||||||
- [browserContext.setDefaultNavigationTimeout(timeout)](#browsercontextsetdefaultnavigationtimeouttimeout)
|
- [browserContext.setDefaultNavigationTimeout(timeout)](#browsercontextsetdefaultnavigationtimeouttimeout)
|
||||||
- [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout)
|
- [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout)
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ export interface BrowserContext {
|
||||||
setHTTPCredentials(httpCredentials: types.Credentials | null): Promise<void>;
|
setHTTPCredentials(httpCredentials: types.Credentials | null): Promise<void>;
|
||||||
addInitScript(script: Function | string | { path?: string, content?: string }, ...args: any[]): Promise<void>;
|
addInitScript(script: Function | string | { path?: string, content?: string }, ...args: any[]): Promise<void>;
|
||||||
exposeFunction(name: string, playwrightFunction: Function): Promise<void>;
|
exposeFunction(name: string, playwrightFunction: Function): Promise<void>;
|
||||||
|
route(url: types.URLMatch, handler: network.RouteHandler): Promise<void>;
|
||||||
waitForEvent(event: string, optionsOrPredicate?: Function | (types.TimeoutOptions & { predicate?: Function })): Promise<any>;
|
waitForEvent(event: string, optionsOrPredicate?: Function | (types.TimeoutOptions & { predicate?: Function })): Promise<any>;
|
||||||
close(): Promise<void>;
|
close(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
@ -62,6 +63,7 @@ export abstract class BrowserContextBase extends platform.EventEmitter implement
|
||||||
readonly _timeoutSettings = new TimeoutSettings();
|
readonly _timeoutSettings = new TimeoutSettings();
|
||||||
readonly _pageBindings = new Map<string, PageBinding>();
|
readonly _pageBindings = new Map<string, PageBinding>();
|
||||||
readonly _options: BrowserContextOptions;
|
readonly _options: BrowserContextOptions;
|
||||||
|
readonly _routes: { url: types.URLMatch, handler: (request: network.Request) => any }[] = [];
|
||||||
_closed = false;
|
_closed = false;
|
||||||
private readonly _closePromise: Promise<Error>;
|
private readonly _closePromise: Promise<Error>;
|
||||||
private _closePromiseFulfill: ((error: Error) => void) | undefined;
|
private _closePromiseFulfill: ((error: Error) => void) | undefined;
|
||||||
|
|
@ -100,6 +102,7 @@ export abstract class BrowserContextBase extends platform.EventEmitter implement
|
||||||
abstract setOffline(offline: boolean): Promise<void>;
|
abstract setOffline(offline: boolean): Promise<void>;
|
||||||
abstract addInitScript(script: string | Function | { path?: string | undefined; content?: string | undefined; }, ...args: any[]): Promise<void>;
|
abstract addInitScript(script: string | Function | { path?: string | undefined; content?: string | undefined; }, ...args: any[]): Promise<void>;
|
||||||
abstract exposeFunction(name: string, playwrightFunction: Function): Promise<void>;
|
abstract exposeFunction(name: string, playwrightFunction: Function): Promise<void>;
|
||||||
|
abstract route(url: types.URLMatch, handler: network.RouteHandler): Promise<void>;
|
||||||
abstract close(): Promise<void>;
|
abstract close(): Promise<void>;
|
||||||
|
|
||||||
setDefaultNavigationTimeout(timeout: number) {
|
setDefaultNavigationTimeout(timeout: number) {
|
||||||
|
|
|
||||||
|
|
@ -393,6 +393,12 @@ export class CRBrowserContext extends BrowserContextBase {
|
||||||
await (page._delegate as CRPage).exposeBinding(binding);
|
await (page._delegate as CRPage).exposeBinding(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async route(url: types.URLMatch, handler: network.RouteHandler): Promise<void> {
|
||||||
|
this._routes.push({ url, handler });
|
||||||
|
for (const page of this._existingPages())
|
||||||
|
await (page._delegate as CRPage).updateRequestInterception();
|
||||||
|
}
|
||||||
|
|
||||||
async close() {
|
async close() {
|
||||||
if (this._closed)
|
if (this._closed)
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ export class CRPage implements PageDelegate {
|
||||||
if (options.geolocation)
|
if (options.geolocation)
|
||||||
promises.push(this._client.send('Emulation.setGeolocationOverride', options.geolocation));
|
promises.push(this._client.send('Emulation.setGeolocationOverride', options.geolocation));
|
||||||
promises.push(this.updateExtraHTTPHeaders());
|
promises.push(this.updateExtraHTTPHeaders());
|
||||||
|
promises.push(this.updateRequestInterception());
|
||||||
if (options.offline)
|
if (options.offline)
|
||||||
promises.push(this._networkManager.setOffline(options.offline));
|
promises.push(this._networkManager.setOffline(options.offline));
|
||||||
if (options.httpCredentials)
|
if (options.httpCredentials)
|
||||||
|
|
@ -376,8 +377,8 @@ export class CRPage implements PageDelegate {
|
||||||
await this._client.send('Emulation.setEmulatedMedia', { media: mediaType || '', features });
|
await this._client.send('Emulation.setEmulatedMedia', { media: mediaType || '', features });
|
||||||
}
|
}
|
||||||
|
|
||||||
async setRequestInterception(enabled: boolean): Promise<void> {
|
async updateRequestInterception(): Promise<void> {
|
||||||
await this._networkManager.setRequestInterception(enabled);
|
await this._networkManager.setRequestInterception(this._page._needsRequestInterception());
|
||||||
}
|
}
|
||||||
|
|
||||||
async setFileChooserIntercepted(enabled: boolean) {
|
async setFileChooserIntercepted(enabled: boolean) {
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,12 @@ export class FFBrowserContext extends BrowserContextBase {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async route(url: types.URLMatch, handler: network.RouteHandler): Promise<void> {
|
||||||
|
this._routes.push({ url, handler });
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
// TODO: update interception on the context if this is a first route.
|
||||||
|
}
|
||||||
|
|
||||||
async close() {
|
async close() {
|
||||||
if (this._closed)
|
if (this._closed)
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -303,8 +303,8 @@ export class FFPage implements PageDelegate {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setRequestInterception(enabled: boolean): Promise<void> {
|
async updateRequestInterception(): Promise<void> {
|
||||||
await this._networkManager.setRequestInterception(enabled);
|
await this._networkManager.setRequestInterception(this._page._needsRequestInterception());
|
||||||
}
|
}
|
||||||
|
|
||||||
async setFileChooserIntercepted(enabled: boolean) {
|
async setFileChooserIntercepted(enabled: boolean) {
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ export type SetNetworkCookieParam = {
|
||||||
sameSite?: 'Strict' | 'Lax' | 'None'
|
sameSite?: 'Strict' | 'Lax' | 'None'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type RouteHandler = (request: Request) => void;
|
||||||
|
|
||||||
export function filterCookies(cookies: NetworkCookie[], urls: string | string[] = []): NetworkCookie[] {
|
export function filterCookies(cookies: NetworkCookie[], urls: string | string[] = []): NetworkCookie[] {
|
||||||
if (!Array.isArray(urls))
|
if (!Array.isArray(urls))
|
||||||
urls = [ urls ];
|
urls = [ urls ];
|
||||||
|
|
|
||||||
29
src/page.ts
29
src/page.ts
|
|
@ -48,7 +48,7 @@ export interface PageDelegate {
|
||||||
updateExtraHTTPHeaders(): Promise<void>;
|
updateExtraHTTPHeaders(): Promise<void>;
|
||||||
setViewportSize(viewportSize: types.Size): Promise<void>;
|
setViewportSize(viewportSize: types.Size): Promise<void>;
|
||||||
setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void>;
|
setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void>;
|
||||||
setRequestInterception(enabled: boolean): Promise<void>;
|
updateRequestInterception(): Promise<void>;
|
||||||
setFileChooserIntercepted(enabled: boolean): Promise<void>;
|
setFileChooserIntercepted(enabled: boolean): Promise<void>;
|
||||||
|
|
||||||
canScreenshotOutsideViewport(): boolean;
|
canScreenshotOutsideViewport(): boolean;
|
||||||
|
|
@ -80,8 +80,6 @@ type PageState = {
|
||||||
mediaType: types.MediaType | null;
|
mediaType: types.MediaType | null;
|
||||||
colorScheme: types.ColorScheme | null;
|
colorScheme: types.ColorScheme | null;
|
||||||
extraHTTPHeaders: network.Headers | null;
|
extraHTTPHeaders: network.Headers | null;
|
||||||
interceptNetwork: boolean | null;
|
|
||||||
hasTouch: boolean | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FileChooser = {
|
export type FileChooser = {
|
||||||
|
|
@ -127,7 +125,7 @@ export class Page extends platform.EventEmitter {
|
||||||
private _workers = new Map<string, Worker>();
|
private _workers = new Map<string, Worker>();
|
||||||
readonly pdf: ((options?: types.PDFOptions) => Promise<platform.BufferType>) | undefined;
|
readonly pdf: ((options?: types.PDFOptions) => Promise<platform.BufferType>) | undefined;
|
||||||
readonly coverage: any;
|
readonly coverage: any;
|
||||||
readonly _requestHandlers: { url: types.URLMatch, handler: (request: network.Request) => void }[] = [];
|
readonly _routes: { url: types.URLMatch, handler: (request: network.Request) => any }[] = [];
|
||||||
_ownedContext: BrowserContext | undefined;
|
_ownedContext: BrowserContext | undefined;
|
||||||
|
|
||||||
constructor(delegate: PageDelegate, browserContext: BrowserContextBase) {
|
constructor(delegate: PageDelegate, browserContext: BrowserContextBase) {
|
||||||
|
|
@ -150,8 +148,6 @@ export class Page extends platform.EventEmitter {
|
||||||
mediaType: null,
|
mediaType: null,
|
||||||
colorScheme: null,
|
colorScheme: null,
|
||||||
extraHTTPHeaders: null,
|
extraHTTPHeaders: null,
|
||||||
interceptNetwork: null,
|
|
||||||
hasTouch: null,
|
|
||||||
};
|
};
|
||||||
this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate));
|
this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate));
|
||||||
this.keyboard = new input.Keyboard(delegate.rawKeyboard);
|
this.keyboard = new input.Keyboard(delegate.rawKeyboard);
|
||||||
|
|
@ -391,19 +387,26 @@ export class Page extends platform.EventEmitter {
|
||||||
await this._delegate.evaluateOnNewDocument(await helper.evaluationScript(script, args));
|
await this._delegate.evaluateOnNewDocument(await helper.evaluationScript(script, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
async route(url: types.URLMatch, handler: (request: network.Request) => void) {
|
_needsRequestInterception(): boolean {
|
||||||
if (!this._state.interceptNetwork) {
|
return this._routes.length > 0 || this._browserContext._routes.length > 0;
|
||||||
this._state.interceptNetwork = true;
|
}
|
||||||
await this._delegate.setRequestInterception(true);
|
|
||||||
}
|
async route(url: types.URLMatch, handler: network.RouteHandler): Promise<void> {
|
||||||
this._requestHandlers.push({ url, handler });
|
this._routes.push({ url, handler });
|
||||||
|
await this._delegate.updateRequestInterception();
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestStarted(request: network.Request) {
|
_requestStarted(request: network.Request) {
|
||||||
this.emit(Events.Page.Request, request);
|
this.emit(Events.Page.Request, request);
|
||||||
if (!request._isIntercepted())
|
if (!request._isIntercepted())
|
||||||
return;
|
return;
|
||||||
for (const { url, handler } of this._requestHandlers) {
|
for (const { url, handler } of this._routes) {
|
||||||
|
if (platform.urlMatches(request.url(), url)) {
|
||||||
|
handler(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const { url, handler } of this._browserContext._routes) {
|
||||||
if (platform.urlMatches(request.url(), url)) {
|
if (platform.urlMatches(request.url(), url)) {
|
||||||
handler(request);
|
handler(request);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -315,6 +315,12 @@ export class WKBrowserContext extends BrowserContextBase {
|
||||||
await (page._delegate as WKPage).exposeBinding(binding);
|
await (page._delegate as WKPage).exposeBinding(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async route(url: types.URLMatch, handler: network.RouteHandler): Promise<void> {
|
||||||
|
this._routes.push({ url, handler });
|
||||||
|
for (const page of this._existingPages())
|
||||||
|
await (page._delegate as WKPage).updateRequestInterception();
|
||||||
|
}
|
||||||
|
|
||||||
async close() {
|
async close() {
|
||||||
if (this._closed)
|
if (this._closed)
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ export class WKPage implements PageDelegate {
|
||||||
if (contextOptions.javaScriptEnabled === false)
|
if (contextOptions.javaScriptEnabled === false)
|
||||||
promises.push(this._pageProxySession.send('Emulation.setJavaScriptEnabled', { enabled: false }));
|
promises.push(this._pageProxySession.send('Emulation.setJavaScriptEnabled', { enabled: false }));
|
||||||
if (this._page._state.viewportSize || contextOptions.viewport)
|
if (this._page._state.viewportSize || contextOptions.viewport)
|
||||||
promises.push(this._updateViewport(true /* updateTouch */));
|
promises.push(this._updateViewport());
|
||||||
promises.push(this.updateHttpCredentials());
|
promises.push(this.updateHttpCredentials());
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
@ -132,8 +132,7 @@ export class WKPage implements PageDelegate {
|
||||||
session.send('Network.enable'),
|
session.send('Network.enable'),
|
||||||
this._workers.initializeSession(session)
|
this._workers.initializeSession(session)
|
||||||
];
|
];
|
||||||
|
if (this._page._needsRequestInterception())
|
||||||
if (this._page._state.interceptNetwork)
|
|
||||||
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true, interceptRequests: true }));
|
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true, interceptRequests: true }));
|
||||||
|
|
||||||
const contextOptions = this._browserContext._options;
|
const contextOptions = this._browserContext._options;
|
||||||
|
|
@ -149,8 +148,7 @@ export class WKPage implements PageDelegate {
|
||||||
promises.push(session.send('Network.setExtraHTTPHeaders', { headers: this._calculateExtraHTTPHeaders() }));
|
promises.push(session.send('Network.setExtraHTTPHeaders', { headers: this._calculateExtraHTTPHeaders() }));
|
||||||
if (contextOptions.offline)
|
if (contextOptions.offline)
|
||||||
promises.push(session.send('Network.setEmulateOfflineState', { offline: true }));
|
promises.push(session.send('Network.setEmulateOfflineState', { offline: true }));
|
||||||
if (this._page._state.hasTouch)
|
promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: contextOptions.viewport ? !!contextOptions.viewport.isMobile : false }));
|
||||||
promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: true }));
|
|
||||||
if (contextOptions.timezoneId) {
|
if (contextOptions.timezoneId) {
|
||||||
promises.push(session.send('Page.setTimeZone', { timeZone: contextOptions.timezoneId }).
|
promises.push(session.send('Page.setTimeZone', { timeZone: contextOptions.timezoneId }).
|
||||||
catch(e => { throw new Error(`Invalid timezone ID: ${contextOptions.timezoneId}`); }));
|
catch(e => { throw new Error(`Invalid timezone ID: ${contextOptions.timezoneId}`); }));
|
||||||
|
|
@ -476,10 +474,10 @@ export class WKPage implements PageDelegate {
|
||||||
|
|
||||||
async setViewportSize(viewportSize: types.Size): Promise<void> {
|
async setViewportSize(viewportSize: types.Size): Promise<void> {
|
||||||
assert(this._page._state.viewportSize === viewportSize);
|
assert(this._page._state.viewportSize === viewportSize);
|
||||||
await this._updateViewport(false /* updateTouch */);
|
await this._updateViewport();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _updateViewport(updateTouch: boolean): Promise<void> {
|
async _updateViewport(): Promise<void> {
|
||||||
let viewport = this._browserContext._options.viewport || { width: 0, height: 0 };
|
let viewport = this._browserContext._options.viewport || { width: 0, height: 0 };
|
||||||
const viewportSize = this._page._state.viewportSize;
|
const viewportSize = this._page._state.viewportSize;
|
||||||
if (viewportSize)
|
if (viewportSize)
|
||||||
|
|
@ -492,12 +490,11 @@ export class WKPage implements PageDelegate {
|
||||||
deviceScaleFactor: viewport.deviceScaleFactor || 1
|
deviceScaleFactor: viewport.deviceScaleFactor || 1
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
if (updateTouch)
|
|
||||||
promises.push(this._updateState('Page.setTouchEmulationEnabled', { enabled: !!viewport.isMobile }));
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setRequestInterception(enabled: boolean): Promise<void> {
|
async updateRequestInterception(): Promise<void> {
|
||||||
|
const enabled = this._page._needsRequestInterception();
|
||||||
await this._updateState('Network.setInterceptionEnabled', { enabled, interceptRequests: enabled });
|
await this._updateState('Network.setInterceptionEnabled', { enabled, interceptRequests: enabled });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -735,7 +732,7 @@ export class WKPage implements PageDelegate {
|
||||||
// TODO(einbinder) this will fail if we are an XHR document request
|
// TODO(einbinder) this will fail if we are an XHR document request
|
||||||
const isNavigationRequest = event.type === 'Document';
|
const isNavigationRequest = event.type === 'Document';
|
||||||
const documentId = isNavigationRequest ? event.loaderId : undefined;
|
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._needsRequestInterception(), frame, event, redirectChain, documentId);
|
||||||
this._requestIdToRequest.set(event.requestId, request);
|
this._requestIdToRequest.set(event.requestId, request);
|
||||||
this._page._frameManager.requestStarted(request.request);
|
this._page._frameManager.requestStarted(request.request);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,6 +367,44 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, FF
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe.fail(FFOX)('BrowserContext.route', () => {
|
||||||
|
it('should intercept', async({browser, server}) => {
|
||||||
|
const context = await browser.newContext();
|
||||||
|
let intercepted = false;
|
||||||
|
await context.route('**/empty.html', request => {
|
||||||
|
intercepted = true;
|
||||||
|
expect(request.url()).toContain('empty.html');
|
||||||
|
expect(request.headers()['user-agent']).toBeTruthy();
|
||||||
|
expect(request.method()).toBe('GET');
|
||||||
|
expect(request.postData()).toBe(undefined);
|
||||||
|
expect(request.isNavigationRequest()).toBe(true);
|
||||||
|
expect(request.resourceType()).toBe('document');
|
||||||
|
expect(request.frame() === page.mainFrame()).toBe(true);
|
||||||
|
expect(request.frame().url()).toBe('about:blank');
|
||||||
|
request.continue();
|
||||||
|
});
|
||||||
|
const page = await context.newPage();
|
||||||
|
const response = await page.goto(server.EMPTY_PAGE);
|
||||||
|
expect(response.ok()).toBe(true);
|
||||||
|
expect(intercepted).toBe(true);
|
||||||
|
await context.close();
|
||||||
|
});
|
||||||
|
it('should yield to page.route', async({browser, server}) => {
|
||||||
|
const context = await browser.newContext();
|
||||||
|
await context.route('**/empty.html', request => {
|
||||||
|
request.fulfill({ status: 200, body: 'context' });
|
||||||
|
});
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.route('**/empty.html', request => {
|
||||||
|
request.fulfill({ status: 200, body: 'page' });
|
||||||
|
});
|
||||||
|
const response = await page.goto(server.EMPTY_PAGE);
|
||||||
|
expect(response.ok()).toBe(true);
|
||||||
|
expect(await response.text()).toBe('page');
|
||||||
|
await context.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('BrowserContext.setHTTPCredentials', function() {
|
describe('BrowserContext.setHTTPCredentials', function() {
|
||||||
it('should work', async({browser, server}) => {
|
it('should work', async({browser, server}) => {
|
||||||
server.setAuth('/empty.html', 'user', 'pass');
|
server.setAuth('/empty.html', 'user', 'pass');
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
|
|
||||||
describe('Page.route', function() {
|
describe('Page.route', function() {
|
||||||
it('should intercept', async({page, server}) => {
|
it('should intercept', async({page, server}) => {
|
||||||
await page.route('/empty.html', request => {
|
let intercepted = false;
|
||||||
|
await page.route('**/empty.html', request => {
|
||||||
expect(request.url()).toContain('empty.html');
|
expect(request.url()).toContain('empty.html');
|
||||||
expect(request.headers()['user-agent']).toBeTruthy();
|
expect(request.headers()['user-agent']).toBeTruthy();
|
||||||
expect(request.method()).toBe('GET');
|
expect(request.method()).toBe('GET');
|
||||||
|
|
@ -41,9 +42,11 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
expect(request.frame() === page.mainFrame()).toBe(true);
|
expect(request.frame() === page.mainFrame()).toBe(true);
|
||||||
expect(request.frame().url()).toBe('about:blank');
|
expect(request.frame().url()).toBe('about:blank');
|
||||||
request.continue();
|
request.continue();
|
||||||
|
intercepted = true;
|
||||||
});
|
});
|
||||||
const response = await page.goto(server.EMPTY_PAGE);
|
const response = await page.goto(server.EMPTY_PAGE);
|
||||||
expect(response.ok()).toBe(true);
|
expect(response.ok()).toBe(true);
|
||||||
|
expect(intercepted).toBe(true);
|
||||||
});
|
});
|
||||||
it('should work when POST is redirected with 302', async({page, server}) => {
|
it('should work when POST is redirected with 302', async({page, server}) => {
|
||||||
server.setRedirect('/rredirect', '/empty.html');
|
server.setRedirect('/rredirect', '/empty.html');
|
||||||
|
|
@ -516,7 +519,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
|
|
||||||
describe('ignoreHTTPSErrors', function() {
|
describe('ignoreHTTPSErrors', function() {
|
||||||
it('should work with request interception', async({browser, httpsServer}) => {
|
it('should work with request interception', async({browser, httpsServer}) => {
|
||||||
const context = await browser.newContext({ ignoreHTTPSErrors: true, interceptNetwork: true });
|
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
|
|
||||||
await page.route('**/*', request => request.continue());
|
await page.route('**/*', request => request.continue());
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,24 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
|
||||||
expect(userAgent).toBe('hey');
|
expect(userAgent).toBe('hey');
|
||||||
expect(request.headers['user-agent']).toBe('hey');
|
expect(request.headers['user-agent']).toBe('hey');
|
||||||
});
|
});
|
||||||
|
it.fail(CHROMIUM || FFOX)('should respect routes from browser context', async function({browser, server}) {
|
||||||
|
const context = await browser.newContext();
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.setContent('<a target=_blank rel=noopener href="empty.html">link</a>');
|
||||||
|
let intercepted = false;
|
||||||
|
await context.route('**/empty.html', request => {
|
||||||
|
request.continue();
|
||||||
|
intercepted = true;
|
||||||
|
});
|
||||||
|
const [popup] = await Promise.all([
|
||||||
|
context.waitForEvent('page').then(pageEvent => pageEvent.page()),
|
||||||
|
page.click('a'),
|
||||||
|
]);
|
||||||
|
await popup.waitForLoadState();
|
||||||
|
await context.close();
|
||||||
|
expect(intercepted).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('window.open', function() {
|
describe('window.open', function() {
|
||||||
|
|
@ -122,6 +140,19 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
|
||||||
await context.close();
|
await context.close();
|
||||||
expect(size).toEqual({width: 400, height: 500});
|
expect(size).toEqual({width: 400, height: 500});
|
||||||
});
|
});
|
||||||
|
it.fail(FFOX)('should respect routes from browser context', async function({browser, server}) {
|
||||||
|
const context = await browser.newContext();
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
let intercepted = false;
|
||||||
|
await context.route('**/empty.html', request => {
|
||||||
|
request.continue();
|
||||||
|
intercepted = true;
|
||||||
|
});
|
||||||
|
await page.evaluate(url => window.__popup = window.open(url), server.EMPTY_PAGE);
|
||||||
|
await context.close();
|
||||||
|
expect(intercepted).toBe(true);
|
||||||
|
});
|
||||||
it('should apply addInitScript from browser context', async function({browser, server}) {
|
it('should apply addInitScript from browser context', async function({browser, server}) {
|
||||||
const context = await browser.newContext();
|
const context = await browser.newContext();
|
||||||
await context.addInitScript(() => window.injected = 123);
|
await context.addInitScript(() => window.injected = 123);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue