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
package/lib/support/cli.js
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
import { format } from 'node:util';
|
|
2
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
3
|
+
import yargs from 'yargs';
|
|
4
|
+
import { hideBin } from 'yargs/helpers';
|
|
5
|
+
import { findUpSync } from 'find-up';
|
|
6
|
+
import get from 'lodash.get';
|
|
7
|
+
import set from 'lodash.set';
|
|
8
|
+
import hasbin from 'hasbin';
|
|
9
|
+
import {
|
|
10
|
+
framerate,
|
|
11
|
+
crf,
|
|
12
|
+
addTimer,
|
|
13
|
+
threads,
|
|
14
|
+
xvfbDisplay
|
|
15
|
+
} from '../video/defaults.js';
|
|
16
|
+
import SCREENSHOT from '../screenshot/defaults.js';
|
|
17
|
+
import {
|
|
18
|
+
features,
|
|
19
|
+
threads as _threads,
|
|
20
|
+
desktop_sampling_interval,
|
|
21
|
+
android_sampling_interval,
|
|
22
|
+
bufferSize
|
|
23
|
+
} from '../firefox/settings/geckoProfilerDefaults.js';
|
|
2
24
|
|
|
3
|
-
const
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const findUp = require('find-up');
|
|
6
|
-
const get = require('lodash.get');
|
|
7
|
-
const set = require('lodash.set');
|
|
8
|
-
const util = require('util');
|
|
9
|
-
const hasbin = require('hasbin');
|
|
10
|
-
const videoDefaults = require('../video/defaults');
|
|
11
|
-
const screenshotDefaults = require('../screenshot/defaults');
|
|
12
|
-
const geckoProfilerDefaults = require('../firefox/settings/geckoProfilerDefaults');
|
|
25
|
+
const configPath = findUpSync(['.browsertime.json']);
|
|
13
26
|
|
|
14
|
-
const configPath = findUp.sync(['.browsertime.json']);
|
|
15
27
|
let config;
|
|
16
|
-
|
|
17
28
|
try {
|
|
18
|
-
config = configPath ? JSON.parse(
|
|
19
|
-
} catch (
|
|
20
|
-
if (
|
|
29
|
+
config = configPath ? JSON.parse(readFileSync(configPath)) : {};
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error instanceof SyntaxError) {
|
|
21
32
|
/* eslint no-console: off */
|
|
22
33
|
console.error(
|
|
23
34
|
'Could not parse the config JSON file ' +
|
|
@@ -25,13 +36,13 @@ try {
|
|
|
25
36
|
'. Is the file really valid JSON?'
|
|
26
37
|
);
|
|
27
38
|
}
|
|
28
|
-
throw
|
|
39
|
+
throw error;
|
|
29
40
|
}
|
|
30
41
|
|
|
31
42
|
function validateInput(argv) {
|
|
32
43
|
// Check NodeJS major version
|
|
33
44
|
const fullVersion = process.versions.node;
|
|
34
|
-
const minVersion =
|
|
45
|
+
const minVersion = 14;
|
|
35
46
|
const majorVersion = fullVersion.split('.')[0];
|
|
36
47
|
if (majorVersion < minVersion) {
|
|
37
48
|
return (
|
|
@@ -48,9 +59,9 @@ function validateInput(argv) {
|
|
|
48
59
|
if (!urlOrFile.startsWith('http')) {
|
|
49
60
|
// is existing file?
|
|
50
61
|
try {
|
|
51
|
-
|
|
52
|
-
} catch
|
|
53
|
-
return
|
|
62
|
+
statSync(urlOrFile);
|
|
63
|
+
} catch {
|
|
64
|
+
return format(
|
|
54
65
|
"'%s' does not exist, is the path to the file correct?",
|
|
55
66
|
urlOrFile
|
|
56
67
|
);
|
|
@@ -65,10 +76,8 @@ function validateInput(argv) {
|
|
|
65
76
|
}
|
|
66
77
|
}
|
|
67
78
|
|
|
68
|
-
if (argv.video || argv.visualMetrics) {
|
|
69
|
-
|
|
70
|
-
return 'You need to have ffmpeg in your path to be able to record a video.';
|
|
71
|
-
}
|
|
79
|
+
if ((argv.video || argv.visualMetrics) && !hasbin.sync(['ffmpeg'])) {
|
|
80
|
+
return 'You need to have ffmpeg in your path to be able to record a video.';
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
if (argv.safari && argv.safari.useSimulator && !argv.safari.deviceUDID) {
|
|
@@ -87,22 +96,20 @@ function validateInput(argv) {
|
|
|
87
96
|
return 'There is no benefit running debug mode inside a Docker container.';
|
|
88
97
|
}
|
|
89
98
|
|
|
90
|
-
if (argv.tcpdump) {
|
|
91
|
-
|
|
92
|
-
return 'You need to have tcpdump in your path to be able to record a tcpdump.';
|
|
93
|
-
}
|
|
99
|
+
if (argv.tcpdump && !hasbin.sync(['tcpdump'])) {
|
|
100
|
+
return 'You need to have tcpdump in your path to be able to record a tcpdump.';
|
|
94
101
|
}
|
|
95
102
|
|
|
96
|
-
if (argv.gnirehtet) {
|
|
97
|
-
|
|
98
|
-
return 'You need to have gnirehtet in your path to be able to use it. If you run on a Mac you can install it using Homebrew: brew install gnirehtet';
|
|
99
|
-
}
|
|
103
|
+
if (argv.gnirehtet && !hasbin.sync(['gnirehtet'])) {
|
|
104
|
+
return 'You need to have gnirehtet in your path to be able to use it. If you run on a Mac you can install it using Homebrew: brew install gnirehtet';
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
if (
|
|
108
|
+
argv.connectivity &&
|
|
109
|
+
argv.connectivity.engine === 'humble' &&
|
|
110
|
+
(!argv.connectivity.humble || !argv.connectivity.humble.url)
|
|
111
|
+
) {
|
|
112
|
+
return 'You need to specify the URL to Humble by using the --connectivity.humble.url option.';
|
|
106
113
|
}
|
|
107
114
|
|
|
108
115
|
if (
|
|
@@ -137,33 +144,34 @@ function validateInput(argv) {
|
|
|
137
144
|
return true;
|
|
138
145
|
}
|
|
139
146
|
|
|
140
|
-
|
|
141
|
-
let
|
|
147
|
+
export function parseCommandLine() {
|
|
148
|
+
let yargsInstance = yargs(hideBin(process.argv));
|
|
149
|
+
let validated = yargsInstance
|
|
142
150
|
.parserConfiguration({ 'deep-merge-config': true })
|
|
143
151
|
.env('BROWSERTIME')
|
|
144
152
|
.usage('$0 [options] <url>/<scriptFile>')
|
|
145
153
|
.require(1, 'One or more url or script files')
|
|
146
154
|
.option('timeouts.browserStart', {
|
|
147
|
-
default:
|
|
155
|
+
default: 60_000,
|
|
148
156
|
type: 'number',
|
|
149
157
|
describe: 'Timeout when waiting for browser to start, in milliseconds',
|
|
150
158
|
group: 'timeouts'
|
|
151
159
|
})
|
|
152
160
|
.option('timeouts.pageLoad', {
|
|
153
|
-
default:
|
|
161
|
+
default: 300_000,
|
|
154
162
|
type: 'number',
|
|
155
163
|
describe: 'Timeout when waiting for url to load, in milliseconds',
|
|
156
164
|
group: 'timeouts'
|
|
157
165
|
})
|
|
158
166
|
.option('timeouts.script', {
|
|
159
|
-
default:
|
|
167
|
+
default: 120_000,
|
|
160
168
|
type: 'number',
|
|
161
169
|
describe: 'Timeout when running browser scripts, in milliseconds',
|
|
162
170
|
group: 'timeouts'
|
|
163
171
|
})
|
|
164
172
|
.option('timeouts.pageCompleteCheck', {
|
|
165
173
|
alias: 'maxLoadTime',
|
|
166
|
-
default:
|
|
174
|
+
default: 120_000,
|
|
167
175
|
type: 'number',
|
|
168
176
|
describe:
|
|
169
177
|
'Timeout when waiting for page to complete loading, in milliseconds',
|
|
@@ -311,11 +319,13 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
311
319
|
describe:
|
|
312
320
|
'Easy way to enable both chrome.timeline for Chrome and geckoProfile for Firefox'
|
|
313
321
|
})
|
|
314
|
-
.option('
|
|
322
|
+
.option('android.powerTesting', {
|
|
323
|
+
alias: 'androidPower',
|
|
315
324
|
type: 'boolean',
|
|
316
325
|
describe:
|
|
317
326
|
'Enables android power testing - charging must be disabled for this.' +
|
|
318
|
-
'(You have to disable charging yourself for this - it depends on the phone model).'
|
|
327
|
+
'(You have to disable charging yourself for this - it depends on the phone model).',
|
|
328
|
+
group: 'android'
|
|
319
329
|
})
|
|
320
330
|
.option('chrome.CPUThrottlingRate', {
|
|
321
331
|
type: 'number',
|
|
@@ -458,24 +468,24 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
458
468
|
})
|
|
459
469
|
.option('firefox.geckoProfilerParams.features', {
|
|
460
470
|
describe: 'Enabled features during gecko profiling',
|
|
461
|
-
default:
|
|
471
|
+
default: features,
|
|
462
472
|
type: 'string',
|
|
463
473
|
group: 'firefox'
|
|
464
474
|
})
|
|
465
475
|
.option('firefox.geckoProfilerParams.threads', {
|
|
466
476
|
describe: 'Threads to profile.',
|
|
467
|
-
default:
|
|
477
|
+
default: _threads,
|
|
468
478
|
type: 'string',
|
|
469
479
|
group: 'firefox'
|
|
470
480
|
})
|
|
471
481
|
.option('firefox.geckoProfilerParams.interval', {
|
|
472
|
-
describe: `Sampling interval in ms. Defaults to ${
|
|
482
|
+
describe: `Sampling interval in ms. Defaults to ${desktop_sampling_interval} on desktop, and ${android_sampling_interval} on android.`,
|
|
473
483
|
type: 'number',
|
|
474
484
|
group: 'firefox'
|
|
475
485
|
})
|
|
476
486
|
.option('firefox.geckoProfilerParams.bufferSize', {
|
|
477
487
|
describe: 'Buffer size in elements. Default is ~90MB.',
|
|
478
|
-
default:
|
|
488
|
+
default: bufferSize,
|
|
479
489
|
type: 'number',
|
|
480
490
|
group: 'firefox'
|
|
481
491
|
})
|
|
@@ -488,7 +498,8 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
488
498
|
})
|
|
489
499
|
.option('firefox.perfStatsParams.mask', {
|
|
490
500
|
describe: 'Mask to decide which features to enable',
|
|
491
|
-
|
|
501
|
+
// eslint-disable-next-line prettier/prettier
|
|
502
|
+
default: 0xFF_FF_FF_FF,
|
|
492
503
|
type: 'number',
|
|
493
504
|
group: 'firefox'
|
|
494
505
|
})
|
|
@@ -567,19 +578,19 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
567
578
|
'Record a video and store the video. Set it to false to remove the video that is created by turning on visualMetrics. To remove fully turn off video recordings, make sure to set video and visualMetrics to false. Requires FFMpeg to be installed.'
|
|
568
579
|
})
|
|
569
580
|
.option('videoParams.framerate', {
|
|
570
|
-
default:
|
|
581
|
+
default: framerate,
|
|
571
582
|
describe: 'Frames per second',
|
|
572
583
|
group: 'video'
|
|
573
584
|
})
|
|
574
585
|
.option('videoParams.crf', {
|
|
575
|
-
default:
|
|
586
|
+
default: crf,
|
|
576
587
|
describe:
|
|
577
588
|
'Constant rate factor see https://trac.ffmpeg.org/wiki/Encode/H.264#crf',
|
|
578
589
|
group: 'video'
|
|
579
590
|
})
|
|
580
591
|
.option('videoParams.addTimer', {
|
|
581
592
|
type: 'boolean',
|
|
582
|
-
default:
|
|
593
|
+
default: addTimer,
|
|
583
594
|
describe: 'Add timer and metrics to the video.',
|
|
584
595
|
group: 'video'
|
|
585
596
|
})
|
|
@@ -635,7 +646,7 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
635
646
|
group: 'video'
|
|
636
647
|
})
|
|
637
648
|
.option('videoParams.threads', {
|
|
638
|
-
default:
|
|
649
|
+
default: threads,
|
|
639
650
|
describe:
|
|
640
651
|
'Number of threads to use for video recording. Default is determined by ffmpeg.',
|
|
641
652
|
group: 'video'
|
|
@@ -661,6 +672,7 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
661
672
|
})
|
|
662
673
|
.option('visualMetricsPortable', {
|
|
663
674
|
type: 'boolean',
|
|
675
|
+
default: true,
|
|
664
676
|
describe:
|
|
665
677
|
'Use the portable visual-metrics processing script (no ImageMagick dependencies).'
|
|
666
678
|
})
|
|
@@ -682,10 +694,12 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
682
694
|
describe:
|
|
683
695
|
'Specify browser. Safari only works on OS X/iOS. Edge only work on OS that supports Edge.'
|
|
684
696
|
})
|
|
685
|
-
.option('ignoreShutdownFailures', {
|
|
697
|
+
.option('android.ignoreShutdownFailures', {
|
|
698
|
+
alias: 'ignoreShutdownFailures',
|
|
686
699
|
type: 'boolean',
|
|
687
700
|
default: false,
|
|
688
|
-
describe: 'If set, shutdown failures will be ignored on Android.'
|
|
701
|
+
describe: 'If set, shutdown failures will be ignored on Android.',
|
|
702
|
+
group: 'android'
|
|
689
703
|
})
|
|
690
704
|
.option('android', {
|
|
691
705
|
type: 'boolean',
|
|
@@ -693,44 +707,58 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
693
707
|
describe:
|
|
694
708
|
'Short key to use Android. Defaults to use com.android.chrome unless --browser is specified.'
|
|
695
709
|
})
|
|
696
|
-
.option('
|
|
710
|
+
.option('android.rooted', {
|
|
697
711
|
type: 'boolean',
|
|
712
|
+
alias: 'androidRooted',
|
|
698
713
|
default: false,
|
|
699
714
|
describe:
|
|
700
|
-
'If your phone is rooted you can use this to set it up following Mozillas best practice for stable metrics.'
|
|
715
|
+
'If your phone is rooted you can use this to set it up following Mozillas best practice for stable metrics.',
|
|
716
|
+
group: 'android'
|
|
701
717
|
})
|
|
702
|
-
.option('
|
|
718
|
+
.option('android.batteryTemperatureLimit', {
|
|
719
|
+
alias: 'androidBatteryTemperatureLimit',
|
|
703
720
|
type: 'integer',
|
|
704
721
|
describe:
|
|
705
|
-
'Do the battery temperature need to be below a specific limit before we start the test?'
|
|
722
|
+
'Do the battery temperature need to be below a specific limit before we start the test?',
|
|
723
|
+
group: 'android'
|
|
706
724
|
})
|
|
707
|
-
.option('
|
|
725
|
+
.option('android.batteryTemperatureWaitTimeInSeconds', {
|
|
726
|
+
alias: 'androidBatteryTemperatureWaitTimeInSeconds',
|
|
708
727
|
type: 'integer',
|
|
709
728
|
default: 120,
|
|
710
729
|
describe:
|
|
711
|
-
'How long time to wait (in seconds) if the androidBatteryTemperatureWaitTimeInSeconds is not met before the next try'
|
|
730
|
+
'How long time to wait (in seconds) if the androidBatteryTemperatureWaitTimeInSeconds is not met before the next try',
|
|
731
|
+
group: 'android'
|
|
712
732
|
})
|
|
713
|
-
.option('
|
|
733
|
+
.option('android.batteryTemperatureReboot', {
|
|
734
|
+
alias: 'androidBatteryTemperatureReboot',
|
|
714
735
|
type: 'boolean',
|
|
715
736
|
default: false,
|
|
716
737
|
describe:
|
|
717
|
-
'If your phone does not get the minimum temperature aftet the wait time, reboot the phone.'
|
|
738
|
+
'If your phone does not get the minimum temperature aftet the wait time, reboot the phone.',
|
|
739
|
+
group: 'android'
|
|
718
740
|
})
|
|
719
|
-
.option('
|
|
741
|
+
.option('android.pretestPowerPress', {
|
|
742
|
+
alias: 'androidPretestPowerPress',
|
|
720
743
|
type: 'boolean',
|
|
721
744
|
default: false,
|
|
722
|
-
describe: 'Press the power button on the phone before a test starts.'
|
|
745
|
+
describe: 'Press the power button on the phone before a test starts.',
|
|
746
|
+
group: 'android'
|
|
723
747
|
})
|
|
724
|
-
.option('
|
|
748
|
+
.option('android.pretestPressHomeButton', {
|
|
749
|
+
alias: 'androidPretestPressHomeButton',
|
|
725
750
|
type: 'boolean',
|
|
726
751
|
default: false,
|
|
727
|
-
describe: 'Press the home button on the phone before a test starts.'
|
|
752
|
+
describe: 'Press the home button on the phone before a test starts.',
|
|
753
|
+
group: 'android'
|
|
728
754
|
})
|
|
729
|
-
.option('
|
|
755
|
+
.option('android.verifyNetwork', {
|
|
756
|
+
alias: 'androidVerifyNetwork',
|
|
730
757
|
type: 'boolean',
|
|
731
758
|
default: false,
|
|
732
759
|
describe:
|
|
733
|
-
'Before a test start, verify that the device has a Internet connection by pinging 8.8.8.8 (or a configurable domain with --androidPingAddress)'
|
|
760
|
+
'Before a test start, verify that the device has a Internet connection by pinging 8.8.8.8 (or a configurable domain with --androidPingAddress)',
|
|
761
|
+
group: 'android'
|
|
734
762
|
})
|
|
735
763
|
/** Process start time. Android-only for now. */
|
|
736
764
|
.option('processStartTime', {
|
|
@@ -812,22 +840,22 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
812
840
|
.option('screenshotParams.type', {
|
|
813
841
|
describe: 'Set the file type of the screenshot',
|
|
814
842
|
choices: ['png', 'jpg'],
|
|
815
|
-
default:
|
|
843
|
+
default: SCREENSHOT.type,
|
|
816
844
|
group: 'Screenshot'
|
|
817
845
|
})
|
|
818
846
|
.option('screenshotParams.png.compressionLevel', {
|
|
819
847
|
describe: 'zlib compression level',
|
|
820
|
-
default:
|
|
848
|
+
default: SCREENSHOT.png.compressionLevel,
|
|
821
849
|
group: 'Screenshot'
|
|
822
850
|
})
|
|
823
851
|
.option('screenshotParams.jpg.quality', {
|
|
824
852
|
describe: 'Quality of the JPEG screenshot. 1-100',
|
|
825
|
-
default:
|
|
853
|
+
default: SCREENSHOT.jpg.quality,
|
|
826
854
|
group: 'Screenshot'
|
|
827
855
|
})
|
|
828
856
|
.option('screenshotParams.maxSize', {
|
|
829
857
|
describe: 'The max size of the screenshot (width and height).',
|
|
830
|
-
default:
|
|
858
|
+
default: SCREENSHOT.maxSize,
|
|
831
859
|
group: 'Screenshot'
|
|
832
860
|
})
|
|
833
861
|
.option('pageCompleteCheck', {
|
|
@@ -1101,7 +1129,7 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
1101
1129
|
describe: 'Start xvfb before the browser is started'
|
|
1102
1130
|
})
|
|
1103
1131
|
.option('xvfbParams.display', {
|
|
1104
|
-
default:
|
|
1132
|
+
default: xvfbDisplay,
|
|
1105
1133
|
describe: 'The display used for xvfb'
|
|
1106
1134
|
})
|
|
1107
1135
|
.option('tcpdump', {
|
|
@@ -1142,11 +1170,13 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
1142
1170
|
describe:
|
|
1143
1171
|
'Run the browser in headless mode. Works for Firefox and Chrome.'
|
|
1144
1172
|
})
|
|
1145
|
-
.option('gnirehtet', {
|
|
1173
|
+
.option('android.gnirehtet', {
|
|
1174
|
+
alias: 'gnirehtet',
|
|
1146
1175
|
type: 'boolean',
|
|
1147
1176
|
default: false,
|
|
1148
1177
|
describe:
|
|
1149
|
-
'Start gnirehtet and reverse tethering the traffic from your Android phone.'
|
|
1178
|
+
'Start gnirehtet and reverse tethering the traffic from your Android phone.',
|
|
1179
|
+
group: 'android'
|
|
1150
1180
|
})
|
|
1151
1181
|
.option('flushDNS', {
|
|
1152
1182
|
type: 'boolean',
|
|
@@ -1196,18 +1226,21 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
1196
1226
|
.alias('h', 'help')
|
|
1197
1227
|
.alias('V', 'version')
|
|
1198
1228
|
.alias('v', 'verbose')
|
|
1229
|
+
.wrap(yargsInstance.terminalWidth())
|
|
1199
1230
|
.check(validateInput);
|
|
1200
1231
|
|
|
1201
|
-
validated.wrap(yargs.terminalWidth());
|
|
1202
|
-
|
|
1203
1232
|
let argv = validated.argv;
|
|
1204
1233
|
|
|
1234
|
+
// hack to keep backward compability to --android
|
|
1235
|
+
if (argv.android[0] === true) {
|
|
1236
|
+
set(argv, 'android.enabled', true);
|
|
1237
|
+
}
|
|
1205
1238
|
if (
|
|
1206
1239
|
argv.firefox &&
|
|
1207
1240
|
(argv.firefox.nightly || argv.firefox.beta || argv.firefox.developer)
|
|
1208
1241
|
) {
|
|
1209
1242
|
argv.browser = 'firefox';
|
|
1210
|
-
if (argv.android) {
|
|
1243
|
+
if (argv.android.enabled) {
|
|
1211
1244
|
// TODO add support for Firefox dev
|
|
1212
1245
|
set(
|
|
1213
1246
|
argv,
|
|
@@ -1225,7 +1258,7 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
1225
1258
|
|
|
1226
1259
|
if (argv.ios) {
|
|
1227
1260
|
set(argv, 'safari.ios', true);
|
|
1228
|
-
} else if (argv.android) {
|
|
1261
|
+
} else if (argv.android.enabled) {
|
|
1229
1262
|
if (argv.browser === 'chrome') {
|
|
1230
1263
|
// Default to Chrome Android.
|
|
1231
1264
|
set(
|
|
@@ -1262,8 +1295,8 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
1262
1295
|
|
|
1263
1296
|
if (!Array.isArray(argv.urlAlias)) argv.urlAlias = [argv.urlAlias];
|
|
1264
1297
|
|
|
1265
|
-
for (
|
|
1266
|
-
urlMetaData[
|
|
1298
|
+
for (const [index, url] of urls.entries()) {
|
|
1299
|
+
urlMetaData[url] = argv.urlAlias[index];
|
|
1267
1300
|
}
|
|
1268
1301
|
set(argv, 'urlMetaData', urlMetaData);
|
|
1269
1302
|
}
|
|
@@ -1284,13 +1317,13 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
1284
1317
|
|
|
1285
1318
|
// Set the timeouts to a maximum while debugging
|
|
1286
1319
|
if (argv.debug) {
|
|
1287
|
-
set(argv, 'timeouts.pageload',
|
|
1288
|
-
set(argv, 'timeouts.script',
|
|
1289
|
-
set(argv, 'timeouts.pageCompleteCheck',
|
|
1320
|
+
set(argv, 'timeouts.pageload', 2_147_483_647);
|
|
1321
|
+
set(argv, 'timeouts.script', 2_147_483_647);
|
|
1322
|
+
set(argv, 'timeouts.pageCompleteCheck', 2_147_483_647);
|
|
1290
1323
|
}
|
|
1291
1324
|
|
|
1292
1325
|
return {
|
|
1293
1326
|
urls: argv._,
|
|
1294
1327
|
options: argv
|
|
1295
1328
|
};
|
|
1296
|
-
}
|
|
1329
|
+
}
|
package/lib/support/dns.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import intel from 'intel';
|
|
3
|
+
import { isAndroidConfigured } from '../android/index.js';
|
|
4
|
+
const log = intel.getLogger('browsertime.dns');
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
const log = require('intel').getLogger('browsertime.dns');
|
|
5
|
-
const { isAndroidConfigured } = require('../android');
|
|
6
|
-
|
|
7
|
-
module.exports = async function (options) {
|
|
6
|
+
export default async function (options) {
|
|
8
7
|
if (isAndroidConfigured(options)) {
|
|
9
8
|
return;
|
|
10
9
|
}
|
|
@@ -36,4 +35,4 @@ module.exports = async function (options) {
|
|
|
36
35
|
reject: false
|
|
37
36
|
});
|
|
38
37
|
}
|
|
39
|
-
}
|
|
38
|
+
}
|
|
@@ -1,46 +1,93 @@
|
|
|
1
|
-
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import dayjs from 'dayjs';
|
|
4
|
+
import intel from 'intel';
|
|
5
|
+
import { toArray } from '../support/util.js';
|
|
6
|
+
import {
|
|
7
|
+
removeFile,
|
|
8
|
+
copyFile,
|
|
9
|
+
removeFileSync,
|
|
10
|
+
copyFileSync
|
|
11
|
+
} from './fileUtil.js';
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const log = intel.getLogger('browsertime');
|
|
2
14
|
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const dayjs = require('dayjs');
|
|
5
|
-
const util = require('../support/util');
|
|
6
|
-
const log = require('intel').getLogger('browsertime');
|
|
7
15
|
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
|
|
8
16
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
export function loadPrePostScriptsSync(scripts) {
|
|
18
|
+
return toArray(scripts).map(script => {
|
|
19
|
+
// if the script is an AsyncFunction, return it.
|
|
20
|
+
if (script instanceof AsyncFunction) {
|
|
21
|
+
return script;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
if (script.endsWith('.js')) {
|
|
25
|
+
const name = script + '.cjs';
|
|
26
|
+
copyFileSync(script, name);
|
|
27
|
+
const data = require(resolve(name));
|
|
28
|
+
removeFileSync(name);
|
|
29
|
+
return data;
|
|
30
|
+
} else {
|
|
31
|
+
return require(resolve(script));
|
|
22
32
|
}
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
} catch (error) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"Couldn't run pre/post script file: " + resolve(script) + ' ' + error
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function loadScriptSync(script) {
|
|
42
|
+
if (script instanceof AsyncFunction) {
|
|
43
|
+
return script;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
if (script.endsWith('.js')) {
|
|
47
|
+
const name = script + '.cjs';
|
|
48
|
+
copyFileSync(script, name);
|
|
49
|
+
const data = require(resolve(name));
|
|
50
|
+
removeFileSync(name);
|
|
51
|
+
return data;
|
|
52
|
+
} else {
|
|
53
|
+
return require(resolve(script));
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// assume the script is a valid inline script by default
|
|
57
|
+
return script;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function loadScript(script, throwError) {
|
|
62
|
+
if (script) {
|
|
63
|
+
// if the script is an AsyncFunction, return it.
|
|
64
|
+
if (script instanceof AsyncFunction) {
|
|
65
|
+
return script;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
if (script.endsWith('.js')) {
|
|
69
|
+
const name = script + '.cjs';
|
|
70
|
+
await copyFile(script, name);
|
|
71
|
+
const data = require(resolve(name));
|
|
72
|
+
await removeFile(name);
|
|
73
|
+
return data;
|
|
74
|
+
} else {
|
|
75
|
+
return require(resolve(script));
|
|
30
76
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
77
|
+
} catch (error) {
|
|
78
|
+
// assume the script is a valid inline script by default
|
|
79
|
+
if (throwError) {
|
|
80
|
+
log.error(
|
|
81
|
+
'Could not parse user script %s with error %s',
|
|
82
|
+
script,
|
|
83
|
+
error
|
|
84
|
+
);
|
|
85
|
+
throw error;
|
|
39
86
|
}
|
|
40
87
|
}
|
|
41
|
-
return script;
|
|
42
|
-
},
|
|
43
|
-
timestamp() {
|
|
44
|
-
return dayjs().format();
|
|
45
88
|
}
|
|
46
|
-
|
|
89
|
+
return script;
|
|
90
|
+
}
|
|
91
|
+
export function timestamp() {
|
|
92
|
+
return dayjs().format();
|
|
93
|
+
}
|
package/lib/support/errors.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
class BrowsertimeError extends Error {
|
|
1
|
+
export class BrowsertimeError extends Error {
|
|
4
2
|
constructor(message, extra) {
|
|
5
3
|
super(message);
|
|
6
4
|
this.extra = extra || {};
|
|
@@ -8,29 +6,22 @@ class BrowsertimeError extends Error {
|
|
|
8
6
|
}
|
|
9
7
|
}
|
|
10
8
|
|
|
11
|
-
class BrowserError extends BrowsertimeError {
|
|
9
|
+
export class BrowserError extends BrowsertimeError {
|
|
12
10
|
constructor(message, extra) {
|
|
13
11
|
super(message, extra);
|
|
14
12
|
}
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
class UrlLoadError extends BrowsertimeError {
|
|
15
|
+
export class UrlLoadError extends BrowsertimeError {
|
|
18
16
|
constructor(message, url, extra) {
|
|
19
17
|
super(message, extra);
|
|
20
18
|
this.url = url;
|
|
21
19
|
}
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
class TimeoutError extends BrowsertimeError {
|
|
22
|
+
export class TimeoutError extends BrowsertimeError {
|
|
25
23
|
constructor(message, url, extra) {
|
|
26
24
|
super(message, extra);
|
|
27
25
|
this.url = url;
|
|
28
26
|
}
|
|
29
27
|
}
|
|
30
|
-
|
|
31
|
-
module.exports = {
|
|
32
|
-
BrowsertimeError,
|
|
33
|
-
BrowserError,
|
|
34
|
-
UrlLoadError,
|
|
35
|
-
TimeoutError
|
|
36
|
-
};
|