diff --git a/docs/src/api/class-route.md b/docs/src/api/class-route.md index 4f1a00757e..4af70a2fad 100644 --- a/docs/src/api/class-route.md +++ b/docs/src/api/class-route.md @@ -503,6 +503,12 @@ If set changes the request URL. New URL must have same protocol as original one. 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. +### option: Route.fetch.timeout +* since: v1.33 +- `timeout` <[float]> + +Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. + ### 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 616b0903cf..2b05af6284 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -320,7 +320,7 @@ export class Route extends ChannelOwner implements api.Ro this._reportHandled(true); } - async fetch(options: FallbackOverrides & { maxRedirects?: number } = {}): Promise { + async fetch(options: FallbackOverrides & { maxRedirects?: number, timeout?: 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 0ae5533252..455352d5e8 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -18206,6 +18206,11 @@ export interface Route { */ postData?: string|Buffer|Serializable; + /** + * Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. + */ + timeout?: number; + /** * If set changes the request URL. New URL must have same protocol as original one. */ diff --git a/tests/page/page-request-intercept.spec.ts b/tests/page/page-request-intercept.spec.ts index 341714a135..5b3a7a6b8d 100644 --- a/tests/page/page-request-intercept.spec.ts +++ b/tests/page/page-request-intercept.spec.ts @@ -206,6 +206,24 @@ it('should fulfill intercepted response using alias', async ({ page, server, isE expect(response.headers()['content-type']).toContain('text/html'); }); +it('should support timeout option in route.fetch', 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'); + + server.setRoute('/slow', (req, res) => { + res.writeHead(200, { + 'content-length': 4096, + 'content-type': 'text/html', + }); + }); + await page.route('**/*', async route => { + const error = await route.fetch({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`Request timed out after 1000ms`); + }); + const error = await page.goto(server.PREFIX + '/slow', { timeout: 2000 }).catch(e => e); + expect(error.message).toContain(`Timeout 2000ms exceeded`); +}); + 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');