From c1d510afafc2bd6111c08bdcd74cff18a7a28bea Mon Sep 17 00:00:00 2001 From: JacksonLei123 Date: Mon, 20 Jan 2025 12:03:40 -0500 Subject: [PATCH] feat: update protocol.yml file to include fetchFailOnStatusCode flag. Include flag for BrowserContext --- docs/src/api/class-apirequest.md | 2 + docs/src/api/params.md | 6 +- packages/playwright-core/src/server/fetch.ts | 9 ++- packages/protocol/src/protocol.yml | 2 + ...owsercontext-fetchFailOnStatusCode.spec.ts | 67 +++++++++++++++++++ tests/library/global-fetch.spec.ts | 32 ++------- 6 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 tests/library/browsercontext-fetchFailOnStatusCode.spec.ts diff --git a/docs/src/api/class-apirequest.md b/docs/src/api/class-apirequest.md index 2f832e1dd8..5abcb8e408 100644 --- a/docs/src/api/class-apirequest.md +++ b/docs/src/api/class-apirequest.md @@ -21,6 +21,8 @@ Creates new instances of [APIRequestContext]. ### option: APIRequest.newContext.extraHTTPHeaders = %%-context-option-extrahttpheaders-%% * since: v1.16 +### option: APIRequest.newContext.fetchFailOnStatusCode = %%-context-option-fetchFailOnStatusCode-%% + ### option: APIRequest.newContext.httpCredentials = %%-context-option-httpcredentials-%% * since: v1.16 diff --git a/docs/src/api/params.md b/docs/src/api/params.md index 7852294989..6684dba874 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -620,9 +620,8 @@ 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]> +## context-option-fetchFailOnStatusCode +- `fetchFailOnStatusCode` <[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. @@ -971,6 +970,7 @@ between the same pixel in compared images, between zero (strict) and one (lax), - %%-context-option-locale-%% - %%-context-option-permissions-%% - %%-context-option-extrahttpheaders-%% +- %%-context-option-fetchFailOnStatusCode-%% - %%-context-option-offline-%% - %%-context-option-httpcredentials-%% - %%-context-option-colorscheme-%% diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index 4885a3245d..c8478dc4b9 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -45,9 +45,7 @@ import { TLSSocket } from 'tls'; type FetchRequestOptions = { userAgent: string; extraHTTPHeaders?: HeadersArray; - apiRequest?: { - failOnStatusCode: boolean; - } + fetchFailOnStatusCode?: boolean; httpCredentials?: HTTPCredentials; proxy?: ProxySettings; timeoutSettings: TimeoutSettings; @@ -208,7 +206,7 @@ export abstract class APIRequestContext extends SdkObject { }); const fetchUid = this._storeResponseBody(fetchResponse.body); this.fetchLog.set(fetchUid, controller.metadata.log); - const failOnStatusCode = params.failOnStatusCode !== undefined ? params.failOnStatusCode : !!defaults.apiRequest?.failOnStatusCode; + const failOnStatusCode = params.failOnStatusCode !== undefined ? params.failOnStatusCode : !!defaults.fetchFailOnStatusCode; if (failOnStatusCode && (fetchResponse.status < 200 || fetchResponse.status >= 400)) { let responseText = ''; if (fetchResponse.body.byteLength) { @@ -614,6 +612,7 @@ export class BrowserContextAPIRequestContext extends APIRequestContext { return { userAgent: this._context._options.userAgent || this._context._browser.userAgent(), extraHTTPHeaders: this._context._options.extraHTTPHeaders, + fetchFailOnStatusCode: this._context._options.fetchFailOnStatusCode, httpCredentials: this._context._options.httpCredentials, proxy: this._context._options.proxy || this._context._browser.options.proxy, timeoutSettings: this._context._timeoutSettings, @@ -665,7 +664,7 @@ export class GlobalAPIRequestContext extends APIRequestContext { baseURL: options.baseURL, userAgent: options.userAgent || getUserAgent(), extraHTTPHeaders: options.extraHTTPHeaders, - apiRequest: { failOnStatusCode: !!options.apiRequest?.failOnStatusCode }, + fetchFailOnStatusCode: !!options.fetchFailOnStatusCode, ignoreHTTPSErrors: !!options.ignoreHTTPSErrors, httpCredentials: options.httpCredentials, clientCertificates: options.clientCertificates, diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index e8b9746b41..bcdc903944 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -468,6 +468,7 @@ ContextOptions: extraHTTPHeaders: type: array? items: NameValue + fetchFailOnStatusCode: boolean? offline: boolean? httpCredentials: type: object? @@ -693,6 +694,7 @@ Playwright: extraHTTPHeaders: type: array? items: NameValue + fetchFailOnStatusCode: boolean? clientCertificates: type: array? items: diff --git a/tests/library/browsercontext-fetchFailOnStatusCode.spec.ts b/tests/library/browsercontext-fetchFailOnStatusCode.spec.ts new file mode 100644 index 0000000000..38129217a9 --- /dev/null +++ b/tests/library/browsercontext-fetchFailOnStatusCode.spec.ts @@ -0,0 +1,67 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { browserTest as it, expect } from '../config/browserTest'; + +it('should throw when fetchFailOnStatusCode is set to true inside BrowserContext options', async ({ browser, server }) => { + it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/34204' }); + const context = await browser.newContext({ fetchFailOnStatusCode: true }); + server.setRoute('/empty.html', (req, res) => { + res.writeHead(404, { 'Content-Length': 10, 'Content-Type': 'text/plain' }); + res.end('Not found.'); + }); + const error = await context.request.fetch(server.EMPTY_PAGE).catch(e => e); + expect(error.message).toContain('404 Not Found'); + await context.close(); +}); + +it('should not throw when failOnStatusCode is set to false inside BrowserContext options', async ({ browser, server }) => { + it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/34204' }); + const context = await browser.newContext({ fetchFailOnStatusCode: false }); + server.setRoute('/empty.html', (req, res) => { + res.writeHead(404, { 'Content-Length': 10, 'Content-Type': 'text/plain' }); + res.end('Not found.'); + }); + const error = await context.request.fetch(server.EMPTY_PAGE).catch(e => e); + expect(error.message).toBeUndefined(); + await context.close(); +}); + +it('should throw when fetchFailOnStatusCode is set to true inside browserType.launchPersistentContext options', async ({ browserType, server, createUserDataDir }) => { + it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/34204' }); + const userDataDir = await createUserDataDir(); + const context = await browserType.launchPersistentContext(userDataDir, { fetchFailOnStatusCode: true }); + server.setRoute('/empty.html', (req, res) => { + res.writeHead(404, { 'Content-Length': 10, 'Content-Type': 'text/plain' }); + res.end('Not found.'); + }); + const error = await context.request.fetch(server.EMPTY_PAGE).catch(e => e); + expect(error.message).toContain('404 Not Found'); + await context.close(); +}); + +it('should not throw when fetchFailOnStatusCode is set to false inside browserType.launchPersistentContext options', async ({ browserType, server, createUserDataDir }) => { + it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/34204' }); + const userDataDir = await createUserDataDir(); + const context = await browserType.launchPersistentContext(userDataDir, { fetchFailOnStatusCode: false }); + server.setRoute('/empty.html', (req, res) => { + res.writeHead(404, { 'Content-Length': 10, 'Content-Type': 'text/plain' }); + res.end('Not found.'); + }); + const error = await context.request.fetch(server.EMPTY_PAGE).catch(e => e); + expect(error.message).toBeUndefined(); + await context.close(); +}); diff --git a/tests/library/global-fetch.spec.ts b/tests/library/global-fetch.spec.ts index 004f459b64..16166bb26c 100644 --- a/tests/library/global-fetch.spec.ts +++ b/tests/library/global-fetch.spec.ts @@ -537,46 +537,26 @@ it('should retry ECONNRESET', { await request.dispose(); }); -it('should throw when failOnStatusCode is set to true', async ({ playwright, server }) => { +it('should throw when fetchFailOnStatusCode is set to true inside APIRequest context options', 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 } }); + const request = await playwright.request.newContext({ fetchFailOnStatusCode: 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'); + await request.dispose(); }); -it('should throw when failOnStatusCode is set to true inside the fetch API call', async ({ playwright, server }) => { +it('should not throw when fetchFailOnStatusCode is set to false inside APIRequest context options', 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 } }); + const request = await playwright.request.newContext({ fetchFailOnStatusCode: 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(); + await request.dispose(); });