feat(route): rename method, add response option (#8386)
This commit is contained in:
parent
20e4d9eee5
commit
59422a00f5
|
|
@ -181,6 +181,11 @@ page.route("**/xhr_endpoint", lambda route: route.fulfill(path="mock_data.json")
|
|||
await page.RouteAsync("**/xhr_endpoint", route => route.FulfillAsync(new RouteFulfillOptions { Path = "mock_data.json" }));
|
||||
```
|
||||
|
||||
### option: Route.fulfill._response
|
||||
- `_response` <[Response]>
|
||||
|
||||
Intercepted response. Will be used to populate all response fields not explicitely overridden.
|
||||
|
||||
### option: Route.fulfill.status
|
||||
- `status` <[int]>
|
||||
|
||||
|
|
|
|||
|
|
@ -245,6 +245,8 @@ type InterceptResponse = true;
|
|||
type NotInterceptResponse = false;
|
||||
|
||||
export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteInitializer> implements api.Route {
|
||||
private _interceptedResponse: api.Response | undefined;
|
||||
|
||||
static from(route: channels.RouteChannel): Route {
|
||||
return (route as any)._object;
|
||||
}
|
||||
|
|
@ -263,8 +265,21 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
|||
});
|
||||
}
|
||||
|
||||
async fulfill(options: { status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) {
|
||||
async fulfill(options: { _response?: Response, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) {
|
||||
return this._wrapApiCall(async (channel: channels.RouteChannel) => {
|
||||
let useInterceptedResponseBody;
|
||||
let { status: statusOption, headers: headersOption, body: bodyOption } = options;
|
||||
if (options._response) {
|
||||
statusOption = statusOption || options._response.status();
|
||||
headersOption = headersOption || options._response.headers();
|
||||
if (options.body === undefined && options.path === undefined) {
|
||||
if (options._response === this._interceptedResponse)
|
||||
useInterceptedResponseBody = true;
|
||||
else
|
||||
bodyOption = await options._response.body();
|
||||
}
|
||||
}
|
||||
|
||||
let body = undefined;
|
||||
let isBase64 = false;
|
||||
let length = 0;
|
||||
|
|
@ -273,19 +288,19 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
|||
body = buffer.toString('base64');
|
||||
isBase64 = true;
|
||||
length = buffer.length;
|
||||
} else if (isString(options.body)) {
|
||||
body = options.body;
|
||||
} else if (isString(bodyOption)) {
|
||||
body = bodyOption;
|
||||
isBase64 = false;
|
||||
length = Buffer.byteLength(body);
|
||||
} else if (options.body) {
|
||||
body = options.body.toString('base64');
|
||||
} else if (bodyOption) {
|
||||
body = bodyOption.toString('base64');
|
||||
isBase64 = true;
|
||||
length = options.body.length;
|
||||
length = bodyOption.length;
|
||||
}
|
||||
|
||||
const headers: Headers = {};
|
||||
for (const header of Object.keys(options.headers || {}))
|
||||
headers[header.toLowerCase()] = String(options.headers![header]);
|
||||
for (const header of Object.keys(headersOption || {}))
|
||||
headers[header.toLowerCase()] = String(headersOption![header]);
|
||||
if (options.contentType)
|
||||
headers['content-type'] = String(options.contentType);
|
||||
else if (options.path)
|
||||
|
|
@ -294,16 +309,18 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
|||
headers['content-length'] = String(length);
|
||||
|
||||
await channel.fulfill({
|
||||
status: options.status || 200,
|
||||
status: statusOption || 200,
|
||||
headers: headersObjectToArray(headers),
|
||||
body,
|
||||
isBase64
|
||||
isBase64,
|
||||
useInterceptedResponseBody
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async _intercept(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer, interceptResponse?: boolean } = {}): Promise<api.Response> {
|
||||
return await this._continue(options, true);
|
||||
async _continueToResponse(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer, interceptResponse?: boolean } = {}): Promise<api.Response> {
|
||||
this._interceptedResponse = await this._continue(options, true);
|
||||
return this._interceptedResponse;
|
||||
}
|
||||
|
||||
async continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer } = {}) {
|
||||
|
|
|
|||
|
|
@ -2531,12 +2531,14 @@ export type RouteFulfillParams = {
|
|||
headers?: NameValue[],
|
||||
body?: string,
|
||||
isBase64?: boolean,
|
||||
useInterceptedResponseBody?: boolean,
|
||||
};
|
||||
export type RouteFulfillOptions = {
|
||||
status?: number,
|
||||
headers?: NameValue[],
|
||||
body?: string,
|
||||
isBase64?: boolean,
|
||||
useInterceptedResponseBody?: boolean,
|
||||
};
|
||||
export type RouteFulfillResult = void;
|
||||
export type RouteResponseBodyParams = {};
|
||||
|
|
|
|||
|
|
@ -2134,6 +2134,7 @@ Route:
|
|||
items: NameValue
|
||||
body: string?
|
||||
isBase64: boolean?
|
||||
useInterceptedResponseBody: boolean?
|
||||
|
||||
responseBody:
|
||||
returns:
|
||||
|
|
|
|||
|
|
@ -1018,6 +1018,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||
headers: tOptional(tArray(tType('NameValue'))),
|
||||
body: tOptional(tString),
|
||||
isBase64: tOptional(tBoolean),
|
||||
useInterceptedResponseBody: tOptional(tBoolean),
|
||||
});
|
||||
scheme.RouteResponseBodyParams = tOptional(tObject({}));
|
||||
scheme.ResourceTiming = tObject({
|
||||
|
|
|
|||
|
|
@ -217,13 +217,13 @@ export class Route extends SdkObject {
|
|||
await this._delegate.abort(errorCode);
|
||||
}
|
||||
|
||||
async fulfill(overrides: { status?: number, headers?: types.HeadersArray, body?: string, isBase64?: boolean }) {
|
||||
async fulfill(overrides: { status?: number, headers?: types.HeadersArray, body?: string, isBase64?: boolean, useInterceptedResponseBody?: boolean }) {
|
||||
assert(!this._handled, 'Route is already handled!');
|
||||
this._handled = true;
|
||||
let body = overrides.body;
|
||||
let isBase64 = overrides.isBase64 || false;
|
||||
if (body === undefined) {
|
||||
if (this._response) {
|
||||
if (this._response && overrides.useInterceptedResponseBody) {
|
||||
body = (await this._delegate.responseBody()).toString('utf8');
|
||||
isBase64 = false;
|
||||
} else {
|
||||
|
|
@ -232,8 +232,8 @@ export class Route extends SdkObject {
|
|||
}
|
||||
}
|
||||
await this._delegate.fulfill({
|
||||
status: overrides.status || this._response?.status() || 200,
|
||||
headers: overrides.headers || this._response?.headers() || [],
|
||||
status: overrides.status || 200,
|
||||
headers: overrides.headers || [],
|
||||
body,
|
||||
isBase64,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { expect, test as it } from './pageTest';
|
|||
it('should fulfill intercepted response', async ({page, server, browserName}) => {
|
||||
await page.route('**/*', async route => {
|
||||
// @ts-expect-error
|
||||
await route._intercept({});
|
||||
await route._continueToResponse({});
|
||||
await route.fulfill({
|
||||
status: 201,
|
||||
headers: {
|
||||
|
|
@ -44,8 +44,9 @@ it('should fulfill response with empty body', async ({page, server, browserName,
|
|||
it.skip(browserName === 'chromium' && browserMajorVersion <= 91, 'Fails in Electron that uses old Chromium');
|
||||
await page.route('**/*', async route => {
|
||||
// @ts-expect-error
|
||||
await route._intercept({});
|
||||
const _response = await route._continueToResponse({});
|
||||
await route.fulfill({
|
||||
_response,
|
||||
status: 201,
|
||||
body: ''
|
||||
});
|
||||
|
|
@ -55,6 +56,53 @@ it('should fulfill response with empty body', async ({page, server, browserName,
|
|||
expect(await response.text()).toBe('');
|
||||
});
|
||||
|
||||
it('should override with defaults when intercepted response not provided', async ({page, server, browserName, browserMajorVersion}) => {
|
||||
it.skip(browserName === 'chromium' && browserMajorVersion <= 91, 'Fails in Electron that uses old Chromium');
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('foo', 'bar');
|
||||
res.end('my content');
|
||||
});
|
||||
await page.route('**/*', async route => {
|
||||
// @ts-expect-error
|
||||
await route._continueToResponse({});
|
||||
await route.fulfill({
|
||||
status: 201,
|
||||
});
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(201);
|
||||
expect(await response.text()).toBe('');
|
||||
if (browserName === 'webkit')
|
||||
expect(response.headers()).toEqual({'content-type': 'text/plain'});
|
||||
else
|
||||
expect(response.headers()).toEqual({ });
|
||||
});
|
||||
|
||||
it('should fulfill with any response', async ({page, server, browserName, browserMajorVersion, isLinux}) => {
|
||||
it.skip(browserName === 'chromium' && browserMajorVersion <= 91, 'Fails in Electron that uses old Chromium');
|
||||
it.fail(browserName === 'webkit' && isLinux, 'Network.responseReceived comes twice');
|
||||
|
||||
server.setRoute('/sample', (req, res) => {
|
||||
res.setHeader('foo', 'bar');
|
||||
res.end('Woo-hoo');
|
||||
});
|
||||
const page2 = await page.context().newPage();
|
||||
const sampleResponse = await page2.goto(`${server.PREFIX}/sample`);
|
||||
|
||||
await page.route('**/*', async route => {
|
||||
// @ts-expect-error
|
||||
await route._continueToResponse({});
|
||||
await route.fulfill({
|
||||
_response: sampleResponse,
|
||||
status: 201,
|
||||
});
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(201);
|
||||
expect(await response.text()).toBe('Woo-hoo');
|
||||
expect(response.headers()['foo']).toBe('bar');
|
||||
});
|
||||
|
||||
it('should throw on continue after intercept', async ({page, server, browserName}) => {
|
||||
let routeCallback;
|
||||
const routePromise = new Promise<Route>(f => routeCallback = f);
|
||||
|
|
@ -63,7 +111,7 @@ it('should throw on continue after intercept', async ({page, server, browserName
|
|||
page.goto(server.EMPTY_PAGE).catch(e => {});
|
||||
const route = await routePromise;
|
||||
// @ts-expect-error
|
||||
await route._intercept();
|
||||
await route._continueToResponse();
|
||||
try {
|
||||
await route.continue();
|
||||
fail('did not throw');
|
||||
|
|
@ -76,8 +124,8 @@ it('should support fulfill after intercept', async ({page, server}) => {
|
|||
const requestPromise = server.waitForRequest('/title.html');
|
||||
await page.route('**', async route => {
|
||||
// @ts-expect-error
|
||||
await route._intercept();
|
||||
await route.fulfill();
|
||||
const _response = await route._continueToResponse();
|
||||
await route.fulfill({ _response });
|
||||
});
|
||||
const response = await page.goto(server.PREFIX + '/title.html');
|
||||
const request = await requestPromise;
|
||||
|
|
@ -95,8 +143,8 @@ it('should intercept failures', async ({page, browserName, browserMajorVersion,
|
|||
await page.route('**', async route => {
|
||||
try {
|
||||
// @ts-expect-error
|
||||
await route._intercept();
|
||||
await route.fulfill();
|
||||
const _response = await route._continueToResponse();
|
||||
await route.fulfill({ _response });
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
|
@ -115,13 +163,13 @@ it('should support request overrides', async ({page, server, browserName, browse
|
|||
const requestPromise = server.waitForRequest('/empty.html');
|
||||
await page.route('**/foo', async route => {
|
||||
// @ts-expect-error
|
||||
await route._intercept({
|
||||
const _response = await route._continueToResponse({
|
||||
url: server.EMPTY_PAGE,
|
||||
method: 'POST',
|
||||
headers: {'foo': 'bar'},
|
||||
postData: 'my data',
|
||||
});
|
||||
await route.fulfill();
|
||||
await route.fulfill({ _response });
|
||||
});
|
||||
await page.goto(server.PREFIX + '/foo');
|
||||
const request = await requestPromise;
|
||||
|
|
@ -142,14 +190,14 @@ it('should give access to the intercepted response', async ({page, server}) => {
|
|||
|
||||
const route = await routePromise;
|
||||
// @ts-expect-error
|
||||
const response = await route._intercept();
|
||||
const response = await route._continueToResponse();
|
||||
|
||||
expect(response.status()).toBe(200);
|
||||
expect(response.ok()).toBeTruthy();
|
||||
expect(response.url()).toBe(server.PREFIX + '/title.html');
|
||||
expect(response.headers()['content-type']).toBe('text/html; charset=utf-8');
|
||||
|
||||
await Promise.all([route.fulfill(), evalPromise]);
|
||||
await Promise.all([route.fulfill({ _response: response }), evalPromise]);
|
||||
});
|
||||
|
||||
it('should give access to the intercepted response status text', async ({page, server, browserName}) => {
|
||||
|
|
@ -167,12 +215,12 @@ it('should give access to the intercepted response status text', async ({page, s
|
|||
const evalPromise = page.evaluate(url => fetch(url), server.PREFIX + '/title.html');
|
||||
const route = await routePromise;
|
||||
// @ts-expect-error
|
||||
const response = await route._intercept();
|
||||
const response = await route._continueToResponse();
|
||||
|
||||
expect(response.statusText()).toBe('You are awesome');
|
||||
expect(response.url()).toBe(server.PREFIX + '/title.html');
|
||||
|
||||
await Promise.all([route.fulfill(), evalPromise]);
|
||||
await Promise.all([route.fulfill({ _response: response }), evalPromise]);
|
||||
});
|
||||
|
||||
it('should give access to the intercepted response body', async ({page, server}) => {
|
||||
|
|
@ -186,17 +234,17 @@ it('should give access to the intercepted response body', async ({page, server})
|
|||
|
||||
const route = await routePromise;
|
||||
// @ts-expect-error
|
||||
const response = await route._intercept();
|
||||
const response = await route._continueToResponse();
|
||||
|
||||
expect((await response.text())).toBe('{"foo": "bar"}\n');
|
||||
|
||||
await Promise.all([route.fulfill(), evalPromise]);
|
||||
await Promise.all([route.fulfill({ _response: response }), evalPromise]);
|
||||
});
|
||||
|
||||
it('should be abortable after interception', async ({page, server, browserName}) => {
|
||||
await page.route(/\.css$/, async route => {
|
||||
// @ts-expect-error
|
||||
await route._intercept();
|
||||
await route._continueToResponse();
|
||||
await route.abort();
|
||||
});
|
||||
let failed = false;
|
||||
|
|
@ -224,7 +272,7 @@ it('should fulfill after redirects', async ({page, server, browserName}) => {
|
|||
await page.route('**/*', async route => {
|
||||
++routeCalls;
|
||||
// @ts-expect-error
|
||||
await route._intercept({});
|
||||
await route._continueToResponse({});
|
||||
await route.fulfill({
|
||||
status: 201,
|
||||
headers: {
|
||||
|
|
@ -266,8 +314,8 @@ it('should fulfill original response after redirects', async ({page, browserName
|
|||
await page.route('**/*', async route => {
|
||||
++routeCalls;
|
||||
// @ts-expect-error
|
||||
await route._intercept({});
|
||||
await route.fulfill();
|
||||
const _response = await route._continueToResponse({});
|
||||
await route.fulfill({ _response });
|
||||
});
|
||||
const response = await page.goto(server.PREFIX + '/redirect/1.html');
|
||||
expect(requestUrls).toEqual(expectedUrls);
|
||||
|
|
@ -301,7 +349,7 @@ it('should abort after redirects', async ({page, browserName, server}) => {
|
|||
await page.route('**/*', async route => {
|
||||
++routeCalls;
|
||||
// @ts-expect-error
|
||||
await route._intercept({});
|
||||
await route._continueToResponse({});
|
||||
await route.abort('connectionreset');
|
||||
});
|
||||
|
||||
|
|
|
|||
5
types/types.d.ts
vendored
5
types/types.d.ts
vendored
|
|
@ -11927,6 +11927,11 @@ export interface Route {
|
|||
* @param options
|
||||
*/
|
||||
fulfill(options?: {
|
||||
/**
|
||||
* Intercepted response. Will be used to populate all response fields not explicitely overridden.
|
||||
*/
|
||||
_response?: Response;
|
||||
|
||||
/**
|
||||
* Response body.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue