mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(fetch): fulfill without passing fetch response body client<->server (#8789)
This commit is contained in:
parent
5a305a9c2e
commit
b11b274b0d
@ -216,7 +216,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
||||
});
|
||||
}
|
||||
|
||||
async _fetch(url: string, options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer, timeout?: number } = {}): Promise<network.FetchResponse> {
|
||||
async _fetch(url: string, options: FetchOptions = {}): Promise<network.FetchResponse> {
|
||||
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => {
|
||||
const postDataBuffer = isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData;
|
||||
const result = await channel.fetch({
|
||||
@ -383,6 +383,8 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
||||
}
|
||||
}
|
||||
|
||||
export type FetchOptions = { url?: string, method?: string, headers?: Headers, postData?: string | Buffer, timeout?: number };
|
||||
|
||||
export async function prepareBrowserContextParams(options: BrowserContextOptions): Promise<channels.BrowserNewContextParams> {
|
||||
if (options.videoSize && !options.videosPath)
|
||||
throw new Error(`"videoSize" option requires "videosPath" to be specified`);
|
||||
|
||||
@ -310,15 +310,18 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
||||
});
|
||||
}
|
||||
|
||||
async fulfill(options: { response?: Response, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) {
|
||||
async fulfill(options: { response?: Response|FetchResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) {
|
||||
return this._wrapApiCall(async (channel: channels.RouteChannel) => {
|
||||
let useInterceptedResponseBody;
|
||||
let fetchResponseUid;
|
||||
let { status: statusOption, headers: headersOption, body: bodyOption } = options;
|
||||
if (options.response) {
|
||||
statusOption ||= options.response.status();
|
||||
headersOption ||= options.response.headers();
|
||||
if (options.body === undefined && options.path === undefined) {
|
||||
if (options.response === this._interceptedResponse)
|
||||
if (options.response instanceof FetchResponse)
|
||||
fetchResponseUid = (options.response as FetchResponse)._fetchUid();
|
||||
else if (options.response === this._interceptedResponse)
|
||||
useInterceptedResponseBody = true;
|
||||
else
|
||||
bodyOption = await options.response.body();
|
||||
@ -358,7 +361,8 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
||||
headers: headersObjectToArray(headers),
|
||||
body,
|
||||
isBase64,
|
||||
useInterceptedResponseBody
|
||||
useInterceptedResponseBody,
|
||||
fetchResponseUid
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -553,7 +557,7 @@ export class FetchResponse {
|
||||
|
||||
async body(): Promise<Buffer> {
|
||||
return this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => {
|
||||
const result = await channel.fetchResponseBody({ fetchUid: this._initializer.fetchUid });
|
||||
const result = await channel.fetchResponseBody({ fetchUid: this._fetchUid() });
|
||||
if (!result.binary)
|
||||
throw new Error('Response has been disposed');
|
||||
return Buffer.from(result.binary!, 'base64');
|
||||
@ -572,9 +576,13 @@ export class FetchResponse {
|
||||
|
||||
async dispose(): Promise<void> {
|
||||
return this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => {
|
||||
await channel.disposeFetchResponse({ fetchUid: this._initializer.fetchUid });
|
||||
await channel.disposeFetchResponse({ fetchUid: this._fetchUid() });
|
||||
});
|
||||
}
|
||||
|
||||
_fetchUid(): string {
|
||||
return this._initializer.fetchUid;
|
||||
}
|
||||
}
|
||||
|
||||
export class WebSocket extends ChannelOwner<channels.WebSocketChannel, channels.WebSocketInitializer> implements api.WebSocket {
|
||||
|
||||
@ -19,9 +19,10 @@ import { Events } from './events';
|
||||
import { assert } from '../utils/utils';
|
||||
import { TimeoutSettings } from '../utils/timeoutSettings';
|
||||
import * as channels from '../protocol/channels';
|
||||
import * as network from './network';
|
||||
import { parseError, serializeError } from '../protocol/serializers';
|
||||
import { Accessibility } from './accessibility';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { BrowserContext, FetchOptions } from './browserContext';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { ConsoleMessage } from './consoleMessage';
|
||||
import { Dialog } from './dialog';
|
||||
@ -437,6 +438,10 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
||||
return this._mainFrame.evaluate(pageFunction, arg);
|
||||
}
|
||||
|
||||
async _fetch(url: string, options: FetchOptions = {}): Promise<network.FetchResponse> {
|
||||
return await this._browserContext._fetch(url, options);
|
||||
}
|
||||
|
||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
||||
return this._wrapApiCall(async (channel: channels.PageChannel) => {
|
||||
const source = await evaluationScript(script, arg);
|
||||
|
||||
@ -2680,6 +2680,7 @@ export type RouteFulfillParams = {
|
||||
body?: string,
|
||||
isBase64?: boolean,
|
||||
useInterceptedResponseBody?: boolean,
|
||||
fetchResponseUid?: string,
|
||||
};
|
||||
export type RouteFulfillOptions = {
|
||||
status?: number,
|
||||
@ -2687,6 +2688,7 @@ export type RouteFulfillOptions = {
|
||||
body?: string,
|
||||
isBase64?: boolean,
|
||||
useInterceptedResponseBody?: boolean,
|
||||
fetchResponseUid?: string,
|
||||
};
|
||||
export type RouteFulfillResult = void;
|
||||
export type RouteResponseBodyParams = {};
|
||||
|
||||
@ -2191,6 +2191,7 @@ Route:
|
||||
body: string?
|
||||
isBase64: boolean?
|
||||
useInterceptedResponseBody: boolean?
|
||||
fetchResponseUid: string?
|
||||
|
||||
responseBody:
|
||||
returns:
|
||||
|
||||
@ -1049,6 +1049,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
body: tOptional(tString),
|
||||
isBase64: tOptional(tBoolean),
|
||||
useInterceptedResponseBody: tOptional(tBoolean),
|
||||
fetchResponseUid: tOptional(tString),
|
||||
});
|
||||
scheme.RouteResponseBodyParams = tOptional(tObject({}));
|
||||
scheme.ResourceTiming = tObject({
|
||||
|
||||
@ -219,13 +219,19 @@ export class Route extends SdkObject {
|
||||
await this._delegate.abort(errorCode);
|
||||
}
|
||||
|
||||
async fulfill(overrides: { status?: number, headers?: types.HeadersArray, body?: string, isBase64?: boolean, useInterceptedResponseBody?: boolean }) {
|
||||
async fulfill(overrides: { status?: number, headers?: types.HeadersArray, body?: string, isBase64?: boolean, useInterceptedResponseBody?: boolean, fetchResponseUid?: string }) {
|
||||
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 && overrides.useInterceptedResponseBody) {
|
||||
if (overrides.fetchResponseUid) {
|
||||
const context = this._request.frame()._page._browserContext;
|
||||
const buffer = context.fetchResponses.get(overrides.fetchResponseUid);
|
||||
assert(buffer, 'Fetch response has been disposed');
|
||||
body = buffer.toString('utf8');
|
||||
isBase64 = false;
|
||||
} else if (this._response && overrides.useInterceptedResponseBody) {
|
||||
body = (await this._delegate.responseBody()).toString('utf8');
|
||||
isBase64 = false;
|
||||
} else {
|
||||
|
||||
@ -193,3 +193,34 @@ it('should include the origin header', async ({page, server, isAndroid}) => {
|
||||
expect(text).toBe('done');
|
||||
expect(interceptedRequest.headers()['origin']).toEqual(server.PREFIX);
|
||||
});
|
||||
|
||||
it('should fulfill with fetch result', async ({page, server}) => {
|
||||
await page.route('**/*', async route => {
|
||||
// @ts-expect-error
|
||||
const response = await page._fetch(server.PREFIX + '/simple.json');
|
||||
// @ts-expect-error
|
||||
route.fulfill({ response });
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
expect(await response.json()).toEqual({'foo': 'bar'});
|
||||
});
|
||||
|
||||
it('should fulfill with fetch result and overrides', async ({page, server}) => {
|
||||
await page.route('**/*', async route => {
|
||||
// @ts-expect-error
|
||||
const response = await page._fetch(server.PREFIX + '/simple.json');
|
||||
route.fulfill({
|
||||
// @ts-expect-error
|
||||
response,
|
||||
status: 201,
|
||||
headers: {
|
||||
'foo': 'bar'
|
||||
}
|
||||
});
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(201);
|
||||
expect((await response.allHeaders()).foo).toEqual('bar');
|
||||
expect(await response.json()).toEqual({'foo': 'bar'});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user