From e3965545a45264cb22f22935984445e14b000b6e Mon Sep 17 00:00:00 2001 From: JacksonLei123 Date: Wed, 15 Jan 2025 17:24:24 -0500 Subject: [PATCH] feat: add failOnStatusCode option to API request context --- docs/src/api/params.md | 6 +++ .../playwright-core/src/protocol/validator.ts | 3 ++ packages/playwright-core/src/server/fetch.ts | 7 ++- packages/playwright-core/types/types.d.ts | 5 +++ packages/protocol/src/channels.d.ts | 3 ++ tests/library/global-fetch.spec.ts | 44 +++++++++++++++++++ 6 files changed, 67 insertions(+), 1 deletion(-) diff --git a/docs/src/api/params.md b/docs/src/api/params.md index a1f909a4fb..7852294989 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -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]> + +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]> diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 9b14551fb8..0619aea8e2 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -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), diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index f231c907c0..e581970ff7 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -45,6 +45,9 @@ import { TLSSocket } from 'tls'; type FetchRequestOptions = { userAgent: string; extraHTTPHeaders?: HeadersArray; + apiRequest?: { + failOnStatusCode: boolean; + } httpCredentials?: HTTPCredentials; proxy?: ProxySettings; timeoutSettings: TimeoutSettings; @@ -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)) { + let 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'); @@ -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, diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index d35137fbe2..e30ef2b040 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -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;} + /** * 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. diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 6f9e36f0c3..f9dcb4471d 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -619,6 +619,9 @@ export type PlaywrightNewRequestOptions = { userAgent?: string, ignoreHTTPSErrors?: boolean, extraHTTPHeaders?: NameValue[], + apiRequest?: { + failOnStatusCode: boolean + }, clientCertificates?: { origin: string, cert?: Binary, diff --git a/tests/library/global-fetch.spec.ts b/tests/library/global-fetch.spec.ts index 52d48c765c..e00df5d928 100644 --- a/tests/library/global-fetch.spec.ts +++ b/tests/library/global-fetch.spec.ts @@ -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 } }); + 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() +});