browsertime 16.17.0 → 17.0.0-beta.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 +44 -0
- package/bin/browsertime.js +43 -43
- package/bin/browsertimeWebPageReplay.js +10 -9
- package/browserscripts/pageinfo/generator.js +1 -1
- package/index.js +14 -13
- package/lib/android/gnirehtet.js +14 -15
- package/lib/android/index.js +92 -89
- package/lib/android/root.js +6 -9
- package/lib/chrome/chromeDevtoolsProtocol.js +46 -45
- package/lib/chrome/har.js +12 -13
- package/lib/chrome/longTaskMetrics.js +3 -5
- package/lib/chrome/parseCpuTrace.js +13 -10
- package/lib/chrome/settings/chromeAndroidOptions.js +1 -3
- package/lib/chrome/settings/chromeDesktopOptions.js +1 -3
- package/lib/chrome/settings/traceCategories.js +1 -3
- package/lib/chrome/traceCategoriesParser.js +35 -36
- package/lib/chrome/webdriver/builder.js +18 -26
- package/lib/chrome/webdriver/chromium.js +56 -46
- package/lib/chrome/webdriver/setupChromiumOptions.js +32 -27
- package/lib/connectivity/humble.js +13 -14
- package/lib/connectivity/index.js +74 -77
- package/lib/connectivity/trafficShapeParser.js +31 -35
- package/lib/core/engine/collector.js +22 -22
- package/lib/core/engine/command/addText.js +24 -27
- package/lib/core/engine/command/android.js +7 -11
- package/lib/core/engine/command/cache.js +3 -4
- package/lib/core/engine/command/chromeDevToolsProtocol.js +20 -20
- package/lib/core/engine/command/click.js +48 -49
- package/lib/core/engine/command/debug.js +3 -4
- package/lib/core/engine/command/javaScript.js +18 -19
- package/lib/core/engine/command/measure.js +31 -37
- package/lib/core/engine/command/meta.js +1 -4
- package/lib/core/engine/command/mouse/clickAndHold.js +36 -39
- package/lib/core/engine/command/mouse/contextClick.js +17 -18
- package/lib/core/engine/command/mouse/doubleClick.js +17 -19
- package/lib/core/engine/command/mouse/index.js +5 -9
- package/lib/core/engine/command/mouse/mouseMove.js +19 -23
- package/lib/core/engine/command/mouse/singleClick.js +15 -19
- package/lib/core/engine/command/navigation.js +12 -14
- package/lib/core/engine/command/screenshot.js +1 -4
- package/lib/core/engine/command/scroll.js +4 -7
- package/lib/core/engine/command/select.js +26 -26
- package/lib/core/engine/command/set.js +25 -23
- package/lib/core/engine/command/stopWatch.js +5 -8
- package/lib/core/engine/command/switch.js +18 -20
- package/lib/core/engine/command/util/lcpHighlightScript.js +1 -2
- package/lib/core/engine/command/util/lsHighlightScript.js +1 -2
- package/lib/core/engine/command/wait.js +18 -18
- package/lib/core/engine/index.js +81 -71
- package/lib/core/engine/iteration.js +83 -79
- package/lib/core/engine/run.js +5 -9
- package/lib/core/pageCompleteChecks/defaultPageCompleteCheck.js +1 -2
- package/lib/core/pageCompleteChecks/pageCompleteCheckByInactivity.js +1 -3
- package/lib/core/pageCompleteChecks/spaInactivity.js +1 -3
- package/lib/core/seleniumRunner.js +92 -100
- package/lib/core/webdriver/index.js +24 -21
- package/lib/edge/webdriver/builder.js +21 -26
- package/lib/extensionserver/httpServer.js +17 -21
- package/lib/extensionserver/index.js +8 -12
- package/lib/extensionserver/setup.js +12 -12
- package/lib/firefox/geckoProfiler.js +27 -21
- package/lib/firefox/getHAR.js +7 -8
- package/lib/firefox/memoryReport.js +9 -11
- package/lib/firefox/perfStats.js +5 -7
- package/lib/firefox/settings/disableSafeBrowsingPreferences.js +1 -3
- package/lib/firefox/settings/disableTrackingProtectionPreferences.js +1 -3
- package/lib/firefox/settings/firefoxPreferences.js +2 -4
- package/lib/firefox/settings/geckoProfilerDefaults.js +5 -9
- package/lib/firefox/webdriver/builder.js +66 -63
- package/lib/firefox/webdriver/firefox.js +43 -48
- package/lib/safari/webdriver/builder.js +4 -5
- package/lib/safari/webdriver/safari.js +4 -8
- package/lib/screenshot/defaults.js +1 -3
- package/lib/screenshot/index.js +24 -21
- package/lib/support/browserScript.js +65 -56
- package/lib/support/cli.js +120 -87
- package/lib/support/dns.js +6 -7
- package/lib/support/engineUtils.js +85 -38
- package/lib/support/errors.js +4 -13
- package/lib/support/fileUtil.js +81 -68
- package/lib/support/filters.js +15 -20
- package/lib/support/getViewPort.js +6 -7
- package/lib/support/har/index.js +208 -216
- package/lib/support/images/index.js +54 -26
- package/lib/support/logging.js +20 -17
- package/lib/support/pathToFolder.js +18 -23
- package/lib/support/preURL.js +5 -6
- package/lib/support/processes.js +4 -6
- package/lib/support/setResourceTimingBufferSize.js +2 -4
- package/lib/support/statistics.js +58 -56
- package/lib/support/stop.js +7 -8
- package/lib/support/storageManager.js +44 -44
- package/lib/support/tcpdump.js +22 -16
- package/lib/support/userTiming.js +7 -13
- package/lib/support/util.js +204 -224
- package/lib/support/xvfb.js +11 -13
- package/lib/video/defaults.js +6 -10
- package/lib/video/postprocessing/finetune/addTextToVideo.js +15 -15
- package/lib/video/postprocessing/finetune/convertFps.js +14 -8
- package/lib/video/postprocessing/finetune/getFont.js +7 -9
- package/lib/video/postprocessing/finetune/getTimingMetrics.js +19 -18
- package/lib/video/postprocessing/finetune/index.js +40 -36
- package/lib/video/postprocessing/finetune/removeOrange.js +10 -11
- package/lib/video/postprocessing/visualmetrics/extraMetrics.js +17 -13
- package/lib/video/postprocessing/visualmetrics/getVideoMetrics.js +12 -13
- package/lib/video/postprocessing/visualmetrics/visualMetrics.js +117 -128
- package/lib/video/screenRecording/android/recorder.js +12 -17
- package/lib/video/screenRecording/desktop/convert.js +13 -11
- package/lib/video/screenRecording/desktop/desktopRecorder.js +29 -24
- package/lib/video/screenRecording/desktop/ffmpegRecorder.js +60 -63
- package/lib/video/screenRecording/desktop/osx/getSPDisplaysDataType.js +4 -6
- package/lib/video/screenRecording/desktop/osx/getScreen.js +9 -10
- package/lib/video/screenRecording/firefox/firefoxWindowRecorder.js +54 -54
- package/lib/video/screenRecording/ios/convertToMp4.js +10 -11
- package/lib/video/screenRecording/ios/iosRecorder.js +23 -21
- package/lib/video/screenRecording/iosSimulator/convertToMp4.js +10 -11
- package/lib/video/screenRecording/iosSimulator/recorder.js +10 -9
- package/lib/video/screenRecording/recorder.js +8 -9
- package/lib/video/screenRecording/setOrangeBackground.js +5 -6
- package/lib/video/video.js +28 -34
- package/package.json +19 -17
|
@@ -1,28 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const builder = require('./webdriver');
|
|
14
|
-
const getViewPort = require('../support/getViewPort');
|
|
15
|
-
const defaultPageCompleteCheck = require('./pageCompleteChecks/defaultPageCompleteCheck');
|
|
16
|
-
const pageCompleteCheckByInactivity = require('./pageCompleteChecks/pageCompleteCheckByInactivity');
|
|
17
|
-
const spaCheck = require('./pageCompleteChecks/spaInactivity');
|
|
1
|
+
import intel from 'intel';
|
|
2
|
+
import merge from 'lodash.merge';
|
|
3
|
+
import get from 'lodash.get';
|
|
4
|
+
import { Condition } from 'selenium-webdriver';
|
|
5
|
+
import { isAndroidConfigured } from '../android/index.js';
|
|
6
|
+
import { UrlLoadError, BrowserError, TimeoutError } from '../support/errors.js';
|
|
7
|
+
import { createWebDriver } from './webdriver/index.js';
|
|
8
|
+
import getViewPort from '../support/getViewPort.js';
|
|
9
|
+
import defaultPageCompleteCheck from './pageCompleteChecks/defaultPageCompleteCheck.js';
|
|
10
|
+
import pageCompleteCheckByInactivity from './pageCompleteChecks/pageCompleteCheckByInactivity.js';
|
|
11
|
+
import spaCheck from './pageCompleteChecks/spaInactivity.js';
|
|
12
|
+
const log = intel.getLogger('browsertime');
|
|
18
13
|
|
|
19
14
|
const defaults = {
|
|
20
15
|
timeouts: {
|
|
21
|
-
browserStart:
|
|
22
|
-
pageLoad:
|
|
23
|
-
script:
|
|
24
|
-
logs:
|
|
25
|
-
pageCompleteCheck:
|
|
16
|
+
browserStart: 60_000,
|
|
17
|
+
pageLoad: 300_000,
|
|
18
|
+
script: 120_000,
|
|
19
|
+
logs: 90_000,
|
|
20
|
+
pageCompleteCheck: 60_000
|
|
26
21
|
},
|
|
27
22
|
index: 0
|
|
28
23
|
};
|
|
@@ -36,7 +31,7 @@ const delay = ms => new Promise(res => setTimeout(res, ms));
|
|
|
36
31
|
* @param {string} errorMessage - the error message in the Error if we timeouts
|
|
37
32
|
*/
|
|
38
33
|
async function timeout(promise, ms, errorMessage) {
|
|
39
|
-
let timer
|
|
34
|
+
let timer;
|
|
40
35
|
|
|
41
36
|
return Promise.race([
|
|
42
37
|
new Promise((resolve, reject) => {
|
|
@@ -69,21 +64,21 @@ class SeleniumRunner {
|
|
|
69
64
|
*/
|
|
70
65
|
async start() {
|
|
71
66
|
const tries = this.browserRestartTries;
|
|
72
|
-
for (let
|
|
67
|
+
for (let index = 0; index < tries; ++index) {
|
|
73
68
|
try {
|
|
74
69
|
this.driver = await timeout(
|
|
75
|
-
|
|
70
|
+
createWebDriver(this.baseDir, this.options),
|
|
76
71
|
this.options.timeouts.browserStart,
|
|
77
72
|
`Failed to start ${this.options.browser} in ${
|
|
78
73
|
this.options.timeouts.browserStart / 1000
|
|
79
74
|
} seconds.`
|
|
80
75
|
);
|
|
81
76
|
break;
|
|
82
|
-
} catch (
|
|
77
|
+
} catch (error) {
|
|
83
78
|
log.info(
|
|
84
79
|
`${this.options.browser} failed to start, trying ${
|
|
85
|
-
tries -
|
|
86
|
-
} more time(s): ${
|
|
80
|
+
tries - index - 1
|
|
81
|
+
} more time(s): ${error.message}`
|
|
87
82
|
);
|
|
88
83
|
}
|
|
89
84
|
}
|
|
@@ -117,7 +112,6 @@ class SeleniumRunner {
|
|
|
117
112
|
// Android do not support set position nor set size
|
|
118
113
|
if (
|
|
119
114
|
// Hack for Wallmart labs
|
|
120
|
-
!this.options.android &&
|
|
121
115
|
!isAndroidConfigured(this.options)
|
|
122
116
|
) {
|
|
123
117
|
await window.setRect({
|
|
@@ -129,9 +123,9 @@ class SeleniumRunner {
|
|
|
129
123
|
}
|
|
130
124
|
}
|
|
131
125
|
}
|
|
132
|
-
} catch (
|
|
133
|
-
throw new BrowserError(
|
|
134
|
-
cause:
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw new BrowserError(error.message, {
|
|
128
|
+
cause: error
|
|
135
129
|
});
|
|
136
130
|
}
|
|
137
131
|
}
|
|
@@ -185,24 +179,24 @@ class SeleniumRunner {
|
|
|
185
179
|
pageCompleteCheckTimeout,
|
|
186
180
|
`Running page complete check ${pageCompleteCheck} took too long`
|
|
187
181
|
);
|
|
188
|
-
} catch (
|
|
189
|
-
if (
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (error instanceof TimeoutError) {
|
|
190
184
|
log.info(
|
|
191
185
|
`The page did not finished loading in ${pageCompleteCheckTimeout} ms. You can adjust the timeout by setting the --maxLoadTime option (in ms).`
|
|
192
186
|
);
|
|
193
187
|
} else {
|
|
194
188
|
log.error(
|
|
195
189
|
`Failed waiting on page ${
|
|
196
|
-
url
|
|
190
|
+
url ?? ''
|
|
197
191
|
} to finished loading, timed out after ${pageCompleteCheckTimeout} ms`,
|
|
198
|
-
|
|
192
|
+
error
|
|
199
193
|
);
|
|
200
194
|
throw new UrlLoadError(
|
|
201
195
|
`Failed waiting on page ${
|
|
202
|
-
url
|
|
196
|
+
url ?? ''
|
|
203
197
|
} to finished loading, timed out after ${pageCompleteCheckTimeout} ms `,
|
|
204
198
|
{
|
|
205
|
-
cause:
|
|
199
|
+
cause: error
|
|
206
200
|
}
|
|
207
201
|
);
|
|
208
202
|
}
|
|
@@ -226,9 +220,9 @@ class SeleniumRunner {
|
|
|
226
220
|
// See https://github.com/sitespeedio/browsertime/issues/1698
|
|
227
221
|
const retries = 5;
|
|
228
222
|
let documentURI;
|
|
229
|
-
for (var
|
|
223
|
+
for (var index = 0; index < retries; index++) {
|
|
230
224
|
documentURI = await driver.executeScript('return document.documentURI;');
|
|
231
|
-
if (documentURI !=
|
|
225
|
+
if (documentURI != undefined) {
|
|
232
226
|
break;
|
|
233
227
|
}
|
|
234
228
|
await delay(1000);
|
|
@@ -236,8 +230,8 @@ class SeleniumRunner {
|
|
|
236
230
|
let startURI;
|
|
237
231
|
try {
|
|
238
232
|
startURI = new URL(documentURI).toString();
|
|
239
|
-
} catch (
|
|
240
|
-
log.error(`Failed to get documentURI ${documentURI}`,
|
|
233
|
+
} catch (error) {
|
|
234
|
+
log.error(`Failed to get documentURI ${documentURI}`, error);
|
|
241
235
|
}
|
|
242
236
|
if (this.options.webdriverPageload) {
|
|
243
237
|
const clearOrange = `(function() {
|
|
@@ -272,18 +266,15 @@ class SeleniumRunner {
|
|
|
272
266
|
// If you run with default settings, the webdriver will give back
|
|
273
267
|
// control ASAP. Therefore you want to wait some extra time
|
|
274
268
|
// before you start to run your page complete check
|
|
275
|
-
|
|
276
|
-
await delay(this.options.pageCompleteCheckStartWait || 5000)
|
|
277
|
-
|
|
278
|
-
// Give the browser some time to navigate
|
|
279
|
-
await delay(2000);
|
|
280
|
-
}
|
|
269
|
+
this.options.pageLoadStrategy === 'none'
|
|
270
|
+
? await delay(this.options.pageCompleteCheckStartWait || 5000)
|
|
271
|
+
: await delay(2000);
|
|
281
272
|
|
|
282
273
|
// We give it a couple of times to finish loading, this makes it
|
|
283
274
|
// more stable in real case scenarios on slow servers.
|
|
284
275
|
let totalWaitTime = 0;
|
|
285
276
|
const tries = get(this.options, 'retries', 5) + 1;
|
|
286
|
-
for (let
|
|
277
|
+
for (let index = 0; index < tries; ++index) {
|
|
287
278
|
try {
|
|
288
279
|
await this.wait(pageCompleteCheck, normalizedURI);
|
|
289
280
|
const newURI = new URL(
|
|
@@ -309,17 +300,17 @@ class SeleniumRunner {
|
|
|
309
300
|
// We navigated to a new page, we don't need to test anymore
|
|
310
301
|
break;
|
|
311
302
|
} else {
|
|
312
|
-
const waitTime = (this.options.retryWaitTime ||
|
|
303
|
+
const waitTime = (this.options.retryWaitTime || 10_000) * (index + 1);
|
|
313
304
|
totalWaitTime += waitTime;
|
|
314
305
|
log.debug(
|
|
315
306
|
`URL ${url} failed to load, the ${
|
|
316
307
|
this.options.browser
|
|
317
308
|
} are still on ${startURI} , trying ${
|
|
318
|
-
tries -
|
|
309
|
+
tries - index - 1
|
|
319
310
|
} more time(s) but first wait for ${waitTime} ms.`
|
|
320
311
|
);
|
|
321
312
|
|
|
322
|
-
if (
|
|
313
|
+
if (index === tries - 1) {
|
|
323
314
|
// If the last tries through an error, rethrow as before
|
|
324
315
|
const message = `Could not load ${url} - the navigation never happend after ${tries} tries and total wait time of ${totalWaitTime} ms`;
|
|
325
316
|
log.error(message);
|
|
@@ -332,18 +323,18 @@ class SeleniumRunner {
|
|
|
332
323
|
);
|
|
333
324
|
}
|
|
334
325
|
}
|
|
335
|
-
} catch (
|
|
326
|
+
} catch (error) {
|
|
336
327
|
log.info(
|
|
337
|
-
`URL failed to load, trying ${tries -
|
|
338
|
-
|
|
328
|
+
`URL failed to load, trying ${tries - index - 1} more time(s): ${
|
|
329
|
+
error.message
|
|
339
330
|
}`
|
|
340
331
|
);
|
|
341
332
|
//
|
|
342
|
-
if (
|
|
333
|
+
if (index === tries - 1) {
|
|
343
334
|
// If the last tries through an error, rethrow as before
|
|
344
|
-
log.error('Could not load URL %s', url,
|
|
335
|
+
log.error('Could not load URL %s', url, error);
|
|
345
336
|
throw new UrlLoadError('Failed to load ' + url, url, {
|
|
346
|
-
cause:
|
|
337
|
+
cause: error
|
|
347
338
|
});
|
|
348
339
|
} else {
|
|
349
340
|
await delay(1000);
|
|
@@ -371,9 +362,12 @@ class SeleniumRunner {
|
|
|
371
362
|
`Failed to take screenshot (type was ${typeof base64EncodedPng}`
|
|
372
363
|
);
|
|
373
364
|
}
|
|
374
|
-
} catch (
|
|
375
|
-
log.error(
|
|
376
|
-
|
|
365
|
+
} catch (error) {
|
|
366
|
+
log.error(
|
|
367
|
+
'Failed to take screenshot' + (url ? ' for ' + url : ''),
|
|
368
|
+
error
|
|
369
|
+
);
|
|
370
|
+
throw new BrowserError('Failed to take screenshot', { cause: error });
|
|
377
371
|
}
|
|
378
372
|
}
|
|
379
373
|
|
|
@@ -384,7 +378,7 @@ class SeleniumRunner {
|
|
|
384
378
|
* @param {*} args - arguments to the script
|
|
385
379
|
* @throws {BrowserError}
|
|
386
380
|
*/
|
|
387
|
-
async runScript(script, name,
|
|
381
|
+
async runScript(script, name, arguments_) {
|
|
388
382
|
let scriptTimeout = this.options.timeouts.script;
|
|
389
383
|
|
|
390
384
|
if (log.isEnabledFor(log.TRACE)) {
|
|
@@ -395,14 +389,14 @@ class SeleniumRunner {
|
|
|
395
389
|
|
|
396
390
|
try {
|
|
397
391
|
const result = await timeout(
|
|
398
|
-
this.driver.executeScript(script,
|
|
392
|
+
this.driver.executeScript(script, arguments_),
|
|
399
393
|
scriptTimeout,
|
|
400
394
|
`Running script ${script} took too long (${scriptTimeout} ms).`
|
|
401
395
|
);
|
|
402
396
|
return result;
|
|
403
|
-
} catch (
|
|
404
|
-
log.error("Couldn't execute script named " + name + ' error:' +
|
|
405
|
-
throw
|
|
397
|
+
} catch (error) {
|
|
398
|
+
log.error("Couldn't execute script named " + name + ' error:' + error);
|
|
399
|
+
throw error;
|
|
406
400
|
}
|
|
407
401
|
}
|
|
408
402
|
|
|
@@ -413,7 +407,7 @@ class SeleniumRunner {
|
|
|
413
407
|
* @param {*} args - arguments to the script
|
|
414
408
|
* @throws {BrowserError}
|
|
415
409
|
*/
|
|
416
|
-
async runPrivilegedScript(script, name,
|
|
410
|
+
async runPrivilegedScript(script, name, arguments_) {
|
|
417
411
|
if (this.options.browser !== 'firefox') {
|
|
418
412
|
throw new BrowserError(
|
|
419
413
|
`Only Firefox browsers can run privileged JavaScript: ${this.options.browser}`,
|
|
@@ -428,15 +422,15 @@ class SeleniumRunner {
|
|
|
428
422
|
|
|
429
423
|
try {
|
|
430
424
|
await this.driver.setContext('chrome');
|
|
431
|
-
const result = await this.driver.executeScript(script,
|
|
425
|
+
const result = await this.driver.executeScript(script, arguments_);
|
|
432
426
|
await this.driver.setContext(oldContext);
|
|
433
427
|
return result;
|
|
434
|
-
} catch (
|
|
428
|
+
} catch (error) {
|
|
435
429
|
log.error(
|
|
436
|
-
"Couldn't execute privileged script named " + name + ' error:' +
|
|
430
|
+
"Couldn't execute privileged script named " + name + ' error:' + error
|
|
437
431
|
);
|
|
438
432
|
await this.driver.setContext(oldContext);
|
|
439
|
-
throw
|
|
433
|
+
throw error;
|
|
440
434
|
}
|
|
441
435
|
}
|
|
442
436
|
|
|
@@ -447,13 +441,13 @@ class SeleniumRunner {
|
|
|
447
441
|
* @param {*} args - arguments to the script
|
|
448
442
|
* @throws {BrowserError}
|
|
449
443
|
*/
|
|
450
|
-
async runAsyncScript(script, name,
|
|
444
|
+
async runAsyncScript(script, name, arguments_) {
|
|
451
445
|
if (log.isEnabledFor(log.TRACE)) {
|
|
452
446
|
log.verbose('Executing async script %s', script);
|
|
453
447
|
} else if (log.isEnabledFor(log.VERBOSE)) {
|
|
454
448
|
log.verbose('Executing async script %s', name);
|
|
455
449
|
}
|
|
456
|
-
return this.driver.executeAsyncScript(script,
|
|
450
|
+
return this.driver.executeAsyncScript(script, arguments_);
|
|
457
451
|
}
|
|
458
452
|
|
|
459
453
|
/**
|
|
@@ -463,7 +457,7 @@ class SeleniumRunner {
|
|
|
463
457
|
* @param {*} args - arguments to the script
|
|
464
458
|
* @throws {BrowserError}
|
|
465
459
|
*/
|
|
466
|
-
async runPrivilegedAsyncScript(script, name,
|
|
460
|
+
async runPrivilegedAsyncScript(script, name, arguments_) {
|
|
467
461
|
if (this.options.browser !== 'firefox') {
|
|
468
462
|
throw new BrowserError(
|
|
469
463
|
`Only Firefox browsers can run privileged JavaScript: ${this.options.browser}`,
|
|
@@ -477,12 +471,14 @@ class SeleniumRunner {
|
|
|
477
471
|
try {
|
|
478
472
|
const oldContext = await this.driver.getContext();
|
|
479
473
|
await this.driver.setContext('chrome');
|
|
480
|
-
const result = await this.driver.executeAsyncScript(script,
|
|
474
|
+
const result = await this.driver.executeAsyncScript(script, arguments_);
|
|
481
475
|
await this.driver.setContext(oldContext);
|
|
482
476
|
return result;
|
|
483
|
-
} catch (
|
|
484
|
-
log.error(
|
|
485
|
-
|
|
477
|
+
} catch (error) {
|
|
478
|
+
log.error(
|
|
479
|
+
"Couldn't execute async script named " + name + ' error:' + error
|
|
480
|
+
);
|
|
481
|
+
throw error;
|
|
486
482
|
}
|
|
487
483
|
}
|
|
488
484
|
|
|
@@ -510,12 +506,12 @@ class SeleniumRunner {
|
|
|
510
506
|
try {
|
|
511
507
|
return timeout(
|
|
512
508
|
this.driver.quit(),
|
|
513
|
-
|
|
509
|
+
120_000,
|
|
514
510
|
'Could not close the browser using driver.quit'
|
|
515
511
|
);
|
|
516
|
-
} catch (
|
|
517
|
-
throw new BrowserError(
|
|
518
|
-
cause:
|
|
512
|
+
} catch (error) {
|
|
513
|
+
throw new BrowserError(error.message, {
|
|
514
|
+
cause: error
|
|
519
515
|
});
|
|
520
516
|
}
|
|
521
517
|
}
|
|
@@ -535,11 +531,9 @@ class SeleniumRunner {
|
|
|
535
531
|
let scriptRunner = this.runScript.bind(this);
|
|
536
532
|
|
|
537
533
|
if (isAsync) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
scriptRunner = this.runAsyncScript.bind(this);
|
|
542
|
-
}
|
|
534
|
+
scriptRunner = privilegeWanted
|
|
535
|
+
? this.runPrivilegedAsyncScript.bind(this)
|
|
536
|
+
: this.runAsyncScript.bind(this);
|
|
543
537
|
} else {
|
|
544
538
|
if (privilegeWanted) {
|
|
545
539
|
scriptRunner = this.runPrivilegedScript.bind(this);
|
|
@@ -568,11 +562,9 @@ class SeleniumRunner {
|
|
|
568
562
|
}
|
|
569
563
|
} else {
|
|
570
564
|
const source = 'return ' + script;
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
return scriptRunner(source, name);
|
|
575
|
-
}
|
|
565
|
+
return this.options.scriptInput && this.options.scriptInput[name]
|
|
566
|
+
? scriptRunner(source, name, this.options.scriptInput[name])
|
|
567
|
+
: scriptRunner(source, name);
|
|
576
568
|
}
|
|
577
569
|
}
|
|
578
570
|
|
|
@@ -598,17 +590,17 @@ class SeleniumRunner {
|
|
|
598
590
|
category,
|
|
599
591
|
isAsync
|
|
600
592
|
);
|
|
601
|
-
} catch (
|
|
602
|
-
if (
|
|
593
|
+
} catch (error) {
|
|
594
|
+
if (error.extra && error.extra.cause === 'PrivilegeError') {
|
|
603
595
|
// Ignore those scripts that fail to execute because they
|
|
604
596
|
// wanted privileges that the browser cannot provide.
|
|
605
597
|
log.verbose(
|
|
606
598
|
'Did not have enough privileges to execute user script: ' +
|
|
607
|
-
|
|
599
|
+
error +
|
|
608
600
|
'; ignoring.'
|
|
609
601
|
);
|
|
610
602
|
} else {
|
|
611
|
-
log.error('Failed to execute user script: ' +
|
|
603
|
+
log.error('Failed to execute user script: ' + error);
|
|
612
604
|
results[categoryName] = undefined;
|
|
613
605
|
}
|
|
614
606
|
}
|
|
@@ -639,8 +631,8 @@ class SeleniumRunner {
|
|
|
639
631
|
// the function is not null and we want to run
|
|
640
632
|
// the function.
|
|
641
633
|
if (!script) {
|
|
642
|
-
let
|
|
643
|
-
if (!
|
|
634
|
+
let function_ = category[scriptName].function;
|
|
635
|
+
if (!function_) {
|
|
644
636
|
throw (
|
|
645
637
|
'Function and script cannot both be null in ' + scriptName + '.'
|
|
646
638
|
);
|
|
@@ -651,7 +643,7 @@ class SeleniumRunner {
|
|
|
651
643
|
// invocation. The script writer cannot do this in their
|
|
652
644
|
// source because it would force the evaluation of the
|
|
653
645
|
// function too soon and cause undefined reference errors.
|
|
654
|
-
script = '( ' +
|
|
646
|
+
script = '( ' + function_ + ' )()';
|
|
655
647
|
requires = category[scriptName].requires;
|
|
656
648
|
isAsyncOverride = category[scriptName].isAsync;
|
|
657
649
|
}
|
|
@@ -676,4 +668,4 @@ class SeleniumRunner {
|
|
|
676
668
|
}
|
|
677
669
|
}
|
|
678
670
|
|
|
679
|
-
|
|
671
|
+
export default SeleniumRunner;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const firefox = require('../../firefox/webdriver/builder');
|
|
8
|
-
const safari = require('../../safari/webdriver/builder');
|
|
1
|
+
import { Builder } from 'selenium-webdriver';
|
|
2
|
+
import isEmpty from 'lodash.isempty';
|
|
3
|
+
import { configureBuilder as configureBuilderChrome } from '../../chrome/webdriver/builder.js';
|
|
4
|
+
import { configureBuilder as configureBuilderEdge } from '../../edge/webdriver/builder.js';
|
|
5
|
+
import { configureBuilder as configureBuilderFirefox } from '../../firefox/webdriver/builder.js';
|
|
6
|
+
import { configureBuilder as configureBuilderSafari } from '../../safari/webdriver/builder.js';
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* Create a new WebDriver instance based on the specified options.
|
|
@@ -13,14 +11,14 @@ const safari = require('../../safari/webdriver/builder');
|
|
|
13
11
|
* @returns {!Promise<webdriver.WebDriver>} a promise that resolves to the webdriver,
|
|
14
12
|
* or rejects if the current configuration is invalid.
|
|
15
13
|
*/
|
|
16
|
-
|
|
14
|
+
export async function createWebDriver(baseDir, options) {
|
|
17
15
|
const browser = options.browser || 'chrome';
|
|
18
16
|
const seleniumUrl = options.selenium ? options.selenium.url : undefined;
|
|
19
17
|
const capabilities = options.selenium
|
|
20
18
|
? options.selenium.capabilities
|
|
21
19
|
: undefined;
|
|
22
20
|
|
|
23
|
-
const builder = new
|
|
21
|
+
const builder = new Builder()
|
|
24
22
|
.forBrowser(browser === 'edge' ? 'MicrosoftEdge' : browser)
|
|
25
23
|
.usingServer(seleniumUrl);
|
|
26
24
|
|
|
@@ -32,25 +30,30 @@ module.exports.createWebDriver = async function (baseDir, options) {
|
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
switch (browser) {
|
|
35
|
-
case 'chrome':
|
|
36
|
-
|
|
33
|
+
case 'chrome': {
|
|
34
|
+
await configureBuilderChrome(builder, baseDir, options);
|
|
37
35
|
break;
|
|
36
|
+
}
|
|
38
37
|
|
|
39
|
-
case 'firefox':
|
|
40
|
-
|
|
38
|
+
case 'firefox': {
|
|
39
|
+
await configureBuilderFirefox(builder, baseDir, options);
|
|
41
40
|
break;
|
|
41
|
+
}
|
|
42
42
|
|
|
43
|
-
case 'safari':
|
|
44
|
-
await
|
|
43
|
+
case 'safari': {
|
|
44
|
+
await configureBuilderSafari(builder, baseDir, options);
|
|
45
45
|
break;
|
|
46
|
+
}
|
|
46
47
|
|
|
47
|
-
case 'edge':
|
|
48
|
-
|
|
48
|
+
case 'edge': {
|
|
49
|
+
await configureBuilderEdge(builder, baseDir, options);
|
|
49
50
|
break;
|
|
51
|
+
}
|
|
50
52
|
|
|
51
|
-
default:
|
|
52
|
-
|
|
53
|
+
default: {
|
|
54
|
+
throw new Error('Unsupported browser: ' + browser);
|
|
55
|
+
}
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
return builder.build();
|
|
56
|
-
}
|
|
59
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { Options, ServiceBuilder } from 'selenium-webdriver/edge.js';
|
|
2
|
+
import intel from 'intel';
|
|
3
|
+
import get from 'lodash.get';
|
|
4
|
+
import pick from 'lodash.pick';
|
|
5
|
+
import isEmpty from 'lodash.isempty';
|
|
6
|
+
import { logging } from 'selenium-webdriver';
|
|
7
|
+
import setupChromiumOptions from '../../chrome/webdriver/setupChromiumOptions.js';
|
|
8
|
+
const log = intel.getLogger('browsertime.edge');
|
|
9
|
+
const { pac, manual } = 'selenium-webdriver/proxy.js';
|
|
10
|
+
|
|
11
|
+
export async function configureBuilder(builder, baseDir, options) {
|
|
11
12
|
const edgeConfig = options.edge || {};
|
|
12
13
|
const chromeConfig = options.chrome || {};
|
|
13
14
|
|
|
@@ -15,16 +16,16 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
|
|
|
15
16
|
const edgePath = get(edgeConfig, 'binaryPath');
|
|
16
17
|
|
|
17
18
|
if (!edgedriverPath) {
|
|
18
|
-
const edgedriver =
|
|
19
|
-
edgedriverPath = edgedriver.binPath();
|
|
19
|
+
const edgedriver = await import('@sitespeed.io/edgedriver');
|
|
20
|
+
edgedriverPath = edgedriver.default.binPath();
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
let edgeOptions = new
|
|
23
|
+
let edgeOptions = new Options();
|
|
23
24
|
if (edgePath) {
|
|
24
25
|
edgeOptions.setBinaryPath(edgePath);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
const serviceBuilder = new
|
|
28
|
+
const serviceBuilder = new ServiceBuilder(edgedriverPath);
|
|
28
29
|
|
|
29
30
|
// Remove the check that matches the Edgedriver version with Edge version.
|
|
30
31
|
serviceBuilder.addArguments('--disable-build-check');
|
|
@@ -47,26 +48,20 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
|
|
|
47
48
|
const proxyPacSettings = pick(options.proxy, ['pac']);
|
|
48
49
|
|
|
49
50
|
if (!isEmpty(proxyPacSettings)) {
|
|
50
|
-
builder.setProxy(
|
|
51
|
+
builder.setProxy(pac(proxyPacSettings));
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
const proxySettings = pick(options.proxy, ['ftp', 'http', 'https', 'bypass']);
|
|
54
55
|
|
|
55
56
|
if (!isEmpty(proxySettings)) {
|
|
56
|
-
builder.setProxy(
|
|
57
|
+
builder.setProxy(manual(proxySettings));
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
let logPrefs = new
|
|
60
|
-
logPrefs.setLevel(
|
|
61
|
-
webdriver.logging.Type.PERFORMANCE,
|
|
62
|
-
webdriver.logging.Level.ALL
|
|
63
|
-
);
|
|
60
|
+
let logPrefs = new logging.Preferences();
|
|
61
|
+
logPrefs.setLevel(logging.Type.PERFORMANCE, logging.Level.ALL);
|
|
64
62
|
|
|
65
63
|
if (chromeConfig.collectConsoleLog) {
|
|
66
|
-
logPrefs.setLevel(
|
|
67
|
-
webdriver.logging.Type.BROWSER,
|
|
68
|
-
webdriver.logging.Level.ALL
|
|
69
|
-
);
|
|
64
|
+
logPrefs.setLevel(logging.Type.BROWSER, logging.Level.ALL);
|
|
70
65
|
}
|
|
71
66
|
|
|
72
67
|
// edgeOptions.setLoggingPrefs(logPrefs);
|
|
@@ -79,4 +74,4 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
|
|
|
79
74
|
setupChromiumOptions(edgeOptions, chromeConfig, options, baseDir);
|
|
80
75
|
|
|
81
76
|
builder.setEdgeOptions(edgeOptions);
|
|
82
|
-
}
|
|
77
|
+
}
|
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
import { promisify } from 'node:util';
|
|
2
|
+
import { createServer } from 'node:http';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const server = http.createServer((req, res) => {
|
|
9
|
-
res.writeHead(200, {
|
|
10
|
-
'Content-Type': 'text/html',
|
|
11
|
-
'Referrer-Policy': 'no-referrer'
|
|
12
|
-
});
|
|
13
|
-
res.end('<html><body></body></html>');
|
|
4
|
+
export async function startServer() {
|
|
5
|
+
const server = createServer((request, res) => {
|
|
6
|
+
res.writeHead(200, {
|
|
7
|
+
'Content-Type': 'text/html',
|
|
8
|
+
'Referrer-Policy': 'no-referrer'
|
|
14
9
|
});
|
|
10
|
+
res.end('<html><body></body></html>');
|
|
11
|
+
});
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
13
|
+
const listen = promisify(server.listen.bind(server));
|
|
14
|
+
await listen();
|
|
15
|
+
return server;
|
|
16
|
+
}
|
|
17
|
+
export async function stopServer(server) {
|
|
18
|
+
const close = promisify(server.close.bind(server));
|
|
19
|
+
return close();
|
|
20
|
+
}
|