browsertime 17.0.0-beta.4 → 17.1.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 (99) hide show
  1. package/CHANGELOG.md +30 -13
  2. package/bin/browsertime.js +4 -4
  3. package/bin/browsertimeWebPageReplay.js +2 -2
  4. package/browsertime/visualmetrics-portable.py +54 -50
  5. package/index.js +2 -2
  6. package/lib/android/gnirehtet.js +1 -2
  7. package/lib/android/root.js +57 -16
  8. package/lib/chrome/chromeDevtoolsProtocol.js +6 -3
  9. package/lib/chrome/har.js +1 -1
  10. package/lib/chrome/longTaskMetrics.js +1 -1
  11. package/lib/chrome/parseCpuTrace.js +2 -2
  12. package/lib/chrome/settings/chromeAndroidOptions.js +1 -1
  13. package/lib/chrome/settings/chromeDesktopOptions.js +1 -1
  14. package/lib/chrome/settings/traceCategories.js +1 -1
  15. package/lib/chrome/traceCategoriesParser.js +3 -3
  16. package/lib/chrome/webdriver/builder.js +1 -1
  17. package/lib/chrome/webdriver/chromium.js +13 -17
  18. package/lib/chrome/webdriver/setupChromiumOptions.js +18 -13
  19. package/lib/connectivity/humble.js +10 -12
  20. package/lib/connectivity/index.js +1 -1
  21. package/lib/core/engine/collector.js +2 -4
  22. package/lib/core/engine/command/addText.js +1 -2
  23. package/lib/core/engine/command/android.js +1 -1
  24. package/lib/core/engine/command/cache.js +1 -2
  25. package/lib/core/engine/command/chromeDevToolsProtocol.js +9 -2
  26. package/lib/core/engine/command/click.js +1 -2
  27. package/lib/core/engine/command/debug.js +1 -3
  28. package/lib/core/engine/command/javaScript.js +1 -2
  29. package/lib/core/engine/command/measure.js +13 -15
  30. package/lib/core/engine/command/meta.js +1 -1
  31. package/lib/core/engine/command/navigation.js +1 -1
  32. package/lib/core/engine/command/screenshot.js +1 -1
  33. package/lib/core/engine/command/scroll.js +1 -1
  34. package/lib/core/engine/command/select.js +1 -1
  35. package/lib/core/engine/command/set.js +1 -1
  36. package/lib/core/engine/command/stopWatch.js +1 -1
  37. package/lib/core/engine/command/switch.js +44 -1
  38. package/lib/core/engine/command/util/lcpHighlightScript.js +1 -1
  39. package/lib/core/engine/command/util/lsHighlightScript.js +1 -1
  40. package/lib/core/engine/command/wait.js +1 -1
  41. package/lib/core/engine/index.js +12 -12
  42. package/lib/core/engine/iteration.js +27 -27
  43. package/lib/core/engine/run.js +1 -1
  44. package/lib/core/pageCompleteChecks/defaultPageCompleteCheck.js +1 -1
  45. package/lib/core/pageCompleteChecks/pageCompleteCheckByInactivity.js +1 -1
  46. package/lib/core/pageCompleteChecks/spaInactivity.js +1 -1
  47. package/lib/core/seleniumRunner.js +9 -11
  48. package/lib/edge/webdriver/builder.js +1 -1
  49. package/lib/extensionserver/index.js +2 -4
  50. package/lib/extensionserver/setup.js +1 -1
  51. package/lib/firefox/geckoProfiler.js +46 -16
  52. package/lib/firefox/getHAR.js +1 -1
  53. package/lib/firefox/memoryReport.js +1 -1
  54. package/lib/firefox/settings/disableSafeBrowsingPreferences.js +1 -1
  55. package/lib/firefox/settings/disableTrackingProtectionPreferences.js +1 -1
  56. package/lib/firefox/settings/firefoxPreferences.js +1 -1
  57. package/lib/firefox/webdriver/builder.js +9 -9
  58. package/lib/firefox/webdriver/firefox.js +24 -6
  59. package/lib/safari/webdriver/safari.js +1 -1
  60. package/lib/screenshot/defaults.js +1 -1
  61. package/lib/screenshot/index.js +3 -5
  62. package/lib/support/cli.js +5 -5
  63. package/lib/support/dns.js +6 -1
  64. package/lib/support/engineUtils.js +5 -13
  65. package/lib/support/getViewPort.js +1 -1
  66. package/lib/support/har/index.js +6 -8
  67. package/lib/support/images/index.js +1 -1
  68. package/lib/support/logging.js +1 -1
  69. package/lib/support/pathToFolder.js +4 -4
  70. package/lib/support/preURL.js +1 -1
  71. package/lib/support/processes.js +1 -1
  72. package/lib/support/setResourceTimingBufferSize.js +1 -1
  73. package/lib/support/stop.js +1 -1
  74. package/lib/support/storageManager.js +1 -3
  75. package/lib/support/tcpdump.js +2 -2
  76. package/lib/support/xvfb.js +2 -4
  77. package/lib/video/postprocessing/finetune/addTextToVideo.js +3 -3
  78. package/lib/video/postprocessing/finetune/convertFps.js +1 -1
  79. package/lib/video/postprocessing/finetune/getFont.js +1 -1
  80. package/lib/video/postprocessing/finetune/getTimingMetrics.js +2 -2
  81. package/lib/video/postprocessing/finetune/index.js +4 -4
  82. package/lib/video/postprocessing/finetune/removeOrange.js +1 -1
  83. package/lib/video/postprocessing/visualmetrics/extraMetrics.js +1 -1
  84. package/lib/video/postprocessing/visualmetrics/getVideoMetrics.js +2 -2
  85. package/lib/video/screenRecording/android/recorder.js +1 -1
  86. package/lib/video/screenRecording/desktop/convert.js +1 -1
  87. package/lib/video/screenRecording/desktop/desktopRecorder.js +3 -3
  88. package/lib/video/screenRecording/desktop/ffmpegRecorder.js +6 -6
  89. package/lib/video/screenRecording/desktop/osx/getSPDisplaysDataType.js +1 -1
  90. package/lib/video/screenRecording/desktop/osx/getScreen.js +1 -1
  91. package/lib/video/screenRecording/firefox/firefoxWindowRecorder.js +1 -1
  92. package/lib/video/screenRecording/ios/convertToMp4.js +1 -1
  93. package/lib/video/screenRecording/ios/iosRecorder.js +2 -2
  94. package/lib/video/screenRecording/iosSimulator/convertToMp4.js +1 -1
  95. package/lib/video/screenRecording/iosSimulator/recorder.js +2 -2
  96. package/lib/video/screenRecording/recorder.js +6 -6
  97. package/lib/video/screenRecording/setOrangeBackground.js +1 -1
  98. package/lib/video/video.js +9 -11
  99. package/package.json +18 -18
@@ -2,34 +2,34 @@ import { platform } from 'node:os';
2
2
  import webdriver from 'selenium-webdriver';
3
3
  import intel from 'intel';
4
4
 
5
- import SeleniumRunner from '../seleniumRunner.js';
6
- import preURL from '../../support/preURL.js';
7
- import setResourceTimingBufferSize from '../../support/setResourceTimingBufferSize.js';
8
- import ScreenshotManager from '../../screenshot/index.js';
9
- import ExtensionServer from '../../extensionserver/index.js';
10
- import Video from '../../video/video.js';
11
- import stop from '../../support/stop.js';
12
- import AddText from './command/addText.js';
13
- import Click from './command/click.js';
14
- import Wait from './command/wait.js';
15
- import Measure from './command/measure.js';
16
- import JavaScript from './command/javaScript.js';
17
- import Switch from './command/switch.js';
18
- import Screenshot from './command/screenshot.js';
19
- import Set from './command/set.js';
20
- import Cache from './command/cache.js';
21
- import Meta from './command/meta.js';
22
- import StopWatch from './command/stopWatch.js';
23
- import Select from './command/select.js';
24
- import Debug from './command/debug.js';
25
- import AndroidCommand from './command/android.js';
26
- import ChromeDevelopmentToolsProtocol from './command/chromeDevToolsProtocol.js';
5
+ import { SeleniumRunner } from '../seleniumRunner.js';
6
+ import { preURL } from '../../support/preURL.js';
7
+ import { setResourceTimingBufferSize } from '../../support/setResourceTimingBufferSize.js';
8
+ import { ScreenshotManager } from '../../screenshot/index.js';
9
+ import { ExtensionServer } from '../../extensionserver/index.js';
10
+ import { Video } from '../../video/video.js';
11
+ import { stop } from '../../support/stop.js';
12
+ import { AddText } from './command/addText.js';
13
+ import { Click } from './command/click.js';
14
+ import { Wait } from './command/wait.js';
15
+ import { Measure } from './command/measure.js';
16
+ import { JavaScript } from './command/javaScript.js';
17
+ import { Switch } from './command/switch.js';
18
+ import { Screenshot } from './command/screenshot.js';
19
+ import { Set } from './command/set.js';
20
+ import { Cache } from './command/cache.js';
21
+ import { Meta } from './command/meta.js';
22
+ import { Watch as StopWatch } from './command/stopWatch.js';
23
+ import { Select } from './command/select.js';
24
+ import { Debug } from './command/debug.js';
25
+ import { AndroidCommand } from './command/android.js';
26
+ import { ChromeDevelopmentToolsProtocol } from './command/chromeDevToolsProtocol.js';
27
27
  import {
28
28
  addConnectivity,
29
29
  removeConnectivity
30
30
  } from '../../connectivity/index.js';
31
31
  import { jsonifyVisualProgress } from '../../support/util.js';
32
- import flushDNS from '../../support/dns.js';
32
+ import { flushDNS } from '../../support/dns.js';
33
33
  import {
34
34
  SingleClick,
35
35
  DoubleClick,
@@ -37,10 +37,10 @@ import {
37
37
  ContextClick,
38
38
  MouseMove
39
39
  } from './command/mouse/index.js';
40
- import getNumberOfRunningProcesses from '../../support/processes.js';
40
+ import { getNumberOfRunningProcesses } from '../../support/processes.js';
41
41
  import { isAndroidConfigured, Android } from '../../android/index.js';
42
- import Scroll from './command/scroll.js';
43
- import Navigation from './command/navigation.js';
42
+ import { Scroll } from './command/scroll.js';
43
+ import { Navigation } from './command/navigation.js';
44
44
  const log = intel.getLogger('browsertime');
45
45
  const delay = ms => new Promise(res => setTimeout(res, ms));
46
46
 
@@ -51,7 +51,7 @@ const ANDROID_DELAY_TIME = 2000;
51
51
  * Browsertime will do through one iteration of testing a URL.
52
52
  * @class
53
53
  */
54
- export default class Iteration {
54
+ export class Iteration {
55
55
  constructor(
56
56
  storageManager,
57
57
  engineDelegate,
@@ -1,4 +1,4 @@
1
- export default function (urlOrFunctions) {
1
+ export function run(urlOrFunctions) {
2
2
  return async function (context, commands) {
3
3
  for (let urlOrFunction of urlOrFunctions) {
4
4
  await (typeof urlOrFunction === 'function'
@@ -1,4 +1,4 @@
1
- export default `
1
+ export const defaultPageCompleteCheck = `
2
2
  return (function(waitTime) {
3
3
  try {
4
4
  var end = window.performance.timing.loadEventEnd;
@@ -1,4 +1,4 @@
1
- export default `
1
+ export const pageCompleteCheckByInactivity = `
2
2
  return (function(waitTime) {
3
3
  const timing = window.performance.timing;
4
4
  const p = window.performance;
@@ -1,4 +1,4 @@
1
- export default `
1
+ export const spaInactivity = `
2
2
  return (function(waitTime) {
3
3
  const timing = window.performance.timing;
4
4
  const p = window.performance;
@@ -5,10 +5,10 @@ import { Condition } from 'selenium-webdriver';
5
5
  import { isAndroidConfigured } from '../android/index.js';
6
6
  import { UrlLoadError, BrowserError, TimeoutError } from '../support/errors.js';
7
7
  import { createWebDriver } from './webdriver/index.js';
8
- import getViewPort from '../support/getViewPort.js';
9
- import defaultPageCompleteCheck from './pageCompleteChecks/defaultPageCompleteCheck.js';
10
- import pageCompleteCheckByInactivity from './pageCompleteChecks/pageCompleteCheckByInactivity.js';
11
- import spaCheck from './pageCompleteChecks/spaInactivity.js';
8
+ import { getViewPort } from '../support/getViewPort.js';
9
+ import { defaultPageCompleteCheck } from './pageCompleteChecks/defaultPageCompleteCheck.js';
10
+ import { pageCompleteCheckByInactivity } from './pageCompleteChecks/pageCompleteCheckByInactivity.js';
11
+ import { spaInactivity as spaCheck } from './pageCompleteChecks/spaInactivity.js';
12
12
  const log = intel.getLogger('browsertime');
13
13
 
14
14
  const defaults = {
@@ -49,7 +49,7 @@ async function timeout(promise, ms, errorMessage) {
49
49
  * Wrapper for Selenium.
50
50
  * @class
51
51
  */
52
- class SeleniumRunner {
52
+ export class SeleniumRunner {
53
53
  constructor(baseDir, options) {
54
54
  this.options = merge({}, defaults, options);
55
55
  this.baseDir = baseDir;
@@ -296,10 +296,7 @@ class SeleniumRunner {
296
296
  `Could not load ${url} is the web page down?`,
297
297
  url
298
298
  );
299
- } else if (newURI !== startURI) {
300
- // We navigated to a new page, we don't need to test anymore
301
- break;
302
- } else {
299
+ } else if (newURI === startURI) {
303
300
  const waitTime = (this.options.retryWaitTime || 10_000) * (index + 1);
304
301
  totalWaitTime += waitTime;
305
302
  log.debug(
@@ -322,6 +319,9 @@ class SeleniumRunner {
322
319
  'Will check again if the browser has navigated to the page'
323
320
  );
324
321
  }
322
+ } else {
323
+ // We navigated to a new page, we don't need to test anymore
324
+ break;
325
325
  }
326
326
  } catch (error) {
327
327
  log.info(
@@ -667,5 +667,3 @@ class SeleniumRunner {
667
667
  return results;
668
668
  }
669
669
  }
670
-
671
- export default SeleniumRunner;
@@ -4,7 +4,7 @@ import get from 'lodash.get';
4
4
  import pick from 'lodash.pick';
5
5
  import isEmpty from 'lodash.isempty';
6
6
  import { logging } from 'selenium-webdriver';
7
- import setupChromiumOptions from '../../chrome/webdriver/setupChromiumOptions.js';
7
+ import { setupChromiumOptions } from '../../chrome/webdriver/setupChromiumOptions.js';
8
8
  const log = intel.getLogger('browsertime.edge');
9
9
  const { pac, manual } = 'selenium-webdriver/proxy.js';
10
10
 
@@ -1,5 +1,5 @@
1
1
  import { startServer, stopServer } from './httpServer.js';
2
- import setup from './setup.js';
2
+ import { setup } from './setup.js';
3
3
 
4
4
  /**
5
5
  * Create a ExtensionServer. The extension server helps us with
@@ -7,7 +7,7 @@ import setup from './setup.js';
7
7
  * use a WebExtension to prepare our tests.
8
8
  * @class
9
9
  */
10
- class ExtensionServer {
10
+ export class ExtensionServer {
11
11
  constructor(options) {
12
12
  this.options = options;
13
13
  }
@@ -48,5 +48,3 @@ class ExtensionServer {
48
48
  }
49
49
  }
50
50
  }
51
-
52
- export default ExtensionServer;
@@ -46,7 +46,7 @@ function generateURL(port, testUrl, options) {
46
46
  });
47
47
  }
48
48
 
49
- export default async function (url, browser, port, options) {
49
+ export async function setup(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);
@@ -6,7 +6,7 @@ import {
6
6
  desktop_sampling_interval
7
7
  } from './settings/geckoProfilerDefaults.js';
8
8
  import { isAndroidConfigured, Android } from '../android/index.js';
9
- import pathToFolder from '../support/pathToFolder.js';
9
+ import { pathToFolder } from '../support/pathToFolder.js';
10
10
  import { BrowserError } from '../support/errors.js';
11
11
  const delay = ms => new Promise(res => setTimeout(res, ms));
12
12
  const log = intel.getLogger('browsertime.firefox');
@@ -32,7 +32,7 @@ async function timeout(promise, ms, errorMessage) {
32
32
  })
33
33
  ]);
34
34
  }
35
- class GeckoProfiler {
35
+ export class GeckoProfiler {
36
36
  constructor(runner, storageManager, firefoxConfig, options) {
37
37
  this.runner = runner;
38
38
  this.storageManager = storageManager;
@@ -73,14 +73,38 @@ class GeckoProfiler {
73
73
  );
74
74
 
75
75
  let script;
76
- if (parameterLength === 7) {
77
- script = `Services.profiler.StartProfiler(${bufferSize},${interval},${featureString},
78
- ${chosenFeatures.length},${threadString},${chosenThreads.length});`;
79
- } else if (parameterLength === 5 || parameterLength === 6) {
80
- script = `Services.profiler.StartProfiler(${bufferSize},${interval},${featureString},${threadString});`;
81
- } else {
82
- log.error('Unknown Gecko Profiler API');
83
- throw new Error('Unknown Gecko Profiler API');
76
+ switch (parameterLength) {
77
+ case 7: {
78
+ // Oldest profiler API. It was deprecated in Firefox 69.
79
+ script = `Services.profiler.StartProfiler(${bufferSize},${interval},${featureString},
80
+ ${chosenFeatures.length},${threadString},${chosenThreads.length});`;
81
+ break;
82
+ }
83
+ case 6: {
84
+ // Newest profiler API. It's being used since Firefox 72.
85
+ // `activeBrowserId` is useful for distinguishing browser tabs in the profiler UI.
86
+ const activeBrowserIdScript = `
87
+ const win = Services.wm.getMostRecentWindow("navigator:browser");
88
+ return win?.gBrowser?.selectedBrowser?.browsingContext?.browserId ?? 0;
89
+ `;
90
+ const activeBrowserId = await runner.runPrivilegedScript(
91
+ activeBrowserIdScript,
92
+ 'Get activeBrowserId'
93
+ );
94
+ script = `Services.profiler.StartProfiler(${bufferSize},${interval},${featureString},
95
+ ${threadString},${activeBrowserId});`;
96
+ break;
97
+ }
98
+ case 5: {
99
+ // The profiler API that was used between Firefox 69 and 72.
100
+ script = `Services.profiler.StartProfiler(${bufferSize},${interval},${featureString},
101
+ ${threadString});`;
102
+ break;
103
+ }
104
+ default: {
105
+ log.error('Unknown Gecko Profiler API');
106
+ throw new Error('Unknown Gecko Profiler API');
107
+ }
84
108
  }
85
109
 
86
110
  log.info(
@@ -140,16 +164,24 @@ class GeckoProfiler {
140
164
  );
141
165
 
142
166
  if (isAndroidConfigured(options)) {
167
+ log.info('Download profile file from Android.');
143
168
  const android = new Android(options);
144
169
  await android._downloadFile(deviceProfileFilename, destinationFilename);
145
170
  }
146
171
 
147
172
  // GZIP the profile and remove the old file
148
- return storageManager.gzip(
149
- destinationFilename,
150
- join(profileDir, `geckoProfile-${index}.json.gz`),
151
- true
173
+ log.info('Gzip file the profile.');
174
+ await timeout(
175
+ storageManager.gzip(
176
+ destinationFilename,
177
+ join(profileDir, `geckoProfile-${index}.json.gz`),
178
+ true
179
+ ),
180
+ 300_000, // 5 minutes
181
+ 'Could not gzip the profile.'
152
182
  );
183
+
184
+ log.info('Done stopping GeckoProfiler.');
153
185
  } catch (error) {
154
186
  log.error(error.message);
155
187
  }
@@ -157,5 +189,3 @@ class GeckoProfiler {
157
189
 
158
190
  addMetaData() {}
159
191
  }
160
-
161
- export default GeckoProfiler;
@@ -3,7 +3,7 @@ const log = intel.getLogger('browsertime.firefox');
3
3
 
4
4
  // Get the HAR from Firefox
5
5
  // Using https://github.com/firefox-devtools/har-export-trigger
6
- export default async function (runner, includeResponseBodies) {
6
+ export async function getHAR(runner, includeResponseBodies) {
7
7
  const script = `
8
8
  const callback = arguments[arguments.length - 1];
9
9
  async function triggerExport() {
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import intel from 'intel';
3
- import pathToFolder from '../support/pathToFolder.js';
3
+ import { pathToFolder } from '../support/pathToFolder.js';
4
4
  import { BrowserError } from '../support/errors.js';
5
5
  import { isAndroidConfigured, Android } from '../android/index.js';
6
6
  const delay = ms => new Promise(res => setTimeout(res, ms));
@@ -1,4 +1,4 @@
1
- export default {
1
+ export const disableSafeBrowsingPreferences = {
2
2
  'browser.safebrowsing.downloads.enabled': false,
3
3
  'browser.safebrowsing.enabled': false,
4
4
  'browser.safebrowsing.phishing.enabled': false,
@@ -1,4 +1,4 @@
1
- export default {
1
+ export const disableTrackingProtectionPreferences = {
2
2
  'privacy.trackingprotection.annotate_channels': false,
3
3
  'privacy.trackingprotection.enabled': false,
4
4
  'privacy.trackingprotection.introURL':
@@ -1,4 +1,4 @@
1
- export default {
1
+ export const defaultFirefoxPreferences = {
2
2
  // Common preferences file used by both unittest and perf harnesses.
3
3
  'app.update.checkInstallTime': false,
4
4
  'app.update.disabledForTesting': true,
@@ -11,9 +11,9 @@ import { pac, manual } from 'selenium-webdriver/proxy.js';
11
11
  import pick from 'lodash.pick';
12
12
  import isEmpty from 'lodash.isempty';
13
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';
14
+ import { defaultFirefoxPreferences } from '../settings/firefoxPreferences.js';
15
+ import { disableSafeBrowsingPreferences } from '../settings/disableSafeBrowsingPreferences.js';
16
+ import { disableTrackingProtectionPreferences } from '../settings/disableTrackingProtectionPreferences.js';
17
17
  import { toArray } from '../../support/util.js';
18
18
  import { Android, isAndroidConfigured } from '../../android/index.js';
19
19
  const log = intel.getLogger('browsertime.firefox');
@@ -90,12 +90,12 @@ export async function configureBuilder(builder, baseDir, options) {
90
90
  }
91
91
  }
92
92
 
93
- if (!firefoxConfig.noDefaultPrefs) {
93
+ if (firefoxConfig.noDefaultPrefs) {
94
+ log.info('Skip setting default preferences for Firefox');
95
+ } else {
94
96
  for (const pref of Object.keys(defaultFirefoxPreferences)) {
95
97
  ffOptions.setPreference(pref, defaultFirefoxPreferences[pref]);
96
98
  }
97
- } else {
98
- log.info('Skip setting default preferences for Firefox');
99
99
  }
100
100
 
101
101
  if (firefoxConfig.disableSafeBrowsing) {
@@ -144,9 +144,9 @@ export async function configureBuilder(builder, baseDir, options) {
144
144
  }
145
145
 
146
146
  if (options.extension) {
147
- const extensions = !Array.isArray(options.extension)
148
- ? [options.extension]
149
- : options.extension;
147
+ const extensions = Array.isArray(options.extension)
148
+ ? options.extension
149
+ : [options.extension];
150
150
  for (const extension of extensions) {
151
151
  ffOptions.addExtensions(extension);
152
152
  }
@@ -4,17 +4,17 @@ import { join } from 'node:path';
4
4
  import intel from 'intel';
5
5
  import get from 'lodash.get';
6
6
  import { getEmptyHAR, mergeHars } from '../../support/har/index.js';
7
- import pathToFolder from '../../support/pathToFolder.js';
7
+ import { pathToFolder } from '../../support/pathToFolder.js';
8
8
  import { isAndroidConfigured, Android } from '../../android/index.js';
9
9
  import { adjustVisualProgressTimestamps } from '../../support/util.js';
10
10
  import { findFiles } from '../../support/fileUtil.js';
11
- import getHAR from '../getHAR.js';
12
- import GeckoProfiler from '../geckoProfiler.js';
11
+ import { getHAR } from '../getHAR.js';
12
+ import { GeckoProfiler } from '../geckoProfiler.js';
13
13
  import { MemoryReport } from '../memoryReport.js';
14
14
  import { PerfStats } from '../perfStats.js';
15
15
  const log = intel.getLogger('browsertime.firefox');
16
16
  const rename = promisify(_rename);
17
- class Firefox {
17
+ export class Firefox {
18
18
  constructor(storageManager, options) {
19
19
  // Lets keep this and hope that we in the future will have HAR for FF again
20
20
  this.skipHar = options.skipHar;
@@ -73,6 +73,14 @@ class Firefox {
73
73
  * Before each URL/test runs.
74
74
  */
75
75
  async beforeEachURL(runner) {
76
+ await runner.runPrivilegedScript(`
77
+ new Promise(async function(resolve) {
78
+ await Services.fog.testFlushAllChildren(); // force any data that wasn't recorded yet to be immediately put in the buffers
79
+ Services.fog.testResetFOG(); // empty the buffers
80
+ resolve();
81
+ });
82
+ `);
83
+
76
84
  if (isAndroidConfigured(this.options) && this.options.androidPower) {
77
85
  await this.android.resetPowerUsage();
78
86
  }
@@ -146,6 +154,18 @@ class Firefox {
146
154
  );
147
155
  }
148
156
 
157
+ let powerusage = await runner.runPrivilegedScript(`
158
+ return new Promise(async function(resolve) {
159
+ Services.fog.initializeFOG(); // prevents timeout when collecting CPU metrics
160
+ await Services.fog.testFlushAllChildren(); // force data from child processes to be sent to the parent.
161
+ resolve(Glean.power.totalCpuTimeMs.testGetValue());
162
+ });
163
+ `);
164
+ if (powerusage) {
165
+ log.info('CPU / Power usage: ' + powerusage);
166
+ }
167
+ result.cpu = powerusage;
168
+
149
169
  if (this.skipHar || isAndroidConfigured(this.options)) {
150
170
  return result;
151
171
  } else {
@@ -274,5 +294,3 @@ class Firefox {
274
294
  */
275
295
  async afterBrowserStopped() {}
276
296
  }
277
-
278
- export default Firefox;
@@ -2,7 +2,7 @@
2
2
  import { execaCommand as command } from 'execa';
3
3
  const delay = ms => new Promise(res => setTimeout(res, ms));
4
4
 
5
- export default class Safari {
5
+ export class Safari {
6
6
  constructor(storageManager, options) {
7
7
  this.options = options;
8
8
  this.safariOptions = options.safari || {};
@@ -1,4 +1,4 @@
1
- export default {
1
+ export const screenshotDefaults = {
2
2
  type: 'jpg',
3
3
  png: {
4
4
  compressionLevel: 6
@@ -1,7 +1,7 @@
1
1
  import { join } from 'node:path';
2
2
  import merge from 'lodash.merge';
3
3
  import intel from 'intel';
4
- import defaultConfig from './defaults.js';
4
+ import { screenshotDefaults } from './defaults.js';
5
5
  import {
6
6
  savePngWithoutResize,
7
7
  savePng,
@@ -9,10 +9,10 @@ import {
9
9
  } from '../support/images/index.js';
10
10
  const SCREENSHOT_DIR = 'screenshots';
11
11
  const log = intel.getLogger('browsertime.screenshot');
12
- class ScreenshotManager {
12
+ export class ScreenshotManager {
13
13
  constructor(storageManager, options) {
14
14
  this.storageManager = storageManager;
15
- this.config = merge({}, defaultConfig, options.screenshotParams);
15
+ this.config = merge({}, screenshotDefaults, options.screenshotParams);
16
16
  this.options = options;
17
17
  this.savedScreenshots = [];
18
18
  }
@@ -76,5 +76,3 @@ class ScreenshotManager {
76
76
  return this.savedScreenshots;
77
77
  }
78
78
  }
79
-
80
- export default ScreenshotManager;
@@ -13,7 +13,7 @@ import {
13
13
  threads,
14
14
  xvfbDisplay
15
15
  } from '../video/defaults.js';
16
- import SCREENSHOT from '../screenshot/defaults.js';
16
+ import { screenshotDefaults } from '../screenshot/defaults.js';
17
17
  import {
18
18
  features,
19
19
  threads as _threads,
@@ -840,22 +840,22 @@ export function parseCommandLine() {
840
840
  .option('screenshotParams.type', {
841
841
  describe: 'Set the file type of the screenshot',
842
842
  choices: ['png', 'jpg'],
843
- default: SCREENSHOT.type,
843
+ default: screenshotDefaults.type,
844
844
  group: 'Screenshot'
845
845
  })
846
846
  .option('screenshotParams.png.compressionLevel', {
847
847
  describe: 'zlib compression level',
848
- default: SCREENSHOT.png.compressionLevel,
848
+ default: screenshotDefaults.png.compressionLevel,
849
849
  group: 'Screenshot'
850
850
  })
851
851
  .option('screenshotParams.jpg.quality', {
852
852
  describe: 'Quality of the JPEG screenshot. 1-100',
853
- default: SCREENSHOT.jpg.quality,
853
+ default: screenshotDefaults.jpg.quality,
854
854
  group: 'Screenshot'
855
855
  })
856
856
  .option('screenshotParams.maxSize', {
857
857
  describe: 'The max size of the screenshot (width and height).',
858
- default: SCREENSHOT.maxSize,
858
+ default: screenshotDefaults.maxSize,
859
859
  group: 'Screenshot'
860
860
  })
861
861
  .option('pageCompleteCheck', {
@@ -3,7 +3,7 @@ import intel from 'intel';
3
3
  import { isAndroidConfigured } from '../android/index.js';
4
4
  const log = intel.getLogger('browsertime.dns');
5
5
 
6
- export default async function (options) {
6
+ export async function flushDNS(options) {
7
7
  if (isAndroidConfigured(options)) {
8
8
  return;
9
9
  }
@@ -25,6 +25,11 @@ export default async function (options) {
25
25
  });
26
26
  } else if (process.platform === 'linux') {
27
27
  log.info('Flush DNS cache on Linux');
28
+
29
+ // Unbuntu 22.04
30
+ await execa('sudo', ['resolvectl', 'flush-caches'], {
31
+ reject: false
32
+ });
28
33
  await execa('sudo', ['systemd-resolve', '--flush-caches'], {
29
34
  reject: false
30
35
  });
@@ -1,6 +1,6 @@
1
1
  import { resolve, dirname, join } from 'node:path';
2
2
  import { promisify } from 'node:util';
3
- import { writeFile as _writeFile, access, unlink as _unlink } from 'node:fs';
3
+ import { writeFile as _writeFile, unlink as _unlink } from 'node:fs';
4
4
  import { pathToFileURL } from 'node:url';
5
5
  import dayjs from 'dayjs';
6
6
  import intel from 'intel';
@@ -11,15 +11,6 @@ const unlink = promisify(_unlink);
11
11
 
12
12
  const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
13
13
 
14
- async function exists(path) {
15
- try {
16
- await access(path);
17
- return true;
18
- } catch {
19
- return false;
20
- }
21
- }
22
-
23
14
  async function loadFile(script, options, throwError) {
24
15
  // if the script is an AsyncFunction, return it.
25
16
  if (script instanceof AsyncFunction) {
@@ -42,15 +33,16 @@ async function loadFile(script, options, throwError) {
42
33
  // Hack a way! Try to add a package.json file in the same folder as the
43
34
  // script file. That way, the .js file will be treated as commonjs =
44
35
  // working as before we moved Browsertime to module
36
+
45
37
  let createdPackageJson = false;
46
38
  try {
47
39
  const packageJson = join(dirname(resolve(script)), 'package.json');
48
- if (!(await exists(packageJson))) {
40
+ try {
49
41
  await writeFile(packageJson, '{}');
50
42
  createdPackageJson = true;
51
- } else {
43
+ } catch {
52
44
  log.error(
53
- 'Could not create package.json file, the %S file will be treated as a esmodule',
45
+ 'Could not create package.json file, the %s file will be treated as a esmodule',
54
46
  script
55
47
  );
56
48
  }
@@ -29,7 +29,7 @@ const sizeMap = {
29
29
  'Samsung Galaxy S8+': '412x846'
30
30
  };
31
31
 
32
- export default function (options) {
32
+ export function getViewPort(options) {
33
33
  // you cannot set the width/height for phone so just keep the viewport undefined
34
34
  if (isAndroidConfigured(options)) {
35
35
  return;
@@ -4,7 +4,7 @@ import pick from 'lodash.pick';
4
4
  import isEmpty from 'lodash.isempty';
5
5
  import get from 'lodash.get';
6
6
  import dayjs from 'dayjs';
7
- import pathToFolder from '../pathToFolder.js';
7
+ import { pathToFolder } from '../pathToFolder.js';
8
8
  import intel from 'intel';
9
9
  const log = intel.getLogger('browsertime');
10
10
  const require = createRequire(import.meta.url);
@@ -22,7 +22,7 @@ function generateUniquePageId(baseId, existingIdMap) {
22
22
  function getDocumentRequests(entries, pageId) {
23
23
  let pageEntries = [...entries];
24
24
  if (pageId) {
25
- pageEntries = [...entries.filter(entry => entry.pageref === pageId)];
25
+ pageEntries = entries.filter(entry => entry.pageref === pageId);
26
26
  }
27
27
 
28
28
  const requests = [];
@@ -199,11 +199,9 @@ export function getMainDocumentTimings(har) {
199
199
  let pageEntries = [...entries];
200
200
  const finalURL = getFinalURL(pageEntries, pageId);
201
201
 
202
- pageEntries = [
203
- ...pageEntries.filter(
204
- entry => entry.pageref === pageId && entry.request.url === finalURL
205
- )
206
- ];
202
+ pageEntries = pageEntries.filter(
203
+ entry => entry.pageref === pageId && entry.request.url === finalURL
204
+ );
207
205
 
208
206
  timings.push({ url, timings: pageEntries[0].timings });
209
207
  }
@@ -219,7 +217,7 @@ export function getFullyLoaded(har) {
219
217
  const url = page._url;
220
218
 
221
219
  let pageEntries = [...entries];
222
- pageEntries = [...pageEntries.filter(entry => entry.pageref === pageId)];
220
+ pageEntries = pageEntries.filter(entry => entry.pageref === pageId);
223
221
 
224
222
  let pageEnd = 0;
225
223
  for (let entry of pageEntries) {
@@ -1,5 +1,5 @@
1
1
  import { join } from 'node:path';
2
- import pathToFolder from '../pathToFolder.js';
2
+ import { pathToFolder } from '../pathToFolder.js';
3
3
 
4
4
  export async function savePngWithoutResize(
5
5
  name,