feat: added follow and redirect arguments to fetch (#17033)
This commit is contained in:
parent
47b9595b95
commit
17b203affb
|
|
@ -161,6 +161,8 @@ context cookies from the response. The method will automatically follow redirect
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
### option: APIRequestContext.delete.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
### option: APIRequestContext.delete.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
### option: APIRequestContext.delete.maxRedirects = %%-js-python-fetch-option-maxredirects-%%
|
||||||
|
* since: v1.26
|
||||||
|
|
||||||
## async method: APIRequestContext.dispose
|
## async method: APIRequestContext.dispose
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
|
@ -214,6 +216,8 @@ If set changes the fetch method (e.g. [PUT](https://developer.mozilla.org/en-US/
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
### option: APIRequestContext.fetch.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
### option: APIRequestContext.fetch.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
### option: APIRequestContext.fetch.maxRedirects = %%-js-python-fetch-option-maxredirects-%%
|
||||||
|
* since: v1.26
|
||||||
|
|
||||||
## async method: APIRequestContext.get
|
## async method: APIRequestContext.get
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
|
@ -239,6 +243,8 @@ context cookies from the response. The method will automatically follow redirect
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
### option: APIRequestContext.get.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
### option: APIRequestContext.get.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
### option: APIRequestContext.get.maxRedirects = %%-js-python-fetch-option-maxredirects-%%
|
||||||
|
* since: v1.26
|
||||||
|
|
||||||
## async method: APIRequestContext.head
|
## async method: APIRequestContext.head
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
|
@ -264,6 +270,8 @@ context cookies from the response. The method will automatically follow redirect
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
### option: APIRequestContext.head.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
### option: APIRequestContext.head.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
### option: APIRequestContext.head.maxRedirects = %%-js-python-fetch-option-maxredirects-%%
|
||||||
|
* since: v1.26
|
||||||
|
|
||||||
## async method: APIRequestContext.patch
|
## async method: APIRequestContext.patch
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
|
@ -299,6 +307,8 @@ context cookies from the response. The method will automatically follow redirect
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
### option: APIRequestContext.patch.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
### option: APIRequestContext.patch.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
### option: APIRequestContext.patch.maxRedirects = %%-js-python-fetch-option-maxredirects-%%
|
||||||
|
* since: v1.26
|
||||||
|
|
||||||
## async method: APIRequestContext.post
|
## async method: APIRequestContext.post
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
|
@ -334,6 +344,8 @@ context cookies from the response. The method will automatically follow redirect
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
### option: APIRequestContext.post.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
### option: APIRequestContext.post.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
### option: APIRequestContext.post.maxRedirects = %%-js-python-fetch-option-maxredirects-%%
|
||||||
|
* since: v1.26
|
||||||
|
|
||||||
## async method: APIRequestContext.put
|
## async method: APIRequestContext.put
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
|
@ -369,6 +381,8 @@ context cookies from the response. The method will automatically follow redirect
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
### option: APIRequestContext.put.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
### option: APIRequestContext.put.ignoreHTTPSErrors = %%-js-python-fetch-option-ignorehttpserrors-%%
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
### option: APIRequestContext.put.maxRedirects = %%-js-python-fetch-option-maxredirects-%%
|
||||||
|
* since: v1.26
|
||||||
|
|
||||||
## async method: APIRequestContext.storageState
|
## async method: APIRequestContext.storageState
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
|
|
||||||
|
|
@ -398,6 +398,12 @@ set to `application/octet-stream` if not explicitly set.
|
||||||
|
|
||||||
Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.
|
Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.
|
||||||
|
|
||||||
|
## js-python-fetch-option-maxredirects
|
||||||
|
* langs: js
|
||||||
|
- `maxRedirects` <[int]>
|
||||||
|
|
||||||
|
Maximum number of request allowed redirects. Defaults to `20`. Pass `0` to disable automatic follow.
|
||||||
|
|
||||||
## evaluate-expression
|
## evaluate-expression
|
||||||
- `expression` <[string]>
|
- `expression` <[string]>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ export type FetchOptions = {
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
failOnStatusCode?: boolean,
|
failOnStatusCode?: boolean,
|
||||||
ignoreHTTPSErrors?: boolean,
|
ignoreHTTPSErrors?: boolean,
|
||||||
|
maxRedirects?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
type NewContextOptions = Omit<channels.PlaywrightNewRequestOptions, 'extraHTTPHeaders' | 'storageState'> & {
|
type NewContextOptions = Omit<channels.PlaywrightNewRequestOptions, 'extraHTTPHeaders' | 'storageState'> & {
|
||||||
|
|
@ -146,9 +147,11 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
||||||
const request: network.Request | undefined = (urlOrRequest instanceof network.Request) ? urlOrRequest as network.Request : undefined;
|
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');
|
assert(request || typeof urlOrRequest === 'string', 'First argument must be either URL string or Request');
|
||||||
assert((options.data === undefined ? 0 : 1) + (options.form === undefined ? 0 : 1) + (options.multipart === undefined ? 0 : 1) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
|
assert((options.data === undefined ? 0 : 1) + (options.form === undefined ? 0 : 1) + (options.multipart === undefined ? 0 : 1) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
|
||||||
|
assert(options.maxRedirects === undefined || options.maxRedirects >= 0, `'maxRedirects' should be greater than or equal to '0'`);
|
||||||
const url = request ? request.url() : urlOrRequest as string;
|
const url = request ? request.url() : urlOrRequest as string;
|
||||||
const params = objectToArray(options.params);
|
const params = objectToArray(options.params);
|
||||||
const method = options.method || request?.method();
|
const method = options.method || request?.method();
|
||||||
|
const maxRedirects = options.maxRedirects;
|
||||||
// Cannot call allHeaders() here as the request may be paused inside route handler.
|
// Cannot call allHeaders() here as the request may be paused inside route handler.
|
||||||
const headersObj = options.headers || request?.headers() ;
|
const headersObj = options.headers || request?.headers() ;
|
||||||
const headers = headersObj ? headersObjectToArray(headersObj) : undefined;
|
const headers = headersObj ? headersObjectToArray(headersObj) : undefined;
|
||||||
|
|
@ -201,6 +204,7 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
||||||
timeout: options.timeout,
|
timeout: options.timeout,
|
||||||
failOnStatusCode: options.failOnStatusCode,
|
failOnStatusCode: options.failOnStatusCode,
|
||||||
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
|
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
|
||||||
|
maxRedirects: maxRedirects,
|
||||||
});
|
});
|
||||||
return new APIResponse(this, result.response);
|
return new APIResponse(this, result.response);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,7 @@ export type APIRequestContextFetchParams = {
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
failOnStatusCode?: boolean,
|
failOnStatusCode?: boolean,
|
||||||
ignoreHTTPSErrors?: boolean,
|
ignoreHTTPSErrors?: boolean,
|
||||||
|
maxRedirects?: number,
|
||||||
};
|
};
|
||||||
export type APIRequestContextFetchOptions = {
|
export type APIRequestContextFetchOptions = {
|
||||||
params?: NameValue[],
|
params?: NameValue[],
|
||||||
|
|
@ -322,6 +323,7 @@ export type APIRequestContextFetchOptions = {
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
failOnStatusCode?: boolean,
|
failOnStatusCode?: boolean,
|
||||||
ignoreHTTPSErrors?: boolean,
|
ignoreHTTPSErrors?: boolean,
|
||||||
|
maxRedirects?: number,
|
||||||
};
|
};
|
||||||
export type APIRequestContextFetchResult = {
|
export type APIRequestContextFetchResult = {
|
||||||
response: APIResponse,
|
response: APIResponse,
|
||||||
|
|
|
||||||
|
|
@ -284,6 +284,7 @@ APIRequestContext:
|
||||||
timeout: number?
|
timeout: number?
|
||||||
failOnStatusCode: boolean?
|
failOnStatusCode: boolean?
|
||||||
ignoreHTTPSErrors: boolean?
|
ignoreHTTPSErrors: boolean?
|
||||||
|
maxRedirects: number?
|
||||||
returns:
|
returns:
|
||||||
response: APIResponse
|
response: APIResponse
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,7 @@ scheme.APIRequestContextFetchParams = tObject({
|
||||||
timeout: tOptional(tNumber),
|
timeout: tOptional(tNumber),
|
||||||
failOnStatusCode: tOptional(tBoolean),
|
failOnStatusCode: tOptional(tBoolean),
|
||||||
ignoreHTTPSErrors: tOptional(tBoolean),
|
ignoreHTTPSErrors: tOptional(tBoolean),
|
||||||
|
maxRedirects: tOptional(tNumber),
|
||||||
});
|
});
|
||||||
scheme.APIRequestContextFetchResult = tObject({
|
scheme.APIRequestContextFetchResult = tObject({
|
||||||
response: tType('APIResponse'),
|
response: tType('APIResponse'),
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
agent,
|
agent,
|
||||||
maxRedirects: 20,
|
maxRedirects: params.maxRedirects === 0 ? -1 : params.maxRedirects === undefined ? 20 : params.maxRedirects,
|
||||||
timeout,
|
timeout,
|
||||||
deadline
|
deadline
|
||||||
};
|
};
|
||||||
|
|
@ -268,7 +268,7 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
if (cookies.length)
|
if (cookies.length)
|
||||||
await this._addCookies(cookies);
|
await this._addCookies(cookies);
|
||||||
|
|
||||||
if (redirectStatus.includes(response.statusCode!)) {
|
if (redirectStatus.includes(response.statusCode!) && options.maxRedirects >= 0) {
|
||||||
if (!options.maxRedirects) {
|
if (!options.maxRedirects) {
|
||||||
reject(new Error('Max redirect count exceeded'));
|
reject(new Error('Max redirect count exceeded'));
|
||||||
request.destroy();
|
request.destroy();
|
||||||
|
|
|
||||||
35
packages/playwright-core/types/types.d.ts
vendored
35
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -12667,6 +12667,11 @@ export interface APIRequestContext {
|
||||||
*/
|
*/
|
||||||
ignoreHTTPSErrors?: boolean;
|
ignoreHTTPSErrors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of request allowed redirects. Defaults to `20`. Pass `0` to disable automatic follow.
|
||||||
|
*/
|
||||||
|
maxRedirects?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request
|
* Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request
|
||||||
* body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless explicitly
|
* body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless explicitly
|
||||||
|
|
@ -12747,6 +12752,11 @@ export interface APIRequestContext {
|
||||||
*/
|
*/
|
||||||
ignoreHTTPSErrors?: boolean;
|
ignoreHTTPSErrors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of request allowed redirects. Defaults to `20`. Pass `0` to disable automatic follow.
|
||||||
|
*/
|
||||||
|
maxRedirects?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set changes the fetch method (e.g. [PUT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) or
|
* If set changes the fetch method (e.g. [PUT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) or
|
||||||
* [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)). If not specified, GET method is used.
|
* [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)). If not specified, GET method is used.
|
||||||
|
|
@ -12810,6 +12820,11 @@ export interface APIRequestContext {
|
||||||
*/
|
*/
|
||||||
ignoreHTTPSErrors?: boolean;
|
ignoreHTTPSErrors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of request allowed redirects. Defaults to `20`. Pass `0` to disable automatic follow.
|
||||||
|
*/
|
||||||
|
maxRedirects?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query parameters to be sent with the URL.
|
* Query parameters to be sent with the URL.
|
||||||
*/
|
*/
|
||||||
|
|
@ -12844,6 +12859,11 @@ export interface APIRequestContext {
|
||||||
*/
|
*/
|
||||||
ignoreHTTPSErrors?: boolean;
|
ignoreHTTPSErrors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of request allowed redirects. Defaults to `20`. Pass `0` to disable automatic follow.
|
||||||
|
*/
|
||||||
|
maxRedirects?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query parameters to be sent with the URL.
|
* Query parameters to be sent with the URL.
|
||||||
*/
|
*/
|
||||||
|
|
@ -12892,6 +12912,11 @@ export interface APIRequestContext {
|
||||||
*/
|
*/
|
||||||
ignoreHTTPSErrors?: boolean;
|
ignoreHTTPSErrors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of request allowed redirects. Defaults to `20`. Pass `0` to disable automatic follow.
|
||||||
|
*/
|
||||||
|
maxRedirects?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request
|
* Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request
|
||||||
* body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless explicitly
|
* body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless explicitly
|
||||||
|
|
@ -12963,6 +12988,11 @@ export interface APIRequestContext {
|
||||||
*/
|
*/
|
||||||
ignoreHTTPSErrors?: boolean;
|
ignoreHTTPSErrors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of request allowed redirects. Defaults to `20`. Pass `0` to disable automatic follow.
|
||||||
|
*/
|
||||||
|
maxRedirects?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request
|
* Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request
|
||||||
* body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless explicitly
|
* body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless explicitly
|
||||||
|
|
@ -13034,6 +13064,11 @@ export interface APIRequestContext {
|
||||||
*/
|
*/
|
||||||
ignoreHTTPSErrors?: boolean;
|
ignoreHTTPSErrors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of request allowed redirects. Defaults to `20`. Pass `0` to disable automatic follow.
|
||||||
|
*/
|
||||||
|
maxRedirects?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request
|
* Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request
|
||||||
* body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless explicitly
|
* body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless explicitly
|
||||||
|
|
|
||||||
|
|
@ -383,3 +383,34 @@ it('should return body for failing requests', async ({ playwright, server }) =>
|
||||||
}
|
}
|
||||||
await request.dispose();
|
await request.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw an error when maxRedirects is exceeded', async ({ playwright, server }) => {
|
||||||
|
server.setRedirect('/a/redirect1', '/b/c/redirect2');
|
||||||
|
server.setRedirect('/b/c/redirect2', '/b/c/redirect3');
|
||||||
|
server.setRedirect('/b/c/redirect3', '/b/c/redirect4');
|
||||||
|
server.setRedirect('/b/c/redirect4', '/simple.json');
|
||||||
|
|
||||||
|
const request = await playwright.request.newContext();
|
||||||
|
for (const method of ['GET', 'PUT', 'POST', 'OPTIONS', 'HEAD', 'PATCH'])
|
||||||
|
for (const maxRedirects of [1, 2, 3]) await expect(async () => request.fetch(`${server.PREFIX}/a/redirect1`, { method: method, maxRedirects: maxRedirects })).rejects.toThrow('Max redirect count exceeded');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not follow redirects when maxRedirects is set to 0', async ({ playwright, server }) => {
|
||||||
|
server.setRedirect('/a/redirect1', '/b/c/redirect2');
|
||||||
|
server.setRedirect('/b/c/redirect2', '/simple.json');
|
||||||
|
|
||||||
|
const request = await playwright.request.newContext();
|
||||||
|
for (const method of ['GET', 'PUT', 'POST', 'OPTIONS', 'HEAD', 'PATCH']){
|
||||||
|
const response = await request.fetch(`${server.PREFIX}/a/redirect1`, { method: method, maxRedirects: 0 });
|
||||||
|
expect(response.headers()['location']).toBe('/b/c/redirect2');
|
||||||
|
expect(response.status()).toBe(302);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when maxRedirects is less than 0', async ({ playwright, server }) => {
|
||||||
|
server.setRedirect('/a/redirect1', '/b/c/redirect2');
|
||||||
|
server.setRedirect('/b/c/redirect2', '/simple.json');
|
||||||
|
|
||||||
|
const request = await playwright.request.newContext();
|
||||||
|
for (const method of ['GET', 'PUT', 'POST', 'OPTIONS', 'HEAD', 'PATCH']) await expect(async () => request.fetch(`${server.PREFIX}/a/redirect1`, { method: method, maxRedirects: -1 })).rejects.toThrow(`'maxRedirects' should be greater than or equal to '0'`);
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue