feat: add failOnStatusCode option to API request context

This commit is contained in:
JacksonLei123 2025-01-15 17:24:24 -05:00
parent aeec0c0e36
commit e3965545a4
6 changed files with 67 additions and 1 deletions

View file

@ -620,6 +620,12 @@ A list of permissions to grant to all pages in this context. See
An object containing additional HTTP headers to be sent with every request. Defaults to none. An object containing additional HTTP headers to be sent with every request. Defaults to none.
## context-option-apirequest
- `apiRequest` <[Object]>
- `failOnStatusCode` <[boolean]>
An object containing an option to throw an error when API request returns status codes other than 2xx and 3xx. By default, response object is returned for all status codes.
## context-option-offline ## context-option-offline
- `offline` <[boolean]> - `offline` <[boolean]>

View file

@ -339,6 +339,9 @@ scheme.PlaywrightNewRequestParams = tObject({
userAgent: tOptional(tString), userAgent: tOptional(tString),
ignoreHTTPSErrors: tOptional(tBoolean), ignoreHTTPSErrors: tOptional(tBoolean),
extraHTTPHeaders: tOptional(tArray(tType('NameValue'))), extraHTTPHeaders: tOptional(tArray(tType('NameValue'))),
apiRequest: tOptional(tObject({
failOnStatusCode: tBoolean
})),
clientCertificates: tOptional(tArray(tObject({ clientCertificates: tOptional(tArray(tObject({
origin: tString, origin: tString,
cert: tOptional(tBinary), cert: tOptional(tBinary),

View file

@ -45,6 +45,9 @@ import { TLSSocket } from 'tls';
type FetchRequestOptions = { type FetchRequestOptions = {
userAgent: string; userAgent: string;
extraHTTPHeaders?: HeadersArray; extraHTTPHeaders?: HeadersArray;
apiRequest?: {
failOnStatusCode: boolean;
}
httpCredentials?: HTTPCredentials; httpCredentials?: HTTPCredentials;
proxy?: ProxySettings; proxy?: ProxySettings;
timeoutSettings: TimeoutSettings; timeoutSettings: TimeoutSettings;
@ -205,7 +208,8 @@ export abstract class APIRequestContext extends SdkObject {
}); });
const fetchUid = this._storeResponseBody(fetchResponse.body); const fetchUid = this._storeResponseBody(fetchResponse.body);
this.fetchLog.set(fetchUid, controller.metadata.log); this.fetchLog.set(fetchUid, controller.metadata.log);
if (params.failOnStatusCode && (fetchResponse.status < 200 || fetchResponse.status >= 400)) { let failOnStatusCode = params.failOnStatusCode !== undefined ? params.failOnStatusCode : !!defaults.apiRequest?.failOnStatusCode;
if (failOnStatusCode && (fetchResponse.status < 200 || fetchResponse.status >= 400)) {
let responseText = ''; let responseText = '';
if (fetchResponse.body.byteLength) { if (fetchResponse.body.byteLength) {
let text = fetchResponse.body.toString('utf8'); let text = fetchResponse.body.toString('utf8');
@ -661,6 +665,7 @@ export class GlobalAPIRequestContext extends APIRequestContext {
baseURL: options.baseURL, baseURL: options.baseURL,
userAgent: options.userAgent || getUserAgent(), userAgent: options.userAgent || getUserAgent(),
extraHTTPHeaders: options.extraHTTPHeaders, extraHTTPHeaders: options.extraHTTPHeaders,
apiRequest: {failOnStatusCode: !!options.apiRequest?.failOnStatusCode},
ignoreHTTPSErrors: !!options.ignoreHTTPSErrors, ignoreHTTPSErrors: !!options.ignoreHTTPSErrors,
httpCredentials: options.httpCredentials, httpCredentials: options.httpCredentials,
clientCertificates: options.clientCertificates, clientCertificates: options.clientCertificates,

View file

@ -17482,6 +17482,11 @@ export interface APIRequest {
*/ */
extraHTTPHeaders?: { [key: string]: string; }; extraHTTPHeaders?: { [key: string]: string; };
/**
* An object containing an option to throw an error when API request returns status codes other than 2xx and 3xx.
*/
apiRequest?: {failOnStatusCode?: boolean;}
/** /**
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no * Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no
* origin is specified, the username and password are sent to any servers upon unauthorized responses. * origin is specified, the username and password are sent to any servers upon unauthorized responses.

View file

@ -619,6 +619,9 @@ export type PlaywrightNewRequestOptions = {
userAgent?: string, userAgent?: string,
ignoreHTTPSErrors?: boolean, ignoreHTTPSErrors?: boolean,
extraHTTPHeaders?: NameValue[], extraHTTPHeaders?: NameValue[],
apiRequest?: {
failOnStatusCode: boolean
},
clientCertificates?: { clientCertificates?: {
origin: string, origin: string,
cert?: Binary, cert?: Binary,

View file

@ -536,3 +536,47 @@ it('should retry ECONNRESET', {
expect(requestCount).toBe(4); expect(requestCount).toBe(4);
await request.dispose(); await request.dispose();
}); });
it('should throw when failOnStatusCode is set to true', async ({ playwright, server }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/34204' });
const request = await playwright.request.newContext({ apiRequest: { failOnStatusCode: true } });
server.setRoute('/empty.html', (req, res) => {
res.writeHead(404, { 'Content-Length': 10, 'Content-Type': 'text/plain' });
res.end('Not found.');
});
const error = await request.fetch(server.EMPTY_PAGE).catch(e => e);
expect(error.message).toContain('404 Not Found')
});
it('should throw when failOnStatusCode is set to true inside the fetch API call', async ({ playwright, server }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/34204' });
const request = await playwright.request.newContext({ apiRequest: { failOnStatusCode: false } });
server.setRoute('/empty.html', (req, res) => {
res.writeHead(404, { 'Content-Length': 10, 'Content-Type': 'text/plain' });
res.end('Not found.');
});
const error = await request.fetch(server.EMPTY_PAGE, {failOnStatusCode: true}).catch(e => e);
expect(error.message).toContain('404 Not Found')
});
it('should not throw when failOnStatusCode is set to false', async ({ playwright, server }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/34204' });
const request = await playwright.request.newContext({ apiRequest: { failOnStatusCode: false } });
server.setRoute('/empty.html', (req, res) => {
res.writeHead(404, { 'Content-Length': 10, 'Content-Type': 'text/plain' });
res.end('Not found.');
});
const error = await request.fetch(server.EMPTY_PAGE).catch(e => e);
expect(error.message).toBeUndefined()
});
it('should not throw when failOnStatusCode is set to false inside the fetch API call', async ({ playwright, server }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/34204' });
const request = await playwright.request.newContext({ apiRequest: { failOnStatusCode: true } });
server.setRoute('/empty.html', (req, res) => {
res.writeHead(404, { 'Content-Length': 10, 'Content-Type': 'text/plain' });
res.end('Not found.');
});
const error = await request.fetch(server.EMPTY_PAGE, {failOnStatusCode: false}).catch(e => e);
expect(error.message).toBeUndefined()
});