browsertime 16.17.0 → 17.0.0-beta.1

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.
Files changed (121) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/bin/browsertime.js +43 -43
  3. package/bin/browsertimeWebPageReplay.js +10 -9
  4. package/browserscripts/pageinfo/generator.js +1 -1
  5. package/index.js +14 -13
  6. package/lib/android/gnirehtet.js +14 -15
  7. package/lib/android/index.js +92 -89
  8. package/lib/android/root.js +6 -9
  9. package/lib/chrome/chromeDevtoolsProtocol.js +46 -45
  10. package/lib/chrome/har.js +12 -13
  11. package/lib/chrome/longTaskMetrics.js +3 -5
  12. package/lib/chrome/parseCpuTrace.js +13 -10
  13. package/lib/chrome/settings/chromeAndroidOptions.js +1 -3
  14. package/lib/chrome/settings/chromeDesktopOptions.js +1 -3
  15. package/lib/chrome/settings/traceCategories.js +1 -3
  16. package/lib/chrome/traceCategoriesParser.js +35 -36
  17. package/lib/chrome/webdriver/builder.js +18 -26
  18. package/lib/chrome/webdriver/chromium.js +56 -46
  19. package/lib/chrome/webdriver/setupChromiumOptions.js +32 -27
  20. package/lib/connectivity/humble.js +13 -14
  21. package/lib/connectivity/index.js +74 -77
  22. package/lib/connectivity/trafficShapeParser.js +31 -35
  23. package/lib/core/engine/collector.js +22 -22
  24. package/lib/core/engine/command/addText.js +24 -27
  25. package/lib/core/engine/command/android.js +7 -11
  26. package/lib/core/engine/command/cache.js +3 -4
  27. package/lib/core/engine/command/chromeDevToolsProtocol.js +20 -20
  28. package/lib/core/engine/command/click.js +48 -49
  29. package/lib/core/engine/command/debug.js +3 -4
  30. package/lib/core/engine/command/javaScript.js +18 -19
  31. package/lib/core/engine/command/measure.js +31 -37
  32. package/lib/core/engine/command/meta.js +1 -4
  33. package/lib/core/engine/command/mouse/clickAndHold.js +36 -39
  34. package/lib/core/engine/command/mouse/contextClick.js +17 -18
  35. package/lib/core/engine/command/mouse/doubleClick.js +17 -19
  36. package/lib/core/engine/command/mouse/index.js +5 -9
  37. package/lib/core/engine/command/mouse/mouseMove.js +19 -23
  38. package/lib/core/engine/command/mouse/singleClick.js +15 -19
  39. package/lib/core/engine/command/navigation.js +12 -14
  40. package/lib/core/engine/command/screenshot.js +1 -4
  41. package/lib/core/engine/command/scroll.js +4 -7
  42. package/lib/core/engine/command/select.js +26 -26
  43. package/lib/core/engine/command/set.js +25 -23
  44. package/lib/core/engine/command/stopWatch.js +5 -8
  45. package/lib/core/engine/command/switch.js +18 -20
  46. package/lib/core/engine/command/util/lcpHighlightScript.js +1 -2
  47. package/lib/core/engine/command/util/lsHighlightScript.js +1 -2
  48. package/lib/core/engine/command/wait.js +18 -18
  49. package/lib/core/engine/index.js +82 -72
  50. package/lib/core/engine/iteration.js +83 -79
  51. package/lib/core/engine/run.js +5 -9
  52. package/lib/core/pageCompleteChecks/defaultPageCompleteCheck.js +1 -2
  53. package/lib/core/pageCompleteChecks/pageCompleteCheckByInactivity.js +1 -3
  54. package/lib/core/pageCompleteChecks/spaInactivity.js +1 -3
  55. package/lib/core/seleniumRunner.js +92 -100
  56. package/lib/core/webdriver/index.js +24 -21
  57. package/lib/edge/webdriver/builder.js +21 -26
  58. package/lib/extensionserver/httpServer.js +17 -21
  59. package/lib/extensionserver/index.js +8 -12
  60. package/lib/extensionserver/setup.js +12 -12
  61. package/lib/firefox/geckoProfiler.js +27 -21
  62. package/lib/firefox/getHAR.js +7 -8
  63. package/lib/firefox/memoryReport.js +9 -11
  64. package/lib/firefox/perfStats.js +5 -7
  65. package/lib/firefox/settings/disableSafeBrowsingPreferences.js +1 -3
  66. package/lib/firefox/settings/disableTrackingProtectionPreferences.js +1 -3
  67. package/lib/firefox/settings/firefoxPreferences.js +2 -4
  68. package/lib/firefox/settings/geckoProfilerDefaults.js +5 -9
  69. package/lib/firefox/webdriver/builder.js +66 -63
  70. package/lib/firefox/webdriver/firefox.js +44 -49
  71. package/lib/safari/webdriver/builder.js +4 -5
  72. package/lib/safari/webdriver/safari.js +4 -8
  73. package/lib/screenshot/defaults.js +1 -3
  74. package/lib/screenshot/index.js +24 -21
  75. package/lib/support/browserScript.js +65 -56
  76. package/lib/support/cli.js +120 -87
  77. package/lib/support/dns.js +6 -7
  78. package/lib/support/engineUtils.js +85 -38
  79. package/lib/support/errors.js +4 -13
  80. package/lib/support/fileUtil.js +81 -68
  81. package/lib/support/filters.js +15 -20
  82. package/lib/support/getViewPort.js +6 -7
  83. package/lib/support/har/index.js +208 -216
  84. package/lib/support/images/index.js +54 -26
  85. package/lib/support/logging.js +20 -17
  86. package/lib/support/pathToFolder.js +18 -23
  87. package/lib/support/preURL.js +5 -6
  88. package/lib/support/processes.js +4 -6
  89. package/lib/support/setResourceTimingBufferSize.js +2 -4
  90. package/lib/support/statistics.js +58 -56
  91. package/lib/support/stop.js +7 -8
  92. package/lib/support/storageManager.js +44 -44
  93. package/lib/support/tcpdump.js +22 -16
  94. package/lib/support/userTiming.js +7 -13
  95. package/lib/support/util.js +204 -224
  96. package/lib/support/xvfb.js +11 -13
  97. package/lib/video/defaults.js +6 -10
  98. package/lib/video/postprocessing/finetune/addTextToVideo.js +15 -15
  99. package/lib/video/postprocessing/finetune/convertFps.js +14 -8
  100. package/lib/video/postprocessing/finetune/getFont.js +7 -9
  101. package/lib/video/postprocessing/finetune/getTimingMetrics.js +19 -18
  102. package/lib/video/postprocessing/finetune/index.js +40 -36
  103. package/lib/video/postprocessing/finetune/removeOrange.js +10 -11
  104. package/lib/video/postprocessing/visualmetrics/extraMetrics.js +17 -13
  105. package/lib/video/postprocessing/visualmetrics/getVideoMetrics.js +12 -13
  106. package/lib/video/postprocessing/visualmetrics/visualMetrics.js +117 -128
  107. package/lib/video/screenRecording/android/recorder.js +12 -17
  108. package/lib/video/screenRecording/desktop/convert.js +13 -11
  109. package/lib/video/screenRecording/desktop/desktopRecorder.js +29 -24
  110. package/lib/video/screenRecording/desktop/ffmpegRecorder.js +60 -63
  111. package/lib/video/screenRecording/desktop/osx/getSPDisplaysDataType.js +4 -6
  112. package/lib/video/screenRecording/desktop/osx/getScreen.js +9 -10
  113. package/lib/video/screenRecording/firefox/firefoxWindowRecorder.js +54 -54
  114. package/lib/video/screenRecording/ios/convertToMp4.js +10 -11
  115. package/lib/video/screenRecording/ios/iosRecorder.js +23 -21
  116. package/lib/video/screenRecording/iosSimulator/convertToMp4.js +10 -11
  117. package/lib/video/screenRecording/iosSimulator/recorder.js +10 -9
  118. package/lib/video/screenRecording/recorder.js +8 -9
  119. package/lib/video/screenRecording/setOrangeBackground.js +5 -6
  120. package/lib/video/video.js +28 -34
  121. package/package.json +19 -17
@@ -1,23 +1,34 @@
1
- 'use strict';
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 yargs = require('yargs');
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(fs.readFileSync(configPath)) : {};
19
- } catch (e) {
20
- if (e instanceof SyntaxError) {
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 e;
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 = 10;
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
- fs.statSync(urlOrFile);
52
- } catch (e) {
53
- return util.format(
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
- if (!hasbin.all.sync(['ffmpeg'])) {
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
- if (!hasbin.all.sync(['tcpdump'])) {
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
- if (!hasbin.all.sync(['gnirehtet'])) {
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 (argv.connectivity && argv.connectivity.engine === 'humble') {
103
- if (!argv.connectivity.humble || !argv.connectivity.humble.url) {
104
- return 'You need to specify the URL to Humble by using the --connectivity.humble.url option.';
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
- module.exports.parseCommandLine = function parseCommandLine() {
141
- let validated = yargs
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: 60000,
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: 300000,
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: 120000,
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: 120000,
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('androidPower', {
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: geckoProfilerDefaults.features,
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: geckoProfilerDefaults.threads,
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 ${geckoProfilerDefaults.desktop_sampling_interval} on desktop, and ${geckoProfilerDefaults.android_sampling_interval} on android.`,
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: geckoProfilerDefaults.bufferSize,
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
- default: 0xffffffff,
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: videoDefaults.framerate,
581
+ default: framerate,
571
582
  describe: 'Frames per second',
572
583
  group: 'video'
573
584
  })
574
585
  .option('videoParams.crf', {
575
- default: videoDefaults.crf,
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: videoDefaults.addTimer,
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: videoDefaults.threads,
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('androidRooted', {
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('androidBatteryTemperatureLimit', {
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('androidBatteryTemperatureWaitTimeInSeconds', {
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('androidBatteryTemperatureReboot', {
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('androidPretestPowerPress', {
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('androidPretestPressHomeButton', {
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('androidVerifyNetwork', {
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: screenshotDefaults.type,
843
+ default: SCREENSHOT.type,
816
844
  group: 'Screenshot'
817
845
  })
818
846
  .option('screenshotParams.png.compressionLevel', {
819
847
  describe: 'zlib compression level',
820
- default: screenshotDefaults.png.compressionLevel,
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: screenshotDefaults.jpg.quality,
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: screenshotDefaults.maxSize,
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: videoDefaults.xvfbDisplay,
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 (let i = 0; i < urls.length; i++) {
1266
- urlMetaData[urls[i]] = argv.urlAlias[i];
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', 2147483647);
1288
- set(argv, 'timeouts.script', 2147483647);
1289
- set(argv, 'timeouts.pageCompleteCheck', 2147483647);
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
+ }
@@ -1,10 +1,9 @@
1
- 'use strict';
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
- const execa = require('execa');
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
- 'use strict';
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
- module.exports = {
10
- loadPrePostScripts(scripts) {
11
- return util.toArray(scripts).map(script => {
12
- // if the script is an AsyncFunction, return it.
13
- if (script instanceof AsyncFunction) {
14
- return script;
15
- }
16
- try {
17
- return require(path.resolve(script));
18
- } catch (e) {
19
- throw new Error(
20
- "Couldn't run pre/post script file: " + path.resolve(script) + ' ' + e
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
- loadScript(script, throwError) {
26
- if (script) {
27
- // if the script is an AsyncFunction, return it.
28
- if (script instanceof AsyncFunction) {
29
- return script;
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
- try {
32
- return require(path.resolve(script));
33
- } catch (e) {
34
- // assume the script is a valid inline script by default
35
- if (throwError) {
36
- log.error('Could not parse user script %s with error %s', script, e);
37
- throw e;
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
+ }
@@ -1,6 +1,4 @@
1
- 'use strict';
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
- };