Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add failOnStatusCode option to API request context #34346

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
6 changes: 6 additions & 0 deletions docs/src/api/params.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

## context-option-apirequest
- `apiRequest` <[Object]>
- `failOnStatusCode` <[boolean]>
JacksonLei123 marked this conversation as resolved.
Show resolved Hide resolved

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
- `offline` <[boolean]>

Expand Down
3 changes: 3 additions & 0 deletions packages/playwright-core/src/protocol/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ scheme.PlaywrightNewRequestParams = tObject({
userAgent: tOptional(tString),
ignoreHTTPSErrors: tOptional(tBoolean),
extraHTTPHeaders: tOptional(tArray(tType('NameValue'))),
apiRequest: tOptional(tObject({
failOnStatusCode: tBoolean
})),
clientCertificates: tOptional(tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
Expand Down
7 changes: 6 additions & 1 deletion packages/playwright-core/src/server/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ import { TLSSocket } from 'tls';
type FetchRequestOptions = {
userAgent: string;
extraHTTPHeaders?: HeadersArray;
apiRequest?: {
failOnStatusCode: boolean;
}
httpCredentials?: HTTPCredentials;
proxy?: ProxySettings;
timeoutSettings: TimeoutSettings;
Expand Down Expand Up @@ -205,7 +208,8 @@ export abstract class APIRequestContext extends SdkObject {
});
const fetchUid = this._storeResponseBody(fetchResponse.body);
this.fetchLog.set(fetchUid, controller.metadata.log);
if (params.failOnStatusCode && (fetchResponse.status < 200 || fetchResponse.status >= 400)) {
const failOnStatusCode = params.failOnStatusCode !== undefined ? params.failOnStatusCode : !!defaults.apiRequest?.failOnStatusCode;
if (failOnStatusCode && (fetchResponse.status < 200 || fetchResponse.status >= 400)) {
let responseText = '';
if (fetchResponse.body.byteLength) {
let text = fetchResponse.body.toString('utf8');
Expand Down Expand Up @@ -661,6 +665,7 @@ export class GlobalAPIRequestContext extends APIRequestContext {
baseURL: options.baseURL,
userAgent: options.userAgent || getUserAgent(),
extraHTTPHeaders: options.extraHTTPHeaders,
apiRequest: { failOnStatusCode: !!options.apiRequest?.failOnStatusCode },
ignoreHTTPSErrors: !!options.ignoreHTTPSErrors,
httpCredentials: options.httpCredentials,
clientCertificates: options.clientCertificates,
Expand Down
5 changes: 5 additions & 0 deletions packages/playwright-core/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17482,6 +17482,11 @@ export interface APIRequest {
*/
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;}
JacksonLei123 marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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.
Expand Down
3 changes: 3 additions & 0 deletions packages/protocol/src/channels.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,9 @@ export type PlaywrightNewRequestOptions = {
userAgent?: string,
ignoreHTTPSErrors?: boolean,
extraHTTPHeaders?: NameValue[],
apiRequest?: {
failOnStatusCode: boolean
},
clientCertificates?: {
origin: string,
cert?: Binary,
Expand Down
44 changes: 44 additions & 0 deletions tests/library/global-fetch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,3 +536,47 @@ it('should retry ECONNRESET', {
expect(requestCount).toBe(4);
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 } });
JacksonLei123 marked this conversation as resolved.
Show resolved Hide resolved
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 }) => {
JacksonLei123 marked this conversation as resolved.
Show resolved Hide resolved
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();
});
Loading