diff --git a/docs/src/api/class-route.md b/docs/src/api/class-route.md index 9999aac531..5274ba7d57 100644 --- a/docs/src/api/class-route.md +++ b/docs/src/api/class-route.md @@ -488,6 +488,9 @@ await page.RouteAsync("https://dog.ceo/api/breeds/list/all", async route => If set changes the request URL. New URL must have same protocol as original one. +### option: Route.fetch.maxRedirects = %%-js-python-csharp-fetch-option-maxredirects-%% +* since: v1.31 + ### option: Route.fetch.method * since: v1.29 - `method` <[string]> diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 3654855161..4c09a9989c 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -326,7 +326,7 @@ export class Route extends ChannelOwner implements api.Ro this._reportHandled(true); } - async fetch(options: FallbackOverrides = {}) { + async fetch(options: FallbackOverrides & { maxRedirects?: number } = {}): Promise { return await this._wrapApiCall(async () => { const context = this.request()._context(); return context.request._innerFetch({ request: this.request(), data: options.postData, ...options }); diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 100045c8c9..f6dcb2300e 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -17924,6 +17924,12 @@ export interface Route { */ headers?: { [key: string]: string; }; + /** + * Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is + * exceeded. Defaults to `20`. Pass `0` to not follow redirects. + */ + maxRedirects?: number; + /** * If set changes the request method (e.g. GET or POST). */ diff --git a/tests/page/page-request-intercept.spec.ts b/tests/page/page-request-intercept.spec.ts index f2fa09c53f..341714a135 100644 --- a/tests/page/page-request-intercept.spec.ts +++ b/tests/page/page-request-intercept.spec.ts @@ -206,6 +206,21 @@ it('should fulfill intercepted response using alias', async ({ page, server, isE expect(response.headers()['content-type']).toContain('text/html'); }); +it('should not follow redirects when maxRedirects is set to 0 in route.fetch', async ({ page, server, isAndroid, isElectron }) => { + it.fixme(isElectron, 'error: Browser context management is not supported.'); + it.skip(isAndroid, 'The internal Android localhost (10.0.0.2) != the localhost on the host'); + + server.setRedirect('/foo', '/empty.html'); + await page.route('**/*', async route => { + const response = await route.fetch({ maxRedirects: 0 }); + expect(response.headers()['location']).toBe('/empty.html'); + expect(response.status()).toBe(302); + await route.fulfill({ body: 'hello' }); + }); + await page.goto(server.PREFIX + '/foo'); + expect(await page.content()).toContain('hello'); +}); + it('should intercept with url override', async ({ page, server, isElectron, isAndroid }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); it.skip(isAndroid, 'The internal Android localhost (10.0.0.2) != the localhost on the host');