browsertime 17.17.0 → 17.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,10 +1,23 @@
1
1
  # Browsertime changelog (we do [semantic versioning](https://semver.org))
2
2
 
3
+
4
+ ## 17.18.0 - 2022-10-23
5
+ ### Added
6
+ * Updated to Chromedriver 119 [#2003](https://github.com/sitespeedio/browsertime/pull/2003). 119 works with both Chrome 118 and 119 so it fixes [#1197](https://github.com/sitespeedio/browsertime/issues/1997).
7
+
8
+ * Add support for network idle method to know when to end a test that uses network logs. Uses Bidi for Firefox and CDP for Chrome to listen on network events to know when to end a test. By default 5 seconds idle network time ends a tests (you could have network responses that hasn't arrived yet) [#1960](https://github.com/sitespeedio/browsertime/pull/1960). Potentially this can help SPA users or users where the page uses iframes. You can try it out by adding `--pageCompleteCheckNetworkIdle` yo your command line. This is still some work in progress but feel free to try ut out.
9
+
10
+ ### Fixed
11
+ * Make sure timer always is cleared. There was case of where we do a rase beteween a promise and a timeout where the timeout timer wasn't cleared/removed [#2005](https://github.com/sitespeedio/browsertime/pull/2005).
12
+
3
13
  ## 17.17.0 - 2022-10-11
4
14
  ### Added
5
15
  * Firefox 118, Edge 117 and Chrome/Chromedriver 118 in the Docker container [#1996](https://github.com/sitespeedio/browsertime/pull/1996).
6
16
  * Expose trace start/stop for Chrome in scripting (the same way as for Firefox). Thank you [KS](https://github.com/92kns) for [#1988](https://github.com/sitespeedio/browsertime/pull/1988). Documentation is coming when the functionality is rolled out in sitespeed.io.
7
17
 
18
+ ### Updated
19
+ * Updated to Selenium 4.12
20
+
8
21
  ## 17.16.0 - 2022-09-04
9
22
  ### Added
10
23
  * Firefox 117 and Edge 116 in the Docker container.
@@ -1,14 +1,27 @@
1
- (function() {
1
+ (function () {
2
2
  const resources = window.performance.getEntriesByType('resource');
3
3
 
4
4
  let resourceDuration = 0;
5
+ let servedFromCache = 0;
6
+ let servedFromCacheSupported = false;
5
7
  for (let i = 0; i < resources.length; i++) {
6
8
  resourceDuration += resources[i].duration;
9
+ if (resources[i].deliveryType !== undefined) {
10
+ servedFromCacheSupported = true;
11
+ if (resources[i].deliveryType === 'cache') {
12
+ servedFromCache++;
13
+ }
14
+ }
7
15
  }
8
16
 
9
- return {
17
+ const info = {
10
18
  count: Number(resources.length),
11
- duration: Number(resourceDuration)
19
+ duration: Number(resourceDuration),
20
+ };
21
+
22
+ if (servedFromCacheSupported === true) {
23
+ info.servedFromCache = Number(servedFromCache);
12
24
  }
13
- })();
14
25
 
26
+ return info;
27
+ })();
@@ -0,0 +1,61 @@
1
+ import intel from 'intel';
2
+ import get from 'lodash.get';
3
+
4
+ const log = intel.getLogger('browsertime.chrome.network');
5
+
6
+ export class NetworkManager {
7
+ constructor(cdpClient, options) {
8
+ this.maxTimeout = get(options, 'timeouts.pageCompleteCheck', 30_000);
9
+ this.idleTime = get(options, 'timeouts.networkIdle', 5000);
10
+
11
+ this.cdp = cdpClient.getRawClient();
12
+ this.inflight = 0;
13
+ this.lastRequestTimestamp;
14
+ this.lastResponseTimestamp;
15
+
16
+ this.cdp.Network.requestWillBeSent(() => {
17
+ this.inflight++;
18
+ this.lastRequestTimestamp = Date.now();
19
+ });
20
+
21
+ this.cdp.Network.loadingFinished(() => {
22
+ this.inflight--;
23
+ this.lastResponseTimestamp = Date.now();
24
+ });
25
+
26
+ this.cdp.Network.loadingFailed(() => {
27
+ this.inflight--;
28
+ this.lastResponseTimestamp = Date.now();
29
+ });
30
+ }
31
+
32
+ async waitForNetworkIdle() {
33
+ const startTime = Date.now();
34
+ // eslint-disable-next-line no-constant-condition
35
+ while (true) {
36
+ const now = Date.now();
37
+ const sinceLastResponseRequest =
38
+ now - Math.max(this.lastResponseTimestamp, this.lastRequestTimestamp);
39
+ const sinceStart = now - startTime;
40
+
41
+ if (sinceLastResponseRequest >= this.idleTime) {
42
+ if (this.inflight > 0) {
43
+ log.info(
44
+ 'Idle time without any request/responses. Inflight requests:' +
45
+ this.inflight
46
+ );
47
+ }
48
+ break;
49
+ }
50
+
51
+ if (sinceStart >= this.maxTimeout) {
52
+ log.info(
53
+ 'Timeout waiting for network. Inflight requests:' + this.inflight
54
+ );
55
+ break;
56
+ }
57
+
58
+ await new Promise(r => setTimeout(r, 200));
59
+ }
60
+ }
61
+ }
@@ -14,6 +14,7 @@ import { logging as _logging } from 'selenium-webdriver';
14
14
  import { parse } from '../traceCategoriesParser.js';
15
15
  import { pathToFolder } from '../../support/pathToFolder.js';
16
16
  import { ChromeDevtoolsProtocol } from '../chromeDevtoolsProtocol.js';
17
+ import { NetworkManager } from '../networkManager.js';
17
18
  import { Android, isAndroidConfigured } from '../../android/index.js';
18
19
  import { getRenderBlocking } from './traceUtilities.js';
19
20
  const unlink = promisify(_unlink);
@@ -502,4 +503,9 @@ export class Chromium {
502
503
  async setCookies(url, cookies) {
503
504
  return this.cdpClient.setCookies(url, cookies);
504
505
  }
506
+
507
+ async waitForNetworkIdle() {
508
+ let network = new NetworkManager(this.cdpClient, this.options);
509
+ return network.waitForNetworkIdle();
510
+ }
505
511
  }
@@ -1,11 +1,12 @@
1
1
  import intel from 'intel';
2
2
  const log = intel.getLogger('browsertime.command.geckoprofiler');
3
3
  export class GeckoProfiler {
4
- constructor(GeckoProfiler, browser, index, options) {
4
+ constructor(GeckoProfiler, browser, index, options, result) {
5
5
  this.GeckoProfiler = GeckoProfiler;
6
6
  this.browser = browser;
7
7
  this.index = index;
8
8
  this.options = options;
9
+ this.result = result;
9
10
  }
10
11
 
11
12
  async start() {
@@ -25,11 +26,7 @@ export class GeckoProfiler {
25
26
  async stop() {
26
27
  if (this.options.browser === 'firefox') {
27
28
  if (this.options.firefox.geckoProfilerRecordingType === 'custom') {
28
- const url = await this.browser
29
- .getDriver()
30
- .executeScript('return document.documentURI;');
31
-
32
- return this.GeckoProfiler.stop(this.index, url);
29
+ return this.GeckoProfiler.stop(this.index, this.result[0].url);
33
30
  }
34
31
  } else {
35
32
  throw new Error('Geckoprofiler only works in Firefox');
@@ -154,7 +154,11 @@ export class Measure {
154
154
  await this.engineDelegate.beforeStartIteration(this.browser, this.index);
155
155
  }
156
156
  this.numberOfVisitedPages++;
157
- return this.browser.loadAndWait(url, this.pageCompleteCheck);
157
+ return this.browser.loadAndWait(
158
+ url,
159
+ this.pageCompleteCheck,
160
+ this.engineDelegate
161
+ );
158
162
  }
159
163
 
160
164
  /**
@@ -229,7 +233,11 @@ export class Measure {
229
233
  this.result[this.numberOfMeasuredPages].url = url;
230
234
  try {
231
235
  await this.engineDelegate.beforeEachURL(this.browser, url);
232
- await this.browser.loadAndWait(url, this.pageCompleteCheck);
236
+ await this.browser.loadAndWait(
237
+ url,
238
+ this.pageCompleteCheck,
239
+ this.engineDelegate
240
+ );
233
241
  return this.stop(url);
234
242
  } catch (error) {
235
243
  this.result[this.numberOfMeasuredPages].error = [error.name];
@@ -159,7 +159,8 @@ export class Iteration {
159
159
  browserProfiler,
160
160
  browser,
161
161
  index,
162
- this.options
162
+ this.options,
163
+ result
163
164
  );
164
165
  const cdp = new ChromeDevelopmentToolsProtocol(
165
166
  engineDelegate,
@@ -24,25 +24,40 @@ const defaults = {
24
24
  const delay = ms => new Promise(res => setTimeout(res, ms));
25
25
 
26
26
  /**
27
- * Timeout a promise after ms. Use promise.race to compete
28
- * about the timeout and the promise.
29
- * @param {promise} promise - The promise to wait for
30
- * @param {int} ms - how long in ms to wait for the promise to fininsh
31
- * @param {string} errorMessage - the error message in the Error if we timeouts
27
+ * @function timeout
28
+ * @description Wraps a promise with a timeout, rejecting the promise with a TimeoutError if it does not settle within the specified time.
29
+ *
30
+ * @param {Promise} promise - The promise to wrap with a timeout.
31
+ * @param {number} ms - The number of milliseconds to wait before timing out.
32
+ * @param {string} errorMessage - The error message for the TimeoutError.
33
+ *
34
+ * @returns {Promise} - A promise that resolves with the value of the input promise if it settles within time, or rejects with a TimeoutError otherwise.
32
35
  */
33
36
  async function timeout(promise, ms, errorMessage) {
34
- let timer;
35
-
36
- return Promise.race([
37
- new Promise((resolve, reject) => {
38
- timer = setTimeout(reject, ms, new TimeoutError(errorMessage));
39
- return timer;
40
- }),
41
- promise.then(value => {
42
- clearTimeout(timer);
43
- return value;
44
- })
45
- ]);
37
+ let timerId;
38
+ let finished = false;
39
+
40
+ // Create a new promise that rejects after `ms` milliseconds.
41
+ const timer = new Promise((_, reject) => {
42
+ timerId = setTimeout(() => {
43
+ if (!finished) {
44
+ // Reject with a TimeoutError if the input promise has not yet settled.
45
+ reject(new Error(errorMessage));
46
+ }
47
+ }, ms);
48
+ });
49
+
50
+ try {
51
+ // Race the input promise against the timer.
52
+ const result = await Promise.race([promise, timer]);
53
+ finished = true;
54
+ clearTimeout(timerId);
55
+ return result;
56
+ } catch (error) {
57
+ finished = true;
58
+ clearTimeout(timerId);
59
+ throw error;
60
+ }
46
61
  }
47
62
 
48
63
  /**
@@ -132,14 +147,15 @@ export class SeleniumRunner {
132
147
 
133
148
  async extraWait(pageCompleteCheck) {
134
149
  await delay(this.options.beforePageCompleteWaitTime || 5000);
135
- return this.wait(pageCompleteCheck);
150
+ return this._waitOnPageCompleteCheck(pageCompleteCheck);
136
151
  }
137
152
  /**
138
153
  * Wait for pageCompleteCheck to end before we return.
139
154
  * @param {string} pageCompleteCheck - JavaScript that checks if the page has finished loading
140
155
  * @throws {UrlLoadError}
141
156
  */
142
- async wait(pageCompleteCheck, url) {
157
+
158
+ async _waitOnPageCompleteCheck(pageCompleteCheck, url) {
143
159
  const waitTime = this.options.pageCompleteWaitTime || 5000;
144
160
  if (!pageCompleteCheck) {
145
161
  pageCompleteCheck = this.options.pageCompleteCheckInactivity
@@ -209,7 +225,7 @@ export class SeleniumRunner {
209
225
  * @param {string} pageCompleteCheck - JavaScript that checks if the page has finished loading
210
226
  * @throws {UrlLoadError}
211
227
  */
212
- async loadAndWait(url, pageCompleteCheck) {
228
+ async loadAndWait(url, pageCompleteCheck, engine) {
213
229
  const driver = this.driver;
214
230
  // Browsers may normalize 'https://x.com' differently; in particular, Firefox normalizes to
215
231
  // 'https://x.com/'. This is a first normalization attempt; there are deeper options that order
@@ -263,81 +279,86 @@ export class SeleniumRunner {
263
279
  await driver.executeScript(navigate);
264
280
  }
265
281
 
266
- // If you run with default settings, the webdriver will give back
267
- // control ASAP. Therefore you want to wait some extra time
268
- // before you start to run your page complete check
269
- this.options.pageLoadStrategy === 'none'
270
- ? await delay(this.options.pageCompleteCheckStartWait || 5000)
271
- : await delay(2000);
272
-
273
- // We give it a couple of times to finish loading, this makes it
274
- // more stable in real case scenarios on slow servers.
275
- let totalWaitTime = 0;
276
- const tries = get(this.options, 'retries', 5) + 1;
277
- for (let index = 0; index < tries; ++index) {
278
- try {
279
- await this.wait(pageCompleteCheck, normalizedURI);
280
- const newURI = new URL(
281
- await driver.executeScript('return document.documentURI;')
282
- ).toString();
283
- // If we use a SPA it could be that we don't test a new URL so just do one try
284
- // and make sure your page complete check take care of other things
285
- if (this.options.spa) {
286
- break;
287
- } else if (normalizedURI === startURI) {
288
- // You are navigating to the current page
289
- break;
290
- } else if (normalizedURI.startsWith('data:text')) {
291
- // Navigations between data/text seems to don't change the URI
292
- break;
293
- } else if (newURI === 'chrome-error://chromewebdata/') {
294
- // This is the timeout URL for Chrome, just continue to try
295
- throw new UrlLoadError(
296
- `Could not load ${url} is the web page down?`,
297
- url
298
- );
299
- } else if (newURI === startURI) {
300
- const waitTime = (this.options.retryWaitTime || 10_000) * (index + 1);
301
- totalWaitTime += waitTime;
302
- log.debug(
303
- `URL ${url} failed to load, the ${
304
- this.options.browser
305
- } are still on ${startURI} , trying ${
306
- tries - index - 1
307
- } more time(s) but first wait for ${waitTime} ms.`
308
- );
282
+ if (this.options.pageCompleteCheckNetworkIdle) {
283
+ return engine.waitForNetworkIdle(driver);
284
+ } else {
285
+ // If you run with default settings, the webdriver will give back
286
+ // control ASAP. Therefore you want to wait some extra time
287
+ // before you start to run your page complete check
288
+ this.options.pageLoadStrategy === 'none'
289
+ ? await delay(this.options.pageCompleteCheckStartWait || 5000)
290
+ : await delay(2000);
291
+
292
+ // We give it a couple of times to finish loading, this makes it
293
+ // more stable in real case scenarios on slow servers.
294
+ let totalWaitTime = 0;
295
+ const tries = get(this.options, 'retries', 5) + 1;
296
+ for (let index = 0; index < tries; ++index) {
297
+ try {
298
+ await this._waitOnPageCompleteCheck(pageCompleteCheck, normalizedURI);
299
+ const newURI = new URL(
300
+ await driver.executeScript('return document.documentURI;')
301
+ ).toString();
302
+ // If we use a SPA it could be that we don't test a new URL so just do one try
303
+ // and make sure your page complete check take care of other things
304
+ if (this.options.spa) {
305
+ break;
306
+ } else if (normalizedURI === startURI) {
307
+ // You are navigating to the current page
308
+ break;
309
+ } else if (normalizedURI.startsWith('data:text')) {
310
+ // Navigations between data/text seems to don't change the URI
311
+ break;
312
+ } else if (newURI === 'chrome-error://chromewebdata/') {
313
+ // This is the timeout URL for Chrome, just continue to try
314
+ throw new UrlLoadError(
315
+ `Could not load ${url} is the web page down?`,
316
+ url
317
+ );
318
+ } else if (newURI === startURI) {
319
+ const waitTime =
320
+ (this.options.retryWaitTime || 10_000) * (index + 1);
321
+ totalWaitTime += waitTime;
322
+ log.debug(
323
+ `URL ${url} failed to load, the ${
324
+ this.options.browser
325
+ } are still on ${startURI} , trying ${
326
+ tries - index - 1
327
+ } more time(s) but first wait for ${waitTime} ms.`
328
+ );
309
329
 
330
+ if (index === tries - 1) {
331
+ // If the last tries through an error, rethrow as before
332
+ const message = `Could not load ${url} - the navigation never happend after ${tries} tries and total wait time of ${totalWaitTime} ms`;
333
+ log.error(message);
334
+ throw new UrlLoadError(message, url);
335
+ } else {
336
+ // We add some wait time before we try again
337
+ await delay(waitTime);
338
+ log.info(
339
+ 'Will check again if the browser has navigated to the page'
340
+ );
341
+ }
342
+ } else {
343
+ // We navigated to a new page, we don't need to test anymore
344
+ break;
345
+ }
346
+ } catch (error) {
347
+ log.info(
348
+ `URL failed to load, trying ${tries - index - 1} more time(s): ${
349
+ error.message
350
+ }`
351
+ );
352
+ //
310
353
  if (index === tries - 1) {
311
354
  // If the last tries through an error, rethrow as before
312
- const message = `Could not load ${url} - the navigation never happend after ${tries} tries and total wait time of ${totalWaitTime} ms`;
313
- log.error(message);
314
- throw new UrlLoadError(message, url);
355
+ log.error('Could not load URL %s', url, error);
356
+ throw new UrlLoadError('Failed to load ' + url, url, {
357
+ cause: error
358
+ });
315
359
  } else {
316
- // We add some wait time before we try again
317
- await delay(waitTime);
318
- log.info(
319
- 'Will check again if the browser has navigated to the page'
320
- );
360
+ await delay(1000);
321
361
  }
322
- } else {
323
- // We navigated to a new page, we don't need to test anymore
324
- break;
325
- }
326
- } catch (error) {
327
- log.info(
328
- `URL failed to load, trying ${tries - index - 1} more time(s): ${
329
- error.message
330
- }`
331
- );
332
- //
333
- if (index === tries - 1) {
334
- // If the last tries through an error, rethrow as before
335
- log.error('Could not load URL %s', url, error);
336
- throw new UrlLoadError('Failed to load ' + url, url, {
337
- cause: error
338
- });
339
- } else {
340
- await delay(1000);
341
362
  }
342
363
  }
343
364
  }
@@ -0,0 +1,109 @@
1
+ import intel from 'intel';
2
+ import get from 'lodash.get';
3
+ const log = intel.getLogger('browsertime.chrome.network');
4
+
5
+ export class NetworkManager {
6
+ constructor(bidi, browsingContextIds, options) {
7
+ this.bidi = bidi;
8
+ this.browsingContextIds = browsingContextIds;
9
+ this.maxTimeout = get(options, 'timeouts.pageCompleteCheck', 30_000);
10
+ this.idleTime = get(options, 'timeouts.networkIdle', 5000);
11
+ }
12
+
13
+ async waitForNetworkIdle() {
14
+ await this.bidi.subscribe(
15
+ 'network.beforeRequestSent',
16
+ this.browsingContextIds
17
+ );
18
+ await this.bidi.subscribe(
19
+ 'network.responseCompleted',
20
+ this.browsingContextIds
21
+ );
22
+ await this.bidi.subscribe('network.fetchError', this.browsingContextIds);
23
+
24
+ let inflight = 0;
25
+ let lastRequestTimestamp;
26
+ let lastResponseTimestamp;
27
+ this.ws = await this.bidi.socket;
28
+ this.ws.on('message', function (event) {
29
+ const { method } = JSON.parse(Buffer.from(event.toString()));
30
+ if (method) {
31
+ switch (method) {
32
+ case 'network.beforeRequestSent': {
33
+ inflight++;
34
+ lastRequestTimestamp = Date.now();
35
+
36
+ break;
37
+ }
38
+ case 'network.responseCompleted': {
39
+ inflight--;
40
+ lastResponseTimestamp = Date.now();
41
+
42
+ break;
43
+ }
44
+ case 'network.fetchError': {
45
+ inflight--;
46
+ lastResponseTimestamp = Date.now();
47
+
48
+ break;
49
+ }
50
+ // No default
51
+ }
52
+ }
53
+ });
54
+
55
+ const startTime = Date.now();
56
+ // eslint-disable-next-line no-constant-condition
57
+ while (true) {
58
+ const now = Date.now();
59
+ const sinceLastResponseRequest =
60
+ now - Math.max(lastResponseTimestamp, lastRequestTimestamp);
61
+ const sinceStart = now - startTime;
62
+
63
+ if (sinceLastResponseRequest >= this.idleTime) {
64
+ if (inflight > 0) {
65
+ log.info(
66
+ 'Idle time without any request/responses. Inflight requests:' +
67
+ inflight
68
+ );
69
+ }
70
+ await this.bidi.unsubscribe(
71
+ 'network.beforeRequestSent',
72
+ this.browsingContextIds
73
+ );
74
+ await this.bidi.unsubscribe(
75
+ 'network.responseCompleted',
76
+ this.browsingContextIds
77
+ );
78
+
79
+ await this.bidi.unsubscribe(
80
+ 'network.fetchError',
81
+ this.browsingContextIds
82
+ );
83
+
84
+ break;
85
+ }
86
+
87
+ if (sinceStart >= this.maxTimeout) {
88
+ log.info(
89
+ 'Timeout waiting for network idle. Inflight requests:' + inflight
90
+ );
91
+ await this.bidi.unsubscribe(
92
+ 'network.beforeRequestSent',
93
+ this.browsingContextIds
94
+ );
95
+ await this.bidi.unsubscribe(
96
+ 'network.responseCompleted',
97
+ this.browsingContextIds
98
+ );
99
+ await this.bidi.unsubscribe(
100
+ 'network.fetchError',
101
+ this.browsingContextIds
102
+ );
103
+ break;
104
+ }
105
+
106
+ await new Promise(r => setTimeout(r, 200));
107
+ }
108
+ }
109
+ }
@@ -175,6 +175,8 @@ export async function configureBuilder(builder, baseDir, options) {
175
175
 
176
176
  ffOptions.addArguments('-no-remote');
177
177
 
178
+ ffOptions.enableBidi();
179
+
178
180
  // Enable bidi for HAR support
179
181
  if (firefoxConfig.bidihar) {
180
182
  ffOptions.enableBidi();
@@ -12,6 +12,7 @@ import { findFiles } from '../../support/fileUtil.js';
12
12
  import { GeckoProfiler } from '../geckoProfiler.js';
13
13
  import { MemoryReport } from '../memoryReport.js';
14
14
  import { PerfStats } from '../perfStats.js';
15
+ import { NetworkManager } from '../networkManager.js';
15
16
  import { getHAR } from '../getHAR.js';
16
17
 
17
18
  const log = intel.getLogger('browsertime.firefox');
@@ -323,4 +324,11 @@ export class Firefox {
323
324
  * Before the browser is stopped/closed.
324
325
  */
325
326
  async afterBrowserStopped() {}
327
+
328
+ async waitForNetworkIdle(driver) {
329
+ const windowId = await driver.getWindowHandle();
330
+ const bidi = await driver.getBidi();
331
+ let network = new NetworkManager(bidi, [windowId], this.options);
332
+ return network.waitForNetworkIdle();
333
+ }
326
334
  }
@@ -98,4 +98,8 @@ export class Safari {
98
98
  });
99
99
  }
100
100
  }
101
+
102
+ async waitForNetworkIdle() {
103
+ throw new Error('waitForNetworkIdle is not implemented in Safari');
104
+ }
101
105
  }
@@ -176,6 +176,13 @@ export function parseCommandLine() {
176
176
  'Timeout when waiting for page to complete loading, in milliseconds',
177
177
  group: 'timeouts'
178
178
  })
179
+ .option('timeouts.networkIdle', {
180
+ default: 5000,
181
+ type: 'number',
182
+ describe:
183
+ 'Timeout when running pageCompleteCheckNetworkIdle, in milliseconds',
184
+ group: 'timeouts'
185
+ })
179
186
  .option('chrome.args', {
180
187
  describe:
181
188
  'Extra command line arguments to pass to the Chrome process (e.g. --no-sandbox). ' +
@@ -911,6 +918,12 @@ export function parseCommandLine() {
911
918
  type: 'boolean',
912
919
  default: false
913
920
  })
921
+ .option('pageCompleteCheckNetworkIdle', {
922
+ describe:
923
+ 'Alternative way to choose when to end your test that works in Chrome and Firefox. Uses CDP or WebDriver Bidi to look at network traffic instead of running JavaScript in the browser to know when to end the test. By default this will wait 5 seconds of inactivity in the network log (no requets/responses in 5 seconds). Use --timeouts.networkIdle to change the 5 seconds. The test will end after 2 minutes if there is still activity on the network. You can change that timout using --timeouts.pageCompleteCheck ',
924
+ type: 'boolean',
925
+ default: false
926
+ })
914
927
  .option('pageCompleteCheckPollTimeout', {
915
928
  type: 'number',
916
929
  default: 1500,
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "browsertime",
3
3
  "description": "Get performance metrics from your web page using Browsertime.",
4
- "version": "17.17.0",
4
+ "version": "17.18.0",
5
5
  "bin": "./bin/browsertime.js",
6
6
  "type": "module",
7
7
  "dependencies": {
8
8
  "@cypress/xvfb": "1.2.4",
9
9
  "@devicefarmer/adbkit": "2.11.3",
10
- "@sitespeed.io/chromedriver": "118.0.5993-70",
10
+ "@sitespeed.io/chromedriver": "119.0.6045-21",
11
11
  "@sitespeed.io/edgedriver": "115.0.1901-183",
12
12
  "@sitespeed.io/geckodriver": "0.33.0",
13
13
  "@sitespeed.io/throttle": "5.0.0",