mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(fetch): make fetch api public (#8853)
This commit is contained in:
parent
8d6bcfb66c
commit
b6180055df
@ -792,6 +792,37 @@ 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
|
||||
|
||||
74
docs/src/api/class-fetchresponse.md
Normal file
74
docs/src/api/class-fetchresponse.md
Normal file
@ -0,0 +1,74 @@
|
||||
# class: FetchResponse
|
||||
|
||||
[FetchResponse] class represents responses received from [`method: BrowserContext.fetch`] and [`method: Page.fetch`] methods.
|
||||
|
||||
## async method: FetchResponse.body
|
||||
- returns: <[Buffer]>
|
||||
|
||||
Returns the buffer with response body.
|
||||
|
||||
## async method: FetchResponse.dispose
|
||||
|
||||
Disposes the body of this response. If not called then the body will stay in memory until the context closes.
|
||||
|
||||
## method: FetchResponse.headers
|
||||
- returns: <[Object]<[string], [string]>>
|
||||
|
||||
An object with all the response HTTP headers associated with this response.
|
||||
|
||||
## method: FetchResponse.headersArray
|
||||
* langs: js, csharp, python
|
||||
- returns: <[Array]<[Array]<[string]>>>
|
||||
|
||||
An array with all the request HTTP headers associated with this response. Header names are not lower-cased.
|
||||
Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times.
|
||||
|
||||
## method: FetchResponse.headersArray
|
||||
* langs: java
|
||||
- returns: <[Array]<[Object]>>
|
||||
- `name` <[string]> Name of the header.
|
||||
- `value` <[string]> Value of the header.
|
||||
|
||||
An array with all the request HTTP headers associated with this response. Header names are not lower-cased.
|
||||
Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times.
|
||||
|
||||
## async method: FetchResponse.json
|
||||
* langs: js, python
|
||||
- returns: <[Serializable]>
|
||||
|
||||
Returns the JSON representation of response body.
|
||||
|
||||
This method will throw if the response body is not parsable via `JSON.parse`.
|
||||
|
||||
## async method: FetchResponse.json
|
||||
* langs: csharp
|
||||
- returns: <[null]|[JsonElement]>
|
||||
|
||||
Returns the JSON representation of response body.
|
||||
|
||||
This method will throw if the response body is not parsable via `JSON.parse`.
|
||||
|
||||
## method: FetchResponse.ok
|
||||
- returns: <[boolean]>
|
||||
|
||||
Contains a boolean stating whether the response was successful (status in the range 200-299) or not.
|
||||
|
||||
## method: FetchResponse.status
|
||||
- returns: <[int]>
|
||||
|
||||
Contains the status code of the response (e.g., 200 for a success).
|
||||
|
||||
## method: FetchResponse.statusText
|
||||
- returns: <[string]>
|
||||
|
||||
Contains the status text of the response (e.g. usually an "OK" for a success).
|
||||
|
||||
## async method: FetchResponse.text
|
||||
- returns: <[string]>
|
||||
|
||||
Returns the text representation of response body.
|
||||
|
||||
## method: FetchResponse.url
|
||||
- returns: <[string]>
|
||||
|
||||
Contains the URL of the response.
|
||||
@ -1736,6 +1736,37 @@ 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.
|
||||
|
||||
@ -220,6 +220,11 @@ Optional response body as raw bytes.
|
||||
File path to respond with. The content type will be inferred from file extension. If `path` is a relative path, then it
|
||||
is resolved relative to the current working directory.
|
||||
|
||||
### option: Route.fulfill.response
|
||||
- `response` <[FetchResponse]>
|
||||
|
||||
[FetchResponse] to fulfill route's request with. Individual fields of the response (such as headers) can be overridden using fulfill options.
|
||||
|
||||
## method: Route.request
|
||||
- returns: <[Request]>
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ export { TimeoutError } from '../utils/errors';
|
||||
export { Frame } from './frame';
|
||||
export { Keyboard, Mouse, Touchscreen } from './input';
|
||||
export { JSHandle } from './jsHandle';
|
||||
export { Request, Response, Route, WebSocket } from './network';
|
||||
export { FetchResponse, Request, Response, Route, WebSocket } from './network';
|
||||
export { Page } from './page';
|
||||
export { Selectors } from './selectors';
|
||||
export { Tracing } from './tracing';
|
||||
|
||||
@ -216,20 +216,18 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
||||
});
|
||||
}
|
||||
|
||||
async _fetch(request: network.Request, options?: { timeout?: number }): Promise<network.FetchResponse>;
|
||||
async _fetch(url: string, options?: FetchOptions): Promise<network.FetchResponse>;
|
||||
async _fetch(urlOrRequest: string|network.Request, options: FetchOptions = {}): Promise<network.FetchResponse> {
|
||||
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 = request?.method() || options.method;
|
||||
const method = options.method || request?.method();
|
||||
// Cannot call allHeaders() here as the request may be paused inside route handler.
|
||||
const headersObj = request?.headers() || options.headers;
|
||||
const headersObj = options.headers || request?.headers() ;
|
||||
const headers = headersObj ? headersObjectToArray(headersObj) : undefined;
|
||||
let postDataBuffer = request?.postDataBuffer();
|
||||
let postDataBuffer = isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData;
|
||||
if (postDataBuffer === undefined)
|
||||
postDataBuffer = (isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData);
|
||||
postDataBuffer = request?.postDataBuffer() || undefined;
|
||||
const postData = (postDataBuffer ? postDataBuffer.toString('base64') : undefined);
|
||||
const result = await channel.fetch({
|
||||
url,
|
||||
@ -395,7 +393,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
||||
}
|
||||
}
|
||||
|
||||
export type FetchOptions = { url?: string, method?: string, headers?: Headers, postData?: string | Buffer, timeout?: number };
|
||||
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)
|
||||
|
||||
@ -310,7 +310,7 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
||||
});
|
||||
}
|
||||
|
||||
async fulfill(options: { response?: Response|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;
|
||||
@ -524,7 +524,7 @@ export class Response extends ChannelOwner<channels.ResponseChannel, channels.Re
|
||||
}
|
||||
}
|
||||
|
||||
export class FetchResponse {
|
||||
export class FetchResponse implements api.FetchResponse {
|
||||
private readonly _initializer: channels.FetchResponse;
|
||||
private readonly _headers: Headers;
|
||||
private readonly _context: BrowserContext;
|
||||
|
||||
@ -443,10 +443,8 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
||||
return this._mainFrame.evaluate(pageFunction, arg);
|
||||
}
|
||||
|
||||
async _fetch(request: network.Request, options?: { timeout?: number }): Promise<network.FetchResponse>;
|
||||
async _fetch(url: string, options?: FetchOptions): Promise<network.FetchResponse>;
|
||||
async _fetch(urlOrRequest: string|network.Request, options: FetchOptions = {}): Promise<network.FetchResponse> {
|
||||
return await this._browserContext._fetch(urlOrRequest as any, options);
|
||||
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) {
|
||||
|
||||
@ -41,8 +41,7 @@ it.afterAll(() => {
|
||||
});
|
||||
|
||||
it('should work', async ({context, server}) => {
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch(server.PREFIX + '/simple.json');
|
||||
const response = await context.fetch(server.PREFIX + '/simple.json');
|
||||
expect(response.url()).toBe(server.PREFIX + '/simple.json');
|
||||
expect(response.status()).toBe(200);
|
||||
expect(response.statusText()).toBe('OK');
|
||||
@ -58,8 +57,7 @@ it('should throw on network error', async ({context, server}) => {
|
||||
req.socket.destroy();
|
||||
});
|
||||
let error;
|
||||
// @ts-expect-error
|
||||
await context._fetch(server.PREFIX + '/test').catch(e => error = e);
|
||||
await context.fetch(server.PREFIX + '/test').catch(e => error = e);
|
||||
expect(error.message).toContain('socket hang up');
|
||||
});
|
||||
|
||||
@ -69,8 +67,7 @@ it('should throw on network error after redirect', async ({context, server}) =>
|
||||
req.socket.destroy();
|
||||
});
|
||||
let error;
|
||||
// @ts-expect-error
|
||||
await context._fetch(server.PREFIX + '/redirect').catch(e => error = e);
|
||||
await context.fetch(server.PREFIX + '/redirect').catch(e => error = e);
|
||||
expect(error.message).toContain('socket hang up');
|
||||
});
|
||||
|
||||
@ -85,8 +82,7 @@ it('should throw on network error when sending body', async ({context, server})
|
||||
req.socket.destroy();
|
||||
});
|
||||
let error;
|
||||
// @ts-expect-error
|
||||
await context._fetch(server.PREFIX + '/test').catch(e => error = e);
|
||||
await context.fetch(server.PREFIX + '/test').catch(e => error = e);
|
||||
expect(error.message).toContain('Error: aborted');
|
||||
});
|
||||
|
||||
@ -102,8 +98,7 @@ it('should throw on network error when sending body after redirect', async ({con
|
||||
req.socket.destroy();
|
||||
});
|
||||
let error;
|
||||
// @ts-expect-error
|
||||
await context._fetch(server.PREFIX + '/redirect').catch(e => error = e);
|
||||
await context.fetch(server.PREFIX + '/redirect').catch(e => error = e);
|
||||
expect(error.message).toContain('Error: aborted');
|
||||
});
|
||||
|
||||
@ -120,8 +115,7 @@ it('should add session cookies to request', async ({context, server}) => {
|
||||
}]);
|
||||
const [req] = await Promise.all([
|
||||
server.waitForRequest('/simple.json'),
|
||||
// @ts-expect-error
|
||||
context._fetch(`http://www.my.playwright.dev:${server.PORT}/simple.json`),
|
||||
context.fetch(`http://www.my.playwright.dev:${server.PORT}/simple.json`),
|
||||
]);
|
||||
expect(req.headers.cookie).toEqual('username=John Doe');
|
||||
});
|
||||
@ -139,8 +133,7 @@ it('should not add context cookie if cookie header passed as a parameter', async
|
||||
}]);
|
||||
const [req] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
// @ts-expect-error
|
||||
context._fetch(`http://www.my.playwright.dev:${server.PORT}/empty.html`, {
|
||||
context.fetch(`http://www.my.playwright.dev:${server.PORT}/empty.html`, {
|
||||
headers: {
|
||||
'Cookie': 'foo=bar'
|
||||
}
|
||||
@ -164,8 +157,7 @@ it('should follow redirects', async ({context, server}) => {
|
||||
}]);
|
||||
const [req, response] = await Promise.all([
|
||||
server.waitForRequest('/simple.json'),
|
||||
// @ts-expect-error
|
||||
context._fetch(`http://www.my.playwright.dev:${server.PORT}/redirect1`),
|
||||
context.fetch(`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`);
|
||||
@ -177,8 +169,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();
|
||||
});
|
||||
// @ts-expect-error
|
||||
await context._fetch(server.PREFIX + '/setcookie.html');
|
||||
await context.fetch(server.PREFIX + '/setcookie.html');
|
||||
const cookies = await context.cookies();
|
||||
expect(new Set(cookies.map(c => ({ name: c.name, value: c.value })))).toEqual(new Set([
|
||||
{
|
||||
@ -199,8 +190,7 @@ it('should not lose body while handling Set-Cookie header', async ({context, pag
|
||||
res.setHeader('Set-Cookie', ['session=value', 'foo=bar; max-age=3600']);
|
||||
res.end('text content');
|
||||
});
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch(server.PREFIX + '/setcookie.html');
|
||||
const response = await context.fetch(server.PREFIX + '/setcookie.html');
|
||||
expect(await response.text()).toBe('text content');
|
||||
});
|
||||
|
||||
@ -220,8 +210,7 @@ it('should handle cookies on redirects', async ({context, server, browserName, i
|
||||
server.waitForRequest('/redirect1'),
|
||||
server.waitForRequest('/a/b/redirect2'),
|
||||
server.waitForRequest('/title.html'),
|
||||
// @ts-expect-error
|
||||
context._fetch(`${server.PREFIX}/redirect1`),
|
||||
context.fetch(`${server.PREFIX}/redirect1`),
|
||||
]);
|
||||
expect(req1.headers.cookie).toBeFalsy();
|
||||
expect(req2.headers.cookie).toBe('r1=v1');
|
||||
@ -232,8 +221,7 @@ it('should handle cookies on redirects', async ({context, server, browserName, i
|
||||
server.waitForRequest('/redirect1'),
|
||||
server.waitForRequest('/a/b/redirect2'),
|
||||
server.waitForRequest('/title.html'),
|
||||
// @ts-expect-error
|
||||
context._fetch(`${server.PREFIX}/redirect1`),
|
||||
context.fetch(`${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']);
|
||||
@ -278,8 +266,7 @@ it('should return raw headers', async ({context, page, server}) => {
|
||||
conn.uncork();
|
||||
conn.end();
|
||||
});
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch(`${server.PREFIX}/headers`);
|
||||
const response = await context.fetch(`${server.PREFIX}/headers`);
|
||||
expect(response.status()).toBe(200);
|
||||
const headers = response.headersArray().filter(([name, value]) => name.toLowerCase().includes('name-'));
|
||||
expect(headers).toEqual([['Name-A', 'v1'], ['name-b', 'v4'], ['Name-a', 'v2'], ['name-A', 'v3']]);
|
||||
@ -307,8 +294,7 @@ it('should work with context level proxy', async ({browserOptions, browserType,
|
||||
|
||||
const [request, response] = await Promise.all([
|
||||
server.waitForRequest('/target.html'),
|
||||
// @ts-expect-error
|
||||
context._fetch(`http://non-existent.com/target.html`)
|
||||
context.fetch(`http://non-existent.com/target.html`)
|
||||
]);
|
||||
expect(response.status()).toBe(200);
|
||||
expect(request.url).toBe('/target.html');
|
||||
@ -329,8 +315,7 @@ it('should pass proxy credentials', async ({browserType, browserOptions, server,
|
||||
proxy: { server: `localhost:${proxyServer.PORT}`, username: 'user', password: 'secret' }
|
||||
});
|
||||
const context = await browser.newContext();
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch('http://non-existent.com/simple.json');
|
||||
const response = await context.fetch('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'});
|
||||
@ -342,8 +327,7 @@ it('should work with http credentials', async ({context, server}) => {
|
||||
|
||||
const [request, response] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
// @ts-expect-error
|
||||
context._fetch(server.EMPTY_PAGE, {
|
||||
context.fetch(server.EMPTY_PAGE, {
|
||||
headers: {
|
||||
'authorization': 'Basic ' + Buffer.from('user:pass').toString('base64')
|
||||
}
|
||||
@ -355,29 +339,25 @@ it('should work with http credentials', async ({context, server}) => {
|
||||
|
||||
it('should work with setHTTPCredentials', async ({context, browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
// @ts-expect-error
|
||||
const response1 = await context._fetch(server.EMPTY_PAGE);
|
||||
const response1 = await context.fetch(server.EMPTY_PAGE);
|
||||
expect(response1.status()).toBe(401);
|
||||
|
||||
await context.setHTTPCredentials({ username: 'user', password: 'pass' });
|
||||
// @ts-expect-error
|
||||
const response2 = await context._fetch(server.EMPTY_PAGE);
|
||||
const response2 = await context.fetch(server.EMPTY_PAGE);
|
||||
expect(response2.status()).toBe(200);
|
||||
});
|
||||
|
||||
it('should return error with wrong credentials', async ({context, browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
await context.setHTTPCredentials({ username: 'user', password: 'wrong' });
|
||||
// @ts-expect-error
|
||||
const response2 = await context._fetch(server.EMPTY_PAGE);
|
||||
const response2 = await context.fetch(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'),
|
||||
// @ts-expect-error
|
||||
context._fetch(`${server.PREFIX}/simple.json`, {
|
||||
context.fetch(`${server.PREFIX}/simple.json`, {
|
||||
method: 'POST',
|
||||
postData: 'My request'
|
||||
})
|
||||
@ -391,8 +371,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'),
|
||||
// @ts-expect-error
|
||||
context._fetch(server.EMPTY_PAGE)
|
||||
context.fetch(server.EMPTY_PAGE)
|
||||
]);
|
||||
expect(request.headers['accept']).toBe('*/*');
|
||||
const userAgent = await page.evaluate(() => navigator.userAgent);
|
||||
@ -404,8 +383,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'),
|
||||
// @ts-expect-error
|
||||
context._fetch(`${server.PREFIX}/redirect`)
|
||||
context.fetch(`${server.PREFIX}/redirect`)
|
||||
]);
|
||||
expect(request.headers['accept']).toBe('*/*');
|
||||
const userAgent = await page.evaluate(() => navigator.userAgent);
|
||||
@ -416,8 +394,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'),
|
||||
// @ts-expect-error
|
||||
context._fetch(server.EMPTY_PAGE, {
|
||||
context.fetch(server.EMPTY_PAGE, {
|
||||
headers: {
|
||||
'User-Agent': 'Playwright',
|
||||
'Accept': 'text/html',
|
||||
@ -437,8 +414,7 @@ it('should propagate custom headers with redirects', async ({context, server}) =
|
||||
server.waitForRequest('/a/redirect1'),
|
||||
server.waitForRequest('/b/c/redirect2'),
|
||||
server.waitForRequest('/simple.json'),
|
||||
// @ts-expect-error
|
||||
context._fetch(`${server.PREFIX}/a/redirect1`, {headers: {'foo': 'bar'}}),
|
||||
context.fetch(`${server.PREFIX}/a/redirect1`, {headers: {'foo': 'bar'}}),
|
||||
]);
|
||||
expect(req1.headers['foo']).toBe('bar');
|
||||
expect(req2.headers['foo']).toBe('bar');
|
||||
@ -453,8 +429,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'),
|
||||
// @ts-expect-error
|
||||
context._fetch(`${server.PREFIX}/a/redirect1`),
|
||||
context.fetch(`${server.PREFIX}/a/redirect1`),
|
||||
]);
|
||||
expect(req1.headers['my-secret']).toBe('Value');
|
||||
expect(req2.headers['my-secret']).toBe('Value');
|
||||
@ -462,8 +437,7 @@ it('should propagate extra http headers with redirects', async ({context, server
|
||||
});
|
||||
|
||||
it('should throw on invalid header value', async ({context, server}) => {
|
||||
// @ts-expect-error
|
||||
const error = await context._fetch(`${server.PREFIX}/a/redirect1`, {
|
||||
const error = await context.fetch(`${server.PREFIX}/a/redirect1`, {
|
||||
headers: {
|
||||
'foo': 'недопустимое значение',
|
||||
}
|
||||
@ -472,11 +446,9 @@ it('should throw on invalid header value', async ({context, server}) => {
|
||||
});
|
||||
|
||||
it('should throw on non-http(s) protocol', async ({context}) => {
|
||||
// @ts-expect-error
|
||||
const error1 = await context._fetch(`data:text/plain,test`).catch(e => e);
|
||||
const error1 = await context.fetch(`data:text/plain,test`).catch(e => e);
|
||||
expect(error1.message).toContain('Protocol "data:" not supported');
|
||||
// @ts-expect-error
|
||||
const error2 = await context._fetch(`file:///tmp/foo`).catch(e => e);
|
||||
const error2 = await context.fetch(`file:///tmp/foo`).catch(e => e);
|
||||
expect(error2.message).toContain('Protocol "file:" not supported');
|
||||
});
|
||||
|
||||
@ -486,8 +458,7 @@ it('should support https', async ({context, httpsServer}) => {
|
||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
||||
suppressCertificateWarning();
|
||||
try {
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch(httpsServer.EMPTY_PAGE);
|
||||
const response = await context.fetch(httpsServer.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
} finally {
|
||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = oldValue;
|
||||
@ -496,8 +467,7 @@ it('should support https', async ({context, httpsServer}) => {
|
||||
|
||||
it('should support ignoreHTTPSErrors', async ({contextFactory, contextOptions, httpsServer}) => {
|
||||
const context = await contextFactory({ ...contextOptions, ignoreHTTPSErrors: true });
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch(httpsServer.EMPTY_PAGE);
|
||||
const response = await context.fetch(httpsServer.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
|
||||
@ -506,8 +476,7 @@ it('should resolve url relative to baseURL', async function({server, contextFact
|
||||
...contextOptions,
|
||||
baseURL: server.PREFIX,
|
||||
});
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch('/empty.html');
|
||||
const response = await context.fetch('/empty.html');
|
||||
expect(response.url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
|
||||
@ -527,8 +496,7 @@ it('should support gzip compression', async function({context, server}) {
|
||||
gzip.end();
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch(server.PREFIX + '/compressed');
|
||||
const response = await context.fetch(server.PREFIX + '/compressed');
|
||||
expect(await response.text()).toBe('Hello, world!');
|
||||
});
|
||||
|
||||
@ -542,8 +510,7 @@ it('should throw informatibe error on corrupted gzip body', async function({cont
|
||||
res.end();
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
const error = await context._fetch(server.PREFIX + '/corrupted').catch(e => e);
|
||||
const error = await context.fetch(server.PREFIX + '/corrupted').catch(e => e);
|
||||
expect(error.message).toContain(`failed to decompress 'gzip' encoding`);
|
||||
});
|
||||
|
||||
@ -563,8 +530,7 @@ it('should support brotli compression', async function({context, server}) {
|
||||
brotli.end();
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch(server.PREFIX + '/compressed');
|
||||
const response = await context.fetch(server.PREFIX + '/compressed');
|
||||
expect(await response.text()).toBe('Hello, world!');
|
||||
});
|
||||
|
||||
@ -578,8 +544,7 @@ it('should throw informatibe error on corrupted brotli body', async function({co
|
||||
res.end();
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
const error = await context._fetch(server.PREFIX + '/corrupted').catch(e => e);
|
||||
const error = await context.fetch(server.PREFIX + '/corrupted').catch(e => e);
|
||||
expect(error.message).toContain(`failed to decompress 'br' encoding`);
|
||||
});
|
||||
|
||||
@ -599,8 +564,7 @@ it('should support deflate compression', async function({context, server}) {
|
||||
deflate.end();
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch(server.PREFIX + '/compressed');
|
||||
const response = await context.fetch(server.PREFIX + '/compressed');
|
||||
expect(await response.text()).toBe('Hello, world!');
|
||||
});
|
||||
|
||||
@ -614,8 +578,7 @@ it('should throw informatibe error on corrupted deflate body', async function({c
|
||||
res.end();
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
const error = await context._fetch(server.PREFIX + '/corrupted').catch(e => e);
|
||||
const error = await context.fetch(server.PREFIX + '/corrupted').catch(e => e);
|
||||
expect(error.message).toContain(`failed to decompress 'deflate' encoding`);
|
||||
});
|
||||
|
||||
@ -627,8 +590,7 @@ it('should support timeout option', async function({context, server}) {
|
||||
});
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
const error = await context._fetch(server.PREFIX + '/slow', { timeout: 10 }).catch(e => e);
|
||||
const error = await context.fetch(server.PREFIX + '/slow', { timeout: 10 }).catch(e => e);
|
||||
expect(error.message).toContain(`Request timed out after 10ms`);
|
||||
});
|
||||
|
||||
@ -642,14 +604,12 @@ it('should respect timeout after redirects', async function({context, server}) {
|
||||
});
|
||||
|
||||
context.setDefaultTimeout(100);
|
||||
// @ts-expect-error
|
||||
const error = await context._fetch(server.PREFIX + '/redirect').catch(e => e);
|
||||
const error = await context.fetch(server.PREFIX + '/redirect').catch(e => e);
|
||||
expect(error.message).toContain(`Request timed out after 100ms`);
|
||||
});
|
||||
|
||||
it('should dispose', async function({context, server}) {
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch(server.PREFIX + '/simple.json');
|
||||
const response = await context.fetch(server.PREFIX + '/simple.json');
|
||||
expect(await response.json()).toEqual({ foo: 'bar' });
|
||||
await response.dispose();
|
||||
const error = await response.body().catch(e => e);
|
||||
@ -657,17 +617,34 @@ it('should dispose', async function({context, server}) {
|
||||
});
|
||||
|
||||
it('should dispose when context closes', async function({context, server}) {
|
||||
// @ts-expect-error
|
||||
const response = await context._fetch(server.PREFIX + '/simple.json');
|
||||
const response = await context.fetch(server.PREFIX + '/simple.json');
|
||||
expect(await response.json()).toEqual({ foo: 'bar' });
|
||||
await context.close();
|
||||
const error = await response.body().catch(e => e);
|
||||
expect(error.message).toContain('Target page, context or browser has been closed');
|
||||
});
|
||||
|
||||
it('should throw on invalid first argument', async function({context, server}) {
|
||||
// @ts-expect-error
|
||||
const error = await context._fetch({}).catch(e => e);
|
||||
it('should throw on invalid first argument', async function({context}) {
|
||||
const error = await context.fetch({} as any).catch(e => e);
|
||||
expect(error.message).toContain('First argument must be either URL string or Request');
|
||||
});
|
||||
|
||||
it('should override request parameters', async function({context, page, server}) {
|
||||
const [pageReq] = await Promise.all([
|
||||
page.waitForRequest('**/*'),
|
||||
page.goto(server.EMPTY_PAGE)
|
||||
]);
|
||||
const [req] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
context.fetch(pageReq, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'foo': 'bar'
|
||||
},
|
||||
postData: 'data'
|
||||
})
|
||||
]);
|
||||
expect(req.method).toBe('POST');
|
||||
expect(req.headers.foo).toBe('bar');
|
||||
expect((await req.postBody).toString('utf8')).toBe('data');
|
||||
});
|
||||
|
||||
@ -197,9 +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 => {
|
||||
// @ts-expect-error
|
||||
const response = await page._fetch(server.PREFIX + '/simple.json');
|
||||
// @ts-expect-error
|
||||
const response = await page.fetch(server.PREFIX + '/simple.json');
|
||||
route.fulfill({ response });
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
@ -210,10 +208,8 @@ 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 => {
|
||||
// @ts-expect-error
|
||||
const response = await page._fetch(server.PREFIX + '/simple.json');
|
||||
const response = await page.fetch(server.PREFIX + '/simple.json');
|
||||
route.fulfill({
|
||||
// @ts-expect-error
|
||||
response,
|
||||
status: 201,
|
||||
headers: {
|
||||
@ -230,10 +226,8 @@ 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 => {
|
||||
// @ts-expect-error
|
||||
const response = await page._fetch(route.request());
|
||||
const response = await page.fetch(route.request());
|
||||
route.fulfill({
|
||||
// @ts-expect-error
|
||||
response,
|
||||
});
|
||||
});
|
||||
|
||||
@ -46,7 +46,6 @@ it('should fulfill response with empty body', async ({page, server, browserName,
|
||||
// @ts-expect-error
|
||||
const response = await route._continueToResponse({});
|
||||
await route.fulfill({
|
||||
// @ts-expect-error
|
||||
response,
|
||||
status: 201,
|
||||
body: ''
|
||||
@ -127,7 +126,6 @@ it('should support fulfill after intercept', async ({page, server}) => {
|
||||
await page.route('**', async route => {
|
||||
// @ts-expect-error
|
||||
const response = await route._continueToResponse();
|
||||
// @ts-expect-error
|
||||
await route.fulfill({ response });
|
||||
});
|
||||
const response = await page.goto(server.PREFIX + '/title.html');
|
||||
@ -147,7 +145,6 @@ it('should intercept failures', async ({page, browserName, browserMajorVersion,
|
||||
try {
|
||||
// @ts-expect-error
|
||||
const response = await route._continueToResponse();
|
||||
// @ts-expect-error
|
||||
await route.fulfill({ response });
|
||||
} catch (e) {
|
||||
error = e;
|
||||
@ -173,7 +170,6 @@ it('should support request overrides', async ({page, server, browserName, browse
|
||||
headers: {'foo': 'bar'},
|
||||
postData: 'my data',
|
||||
});
|
||||
// @ts-expect-error
|
||||
await route.fulfill({ response });
|
||||
});
|
||||
await page.goto(server.PREFIX + '/foo');
|
||||
@ -228,7 +224,6 @@ it('should give access to the intercepted response status text', async ({page, s
|
||||
expect(response.statusText()).toBe('You are awesome');
|
||||
expect(response.url()).toBe(server.PREFIX + '/title.html');
|
||||
|
||||
// @ts-expect-error
|
||||
await Promise.all([route.fulfill({ response }), evalPromise]);
|
||||
});
|
||||
|
||||
@ -247,7 +242,6 @@ it('should give access to the intercepted response body', async ({page, server})
|
||||
|
||||
expect((await response.text())).toBe('{"foo": "bar"}\n');
|
||||
|
||||
// @ts-expect-error
|
||||
await Promise.all([route.fulfill({ response }), evalPromise]);
|
||||
});
|
||||
|
||||
@ -325,7 +319,6 @@ it('should fulfill original response after redirects', async ({page, browserName
|
||||
++routeCalls;
|
||||
// @ts-expect-error
|
||||
const response = await route._continueToResponse({});
|
||||
// @ts-expect-error
|
||||
await route.fulfill({ response });
|
||||
});
|
||||
const response = await page.goto(server.PREFIX + '/redirect/1.html');
|
||||
|
||||
122
types/types.d.ts
vendored
122
types/types.d.ts
vendored
@ -1995,6 +1995,34 @@ 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
|
||||
@ -6394,6 +6422,34 @@ 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.
|
||||
@ -12607,6 +12663,66 @@ export interface Electron {
|
||||
}): Promise<ElectronApplication>;
|
||||
}
|
||||
|
||||
/**
|
||||
* [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.
|
||||
*/
|
||||
export interface FetchResponse {
|
||||
/**
|
||||
* Returns the buffer with response body.
|
||||
*/
|
||||
body(): Promise<Buffer>;
|
||||
|
||||
/**
|
||||
* Disposes the body of this response. If not called then the body will stay in memory until the context closes.
|
||||
*/
|
||||
dispose(): Promise<void>;
|
||||
|
||||
/**
|
||||
* An object with all the response HTTP headers associated with this response.
|
||||
*/
|
||||
headers(): { [key: string]: string; };
|
||||
|
||||
/**
|
||||
* An array with all the request HTTP headers associated with this response. Header names are not lower-cased. Headers with
|
||||
* multiple entries, such as `Set-Cookie`, appear in the array multiple times.
|
||||
*/
|
||||
headersArray(): Array<Array<string>>;
|
||||
|
||||
/**
|
||||
* Returns the JSON representation of response body.
|
||||
*
|
||||
* This method will throw if the response body is not parsable via `JSON.parse`.
|
||||
*/
|
||||
json(): Promise<Serializable>;
|
||||
|
||||
/**
|
||||
* Contains a boolean stating whether the response was successful (status in the range 200-299) or not.
|
||||
*/
|
||||
ok(): boolean;
|
||||
|
||||
/**
|
||||
* Contains the status code of the response (e.g., 200 for a success).
|
||||
*/
|
||||
status(): number;
|
||||
|
||||
/**
|
||||
* Contains the status text of the response (e.g. usually an "OK" for a success).
|
||||
*/
|
||||
statusText(): string;
|
||||
|
||||
/**
|
||||
* Returns the text representation of response body.
|
||||
*/
|
||||
text(): Promise<string>;
|
||||
|
||||
/**
|
||||
* Contains the URL of the response.
|
||||
*/
|
||||
url(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* [FileChooser] objects are dispatched by the page in the
|
||||
* [page.on('filechooser')](https://playwright.dev/docs/api/class-page#page-event-file-chooser) event.
|
||||
@ -13472,6 +13588,12 @@ export interface Route {
|
||||
*/
|
||||
path?: string;
|
||||
|
||||
/**
|
||||
* [FetchResponse] to fulfill route's request with. Individual fields of the response (such as headers) can be overridden
|
||||
* using fulfill options.
|
||||
*/
|
||||
response?: FetchResponse;
|
||||
|
||||
/**
|
||||
* Response status code, defaults to `200`.
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user