browsertime 16.16.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.
Files changed (121) hide show
  1. package/CHANGELOG.md +52 -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 +25 -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 +81 -71
  50. package/lib/core/engine/iteration.js +93 -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 +43 -48
  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 +123 -85
  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,7 +1,5 @@
1
- 'use strict';
2
-
3
- const httpServer = require('./httpServer');
4
- const setup = require('./setup');
1
+ import { startServer, stopServer } from './httpServer.js';
2
+ import setup from './setup.js';
5
3
 
6
4
  /**
7
5
  * Create a ExtensionServer. The extension server helps us with
@@ -16,8 +14,7 @@ class ExtensionServer {
16
14
 
17
15
  _useServer(newOptions) {
18
16
  const options = newOptions || this.options;
19
- if (
20
- options.browser === 'firefox' &&
17
+ return options.browser === 'firefox' &&
21
18
  (options.cacheClearRaw ||
22
19
  options.requestheader ||
23
20
  options.block ||
@@ -25,20 +22,19 @@ class ExtensionServer {
25
22
  options.cookie ||
26
23
  options.injectJs ||
27
24
  options.clearCacheKeepCookies)
28
- ) {
29
- return true;
30
- } else return false;
25
+ ? true
26
+ : false;
31
27
  }
32
28
 
33
29
  async _start() {
34
30
  if (!this.extensionServer) {
35
- this.extensionServer = await httpServer.startServer();
31
+ this.extensionServer = await startServer();
36
32
  }
37
33
  }
38
34
 
39
35
  async _stop() {
40
36
  if (this.extensionServer) {
41
- await httpServer.stopServer(this.extensionServer);
37
+ await stopServer(this.extensionServer);
42
38
  this.extensionServer = undefined;
43
39
  }
44
40
  }
@@ -53,4 +49,4 @@ class ExtensionServer {
53
49
  }
54
50
  }
55
51
 
56
- module.exports = ExtensionServer;
52
+ export default ExtensionServer;
@@ -1,16 +1,16 @@
1
- ('use strict');
2
- const util = require('../support/util');
3
- const urlParser = require('url');
4
- const log = require('intel').getLogger('browsertime');
1
+ import { format } from 'node:url';
2
+ import intel from 'intel';
3
+ import { toArray } from '../support/util.js';
4
+ const log = intel.getLogger('browsertime');
5
5
  const delay = ms => new Promise(res => setTimeout(res, ms));
6
6
 
7
7
  function generateURL(port, testUrl, options) {
8
8
  const query = {};
9
9
  if (options.block) {
10
- query.bl = util.toArray(options.block);
10
+ query.bl = toArray(options.block);
11
11
  }
12
12
  if (options.requestheader) {
13
- query.rh = util.toArray(options.requestheader);
13
+ query.rh = toArray(options.requestheader);
14
14
  }
15
15
  if (options.cacheClearRaw) {
16
16
  query.clear = 'true';
@@ -29,16 +29,16 @@ function generateURL(port, testUrl, options) {
29
29
  }
30
30
 
31
31
  if (options.cookie) {
32
- const cookies = util.toArray(options.cookie);
32
+ const cookies = toArray(options.cookie);
33
33
  query.cookie = [];
34
34
  for (const cookie of cookies) {
35
- const name = cookie.substring(0, cookie.indexOf('='));
36
- const value = cookie.substring(cookie.indexOf('=') + 1);
35
+ const name = cookie.slice(0, Math.max(0, cookie.indexOf('=')));
36
+ const value = cookie.slice(Math.max(0, cookie.indexOf('=') + 1));
37
37
  query.cookie.push(name + '@' + value + '@' + testUrl);
38
38
  }
39
39
  }
40
40
 
41
- return urlParser.format({
41
+ return format({
42
42
  protocol: 'http',
43
43
  hostname: '127.0.0.1',
44
44
  port,
@@ -46,10 +46,10 @@ function generateURL(port, testUrl, options) {
46
46
  });
47
47
  }
48
48
 
49
- module.exports = async function (url, browser, port, options) {
49
+ export default async function (url, browser, port, options) {
50
50
  const configUrl = generateURL(port, url, options);
51
51
  log.debug('Configuring browser plugin via %s', configUrl);
52
52
  await browser.loadAndWait(configUrl);
53
53
  // Add some time to make sure the cache is cleared etc
54
54
  await delay(500);
55
- };
55
+ }
@@ -1,12 +1,15 @@
1
- 'use strict';
2
- const log = require('intel').getLogger('browsertime.firefox');
3
- const geckoProfilerDefaults = require('./settings/geckoProfilerDefaults');
4
- const { isAndroidConfigured, Android } = require('../android');
5
- const path = require('path');
6
- const get = require('lodash.get');
7
- const pathToFolder = require('../support/pathToFolder');
8
- const { BrowserError } = require('../support/errors');
1
+ import { join } from 'node:path';
2
+ import get from 'lodash.get';
3
+ import intel from 'intel';
4
+ import {
5
+ android_sampling_interval,
6
+ desktop_sampling_interval
7
+ } from './settings/geckoProfilerDefaults.js';
8
+ import { isAndroidConfigured, Android } from '../android/index.js';
9
+ import pathToFolder from '../support/pathToFolder.js';
10
+ import { BrowserError } from '../support/errors.js';
9
11
  const delay = ms => new Promise(res => setTimeout(res, ms));
12
+ const log = intel.getLogger('browsertime.firefox');
10
13
 
11
14
  /**
12
15
  * Timeout a promise after ms. Use promise.race to compete
@@ -16,7 +19,7 @@ const delay = ms => new Promise(res => setTimeout(res, ms));
16
19
  * @param {string} errorMessage - the error message in the Error if we timeouts
17
20
  */
18
21
  async function timeout(promise, ms, errorMessage) {
19
- let timer = null;
22
+ let timer;
20
23
 
21
24
  return Promise.race([
22
25
  new Promise((resolve, reject) => {
@@ -56,8 +59,8 @@ class GeckoProfiler {
56
59
  !firefoxConfig.geckoProfilerParams.interval
57
60
  ) {
58
61
  interval = isAndroidConfigured(options)
59
- ? geckoProfilerDefaults.android_sampling_interval
60
- : geckoProfilerDefaults.desktop_sampling_interval;
62
+ ? android_sampling_interval
63
+ : desktop_sampling_interval;
61
64
  }
62
65
 
63
66
  const bufferSize = firefoxConfig.geckoProfilerParams.bufferSize;
@@ -95,12 +98,9 @@ class GeckoProfiler {
95
98
  const storageManager = this.storageManager;
96
99
  const options = this.options;
97
100
  let profileDir = await storageManager.createSubDataDir(
98
- path.join(pathToFolder(url, options))
99
- );
100
- let destinationFilename = path.join(
101
- profileDir,
102
- `geckoProfile-${index}.json`
101
+ join(pathToFolder(url, options))
103
102
  );
103
+ let destinationFilename = join(profileDir, `geckoProfile-${index}.json`);
104
104
 
105
105
  let deviceProfileFilename = destinationFilename;
106
106
  if (isAndroidConfigured(options)) {
@@ -115,6 +115,12 @@ class GeckoProfiler {
115
115
  log.info(
116
116
  `Collecting Gecko profile from ${deviceProfileFilename} to ${destinationFilename}`
117
117
  );
118
+ // Pause profiler before we collect the profile, so that we don't capture
119
+ // more samples while the parent process waits for subprocess profiles.
120
+ await runner.runPrivilegedScript(
121
+ 'Services.profiler.Pause();',
122
+ 'Pause GeckoProfiler.'
123
+ );
118
124
  const script = `
119
125
  var callback = arguments[arguments.length - 1];
120
126
  Services.profiler.dumpProfileToFileAsync(String.raw\`${deviceProfileFilename}\`)
@@ -124,7 +130,7 @@ class GeckoProfiler {
124
130
  try {
125
131
  await timeout(
126
132
  runner.runPrivilegedAsyncScript(script, 'Collect GeckoProfiler'),
127
- 1200000,
133
+ 1_200_000,
128
134
  'Could not get the Geckoprofiler log'
129
135
  );
130
136
  log.info('Stop GeckoProfiler.');
@@ -141,15 +147,15 @@ class GeckoProfiler {
141
147
  // GZIP the profile and remove the old file
142
148
  return storageManager.gzip(
143
149
  destinationFilename,
144
- path.join(profileDir, `geckoProfile-${index}.json.gz`),
150
+ join(profileDir, `geckoProfile-${index}.json.gz`),
145
151
  true
146
152
  );
147
- } catch (e) {
148
- log.error(e.message);
153
+ } catch (error) {
154
+ log.error(error.message);
149
155
  }
150
156
  }
151
157
 
152
158
  addMetaData() {}
153
159
  }
154
160
 
155
- module.exports = GeckoProfiler;
161
+ export default GeckoProfiler;
@@ -1,10 +1,9 @@
1
- 'use strict';
2
-
3
- const log = require('intel').getLogger('browsertime.firefox');
1
+ import intel from 'intel';
2
+ const log = intel.getLogger('browsertime.firefox');
4
3
 
5
4
  // Get the HAR from Firefox
6
5
  // Using https://github.com/firefox-devtools/har-export-trigger
7
- module.exports = async function (runner, includeResponseBodies) {
6
+ export default async function (runner, includeResponseBodies) {
8
7
  const script = `
9
8
  const callback = arguments[arguments.length - 1];
10
9
  async function triggerExport() {
@@ -49,7 +48,7 @@ module.exports = async function (runner, includeResponseBodies) {
49
48
  delete entry.response.content.text;
50
49
  } else if (
51
50
  entry.response.content.mimeType &&
52
- entry.response.content.mimeType.indexOf('text/html') === -1
51
+ !entry.response.content.mimeType.includes('text/html')
53
52
  ) {
54
53
  delete entry.response.content.text;
55
54
  }
@@ -61,7 +60,7 @@ module.exports = async function (runner, includeResponseBodies) {
61
60
  'Got an error from HAR Export Trigger ' + JSON.stringify(harResult)
62
61
  );
63
62
  }
64
- } catch (e) {
65
- log.error('Could not get the HAR from Firefox', e);
63
+ } catch (error) {
64
+ log.error('Could not get the HAR from Firefox', error);
66
65
  }
67
- };
66
+ }
@@ -1,10 +1,10 @@
1
- 'use strict';
2
- const log = require('intel').getLogger('browsertime.firefox');
3
- const path = require('path');
4
- const pathToFolder = require('../support/pathToFolder');
5
- const { BrowserError } = require('../support/errors');
6
- const { isAndroidConfigured, Android } = require('../android');
1
+ import path from 'node:path';
2
+ import intel from 'intel';
3
+ import pathToFolder from '../support/pathToFolder.js';
4
+ import { BrowserError } from '../support/errors.js';
5
+ import { isAndroidConfigured, Android } from '../android/index.js';
7
6
  const delay = ms => new Promise(res => setTimeout(res, ms));
7
+ const log = intel.getLogger('browsertime.firefox');
8
8
 
9
9
  /**
10
10
  * Timeout a promise after ms. Use promise.race to compete
@@ -14,7 +14,7 @@ const delay = ms => new Promise(res => setTimeout(res, ms));
14
14
  * @param {string} errorMessage - the error message in the Error if we timeouts
15
15
  */
16
16
  async function timeout(promise, ms, errorMessage) {
17
- let timer = null;
17
+ let timer;
18
18
 
19
19
  return Promise.race([
20
20
  new Promise((resolve, reject) => {
@@ -28,7 +28,7 @@ async function timeout(promise, ms, errorMessage) {
28
28
  ]);
29
29
  }
30
30
 
31
- class MemoryReport {
31
+ export class MemoryReport {
32
32
  constructor(runner, storageManager, firefoxConfig, options) {
33
33
  this.runner = runner;
34
34
  this.storageManager = storageManager;
@@ -91,7 +91,7 @@ class MemoryReport {
91
91
  let script = `Cc['@mozilla.org/memory-info-dumper;1'].getService(Ci.nsIMemoryInfoDumper).dumpMemoryReportsToNamedFile(String.raw\`${deviceReportFilename}\`, null, null, false, ${minimizeFirst});`;
92
92
  await timeout(
93
93
  runner.runPrivilegedScript(script, 'Collecting memory report'),
94
- 1200000,
94
+ 1_200_000,
95
95
  'Could not get memory report'
96
96
  );
97
97
 
@@ -106,5 +106,3 @@ class MemoryReport {
106
106
  return this.parse(reportFilename, subDir);
107
107
  }
108
108
  }
109
-
110
- module.exports = MemoryReport;
@@ -1,7 +1,7 @@
1
- 'use strict';
2
- const log = require('intel').getLogger('browsertime.firefox');
1
+ import intel from 'intel';
2
+ const log = intel.getLogger('browsertime.firefox');
3
3
 
4
- class PerfStats {
4
+ export class PerfStats {
5
5
  constructor(runner, firefoxConfig) {
6
6
  this.runner = runner;
7
7
  this.firefoxConfig = firefoxConfig;
@@ -56,10 +56,8 @@ class PerfStats {
56
56
  }
57
57
  }
58
58
  return combinedPerfStats;
59
- } catch (e) {
60
- log.error('Could not collect perf stats', e);
59
+ } catch (error) {
60
+ log.error('Could not collect perf stats', error);
61
61
  }
62
62
  }
63
63
  }
64
-
65
- module.exports = PerfStats;
@@ -1,6 +1,4 @@
1
- 'use strict';
2
-
3
- module.exports = {
1
+ export default {
4
2
  'browser.safebrowsing.downloads.enabled': false,
5
3
  'browser.safebrowsing.enabled': false,
6
4
  'browser.safebrowsing.phishing.enabled': false,
@@ -1,6 +1,4 @@
1
- 'use strict';
2
-
3
- module.exports = {
1
+ export default {
4
2
  'privacy.trackingprotection.annotate_channels': false,
5
3
  'privacy.trackingprotection.enabled': false,
6
4
  'privacy.trackingprotection.introURL':
@@ -1,6 +1,4 @@
1
- 'use strict';
2
-
3
- module.exports = {
1
+ export default {
4
2
  // Common preferences file used by both unittest and perf harnesses.
5
3
  'app.update.checkInstallTime': false,
6
4
  'app.update.disabledForTesting': true,
@@ -139,7 +137,7 @@ module.exports = {
139
137
  // int32_t) to avoid it kicking in during tests. The maintenance can take a
140
138
  // relatively long time which may cause unnecessary intermittents and slow down
141
139
  // tests. This, like many things, will stop working correctly in 2038.
142
- 'places.database.lastMaintenance': 2147483647,
140
+ 'places.database.lastMaintenance': 2_147_483_647,
143
141
  'plugin.state.flash': 0,
144
142
  'plugins.flashBlock.enabled': false,
145
143
  'privacy.reduceTimerPrecision': false, // Bug 1445243 - reduces precision of tests
@@ -1,9 +1,5 @@
1
- 'use strict';
2
-
3
- module.exports = {
4
- features: 'js,stackwalk,leaf',
5
- threads: 'GeckoMain,Compositor,Renderer',
6
- desktop_sampling_interval: 1,
7
- android_sampling_interval: 4,
8
- bufferSize: 13107200 // 100MB
9
- };
1
+ export const features = 'js,stackwalk,leaf';
2
+ export const threads = 'GeckoMain,Compositor,Renderer';
3
+ export const desktop_sampling_interval = 1;
4
+ export const android_sampling_interval = 4;
5
+ export const bufferSize = 13_107_200; // 100MB;
@@ -1,30 +1,36 @@
1
- 'use strict';
2
-
3
- const path = require('path');
4
- const firefox = require('selenium-webdriver/firefox');
5
- const log = require('intel').getLogger('browsertime.firefox');
6
- const proxy = require('selenium-webdriver/proxy');
7
- const pick = require('lodash.pick');
8
- const isEmpty = require('lodash.isempty');
9
- const get = require('lodash.get');
10
- const defaultFirefoxPreferences = require('../settings/firefoxPreferences');
11
- const disableSafeBrowsingPreferences = require('../settings/disableSafeBrowsingPreferences');
12
- const disableTrackingProtectionPreferences = require('../settings/disableTrackingProtectionPreferences');
13
- const util = require('../../support/util');
14
- const { Android } = require('../../android');
15
-
16
- module.exports.configureBuilder = function (builder, baseDir, options) {
1
+ import { resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import path from 'node:path';
4
+ import {
5
+ ServiceBuilder,
6
+ Options,
7
+ Channel
8
+ } from 'selenium-webdriver/firefox.js';
9
+ import intel from 'intel';
10
+ import { pac, manual } from 'selenium-webdriver/proxy.js';
11
+ import pick from 'lodash.pick';
12
+ import isEmpty from 'lodash.isempty';
13
+ import get from 'lodash.get';
14
+ import defaultFirefoxPreferences from '../settings/firefoxPreferences.js';
15
+ import disableSafeBrowsingPreferences from '../settings/disableSafeBrowsingPreferences.js';
16
+ import disableTrackingProtectionPreferences from '../settings/disableTrackingProtectionPreferences.js';
17
+ import { toArray } from '../../support/util.js';
18
+ import { Android, isAndroidConfigured } from '../../android/index.js';
19
+ const log = intel.getLogger('browsertime.firefox');
20
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
+
22
+ export async function configureBuilder(builder, baseDir, options) {
17
23
  // Here we configure everything that we need to start Firefox
18
24
  const firefoxConfig = options.firefox || {};
19
- const moduleRootPath = path.resolve(__dirname, '..', '..', '..');
25
+ const moduleRootPath = resolve(__dirname, '..', '..', '..');
20
26
 
21
27
  let geckodriverPath = get(firefoxConfig, 'geckodriverPath');
22
28
  if (!geckodriverPath) {
23
- const geckodriver = require('@sitespeed.io/geckodriver');
24
- geckodriverPath = geckodriver.binPath();
29
+ const geckodriver = await import('@sitespeed.io/geckodriver');
30
+ geckodriverPath = geckodriver.default.binPath();
25
31
  }
26
32
 
27
- const serviceBuilder = new firefox.ServiceBuilder(geckodriverPath);
33
+ const serviceBuilder = new ServiceBuilder(geckodriverPath);
28
34
  if (options.verbose >= 2) {
29
35
  // This echoes the output from geckodriver to the console.
30
36
  serviceBuilder.setStdio('inherit');
@@ -35,19 +41,20 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
35
41
  }
36
42
 
37
43
  // https://firefox-source-docs.mozilla.org/testing/geckodriver/Flags.html
38
- let geckodriverArgs = get(firefoxConfig, 'geckodriverArgs');
39
- if (geckodriverArgs) {
40
- if (!Array.isArray(geckodriverArgs)) geckodriverArgs = [geckodriverArgs];
41
- for (let arg of geckodriverArgs) {
42
- serviceBuilder.addArguments(arg);
44
+ let geckodriverArguments = get(firefoxConfig, 'geckodriverArgs');
45
+ if (geckodriverArguments) {
46
+ if (!Array.isArray(geckodriverArguments))
47
+ geckodriverArguments = [geckodriverArguments];
48
+ for (let argument of geckodriverArguments) {
49
+ serviceBuilder.addArguments(argument);
43
50
  }
44
51
  }
45
52
 
46
53
  builder.setFirefoxService(serviceBuilder);
47
54
 
48
- const ffOptions = new firefox.Options();
55
+ const ffOptions = new Options();
49
56
 
50
- const profileTemplatePath = path.resolve(
57
+ const profileTemplatePath = resolve(
51
58
  moduleRootPath,
52
59
  'browsersupport',
53
60
  'firefox-profile'
@@ -69,7 +76,7 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
69
76
  // Output the window recorder image frames to a base directory.
70
77
  // This pref expects a trailing slash.
71
78
  if (firefoxConfig.windowRecorder) {
72
- if (options.android) {
79
+ if (isAndroidConfigured(options)) {
73
80
  const android = new Android(options);
74
81
  const pathToRecording = android.getFullPathOnSdCard(
75
82
  'browsertime-firefox-windowrecording'
@@ -84,23 +91,23 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
84
91
  }
85
92
 
86
93
  if (!firefoxConfig.noDefaultPrefs) {
87
- Object.keys(defaultFirefoxPreferences).forEach(function (pref) {
94
+ for (const pref of Object.keys(defaultFirefoxPreferences)) {
88
95
  ffOptions.setPreference(pref, defaultFirefoxPreferences[pref]);
89
- });
96
+ }
90
97
  } else {
91
98
  log.info('Skip setting default preferences for Firefox');
92
99
  }
93
100
 
94
101
  if (firefoxConfig.disableSafeBrowsing) {
95
- Object.keys(disableSafeBrowsingPreferences).forEach(function (pref) {
102
+ for (const pref of Object.keys(disableSafeBrowsingPreferences)) {
96
103
  ffOptions.setPreference(pref, disableSafeBrowsingPreferences[pref]);
97
- });
104
+ }
98
105
  }
99
106
 
100
107
  if (firefoxConfig.disableTrackingProtection) {
101
- Object.keys(disableTrackingProtectionPreferences).forEach(function (pref) {
108
+ for (const pref of Object.keys(disableTrackingProtectionPreferences)) {
102
109
  ffOptions.setPreference(pref, disableTrackingProtectionPreferences[pref]);
103
- });
110
+ }
104
111
  }
105
112
 
106
113
  if (options.debug) {
@@ -110,7 +117,7 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
110
117
  }
111
118
 
112
119
  if (!options.skipHar) {
113
- if (options.android) {
120
+ if (isAndroidConfigured(options)) {
114
121
  log.info('Skip install HAR trigger on Android');
115
122
  } else {
116
123
  // Hack for opening the toolbar
@@ -122,11 +129,7 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
122
129
  }
123
130
 
124
131
  ffOptions.addExtensions(
125
- path.resolve(
126
- moduleRootPath,
127
- 'vendor',
128
- 'har_export_trigger-0.6.1-an+fx.xpi'
129
- )
132
+ resolve(moduleRootPath, 'vendor', 'har_export_trigger-0.6.1-an+fx.xpi')
130
133
  );
131
134
  }
132
135
  }
@@ -136,7 +139,7 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
136
139
 
137
140
  if (!firefoxConfig.disableBrowsertimeExtension) {
138
141
  ffOptions.addExtensions(
139
- path.resolve(moduleRootPath, 'vendor', 'browsertime-0.18.0-an+fx.xpi')
142
+ resolve(moduleRootPath, 'vendor', 'browsertime-0.18.0-an+fx.xpi')
140
143
  );
141
144
  }
142
145
 
@@ -151,16 +154,16 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
151
154
 
152
155
  ffOptions.setPreference('devtools.chrome.enabled', true);
153
156
 
154
- const userPrefs = util.toArray(firefoxConfig.preference);
157
+ const userPrefs = toArray(firefoxConfig.preference);
155
158
  log.debug('Set Firefox preference %j', userPrefs);
156
159
  for (const pref of userPrefs) {
157
- const name = pref.substr(0, pref.indexOf(':'));
158
- let value = pref.substr(pref.indexOf(':') + 1);
160
+ const name = pref.slice(0, Math.max(0, pref.indexOf(':')));
161
+ let value = pref.slice(pref.indexOf(':') + 1);
159
162
  if (value === 'false') {
160
163
  ffOptions.setPreference(name, false);
161
164
  } else if (value === 'true') {
162
165
  ffOptions.setPreference(name, true);
163
- } else if (isNaN(value)) {
166
+ } else if (Number.isNaN(value)) {
164
167
  ffOptions.setPreference(name, value);
165
168
  } else {
166
169
  ffOptions.setPreference(name, Number(value));
@@ -168,12 +171,10 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
168
171
  }
169
172
 
170
173
  let firefoxTypes = [
171
- get(firefoxConfig, 'binaryPath')
172
- ? get(firefoxConfig, 'binaryPath')
173
- : undefined,
174
- get(firefoxConfig, 'nightly') ? firefox.Channel.NIGHTLY : undefined,
175
- get(firefoxConfig, 'beta') ? firefox.Channel.BETA : undefined,
176
- get(firefoxConfig, 'developer') ? firefox.Channel.AURORA : undefined
174
+ get(firefoxConfig, 'binaryPath') ?? undefined,
175
+ get(firefoxConfig, 'nightly') ? Channel.NIGHTLY : undefined,
176
+ get(firefoxConfig, 'beta') ? Channel.BETA : undefined,
177
+ get(firefoxConfig, 'developer') ? Channel.AURORA : undefined
177
178
  ];
178
179
 
179
180
  firefoxTypes = firefoxTypes.filter(function (n) {
@@ -183,9 +184,9 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
183
184
  // Do not set binary when using Android
184
185
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1751196
185
186
 
186
- if (!options.android) {
187
+ if (!isAndroidConfigured(options)) {
187
188
  ffOptions.setBinary(
188
- firefoxTypes.length > 0 ? firefoxTypes[0] : firefox.Channel.RELEASE
189
+ firefoxTypes.length > 0 ? firefoxTypes[0] : Channel.RELEASE
189
190
  );
190
191
  }
191
192
 
@@ -195,12 +196,12 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
195
196
  ffOptions.addArguments(firefoxConfig.args);
196
197
  }
197
198
 
198
- const envs = util.toArray(firefoxConfig.env);
199
+ const environments = toArray(firefoxConfig.env);
199
200
  ffOptions.firefoxOptions_().env = ffOptions.firefoxOptions_().env || {};
200
- for (const env of envs) {
201
- const name = env.substr(0, env.indexOf('='));
202
- const value = env.substr(env.indexOf('=') + 1);
203
- if (options.android) {
201
+ for (const environment of environments) {
202
+ const name = environment.slice(0, Math.max(0, environment.indexOf('=')));
203
+ const value = environment.slice(environment.indexOf('=') + 1);
204
+ if (isAndroidConfigured(options)) {
204
205
  ffOptions.firefoxOptions_().env[name] = value;
205
206
  } else {
206
207
  process.env[name] = value;
@@ -219,7 +220,7 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
219
220
  .getCapabilities()
220
221
  .set('pageLoadStrategy', get(options, 'pageLoadStrategy', 'normal'));
221
222
 
222
- if (options.android) {
223
+ if (isAndroidConfigured(options)) {
223
224
  // Monkey patch to avoid changing `selenium-webdriver` before the
224
225
  // corresponding `geckodriver` functionality has been replaced:
225
226
  // see this [Firefox bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1525126)
@@ -249,21 +250,23 @@ module.exports.configureBuilder = function (builder, baseDir, options) {
249
250
  if (android && android.intentArgument) {
250
251
  ffOptions.firefoxOptions_().androidIntentArguments = (
251
252
  ffOptions.firefoxOptions_().androidIntentArguments || []
252
- ).concat(...util.toArray(android.intentArgument));
253
+ )
254
+ // eslint-disable-next-line unicorn/prefer-spread
255
+ .concat(...toArray(android.intentArgument));
253
256
  }
254
257
  }
255
258
 
256
259
  const proxyPacSettings = pick(options.proxy, ['pac']);
257
260
 
258
261
  if (!isEmpty(proxyPacSettings)) {
259
- builder.setProxy(proxy.pac(proxyPacSettings));
262
+ builder.setProxy(pac(proxyPacSettings));
260
263
  }
261
264
 
262
265
  const proxySettings = pick(options.proxy, ['ftp', 'http', 'https', 'bypass']);
263
266
 
264
267
  if (!isEmpty(proxySettings)) {
265
- builder.setProxy(proxy.manual(proxySettings));
268
+ builder.setProxy(manual(proxySettings));
266
269
  }
267
270
 
268
271
  builder.setFirefoxOptions(ffOptions);
269
- };
272
+ }