From ec8e40f1cb2d9214387bfbbc2e5dcf48b8db7148 Mon Sep 17 00:00:00 2001 From: Radu Aron Date: Wed, 10 Jan 2018 23:04:01 +0200 Subject: [PATCH] feat(Page): Add global navigation timeout setting (#1728) This patch introduces `page.setDefaultNavigationTimeout` method to override the default 30 seconds navigation timeout. Fixes #1514 --- docs/api.md | 21 ++++++++++++++++----- lib/NavigatorWatcher.js | 5 +++-- lib/Page.js | 18 ++++++++++++++---- test/test.js | 8 ++++++++ 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/docs/api.md b/docs/api.md index b6a5b051..913be3f4 100644 --- a/docs/api.md +++ b/docs/api.md @@ -79,6 +79,7 @@ * [page.select(selector, ...values)](#pageselectselector-values) * [page.setContent(html)](#pagesetcontenthtml) * [page.setCookie(...cookies)](#pagesetcookiecookies) + * [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) * [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders) * [page.setJavaScriptEnabled(enabled)](#pagesetjavascriptenabledenabled) * [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled) @@ -837,7 +838,7 @@ If there's no element matching `selector`, the method throws an error. #### page.goBack(options) - `options` <[Object]> Navigation parameters which might have the following properties: - - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. + - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) method. - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either: - `load` - consider navigation to be finished when the `load` event is fired. - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired. @@ -850,7 +851,7 @@ Navigate to the previous page in history. #### page.goForward(options) - `options` <[Object]> Navigation parameters which might have the following properties: - - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. + - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) method. - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either: - `load` - consider navigation to be finished when the `load` event is fired. - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired. @@ -864,7 +865,7 @@ Navigate to the next page in history. #### page.goto(url, options) - `url` <[string]> URL to navigate page to. The url should include scheme, e.g. `https://`. - `options` <[Object]> Navigation parameters which might have the following properties: - - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. + - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) method. - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either: - `load` - consider navigation to be finished when the `load` event is fired. - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired. @@ -1004,7 +1005,7 @@ Shortcut for [page.mainFrame().executionContext().queryObjects(prototypeHandle)] #### page.reload(options) - `options` <[Object]> Navigation parameters which might have the following properties: - - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. + - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) method. - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either: - `load` - consider navigation to be finished when the `load` event is fired. - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired. @@ -1058,6 +1059,16 @@ Shortcut for [page.mainFrame().select()](#frameselectselector-values) - `sameSite` <[string]> `"Strict"` or `"Lax"`. - returns: <[Promise]> +#### page.setDefaultNavigationTimeout(timeout) +- `timeout` <[number]> Maximum navigation time in milliseconds + +This setting will change the default maximum navigation time of 30 seconds for the following methods: +- [page.goto(url, options)](#pagegotourl-options) +- [page.goBack(options)](#pagegobackoptions) +- [page.goForward(options)](#pagegoforwardoptions) +- [page.reload(options)](#pagereloadoptions) +- [page.waitForNavigation(options)](#pagewaitfornavigationoptions) + #### page.setExtraHTTPHeaders(headers) - `headers` <[Object]> An object containing additional http headers to be sent with every request. All header values must be strings. - returns: <[Promise]> @@ -1209,7 +1220,7 @@ Shortcut for [page.mainFrame().waitForFunction(pageFunction[, options[, ...args] #### page.waitForNavigation(options) - `options` <[Object]> Navigation parameters which might have the following properties: - - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. + - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) method. - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either: - `load` - consider navigation to be finished when the `load` event is fired. - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired. diff --git a/lib/NavigatorWatcher.js b/lib/NavigatorWatcher.js index 4a1c9411..2c058faf 100644 --- a/lib/NavigatorWatcher.js +++ b/lib/NavigatorWatcher.js @@ -21,9 +21,10 @@ class NavigatorWatcher { /** * @param {!FrameManager} frameManager * @param {!Puppeteer.Frame} frame + * @param {number} timeout * @param {!Object=} options */ - constructor(frameManager, frame, options = {}) { + constructor(frameManager, frame, timeout, options = {}) { console.assert(options.networkIdleTimeout === undefined, 'ERROR: networkIdleTimeout option is no longer supported.'); console.assert(options.networkIdleInflight === undefined, 'ERROR: networkIdleInflight option is no longer supported.'); console.assert(options.waitUntil !== 'networkidle', 'ERROR: "networkidle" option is no longer supported. Use "networkidle2" instead'); @@ -41,7 +42,7 @@ class NavigatorWatcher { this._frameManager = frameManager; this._frame = frame; this._initialLoaderId = frame._loaderId; - this._timeout = typeof options.timeout === 'number' ? options.timeout : 30000; + this._timeout = timeout; this._eventListeners = [ helper.addEventListener(this._frameManager, FrameManager.Events.LifecycleEvent, this._checkLifecycleComplete.bind(this)), helper.addEventListener(this._frameManager, FrameManager.Events.FrameDetached, this._checkLifecycleComplete.bind(this)) diff --git a/lib/Page.js b/lib/Page.js index 5c214b64..5ac3356e 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -79,6 +79,7 @@ class Page extends EventEmitter { this._pageBindings = new Map(); this._ignoreHTTPSErrors = ignoreHTTPSErrors; this._coverage = new Coverage(client); + this._defaultNavigationTimeout = 30000; this._screenshotTaskQueue = screenshotTaskQueue; @@ -170,6 +171,13 @@ class Page extends EventEmitter { return this._networkManager.setOfflineMode(enabled); } + /** + * @param {number} timeout + */ + setDefaultNavigationTimeout(timeout) { + this._defaultNavigationTimeout = timeout; + } + /** * @param {!Object} event */ @@ -471,7 +479,7 @@ class Page extends EventEmitter { * @param {!Object=} options * @return {!Promise} */ - async goto(url, options) { + async goto(url, options = {}) { const referrer = this._networkManager.extraHTTPHeaders()['referer']; const requests = new Map(); @@ -480,7 +488,8 @@ class Page extends EventEmitter { ]; const mainFrame = this._frameManager.mainFrame(); - const watcher = new NavigatorWatcher(this._frameManager, mainFrame, options); + const timeout = typeof options.timeout === 'number' ? options.timeout : this._defaultNavigationTimeout; + const watcher = new NavigatorWatcher(this._frameManager, mainFrame, timeout, options); const navigationPromise = watcher.navigationPromise(); let error = await Promise.race([ navigate(this._client, url, referrer), @@ -527,9 +536,10 @@ class Page extends EventEmitter { * @param {!Object=} options * @return {!Promise} */ - async waitForNavigation(options) { + async waitForNavigation(options = {}) { const mainFrame = this._frameManager.mainFrame(); - const watcher = new NavigatorWatcher(this._frameManager, mainFrame, options); + const timeout = typeof options.timeout === 'number' ? options.timeout : this._defaultNavigationTimeout; + const watcher = new NavigatorWatcher(this._frameManager, mainFrame, timeout, options); const responses = new Map(); const listener = helper.addEventListener(this._networkManager, NetworkManager.Events.Response, response => responses.set(response.url(), response)); diff --git a/test/test.js b/test/test.js index 7e090e42..e8b99712 100644 --- a/test/test.js +++ b/test/test.js @@ -1063,6 +1063,14 @@ describe('Page', function() { await page.goto(server.PREFIX + '/empty.html', {timeout: 1}).catch(e => error = e); expect(error.message).toContain('Navigation Timeout Exceeded: 1ms'); }); + it('should fail when exceeding default maximum navigation timeout', async({page, server}) => { + // Hang for request to the empty.html + server.setRoute('/empty.html', (req, res) => { }); + let error = null; + page.setDefaultNavigationTimeout(1); + await page.goto(server.PREFIX + '/empty.html').catch(e => error = e); + expect(error.message).toContain('Navigation Timeout Exceeded: 1ms'); + }); it('should disable timeout when its set to 0', async({page, server}) => { let error = null; let loaded = false;