feat(fetch): introduce failOnStatusCode (#8896)
This commit is contained in:
parent
bb33b8923e
commit
b79be5d98d
|
|
@ -41,6 +41,12 @@ Allows to set post data of the fetch.
|
||||||
|
|
||||||
Request timeout in milliseconds.
|
Request timeout in milliseconds.
|
||||||
|
|
||||||
|
### option: FetchRequest.fetch.failOnStatusCode
|
||||||
|
- `failOnStatusCode` <[boolean]>
|
||||||
|
|
||||||
|
Whether to throw on response codes other than 2xx and 3xx. By default response object is returned
|
||||||
|
for all status codes.
|
||||||
|
|
||||||
## async method: FetchRequest.get
|
## async method: FetchRequest.get
|
||||||
- returns: <[FetchResponse]>
|
- returns: <[FetchResponse]>
|
||||||
|
|
||||||
|
|
@ -67,6 +73,12 @@ Allows to set HTTP headers.
|
||||||
|
|
||||||
Request timeout in milliseconds.
|
Request timeout in milliseconds.
|
||||||
|
|
||||||
|
### option: FetchRequest.get.failOnStatusCode
|
||||||
|
- `failOnStatusCode` <[boolean]>
|
||||||
|
|
||||||
|
Whether to throw on response codes other than 2xx and 3xx. By default response object is returned
|
||||||
|
for all status codes.
|
||||||
|
|
||||||
## async method: FetchRequest.post
|
## async method: FetchRequest.post
|
||||||
- returns: <[FetchResponse]>
|
- returns: <[FetchResponse]>
|
||||||
|
|
||||||
|
|
@ -97,3 +109,9 @@ Allows to set post data of the fetch.
|
||||||
- `timeout` <[float]>
|
- `timeout` <[float]>
|
||||||
|
|
||||||
Request timeout in milliseconds.
|
Request timeout in milliseconds.
|
||||||
|
|
||||||
|
### option: FetchRequest.post.failOnStatusCode
|
||||||
|
- `failOnStatusCode` <[boolean]>
|
||||||
|
|
||||||
|
Whether to throw on response codes other than 2xx and 3xx. By default response object is returned
|
||||||
|
for all status codes.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@ export type FetchOptions = {
|
||||||
method?: string,
|
method?: string,
|
||||||
headers?: Headers,
|
headers?: Headers,
|
||||||
data?: string | Buffer,
|
data?: string | Buffer,
|
||||||
timeout?: number
|
timeout?: number,
|
||||||
|
failOnStatusCode?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class FetchRequest implements api.FetchRequest {
|
export class FetchRequest implements api.FetchRequest {
|
||||||
|
|
@ -44,6 +45,7 @@ export class FetchRequest implements api.FetchRequest {
|
||||||
params?: { [key: string]: string; };
|
params?: { [key: string]: string; };
|
||||||
headers?: { [key: string]: string; };
|
headers?: { [key: string]: string; };
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|
failOnStatusCode?: boolean;
|
||||||
}): Promise<FetchResponse> {
|
}): Promise<FetchResponse> {
|
||||||
return this.fetch(urlOrRequest, {
|
return this.fetch(urlOrRequest, {
|
||||||
...options,
|
...options,
|
||||||
|
|
@ -58,6 +60,7 @@ export class FetchRequest implements api.FetchRequest {
|
||||||
headers?: { [key: string]: string; };
|
headers?: { [key: string]: string; };
|
||||||
data?: string | Buffer;
|
data?: string | Buffer;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|
failOnStatusCode?: boolean;
|
||||||
}): Promise<FetchResponse> {
|
}): Promise<FetchResponse> {
|
||||||
return this.fetch(urlOrRequest, {
|
return this.fetch(urlOrRequest, {
|
||||||
...options,
|
...options,
|
||||||
|
|
@ -86,6 +89,7 @@ export class FetchRequest implements api.FetchRequest {
|
||||||
headers,
|
headers,
|
||||||
postData,
|
postData,
|
||||||
timeout: options.timeout,
|
timeout: options.timeout,
|
||||||
|
failOnStatusCode: options.failOnStatusCode,
|
||||||
});
|
});
|
||||||
if (result.error)
|
if (result.error)
|
||||||
throw new Error(`Request failed: ${result.error}`);
|
throw new Error(`Request failed: ${result.error}`);
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
headers: params.headers ? headersArrayToObject(params.headers, false) : undefined,
|
headers: params.headers ? headersArrayToObject(params.headers, false) : undefined,
|
||||||
postData: params.postData ? Buffer.from(params.postData, 'base64') : undefined,
|
postData: params.postData ? Buffer.from(params.postData, 'base64') : undefined,
|
||||||
timeout: params.timeout,
|
timeout: params.timeout,
|
||||||
|
failOnStatusCode: params.failOnStatusCode,
|
||||||
});
|
});
|
||||||
let response;
|
let response;
|
||||||
if (fetchResponse) {
|
if (fetchResponse) {
|
||||||
|
|
|
||||||
|
|
@ -862,6 +862,7 @@ export type BrowserContextFetchParams = {
|
||||||
headers?: NameValue[],
|
headers?: NameValue[],
|
||||||
postData?: Binary,
|
postData?: Binary,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
|
failOnStatusCode?: boolean,
|
||||||
};
|
};
|
||||||
export type BrowserContextFetchOptions = {
|
export type BrowserContextFetchOptions = {
|
||||||
params?: NameValue[],
|
params?: NameValue[],
|
||||||
|
|
@ -869,6 +870,7 @@ export type BrowserContextFetchOptions = {
|
||||||
headers?: NameValue[],
|
headers?: NameValue[],
|
||||||
postData?: Binary,
|
postData?: Binary,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
|
failOnStatusCode?: boolean,
|
||||||
};
|
};
|
||||||
export type BrowserContextFetchResult = {
|
export type BrowserContextFetchResult = {
|
||||||
response?: FetchResponse,
|
response?: FetchResponse,
|
||||||
|
|
|
||||||
|
|
@ -625,6 +625,7 @@ BrowserContext:
|
||||||
items: NameValue
|
items: NameValue
|
||||||
postData: binary?
|
postData: binary?
|
||||||
timeout: number?
|
timeout: number?
|
||||||
|
failOnStatusCode: boolean?
|
||||||
returns:
|
returns:
|
||||||
response: FetchResponse?
|
response: FetchResponse?
|
||||||
error: string?
|
error: string?
|
||||||
|
|
|
||||||
|
|
@ -399,6 +399,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
headers: tOptional(tArray(tType('NameValue'))),
|
headers: tOptional(tArray(tType('NameValue'))),
|
||||||
postData: tOptional(tBinary),
|
postData: tOptional(tBinary),
|
||||||
timeout: tOptional(tNumber),
|
timeout: tOptional(tNumber),
|
||||||
|
failOnStatusCode: tOptional(tBoolean),
|
||||||
});
|
});
|
||||||
scheme.BrowserContextFetchResponseBodyParams = tObject({
|
scheme.BrowserContextFetchResponseBodyParams = tObject({
|
||||||
fetchUid: tString,
|
fetchUid: tString,
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ export async function playwrightFetch(context: BrowserContext, params: types.Fet
|
||||||
|
|
||||||
const fetchResponse = await sendRequest(context, requestUrl, options, params.postData);
|
const fetchResponse = await sendRequest(context, requestUrl, options, params.postData);
|
||||||
const fetchUid = context.storeFetchResponseBody(fetchResponse.body);
|
const fetchUid = context.storeFetchResponseBody(fetchResponse.body);
|
||||||
|
if (params.failOnStatusCode && (fetchResponse.status < 200 || fetchResponse.status >= 400))
|
||||||
|
return { error: `${fetchResponse.status} ${fetchResponse.statusText}` };
|
||||||
return { fetchResponse: { ...fetchResponse, fetchUid } };
|
return { fetchResponse: { ...fetchResponse, fetchUid } };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { error: String(e) };
|
return { error: String(e) };
|
||||||
|
|
|
||||||
|
|
@ -379,6 +379,7 @@ export type FetchOptions = {
|
||||||
headers?: { [name: string]: string },
|
headers?: { [name: string]: string },
|
||||||
postData?: Buffer,
|
postData?: Buffer,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
|
failOnStatusCode?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FetchResponse = {
|
export type FetchResponse = {
|
||||||
|
|
|
||||||
|
|
@ -128,13 +128,16 @@ it('should add session cookies to request', async ({context, server}) => {
|
||||||
expect(req.headers.cookie).toEqual('username=John Doe');
|
expect(req.headers.cookie).toEqual('username=John Doe');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support queryParams', async ({context, server}) => {
|
for (const method of ['get', 'post', 'fetch']) {
|
||||||
let request;
|
it(`${method} should support queryParams`, async ({context, server}) => {
|
||||||
server.setRoute('/empty.html', (req, res) => {
|
let request;
|
||||||
request = req;
|
const url = new URL(server.EMPTY_PAGE);
|
||||||
server.serveFile(req, res);
|
url.searchParams.set('p1', 'v1');
|
||||||
});
|
url.searchParams.set('парам2', 'знач2');
|
||||||
for (const method of ['get', 'post', 'fetch']) {
|
server.setRoute(url.pathname + url.search, (req, res) => {
|
||||||
|
request = req;
|
||||||
|
server.serveFile(req, res);
|
||||||
|
});
|
||||||
await context.request[method](server.EMPTY_PAGE + '?p1=foo', {
|
await context.request[method](server.EMPTY_PAGE + '?p1=foo', {
|
||||||
params: {
|
params: {
|
||||||
'p1': 'v1',
|
'p1': 'v1',
|
||||||
|
|
@ -144,8 +147,15 @@ it('should support queryParams', async ({context, server}) => {
|
||||||
const params = new URLSearchParams(request.url.substr(request.url.indexOf('?')));
|
const params = new URLSearchParams(request.url.substr(request.url.indexOf('?')));
|
||||||
expect(params.get('p1')).toEqual('v1');
|
expect(params.get('p1')).toEqual('v1');
|
||||||
expect(params.get('парам2')).toEqual('знач2');
|
expect(params.get('парам2')).toEqual('знач2');
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
it(`${method} should support failOnStatusCode`, async ({context, server}) => {
|
||||||
|
const error = await context.request[method](server.PREFIX + '/does-not-exist.html', {
|
||||||
|
failOnStatusCode: true
|
||||||
|
}).catch(e => e);
|
||||||
|
expect(error.message).toContain('Request failed: 404 Not Found');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
it('should not add context cookie if cookie header passed as a parameter', async ({context, server}) => {
|
it('should not add context cookie if cookie header passed as a parameter', async ({context, server}) => {
|
||||||
await context.addCookies([{
|
await context.addCookies([{
|
||||||
|
|
|
||||||
15
types/types.d.ts
vendored
15
types/types.d.ts
vendored
|
|
@ -12636,6 +12636,11 @@ export interface FetchRequest {
|
||||||
*/
|
*/
|
||||||
data?: string|Buffer;
|
data?: string|Buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status codes.
|
||||||
|
*/
|
||||||
|
failOnStatusCode?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to set HTTP headers.
|
* Allows to set HTTP headers.
|
||||||
*/
|
*/
|
||||||
|
|
@ -12664,6 +12669,11 @@ export interface FetchRequest {
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
get(urlOrRequest: string|Request, options?: {
|
get(urlOrRequest: string|Request, options?: {
|
||||||
|
/**
|
||||||
|
* Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status codes.
|
||||||
|
*/
|
||||||
|
failOnStatusCode?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to set HTTP headers.
|
* Allows to set HTTP headers.
|
||||||
*/
|
*/
|
||||||
|
|
@ -12692,6 +12702,11 @@ export interface FetchRequest {
|
||||||
*/
|
*/
|
||||||
data?: string|Buffer;
|
data?: string|Buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status codes.
|
||||||
|
*/
|
||||||
|
failOnStatusCode?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to set HTTP headers.
|
* Allows to set HTTP headers.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -238,10 +238,10 @@ class TestServer {
|
||||||
request.on('data', chunk => body = Buffer.concat([body, chunk]));
|
request.on('data', chunk => body = Buffer.concat([body, chunk]));
|
||||||
request.on('end', () => resolve(body));
|
request.on('end', () => resolve(body));
|
||||||
});
|
});
|
||||||
const pathName = url.parse(request.url).pathname;
|
const path = url.parse(request.url).path;
|
||||||
this.debugServer(`request ${request.method} ${pathName}`);
|
this.debugServer(`request ${request.method} ${path}`);
|
||||||
if (this._auths.has(pathName)) {
|
if (this._auths.has(path)) {
|
||||||
const auth = this._auths.get(pathName);
|
const auth = this._auths.get(path);
|
||||||
const credentials = Buffer.from((request.headers.authorization || '').split(' ')[1] || '', 'base64').toString();
|
const credentials = Buffer.from((request.headers.authorization || '').split(' ')[1] || '', 'base64').toString();
|
||||||
this.debugServer(`request credentials ${credentials}`);
|
this.debugServer(`request credentials ${credentials}`);
|
||||||
this.debugServer(`actual credentials ${auth.username}:${auth.password}`);
|
this.debugServer(`actual credentials ${auth.username}:${auth.password}`);
|
||||||
|
|
@ -253,11 +253,11 @@ class TestServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Notify request subscriber.
|
// Notify request subscriber.
|
||||||
if (this._requestSubscribers.has(pathName)) {
|
if (this._requestSubscribers.has(path)) {
|
||||||
this._requestSubscribers.get(pathName)[fulfillSymbol].call(null, request);
|
this._requestSubscribers.get(path)[fulfillSymbol].call(null, request);
|
||||||
this._requestSubscribers.delete(pathName);
|
this._requestSubscribers.delete(path);
|
||||||
}
|
}
|
||||||
const handler = this._routes.get(pathName);
|
const handler = this._routes.get(path);
|
||||||
if (handler) {
|
if (handler) {
|
||||||
handler.call(null, request, response);
|
handler.call(null, request, response);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue