feat(Page): Add global navigation timeout setting (#1728)

This patch introduces `page.setDefaultNavigationTimeout` method to override the 
default 30 seconds navigation timeout.

Fixes #1514
This commit is contained in:
Radu Aron 2018-01-10 23:04:01 +02:00 committed by Andrey Lushnikov
parent 3985dee54e
commit ec8e40f1cb
4 changed files with 41 additions and 11 deletions

View File

@ -79,6 +79,7 @@
* [page.select(selector, ...values)](#pageselectselector-values) * [page.select(selector, ...values)](#pageselectselector-values)
* [page.setContent(html)](#pagesetcontenthtml) * [page.setContent(html)](#pagesetcontenthtml)
* [page.setCookie(...cookies)](#pagesetcookiecookies) * [page.setCookie(...cookies)](#pagesetcookiecookies)
* [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout)
* [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders) * [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
* [page.setJavaScriptEnabled(enabled)](#pagesetjavascriptenabledenabled) * [page.setJavaScriptEnabled(enabled)](#pagesetjavascriptenabledenabled)
* [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled) * [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled)
@ -837,7 +838,7 @@ If there's no element matching `selector`, the method throws an error.
#### page.goBack(options) #### page.goBack(options)
- `options` <[Object]> Navigation parameters which might have the following properties: - `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: - `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. - `load` - consider navigation to be finished when the `load` event is fired.
- `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` 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) #### page.goForward(options)
- `options` <[Object]> Navigation parameters which might have the following properties: - `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: - `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. - `load` - consider navigation to be finished when the `load` event is fired.
- `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` 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) #### page.goto(url, options)
- `url` <[string]> URL to navigate page to. The url should include scheme, e.g. `https://`. - `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: - `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: - `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. - `load` - consider navigation to be finished when the `load` event is fired.
- `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` 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) #### page.reload(options)
- `options` <[Object]> Navigation parameters which might have the following properties: - `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: - `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. - `load` - consider navigation to be finished when the `load` event is fired.
- `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` 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"`. - `sameSite` <[string]> `"Strict"` or `"Lax"`.
- returns: <[Promise]> - 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) #### page.setExtraHTTPHeaders(headers)
- `headers` <[Object]> An object containing additional http headers to be sent with every request. All header values must be strings. - `headers` <[Object]> An object containing additional http headers to be sent with every request. All header values must be strings.
- returns: <[Promise]> - returns: <[Promise]>
@ -1209,7 +1220,7 @@ Shortcut for [page.mainFrame().waitForFunction(pageFunction[, options[, ...args]
#### page.waitForNavigation(options) #### page.waitForNavigation(options)
- `options` <[Object]> Navigation parameters which might have the following properties: - `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: - `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. - `load` - consider navigation to be finished when the `load` event is fired.
- `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired. - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired.

View File

@ -21,9 +21,10 @@ class NavigatorWatcher {
/** /**
* @param {!FrameManager} frameManager * @param {!FrameManager} frameManager
* @param {!Puppeteer.Frame} frame * @param {!Puppeteer.Frame} frame
* @param {number} timeout
* @param {!Object=} options * @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.networkIdleTimeout === undefined, 'ERROR: networkIdleTimeout option is no longer supported.');
console.assert(options.networkIdleInflight === undefined, 'ERROR: networkIdleInflight 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'); 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._frameManager = frameManager;
this._frame = frame; this._frame = frame;
this._initialLoaderId = frame._loaderId; this._initialLoaderId = frame._loaderId;
this._timeout = typeof options.timeout === 'number' ? options.timeout : 30000; this._timeout = timeout;
this._eventListeners = [ this._eventListeners = [
helper.addEventListener(this._frameManager, FrameManager.Events.LifecycleEvent, this._checkLifecycleComplete.bind(this)), helper.addEventListener(this._frameManager, FrameManager.Events.LifecycleEvent, this._checkLifecycleComplete.bind(this)),
helper.addEventListener(this._frameManager, FrameManager.Events.FrameDetached, this._checkLifecycleComplete.bind(this)) helper.addEventListener(this._frameManager, FrameManager.Events.FrameDetached, this._checkLifecycleComplete.bind(this))

View File

@ -79,6 +79,7 @@ class Page extends EventEmitter {
this._pageBindings = new Map(); this._pageBindings = new Map();
this._ignoreHTTPSErrors = ignoreHTTPSErrors; this._ignoreHTTPSErrors = ignoreHTTPSErrors;
this._coverage = new Coverage(client); this._coverage = new Coverage(client);
this._defaultNavigationTimeout = 30000;
this._screenshotTaskQueue = screenshotTaskQueue; this._screenshotTaskQueue = screenshotTaskQueue;
@ -170,6 +171,13 @@ class Page extends EventEmitter {
return this._networkManager.setOfflineMode(enabled); return this._networkManager.setOfflineMode(enabled);
} }
/**
* @param {number} timeout
*/
setDefaultNavigationTimeout(timeout) {
this._defaultNavigationTimeout = timeout;
}
/** /**
* @param {!Object} event * @param {!Object} event
*/ */
@ -471,7 +479,7 @@ class Page extends EventEmitter {
* @param {!Object=} options * @param {!Object=} options
* @return {!Promise<?Response>} * @return {!Promise<?Response>}
*/ */
async goto(url, options) { async goto(url, options = {}) {
const referrer = this._networkManager.extraHTTPHeaders()['referer']; const referrer = this._networkManager.extraHTTPHeaders()['referer'];
const requests = new Map(); const requests = new Map();
@ -480,7 +488,8 @@ class Page extends EventEmitter {
]; ];
const mainFrame = this._frameManager.mainFrame(); 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(); const navigationPromise = watcher.navigationPromise();
let error = await Promise.race([ let error = await Promise.race([
navigate(this._client, url, referrer), navigate(this._client, url, referrer),
@ -527,9 +536,10 @@ class Page extends EventEmitter {
* @param {!Object=} options * @param {!Object=} options
* @return {!Promise<!Response>} * @return {!Promise<!Response>}
*/ */
async waitForNavigation(options) { async waitForNavigation(options = {}) {
const mainFrame = this._frameManager.mainFrame(); 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 responses = new Map();
const listener = helper.addEventListener(this._networkManager, NetworkManager.Events.Response, response => responses.set(response.url(), response)); const listener = helper.addEventListener(this._networkManager, NetworkManager.Events.Response, response => responses.set(response.url(), response));

View File

@ -1063,6 +1063,14 @@ describe('Page', function() {
await page.goto(server.PREFIX + '/empty.html', {timeout: 1}).catch(e => error = e); await page.goto(server.PREFIX + '/empty.html', {timeout: 1}).catch(e => error = e);
expect(error.message).toContain('Navigation Timeout Exceeded: 1ms'); 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}) => { it('should disable timeout when its set to 0', async({page, server}) => {
let error = null; let error = null;
let loaded = false; let loaded = false;