feat(api): move fetch api into the namespace (#8871)
This commit is contained in:
parent
bf35da3656
commit
64f9c3ba1d
|
|
@ -792,37 +792,6 @@ Name of the function on the window object.
|
|||
|
||||
Callback function that will be called in the Playwright's context.
|
||||
|
||||
## async method: BrowserContext.fetch
|
||||
- returns: <[FetchResponse]>
|
||||
|
||||
Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
|
||||
context cookies from the response. The method will automatically follow redirects.
|
||||
|
||||
### param: BrowserContext.fetch.urlOrRequest
|
||||
- `urlOrRequest` <[string]|[Request]>
|
||||
|
||||
Target URL or Request to get all fetch parameters from.
|
||||
|
||||
### option: BrowserContext.fetch.method
|
||||
- `method` <[string]>
|
||||
|
||||
If set changes the request method (e.g. PUT or POST). If not specified, GET method is used.
|
||||
|
||||
### option: BrowserContext.fetch.headers
|
||||
- `headers` <[Object]<[string], [string]>>
|
||||
|
||||
Allows to set HTTP headers.
|
||||
|
||||
### option: BrowserContext.fetch.postData
|
||||
- `postData` <[string]|[Buffer]>
|
||||
|
||||
Allows to set post data of the request.
|
||||
|
||||
### option: BrowserContext.fetch.timeout
|
||||
- `timeout` <[float]>
|
||||
|
||||
Request timeout in milliseconds.
|
||||
|
||||
## async method: BrowserContext.grantPermissions
|
||||
|
||||
Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if
|
||||
|
|
@ -880,6 +849,11 @@ Creates a new page in the browser context.
|
|||
|
||||
Returns all open pages in the context.
|
||||
|
||||
## property: BrowserContext.request
|
||||
- type: <[FetchRequest]>
|
||||
|
||||
API testing helper associated with this context. Requests made with this API will use context cookies.
|
||||
|
||||
## async method: BrowserContext.route
|
||||
|
||||
Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
|
|
|
|||
84
docs/src/api/class-fetchrequest.md
Normal file
84
docs/src/api/class-fetchrequest.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# class: FetchRequest
|
||||
|
||||
This API is used for Web API testing. You can use it to trigger API endpoints, configure micro-services, prepare
|
||||
environment or the service to your e2e test. When used on [Page] or a [BrowserContext], this API will automatically use
|
||||
the cookies from the corresponding [BrowserContext]. This means that if you log in using this API, your e2e test
|
||||
will be logged in and vice versa.
|
||||
|
||||
## async method: FetchRequest.fetch
|
||||
- returns: <[FetchResponse]>
|
||||
|
||||
Sends HTTP(S) fetch and returns its response. The method will populate fetch cookies from the context and update
|
||||
context cookies from the response. The method will automatically follow redirects.
|
||||
|
||||
### param: FetchRequest.fetch.urlOrRequest
|
||||
- `urlOrRequest` <[string]|[Request]>
|
||||
|
||||
Target URL or Request to get all fetch parameters from.
|
||||
|
||||
### option: FetchRequest.fetch.method
|
||||
- `method` <[string]>
|
||||
|
||||
If set changes the fetch method (e.g. PUT or POST). If not specified, GET method is used.
|
||||
|
||||
### option: FetchRequest.fetch.headers
|
||||
- `headers` <[Object]<[string], [string]>>
|
||||
|
||||
Allows to set HTTP headers.
|
||||
|
||||
### option: FetchRequest.fetch.data
|
||||
- `data` <[string]|[Buffer]>
|
||||
|
||||
Allows to set post data of the fetch.
|
||||
|
||||
### option: FetchRequest.fetch.timeout
|
||||
- `timeout` <[float]>
|
||||
|
||||
Request timeout in milliseconds.
|
||||
|
||||
## async method: FetchRequest.get
|
||||
- returns: <[FetchResponse]>
|
||||
|
||||
Sends HTTP(S) GET request and returns its response. The method will populate fetch cookies from the context and update
|
||||
context cookies from the response. The method will automatically follow redirects.
|
||||
|
||||
### param: FetchRequest.get.urlOrRequest
|
||||
- `urlOrRequest` <[string]|[Request]>
|
||||
|
||||
Target URL or Request to get all fetch parameters from.
|
||||
|
||||
### option: FetchRequest.get.headers
|
||||
- `headers` <[Object]<[string], [string]>>
|
||||
|
||||
Allows to set HTTP headers.
|
||||
|
||||
### option: FetchRequest.get.timeout
|
||||
- `timeout` <[float]>
|
||||
|
||||
Request timeout in milliseconds.
|
||||
|
||||
## async method: FetchRequest.post
|
||||
- returns: <[FetchResponse]>
|
||||
|
||||
Sends HTTP(S) fetch and returns its response. The method will populate fetch cookies from the context and update
|
||||
context cookies from the response. The method will automatically follow redirects.
|
||||
|
||||
### param: FetchRequest.post.urlOrRequest
|
||||
- `urlOrRequest` <[string]|[Request]>
|
||||
|
||||
Target URL or Request to get all fetch parameters from.
|
||||
|
||||
### option: FetchRequest.post.headers
|
||||
- `headers` <[Object]<[string], [string]>>
|
||||
|
||||
Allows to set HTTP headers.
|
||||
|
||||
### option: FetchRequest.post.data
|
||||
- `data` <[string]|[Buffer]>
|
||||
|
||||
Allows to set post data of the fetch.
|
||||
|
||||
### option: FetchRequest.post.timeout
|
||||
- `timeout` <[float]>
|
||||
|
||||
Request timeout in milliseconds.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# class: FetchResponse
|
||||
|
||||
[FetchResponse] class represents responses received from [`method: BrowserContext.fetch`] and [`method: Page.fetch`] methods.
|
||||
[FetchResponse] class represents responses received from [`method: FetchRequest.fetch`].
|
||||
|
||||
## async method: FetchResponse.body
|
||||
- returns: <[Buffer]>
|
||||
|
|
|
|||
|
|
@ -1736,37 +1736,6 @@ Name of the function on the window object
|
|||
|
||||
Callback function which will be called in Playwright's context.
|
||||
|
||||
## async method: Page.fetch
|
||||
- returns: <[FetchResponse]>
|
||||
|
||||
Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
|
||||
context cookies from the response. The method will automatically follow redirects.
|
||||
|
||||
### param: Page.fetch.urlOrRequest
|
||||
- `urlOrRequest` <[string]|[Request]>
|
||||
|
||||
Target URL or Request to get all fetch parameters from.
|
||||
|
||||
### option: Page.fetch.method
|
||||
- `method` <[string]>
|
||||
|
||||
If set changes the request method (e.g. PUT or POST). If not specified, GET method is used.
|
||||
|
||||
### option: Page.fetch.headers
|
||||
- `headers` <[Object]<[string], [string]>>
|
||||
|
||||
Allows to set HTTP headers.
|
||||
|
||||
### option: Page.fetch.postData
|
||||
- `postData` <[string]|[Buffer]>
|
||||
|
||||
Allows to set post data of the request.
|
||||
|
||||
### option: Page.fetch.timeout
|
||||
- `timeout` <[float]>
|
||||
|
||||
Request timeout in milliseconds.
|
||||
|
||||
## async method: Page.fill
|
||||
|
||||
This method waits for an element matching [`param: selector`], waits for [actionability](./actionability.md) checks, focuses the element, fills it and triggers an `input` event after filling. Note that you can pass an empty string to clear the input field.
|
||||
|
|
@ -2440,6 +2409,11 @@ last redirect.
|
|||
|
||||
### option: Page.reload.timeout = %%-navigation-timeout-%%
|
||||
|
||||
## property: Page.request
|
||||
- type: <[FetchRequest]>
|
||||
|
||||
API testing helper associated with this page. Requests made with this API will use page cookies.
|
||||
|
||||
## async method: Page.route
|
||||
|
||||
Routing provides the capability to modify network requests that are made by a page.
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ export { TimeoutError } from '../utils/errors';
|
|||
export { Frame } from './frame';
|
||||
export { Keyboard, Mouse, Touchscreen } from './input';
|
||||
export { JSHandle } from './jsHandle';
|
||||
export { FetchResponse, Request, Response, Route, WebSocket } from './network';
|
||||
export { Request, Response, Route, WebSocket } from './network';
|
||||
export { FetchRequest, FetchResponse } from './fetch';
|
||||
export { Page } from './page';
|
||||
export { Selectors } from './selectors';
|
||||
export { Tracing } from './tracing';
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import { Events } from './events';
|
|||
import { TimeoutSettings } from '../utils/timeoutSettings';
|
||||
import { Waiter } from './waiter';
|
||||
import { URLMatch, Headers, WaitForEventOptions, BrowserContextOptions, StorageState, LaunchOptions } from './types';
|
||||
import { isUnderTest, headersObjectToArray, mkdirIfNeeded, isString, assert } from '../utils/utils';
|
||||
import { isUnderTest, headersObjectToArray, mkdirIfNeeded } from '../utils/utils';
|
||||
import { isSafeCloseError } from '../utils/errors';
|
||||
import * as api from '../../types/types';
|
||||
import * as structs from '../../types/structs';
|
||||
|
|
@ -36,6 +36,7 @@ import { CDPSession } from './cdpSession';
|
|||
import { Tracing } from './tracing';
|
||||
import type { BrowserType } from './browserType';
|
||||
import { Artifact } from './artifact';
|
||||
import { FetchRequest } from './fetch';
|
||||
|
||||
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel, channels.BrowserContextInitializer> implements api.BrowserContext {
|
||||
_pages = new Set<Page>();
|
||||
|
|
@ -48,8 +49,8 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
|||
private _closedPromise: Promise<void>;
|
||||
_options: channels.BrowserNewContextParams = { };
|
||||
|
||||
readonly request: FetchRequest;
|
||||
readonly tracing: Tracing;
|
||||
private _closed = false;
|
||||
readonly _backgroundPages = new Set<Page>();
|
||||
readonly _serviceWorkers = new Set<Worker>();
|
||||
readonly _isChromium: boolean;
|
||||
|
|
@ -68,6 +69,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
|||
this._browser = parent;
|
||||
this._isChromium = this._browser?._name === 'chromium';
|
||||
this.tracing = new Tracing(this);
|
||||
this.request = new FetchRequest(this);
|
||||
|
||||
this._channel.on('bindingCall', ({binding}) => this._onBinding(BindingCall.from(binding)));
|
||||
this._channel.on('close', () => this._onClose());
|
||||
|
|
@ -216,32 +218,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
|||
});
|
||||
}
|
||||
|
||||
async fetch(urlOrRequest: string|api.Request, options: FetchOptions = {}): Promise<network.FetchResponse> {
|
||||
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => {
|
||||
const request: network.Request | undefined = (urlOrRequest instanceof network.Request) ? urlOrRequest as network.Request : undefined;
|
||||
assert(request || typeof urlOrRequest === 'string', 'First argument must be either URL string or Request');
|
||||
const url = request ? request.url() : urlOrRequest as string;
|
||||
const method = options.method || request?.method();
|
||||
// Cannot call allHeaders() here as the request may be paused inside route handler.
|
||||
const headersObj = options.headers || request?.headers() ;
|
||||
const headers = headersObj ? headersObjectToArray(headersObj) : undefined;
|
||||
let postDataBuffer = isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData;
|
||||
if (postDataBuffer === undefined)
|
||||
postDataBuffer = request?.postDataBuffer() || undefined;
|
||||
const postData = (postDataBuffer ? postDataBuffer.toString('base64') : undefined);
|
||||
const result = await channel.fetch({
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
postData,
|
||||
timeout: options.timeout,
|
||||
});
|
||||
if (result.error)
|
||||
throw new Error(`Request failed: ${result.error}`);
|
||||
return new network.FetchResponse(this, result.response!);
|
||||
});
|
||||
}
|
||||
|
||||
async setGeolocation(geolocation: { longitude: number, latitude: number, accuracy?: number } | null): Promise<void> {
|
||||
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => {
|
||||
await channel.setGeolocation({ geolocation: geolocation || undefined });
|
||||
|
|
@ -351,7 +327,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
|||
}
|
||||
|
||||
_onClose() {
|
||||
this._closed = true;
|
||||
if (this._browser)
|
||||
this._browser._contexts.delete(this);
|
||||
this._browserType?._contexts?.delete(this);
|
||||
|
|
@ -393,8 +368,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
|||
}
|
||||
}
|
||||
|
||||
export type FetchOptions = { 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`);
|
||||
|
|
|
|||
150
src/client/fetch.ts
Normal file
150
src/client/fetch.ts
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as api from '../../types/types';
|
||||
import { HeadersArray } from '../common/types';
|
||||
import * as channels from '../protocol/channels';
|
||||
import { assert, headersObjectToArray, isString } from '../utils/utils';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import * as network from './network';
|
||||
import { RawHeaders } from './network';
|
||||
import { Headers } from './types';
|
||||
|
||||
export type FetchOptions = { method?: string, headers?: Headers, data?: string | Buffer, timeout?: number };
|
||||
|
||||
export class FetchRequest implements api.FetchRequest {
|
||||
private _context: BrowserContext;
|
||||
|
||||
constructor(context: BrowserContext) {
|
||||
this._context = context;
|
||||
}
|
||||
|
||||
async get(
|
||||
urlOrRequest: string | api.Request,
|
||||
options?: {
|
||||
headers?: { [key: string]: string; };
|
||||
timeout?: number;
|
||||
}): Promise<FetchResponse> {
|
||||
return this.fetch(urlOrRequest, {
|
||||
...options,
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
async post(
|
||||
urlOrRequest: string | api.Request,
|
||||
options?: {
|
||||
headers?: { [key: string]: string; };
|
||||
data?: string | Buffer;
|
||||
timeout?: number;
|
||||
}): Promise<FetchResponse> {
|
||||
return this.fetch(urlOrRequest, {
|
||||
...options,
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
|
||||
async fetch(urlOrRequest: string | api.Request, options: FetchOptions = {}): Promise<FetchResponse> {
|
||||
return this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => {
|
||||
const request: network.Request | undefined = (urlOrRequest instanceof network.Request) ? urlOrRequest as network.Request : undefined;
|
||||
assert(request || typeof urlOrRequest === 'string', 'First argument must be either URL string or Request');
|
||||
const url = request ? request.url() : urlOrRequest as string;
|
||||
const method = options.method || request?.method();
|
||||
// Cannot call allHeaders() here as the request may be paused inside route handler.
|
||||
const headersObj = options.headers || request?.headers() ;
|
||||
const headers = headersObj ? headersObjectToArray(headersObj) : undefined;
|
||||
let postDataBuffer = isString(options.data) ? Buffer.from(options.data, 'utf8') : options.data;
|
||||
if (postDataBuffer === undefined)
|
||||
postDataBuffer = request?.postDataBuffer() || undefined;
|
||||
const postData = (postDataBuffer ? postDataBuffer.toString('base64') : undefined);
|
||||
const result = await channel.fetch({
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
postData,
|
||||
timeout: options.timeout,
|
||||
});
|
||||
if (result.error)
|
||||
throw new Error(`Request failed: ${result.error}`);
|
||||
return new FetchResponse(this._context, result.response!);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FetchResponse implements api.FetchResponse {
|
||||
private readonly _initializer: channels.FetchResponse;
|
||||
private readonly _headers: RawHeaders;
|
||||
private readonly _context: BrowserContext;
|
||||
|
||||
constructor(context: BrowserContext, initializer: channels.FetchResponse) {
|
||||
this._context = context;
|
||||
this._initializer = initializer;
|
||||
this._headers = new RawHeaders(this._initializer.headers);
|
||||
}
|
||||
|
||||
ok(): boolean {
|
||||
return this._initializer.status === 0 || (this._initializer.status >= 200 && this._initializer.status <= 299);
|
||||
}
|
||||
|
||||
url(): string {
|
||||
return this._initializer.url;
|
||||
}
|
||||
|
||||
status(): number {
|
||||
return this._initializer.status;
|
||||
}
|
||||
|
||||
statusText(): string {
|
||||
return this._initializer.statusText;
|
||||
}
|
||||
|
||||
headers(): Headers {
|
||||
return this._headers.headers();
|
||||
}
|
||||
|
||||
headersArray(): HeadersArray {
|
||||
return this._headers.headersArray();
|
||||
}
|
||||
|
||||
async body(): Promise<Buffer> {
|
||||
return this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => {
|
||||
const result = await channel.fetchResponseBody({ fetchUid: this._fetchUid() });
|
||||
if (!result.binary)
|
||||
throw new Error('Response has been disposed');
|
||||
return Buffer.from(result.binary!, 'base64');
|
||||
});
|
||||
}
|
||||
|
||||
async text(): Promise<string> {
|
||||
const content = await this.body();
|
||||
return content.toString('utf8');
|
||||
}
|
||||
|
||||
async json(): Promise<object> {
|
||||
const content = await this.text();
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
async dispose(): Promise<void> {
|
||||
return this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => {
|
||||
await channel.disposeFetchResponse({ fetchUid: this._fetchUid() });
|
||||
});
|
||||
}
|
||||
|
||||
_fetchUid(): string {
|
||||
return this._initializer.fetchUid;
|
||||
}
|
||||
}
|
||||
|
|
@ -29,8 +29,8 @@ import { Waiter } from './waiter';
|
|||
import * as api from '../../types/types';
|
||||
import { HeadersArray, URLMatch } from '../common/types';
|
||||
import { urlMatches } from './clientHelper';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { MultiMap } from '../utils/multimap';
|
||||
import { FetchResponse } from './fetch';
|
||||
|
||||
export type NetworkCookie = {
|
||||
name: string,
|
||||
|
|
@ -325,7 +325,7 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
|||
});
|
||||
}
|
||||
|
||||
async fulfill(options: { response?: api.Response|api.FetchResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) {
|
||||
async fulfill(options: { response?: api.Response | api.FetchResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) {
|
||||
return this._wrapApiCall(async (channel: channels.RouteChannel) => {
|
||||
let useInterceptedResponseBody;
|
||||
let fetchResponseUid;
|
||||
|
|
@ -547,71 +547,6 @@ export class Response extends ChannelOwner<channels.ResponseChannel, channels.Re
|
|||
}
|
||||
}
|
||||
|
||||
export class FetchResponse implements api.FetchResponse {
|
||||
private readonly _initializer: channels.FetchResponse;
|
||||
private readonly _headers: RawHeaders;
|
||||
private readonly _context: BrowserContext;
|
||||
|
||||
constructor(context: BrowserContext, initializer: channels.FetchResponse) {
|
||||
this._context = context;
|
||||
this._initializer = initializer;
|
||||
this._headers = new RawHeaders(this._initializer.headers);
|
||||
}
|
||||
|
||||
ok(): boolean {
|
||||
return this._initializer.status === 0 || (this._initializer.status >= 200 && this._initializer.status <= 299);
|
||||
}
|
||||
|
||||
url(): string {
|
||||
return this._initializer.url;
|
||||
}
|
||||
|
||||
status(): number {
|
||||
return this._initializer.status;
|
||||
}
|
||||
|
||||
statusText(): string {
|
||||
return this._initializer.statusText;
|
||||
}
|
||||
|
||||
headers(): Headers {
|
||||
return this._headers.headers();
|
||||
}
|
||||
|
||||
headersArray(): HeadersArray {
|
||||
return this._headers.headersArray();
|
||||
}
|
||||
|
||||
async body(): Promise<Buffer> {
|
||||
return this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => {
|
||||
const result = await channel.fetchResponseBody({ fetchUid: this._fetchUid() });
|
||||
if (!result.binary)
|
||||
throw new Error('Response has been disposed');
|
||||
return Buffer.from(result.binary!, 'base64');
|
||||
});
|
||||
}
|
||||
|
||||
async text(): Promise<string> {
|
||||
const content = await this.body();
|
||||
return content.toString('utf8');
|
||||
}
|
||||
|
||||
async json(): Promise<object> {
|
||||
const content = await this.text();
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
async dispose(): Promise<void> {
|
||||
return this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => {
|
||||
await channel.disposeFetchResponse({ fetchUid: this._fetchUid() });
|
||||
});
|
||||
}
|
||||
|
||||
_fetchUid(): string {
|
||||
return this._initializer.fetchUid;
|
||||
}
|
||||
}
|
||||
|
||||
export class WebSocket extends ChannelOwner<channels.WebSocketChannel, channels.WebSocketInitializer> implements api.WebSocket {
|
||||
private _page: Page;
|
||||
private _isClosed: boolean;
|
||||
|
|
|
|||
|
|
@ -19,10 +19,9 @@ 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, FetchOptions } from './browserContext';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { ConsoleMessage } from './consoleMessage';
|
||||
import { Dialog } from './dialog';
|
||||
|
|
@ -48,6 +47,7 @@ import { isString, isRegExp, isObject, mkdirIfNeeded, headersObjectToArray } fro
|
|||
import { isSafeCloseError } from '../utils/errors';
|
||||
import { Video } from './video';
|
||||
import { Artifact } from './artifact';
|
||||
import { FetchRequest } from './fetch';
|
||||
|
||||
type PDFOptions = Omit<channels.PagePdfParams, 'width' | 'height' | 'margin'> & {
|
||||
width?: string | number,
|
||||
|
|
@ -78,6 +78,7 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
|||
readonly coverage: Coverage;
|
||||
readonly keyboard: Keyboard;
|
||||
readonly mouse: Mouse;
|
||||
readonly request: FetchRequest;
|
||||
readonly touchscreen: Touchscreen;
|
||||
|
||||
readonly _bindings = new Map<string, (source: structs.BindingSource, ...args: any[]) => any>();
|
||||
|
|
@ -101,6 +102,7 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
|||
this.accessibility = new Accessibility(this._channel);
|
||||
this.keyboard = new Keyboard(this);
|
||||
this.mouse = new Mouse(this);
|
||||
this.request = new FetchRequest(this._browserContext);
|
||||
this.touchscreen = new Touchscreen(this);
|
||||
|
||||
this._mainFrame = Frame.from(initializer.mainFrame);
|
||||
|
|
@ -443,10 +445,6 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
|||
return this._mainFrame.evaluate(pageFunction, arg);
|
||||
}
|
||||
|
||||
async fetch(urlOrRequest: string|network.Request, options: FetchOptions = {}): Promise<network.FetchResponse> {
|
||||
return await this._browserContext.fetch(urlOrRequest as any, 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);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,19 @@ it.afterAll(() => {
|
|||
});
|
||||
|
||||
it('should work', async ({context, server}) => {
|
||||
const response = await context.fetch(server.PREFIX + '/simple.json');
|
||||
const response = await context.request.get(server.PREFIX + '/simple.json');
|
||||
expect(response.url()).toBe(server.PREFIX + '/simple.json');
|
||||
expect(response.status()).toBe(200);
|
||||
expect(response.statusText()).toBe('OK');
|
||||
expect(response.ok()).toBeTruthy();
|
||||
expect(response.url()).toBe(server.PREFIX + '/simple.json');
|
||||
expect(response.headers()['content-type']).toBe('application/json; charset=utf-8');
|
||||
expect(response.headersArray()).toContainEqual({ name: 'Content-Type', value: 'application/json; charset=utf-8' });
|
||||
expect(await response.text()).toBe('{"foo": "bar"}\n');
|
||||
});
|
||||
|
||||
it('fetch should work', async ({context, server}) => {
|
||||
const response = await context.request.fetch(server.PREFIX + '/simple.json');
|
||||
expect(response.url()).toBe(server.PREFIX + '/simple.json');
|
||||
expect(response.status()).toBe(200);
|
||||
expect(response.statusText()).toBe('OK');
|
||||
|
|
@ -56,8 +68,7 @@ it('should throw on network error', async ({context, server}) => {
|
|||
server.setRoute('/test', (req, res) => {
|
||||
req.socket.destroy();
|
||||
});
|
||||
let error;
|
||||
await context.fetch(server.PREFIX + '/test').catch(e => error = e);
|
||||
const error = await context.request.get(server.PREFIX + '/test').catch(e => e);
|
||||
expect(error.message).toContain('socket hang up');
|
||||
});
|
||||
|
||||
|
|
@ -66,8 +77,7 @@ it('should throw on network error after redirect', async ({context, server}) =>
|
|||
server.setRoute('/test', (req, res) => {
|
||||
req.socket.destroy();
|
||||
});
|
||||
let error;
|
||||
await context.fetch(server.PREFIX + '/redirect').catch(e => error = e);
|
||||
const error = await context.request.get(server.PREFIX + '/redirect').catch(e => e);
|
||||
expect(error.message).toContain('socket hang up');
|
||||
});
|
||||
|
||||
|
|
@ -81,8 +91,7 @@ it('should throw on network error when sending body', async ({context, server})
|
|||
res.uncork();
|
||||
req.socket.destroy();
|
||||
});
|
||||
let error;
|
||||
await context.fetch(server.PREFIX + '/test').catch(e => error = e);
|
||||
const error = await context.request.get(server.PREFIX + '/test').catch(e => e);
|
||||
expect(error.message).toContain('Error: aborted');
|
||||
});
|
||||
|
||||
|
|
@ -97,8 +106,7 @@ it('should throw on network error when sending body after redirect', async ({con
|
|||
res.uncork();
|
||||
req.socket.destroy();
|
||||
});
|
||||
let error;
|
||||
await context.fetch(server.PREFIX + '/redirect').catch(e => error = e);
|
||||
const error = await context.request.get(server.PREFIX + '/redirect').catch(e => e);
|
||||
expect(error.message).toContain('Error: aborted');
|
||||
});
|
||||
|
||||
|
|
@ -115,7 +123,7 @@ it('should add session cookies to request', async ({context, server}) => {
|
|||
}]);
|
||||
const [req] = await Promise.all([
|
||||
server.waitForRequest('/simple.json'),
|
||||
context.fetch(`http://www.my.playwright.dev:${server.PORT}/simple.json`),
|
||||
context.request.get(`http://www.my.playwright.dev:${server.PORT}/simple.json`),
|
||||
]);
|
||||
expect(req.headers.cookie).toEqual('username=John Doe');
|
||||
});
|
||||
|
|
@ -133,7 +141,7 @@ it('should not add context cookie if cookie header passed as a parameter', async
|
|||
}]);
|
||||
const [req] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
context.fetch(`http://www.my.playwright.dev:${server.PORT}/empty.html`, {
|
||||
context.request.get(`http://www.my.playwright.dev:${server.PORT}/empty.html`, {
|
||||
headers: {
|
||||
'Cookie': 'foo=bar'
|
||||
}
|
||||
|
|
@ -157,7 +165,7 @@ it('should follow redirects', async ({context, server}) => {
|
|||
}]);
|
||||
const [req, response] = await Promise.all([
|
||||
server.waitForRequest('/simple.json'),
|
||||
context.fetch(`http://www.my.playwright.dev:${server.PORT}/redirect1`),
|
||||
context.request.get(`http://www.my.playwright.dev:${server.PORT}/redirect1`),
|
||||
]);
|
||||
expect(req.headers.cookie).toEqual('username=John Doe');
|
||||
expect(response.url()).toBe(`http://www.my.playwright.dev:${server.PORT}/simple.json`);
|
||||
|
|
@ -169,7 +177,7 @@ it('should add cookies from Set-Cookie header', async ({context, page, server})
|
|||
res.setHeader('Set-Cookie', ['session=value', 'foo=bar; max-age=3600']);
|
||||
res.end();
|
||||
});
|
||||
await context.fetch(server.PREFIX + '/setcookie.html');
|
||||
await context.request.get(server.PREFIX + '/setcookie.html');
|
||||
const cookies = await context.cookies();
|
||||
expect(new Set(cookies.map(c => ({ name: c.name, value: c.value })))).toEqual(new Set([
|
||||
{
|
||||
|
|
@ -185,12 +193,12 @@ it('should add cookies from Set-Cookie header', async ({context, page, server})
|
|||
expect((await page.evaluate(() => document.cookie)).split(';').map(s => s.trim()).sort()).toEqual(['foo=bar', 'session=value']);
|
||||
});
|
||||
|
||||
it('should not lose body while handling Set-Cookie header', async ({context, page, server}) => {
|
||||
it('should not lose body while handling Set-Cookie header', async ({context, server}) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', ['session=value', 'foo=bar; max-age=3600']);
|
||||
res.end('text content');
|
||||
});
|
||||
const response = await context.fetch(server.PREFIX + '/setcookie.html');
|
||||
const response = await context.request.get(server.PREFIX + '/setcookie.html');
|
||||
expect(await response.text()).toBe('text content');
|
||||
});
|
||||
|
||||
|
|
@ -210,7 +218,7 @@ it('should handle cookies on redirects', async ({context, server, browserName, i
|
|||
server.waitForRequest('/redirect1'),
|
||||
server.waitForRequest('/a/b/redirect2'),
|
||||
server.waitForRequest('/title.html'),
|
||||
context.fetch(`${server.PREFIX}/redirect1`),
|
||||
context.request.get(`${server.PREFIX}/redirect1`),
|
||||
]);
|
||||
expect(req1.headers.cookie).toBeFalsy();
|
||||
expect(req2.headers.cookie).toBe('r1=v1');
|
||||
|
|
@ -221,7 +229,7 @@ it('should handle cookies on redirects', async ({context, server, browserName, i
|
|||
server.waitForRequest('/redirect1'),
|
||||
server.waitForRequest('/a/b/redirect2'),
|
||||
server.waitForRequest('/title.html'),
|
||||
context.fetch(`${server.PREFIX}/redirect1`),
|
||||
context.request.get(`${server.PREFIX}/redirect1`),
|
||||
]);
|
||||
expect(req1.headers.cookie).toBe('r1=v1');
|
||||
expect(req2.headers.cookie.split(';').map(s => s.trim()).sort()).toEqual(['r1=v1', 'r2=v2']);
|
||||
|
|
@ -266,7 +274,7 @@ it('should return raw headers', async ({context, page, server}) => {
|
|||
conn.uncork();
|
||||
conn.end();
|
||||
});
|
||||
const response = await context.fetch(`${server.PREFIX}/headers`);
|
||||
const response = await context.request.get(`${server.PREFIX}/headers`);
|
||||
expect(response.status()).toBe(200);
|
||||
const headers = response.headersArray().filter(({ name }) => name.toLowerCase().includes('name-'));
|
||||
expect(headers).toEqual([{ name: 'Name-A', value: 'v1' }, { name: 'name-b', value: 'v4' }, { name: 'Name-a', value: 'v2' }, { name: 'name-A', value: 'v3' }]);
|
||||
|
|
@ -294,7 +302,7 @@ it('should work with context level proxy', async ({browserOptions, browserType,
|
|||
|
||||
const [request, response] = await Promise.all([
|
||||
server.waitForRequest('/target.html'),
|
||||
context.fetch(`http://non-existent.com/target.html`)
|
||||
context.request.get(`http://non-existent.com/target.html`)
|
||||
]);
|
||||
expect(response.status()).toBe(200);
|
||||
expect(request.url).toBe('/target.html');
|
||||
|
|
@ -315,7 +323,7 @@ it('should pass proxy credentials', async ({browserType, browserOptions, server,
|
|||
proxy: { server: `localhost:${proxyServer.PORT}`, username: 'user', password: 'secret' }
|
||||
});
|
||||
const context = await browser.newContext();
|
||||
const response = await context.fetch('http://non-existent.com/simple.json');
|
||||
const response = await context.request.get('http://non-existent.com/simple.json');
|
||||
expect(proxyServer.connectHosts).toContain('non-existent.com:80');
|
||||
expect(auth).toBe('Basic ' + Buffer.from('user:secret').toString('base64'));
|
||||
expect(await response.json()).toEqual({foo: 'bar'});
|
||||
|
|
@ -327,7 +335,7 @@ it('should work with http credentials', async ({context, server}) => {
|
|||
|
||||
const [request, response] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
context.fetch(server.EMPTY_PAGE, {
|
||||
context.request.get(server.EMPTY_PAGE, {
|
||||
headers: {
|
||||
'authorization': 'Basic ' + Buffer.from('user:pass').toString('base64')
|
||||
}
|
||||
|
|
@ -337,29 +345,28 @@ it('should work with http credentials', async ({context, server}) => {
|
|||
expect(request.url).toBe('/empty.html');
|
||||
});
|
||||
|
||||
it('should work with setHTTPCredentials', async ({context, browser, server}) => {
|
||||
it('should work with setHTTPCredentials', async ({context, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const response1 = await context.fetch(server.EMPTY_PAGE);
|
||||
const response1 = await context.request.get(server.EMPTY_PAGE);
|
||||
expect(response1.status()).toBe(401);
|
||||
|
||||
await context.setHTTPCredentials({ username: 'user', password: 'pass' });
|
||||
const response2 = await context.fetch(server.EMPTY_PAGE);
|
||||
const response2 = await context.request.get(server.EMPTY_PAGE);
|
||||
expect(response2.status()).toBe(200);
|
||||
});
|
||||
|
||||
it('should return error with wrong credentials', async ({context, browser, server}) => {
|
||||
it('should return error with wrong credentials', async ({context, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
await context.setHTTPCredentials({ username: 'user', password: 'wrong' });
|
||||
const response2 = await context.fetch(server.EMPTY_PAGE);
|
||||
const response2 = await context.request.get(server.EMPTY_PAGE);
|
||||
expect(response2.status()).toBe(401);
|
||||
});
|
||||
|
||||
it('should support post data', async ({context, server}) => {
|
||||
const [request, response] = await Promise.all([
|
||||
server.waitForRequest('/simple.json'),
|
||||
context.fetch(`${server.PREFIX}/simple.json`, {
|
||||
method: 'POST',
|
||||
postData: 'My request'
|
||||
context.request.post(`${server.PREFIX}/simple.json`, {
|
||||
data: 'My request'
|
||||
})
|
||||
]);
|
||||
expect(request.method).toBe('POST');
|
||||
|
|
@ -371,7 +378,7 @@ it('should support post data', async ({context, server}) => {
|
|||
it('should add default headers', async ({context, server, page}) => {
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
context.fetch(server.EMPTY_PAGE)
|
||||
context.request.get(server.EMPTY_PAGE)
|
||||
]);
|
||||
expect(request.headers['accept']).toBe('*/*');
|
||||
const userAgent = await page.evaluate(() => navigator.userAgent);
|
||||
|
|
@ -383,7 +390,7 @@ it('should add default headers to redirects', async ({context, server, page}) =>
|
|||
server.setRedirect('/redirect', '/empty.html');
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
context.fetch(`${server.PREFIX}/redirect`)
|
||||
context.request.get(`${server.PREFIX}/redirect`)
|
||||
]);
|
||||
expect(request.headers['accept']).toBe('*/*');
|
||||
const userAgent = await page.evaluate(() => navigator.userAgent);
|
||||
|
|
@ -394,7 +401,7 @@ it('should add default headers to redirects', async ({context, server, page}) =>
|
|||
it('should allow to override default headers', async ({context, server, page}) => {
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
context.fetch(server.EMPTY_PAGE, {
|
||||
context.request.get(server.EMPTY_PAGE, {
|
||||
headers: {
|
||||
'User-Agent': 'Playwright',
|
||||
'Accept': 'text/html',
|
||||
|
|
@ -414,7 +421,7 @@ it('should propagate custom headers with redirects', async ({context, server}) =
|
|||
server.waitForRequest('/a/redirect1'),
|
||||
server.waitForRequest('/b/c/redirect2'),
|
||||
server.waitForRequest('/simple.json'),
|
||||
context.fetch(`${server.PREFIX}/a/redirect1`, {headers: {'foo': 'bar'}}),
|
||||
context.request.get(`${server.PREFIX}/a/redirect1`, {headers: {'foo': 'bar'}}),
|
||||
]);
|
||||
expect(req1.headers['foo']).toBe('bar');
|
||||
expect(req2.headers['foo']).toBe('bar');
|
||||
|
|
@ -429,7 +436,7 @@ it('should propagate extra http headers with redirects', async ({context, server
|
|||
server.waitForRequest('/a/redirect1'),
|
||||
server.waitForRequest('/b/c/redirect2'),
|
||||
server.waitForRequest('/simple.json'),
|
||||
context.fetch(`${server.PREFIX}/a/redirect1`),
|
||||
context.request.get(`${server.PREFIX}/a/redirect1`),
|
||||
]);
|
||||
expect(req1.headers['my-secret']).toBe('Value');
|
||||
expect(req2.headers['my-secret']).toBe('Value');
|
||||
|
|
@ -437,7 +444,7 @@ it('should propagate extra http headers with redirects', async ({context, server
|
|||
});
|
||||
|
||||
it('should throw on invalid header value', async ({context, server}) => {
|
||||
const error = await context.fetch(`${server.PREFIX}/a/redirect1`, {
|
||||
const error = await context.request.get(`${server.PREFIX}/a/redirect1`, {
|
||||
headers: {
|
||||
'foo': 'недопустимое значение',
|
||||
}
|
||||
|
|
@ -446,9 +453,9 @@ it('should throw on invalid header value', async ({context, server}) => {
|
|||
});
|
||||
|
||||
it('should throw on non-http(s) protocol', async ({context}) => {
|
||||
const error1 = await context.fetch(`data:text/plain,test`).catch(e => e);
|
||||
const error1 = await context.request.get(`data:text/plain,test`).catch(e => e);
|
||||
expect(error1.message).toContain('Protocol "data:" not supported');
|
||||
const error2 = await context.fetch(`file:///tmp/foo`).catch(e => e);
|
||||
const error2 = await context.request.get(`file:///tmp/foo`).catch(e => e);
|
||||
expect(error2.message).toContain('Protocol "file:" not supported');
|
||||
});
|
||||
|
||||
|
|
@ -458,7 +465,7 @@ it('should support https', async ({context, httpsServer}) => {
|
|||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
||||
suppressCertificateWarning();
|
||||
try {
|
||||
const response = await context.fetch(httpsServer.EMPTY_PAGE);
|
||||
const response = await context.request.get(httpsServer.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
} finally {
|
||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = oldValue;
|
||||
|
|
@ -467,7 +474,7 @@ it('should support https', async ({context, httpsServer}) => {
|
|||
|
||||
it('should support ignoreHTTPSErrors', async ({contextFactory, contextOptions, httpsServer}) => {
|
||||
const context = await contextFactory({ ...contextOptions, ignoreHTTPSErrors: true });
|
||||
const response = await context.fetch(httpsServer.EMPTY_PAGE);
|
||||
const response = await context.request.get(httpsServer.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
|
||||
|
|
@ -476,7 +483,7 @@ it('should resolve url relative to baseURL', async function({server, contextFact
|
|||
...contextOptions,
|
||||
baseURL: server.PREFIX,
|
||||
});
|
||||
const response = await context.fetch('/empty.html');
|
||||
const response = await context.request.get('/empty.html');
|
||||
expect(response.url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
|
||||
|
|
@ -496,7 +503,7 @@ it('should support gzip compression', async function({context, server}) {
|
|||
gzip.end();
|
||||
});
|
||||
|
||||
const response = await context.fetch(server.PREFIX + '/compressed');
|
||||
const response = await context.request.get(server.PREFIX + '/compressed');
|
||||
expect(await response.text()).toBe('Hello, world!');
|
||||
});
|
||||
|
||||
|
|
@ -510,7 +517,7 @@ it('should throw informatibe error on corrupted gzip body', async function({cont
|
|||
res.end();
|
||||
});
|
||||
|
||||
const error = await context.fetch(server.PREFIX + '/corrupted').catch(e => e);
|
||||
const error = await context.request.get(server.PREFIX + '/corrupted').catch(e => e);
|
||||
expect(error.message).toContain(`failed to decompress 'gzip' encoding`);
|
||||
});
|
||||
|
||||
|
|
@ -530,7 +537,7 @@ it('should support brotli compression', async function({context, server}) {
|
|||
brotli.end();
|
||||
});
|
||||
|
||||
const response = await context.fetch(server.PREFIX + '/compressed');
|
||||
const response = await context.request.get(server.PREFIX + '/compressed');
|
||||
expect(await response.text()).toBe('Hello, world!');
|
||||
});
|
||||
|
||||
|
|
@ -544,7 +551,7 @@ it('should throw informatibe error on corrupted brotli body', async function({co
|
|||
res.end();
|
||||
});
|
||||
|
||||
const error = await context.fetch(server.PREFIX + '/corrupted').catch(e => e);
|
||||
const error = await context.request.get(server.PREFIX + '/corrupted').catch(e => e);
|
||||
expect(error.message).toContain(`failed to decompress 'br' encoding`);
|
||||
});
|
||||
|
||||
|
|
@ -564,7 +571,7 @@ it('should support deflate compression', async function({context, server}) {
|
|||
deflate.end();
|
||||
});
|
||||
|
||||
const response = await context.fetch(server.PREFIX + '/compressed');
|
||||
const response = await context.request.get(server.PREFIX + '/compressed');
|
||||
expect(await response.text()).toBe('Hello, world!');
|
||||
});
|
||||
|
||||
|
|
@ -578,7 +585,7 @@ it('should throw informatibe error on corrupted deflate body', async function({c
|
|||
res.end();
|
||||
});
|
||||
|
||||
const error = await context.fetch(server.PREFIX + '/corrupted').catch(e => e);
|
||||
const error = await context.request.get(server.PREFIX + '/corrupted').catch(e => e);
|
||||
expect(error.message).toContain(`failed to decompress 'deflate' encoding`);
|
||||
});
|
||||
|
||||
|
|
@ -590,7 +597,7 @@ it('should support timeout option', async function({context, server}) {
|
|||
});
|
||||
});
|
||||
|
||||
const error = await context.fetch(server.PREFIX + '/slow', { timeout: 10 }).catch(e => e);
|
||||
const error = await context.request.get(server.PREFIX + '/slow', { timeout: 10 }).catch(e => e);
|
||||
expect(error.message).toContain(`Request timed out after 10ms`);
|
||||
});
|
||||
|
||||
|
|
@ -604,12 +611,12 @@ it('should respect timeout after redirects', async function({context, server}) {
|
|||
});
|
||||
|
||||
context.setDefaultTimeout(100);
|
||||
const error = await context.fetch(server.PREFIX + '/redirect').catch(e => e);
|
||||
const error = await context.request.get(server.PREFIX + '/redirect').catch(e => e);
|
||||
expect(error.message).toContain(`Request timed out after 100ms`);
|
||||
});
|
||||
|
||||
it('should dispose', async function({context, server}) {
|
||||
const response = await context.fetch(server.PREFIX + '/simple.json');
|
||||
const response = await context.request.get(server.PREFIX + '/simple.json');
|
||||
expect(await response.json()).toEqual({ foo: 'bar' });
|
||||
await response.dispose();
|
||||
const error = await response.body().catch(e => e);
|
||||
|
|
@ -617,7 +624,7 @@ it('should dispose', async function({context, server}) {
|
|||
});
|
||||
|
||||
it('should dispose when context closes', async function({context, server}) {
|
||||
const response = await context.fetch(server.PREFIX + '/simple.json');
|
||||
const response = await context.request.get(server.PREFIX + '/simple.json');
|
||||
expect(await response.json()).toEqual({ foo: 'bar' });
|
||||
await context.close();
|
||||
const error = await response.body().catch(e => e);
|
||||
|
|
@ -625,7 +632,7 @@ it('should dispose when context closes', async function({context, server}) {
|
|||
});
|
||||
|
||||
it('should throw on invalid first argument', async function({context}) {
|
||||
const error = await context.fetch({} as any).catch(e => e);
|
||||
const error = await context.request.get({} as any).catch(e => e);
|
||||
expect(error.message).toContain('First argument must be either URL string or Request');
|
||||
});
|
||||
|
||||
|
|
@ -636,12 +643,11 @@ it('should override request parameters', async function({context, page, server})
|
|||
]);
|
||||
const [req] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
context.fetch(pageReq, {
|
||||
method: 'POST',
|
||||
context.request.post(pageReq, {
|
||||
headers: {
|
||||
'foo': 'bar'
|
||||
},
|
||||
postData: 'data'
|
||||
data: 'data'
|
||||
})
|
||||
]);
|
||||
expect(req.method).toBe('POST');
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ it('should include the origin header', async ({page, server, isAndroid}) => {
|
|||
it('should fulfill with fetch result', async ({page, server, isElectron}) => {
|
||||
it.fixme(isElectron, 'error: Browser context management is not supported.');
|
||||
await page.route('**/*', async route => {
|
||||
const response = await page.fetch(server.PREFIX + '/simple.json');
|
||||
const response = await page.request.get(server.PREFIX + '/simple.json');
|
||||
route.fulfill({ response });
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
|
|
@ -208,7 +208,7 @@ it('should fulfill with fetch result', async ({page, server, isElectron}) => {
|
|||
it('should fulfill with fetch result and overrides', async ({page, server, isElectron}) => {
|
||||
it.fixme(isElectron, 'error: Browser context management is not supported.');
|
||||
await page.route('**/*', async route => {
|
||||
const response = await page.fetch(server.PREFIX + '/simple.json');
|
||||
const response = await page.request.get(server.PREFIX + '/simple.json');
|
||||
route.fulfill({
|
||||
response,
|
||||
status: 201,
|
||||
|
|
@ -226,7 +226,7 @@ it('should fulfill with fetch result and overrides', async ({page, server, isEle
|
|||
it('should fetch original request and fulfill', async ({page, server, isElectron}) => {
|
||||
it.fixme(isElectron, 'error: Browser context management is not supported.');
|
||||
await page.route('**/*', async route => {
|
||||
const response = await page.fetch(route.request());
|
||||
const response = await page.request.get(route.request());
|
||||
route.fulfill({
|
||||
response,
|
||||
});
|
||||
|
|
|
|||
146
types/types.d.ts
vendored
146
types/types.d.ts
vendored
|
|
@ -1995,34 +1995,6 @@ export interface Page {
|
|||
*/
|
||||
exposeFunction(name: string, callback: Function): Promise<void>;
|
||||
|
||||
/**
|
||||
* Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
|
||||
* context cookies from the response. The method will automatically follow redirects.
|
||||
* @param urlOrRequest Target URL or Request to get all fetch parameters from.
|
||||
* @param options
|
||||
*/
|
||||
fetch(urlOrRequest: string|Request, options?: {
|
||||
/**
|
||||
* Allows to set HTTP headers.
|
||||
*/
|
||||
headers?: { [key: string]: string; };
|
||||
|
||||
/**
|
||||
* If set changes the request method (e.g. PUT or POST). If not specified, GET method is used.
|
||||
*/
|
||||
method?: string;
|
||||
|
||||
/**
|
||||
* Allows to set post data of the request.
|
||||
*/
|
||||
postData?: string|Buffer;
|
||||
|
||||
/**
|
||||
* Request timeout in milliseconds.
|
||||
*/
|
||||
timeout?: number;
|
||||
}): Promise<FetchResponse>;
|
||||
|
||||
/**
|
||||
* This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/docs/actionability) checks, focuses the
|
||||
* element, fills it and triggers an `input` event after filling. Note that you can pass an empty string to clear the input
|
||||
|
|
@ -2777,6 +2749,11 @@ export interface Page {
|
|||
waitUntil?: "load"|"domcontentloaded"|"networkidle";
|
||||
}): Promise<null|Response>;
|
||||
|
||||
/**
|
||||
* API testing helper associated with this page. Requests made with this API will use page cookies.
|
||||
*/
|
||||
request: FetchRequest;
|
||||
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by a page.
|
||||
*
|
||||
|
|
@ -6422,34 +6399,6 @@ export interface BrowserContext {
|
|||
*/
|
||||
exposeFunction(name: string, callback: Function): Promise<void>;
|
||||
|
||||
/**
|
||||
* Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
|
||||
* context cookies from the response. The method will automatically follow redirects.
|
||||
* @param urlOrRequest Target URL or Request to get all fetch parameters from.
|
||||
* @param options
|
||||
*/
|
||||
fetch(urlOrRequest: string|Request, options?: {
|
||||
/**
|
||||
* Allows to set HTTP headers.
|
||||
*/
|
||||
headers?: { [key: string]: string; };
|
||||
|
||||
/**
|
||||
* If set changes the request method (e.g. PUT or POST). If not specified, GET method is used.
|
||||
*/
|
||||
method?: string;
|
||||
|
||||
/**
|
||||
* Allows to set post data of the request.
|
||||
*/
|
||||
postData?: string|Buffer;
|
||||
|
||||
/**
|
||||
* Request timeout in milliseconds.
|
||||
*/
|
||||
timeout?: number;
|
||||
}): Promise<FetchResponse>;
|
||||
|
||||
/**
|
||||
* Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if
|
||||
* specified.
|
||||
|
|
@ -6496,6 +6445,11 @@ export interface BrowserContext {
|
|||
*/
|
||||
pages(): Array<Page>;
|
||||
|
||||
/**
|
||||
* API testing helper associated with this context. Requests made with this API will use context cookies.
|
||||
*/
|
||||
request: FetchRequest;
|
||||
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
|
|
@ -12663,10 +12617,86 @@ export interface Electron {
|
|||
}): Promise<ElectronApplication>;
|
||||
}
|
||||
|
||||
/**
|
||||
* This API is used for Web API testing. You can use it to trigger API endpoints, configure micro-services, prepare
|
||||
* environment or the service to your e2e test. When used on [Page] or a [BrowserContext], this API will automatically use
|
||||
* the cookies from the corresponding [BrowserContext]. This means that if you log in using this API, your e2e test will be
|
||||
* logged in and vice versa.
|
||||
*/
|
||||
export interface FetchRequest {
|
||||
/**
|
||||
* Sends HTTP(S) fetch and returns its response. The method will populate fetch cookies from the context and update context
|
||||
* cookies from the response. The method will automatically follow redirects.
|
||||
* @param urlOrRequest Target URL or Request to get all fetch parameters from.
|
||||
* @param options
|
||||
*/
|
||||
fetch(urlOrRequest: string|Request, options?: {
|
||||
/**
|
||||
* Allows to set post data of the fetch.
|
||||
*/
|
||||
data?: string|Buffer;
|
||||
|
||||
/**
|
||||
* Allows to set HTTP headers.
|
||||
*/
|
||||
headers?: { [key: string]: string; };
|
||||
|
||||
/**
|
||||
* If set changes the fetch method (e.g. PUT or POST). If not specified, GET method is used.
|
||||
*/
|
||||
method?: string;
|
||||
|
||||
/**
|
||||
* Request timeout in milliseconds.
|
||||
*/
|
||||
timeout?: number;
|
||||
}): Promise<FetchResponse>;
|
||||
|
||||
/**
|
||||
* Sends HTTP(S) GET request and returns its response. The method will populate fetch cookies from the context and update
|
||||
* context cookies from the response. The method will automatically follow redirects.
|
||||
* @param urlOrRequest Target URL or Request to get all fetch parameters from.
|
||||
* @param options
|
||||
*/
|
||||
get(urlOrRequest: string|Request, options?: {
|
||||
/**
|
||||
* Allows to set HTTP headers.
|
||||
*/
|
||||
headers?: { [key: string]: string; };
|
||||
|
||||
/**
|
||||
* Request timeout in milliseconds.
|
||||
*/
|
||||
timeout?: number;
|
||||
}): Promise<FetchResponse>;
|
||||
|
||||
/**
|
||||
* Sends HTTP(S) fetch and returns its response. The method will populate fetch cookies from the context and update context
|
||||
* cookies from the response. The method will automatically follow redirects.
|
||||
* @param urlOrRequest Target URL or Request to get all fetch parameters from.
|
||||
* @param options
|
||||
*/
|
||||
post(urlOrRequest: string|Request, options?: {
|
||||
/**
|
||||
* Allows to set post data of the fetch.
|
||||
*/
|
||||
data?: string|Buffer;
|
||||
|
||||
/**
|
||||
* Allows to set HTTP headers.
|
||||
*/
|
||||
headers?: { [key: string]: string; };
|
||||
|
||||
/**
|
||||
* Request timeout in milliseconds.
|
||||
*/
|
||||
timeout?: number;
|
||||
}): Promise<FetchResponse>;
|
||||
}
|
||||
|
||||
/**
|
||||
* [FetchResponse] class represents responses received from
|
||||
* [browserContext.fetch(urlOrRequest[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-fetch)
|
||||
* and [page.fetch(urlOrRequest[, options])](https://playwright.dev/docs/api/class-page#page-fetch) methods.
|
||||
* [fetchRequest.fetch(urlOrRequest[, options])](https://playwright.dev/docs/api/class-fetchrequest#fetch-request-fetch).
|
||||
*/
|
||||
export interface FetchResponse {
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue