browsertime 24.0.0-alpha.3 → 24.0.0-alpha.4

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 (37) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/bin/browsertime.js +12 -13
  3. package/lib/android/index.js +5 -5
  4. package/lib/chrome/longTaskMetrics.js +8 -5
  5. package/lib/chrome/networkManager.js +7 -3
  6. package/lib/chrome/webdriver/builder.js +3 -4
  7. package/lib/connectivity/humble.js +2 -2
  8. package/lib/connectivity/index.js +11 -3
  9. package/lib/core/engine/collector.js +31 -20
  10. package/lib/core/engine/command/measure.js +18 -6
  11. package/lib/core/engine/index.js +6 -3
  12. package/lib/core/seleniumRunner.js +2 -2
  13. package/lib/edge/webdriver/builder.js +4 -5
  14. package/lib/firefox/geckoProfiler.js +7 -7
  15. package/lib/firefox/networkManager.js +7 -3
  16. package/lib/firefox/webdriver/builder.js +14 -12
  17. package/lib/firefox/webdriver/firefox.js +5 -3
  18. package/lib/support/cli.js +24 -21
  19. package/lib/support/getViewPort.js +8 -4
  20. package/lib/support/har/index.js +7 -4
  21. package/lib/support/statistics.js +8 -7
  22. package/lib/support/util.js +53 -0
  23. package/lib/support/xvfb.js +4 -4
  24. package/lib/video/postprocessing/finetune/index.js +3 -3
  25. package/lib/video/postprocessing/visualmetrics/visualMetrics.js +14 -5
  26. package/lib/video/screenRecording/android/recorder.js +8 -3
  27. package/lib/video/screenRecording/desktop/desktopRecorder.js +9 -8
  28. package/lib/video/video.js +2 -3
  29. package/package.json +2 -4
  30. package/types/core/engine/command/measure.d.ts.map +1 -1
  31. package/types/support/getViewPort.d.ts.map +1 -1
  32. package/types/support/util.d.ts +6 -0
  33. package/types/support/util.d.ts.map +1 -1
  34. package/types/video/postprocessing/visualmetrics/visualMetrics.d.ts.map +1 -1
  35. package/types/video/screenRecording/android/recorder.d.ts.map +1 -1
  36. package/types/video/screenRecording/desktop/desktopRecorder.d.ts.map +1 -1
  37. package/types/video/video.d.ts.map +1 -1
@@ -6,11 +6,10 @@ import {
6
6
  } from 'selenium-webdriver/firefox.js';
7
7
  import { getLogger } from '@sitespeed.io/log';
8
8
  import { pac, manual } from 'selenium-webdriver/proxy.js';
9
- import get from 'lodash.get';
10
9
  import { defaultFirefoxPreferences } from '../settings/firefoxPreferences.js';
11
10
  import { disableSafeBrowsingPreferences } from '../settings/disableSafeBrowsingPreferences.js';
12
11
  import { disableTrackingProtectionPreferences } from '../settings/disableTrackingProtectionPreferences.js';
13
- import { toArray, pick, isEmpty } from '../../support/util.js';
12
+ import { toArray, pick, isEmpty, getProperty } from '../../support/util.js';
14
13
  import { Android, isAndroidConfigured } from '../../android/index.js';
15
14
  const log = getLogger('browsertime.firefox');
16
15
 
@@ -18,7 +17,7 @@ export async function configureBuilder(builder, baseDir, options) {
18
17
  // Here we configure everything that we need to start Firefox
19
18
  const firefoxConfig = options.firefox || {};
20
19
 
21
- let geckodriverPath = get(firefoxConfig, 'geckodriverPath');
20
+ let geckodriverPath = getProperty(firefoxConfig, 'geckodriverPath');
22
21
  if (!geckodriverPath) {
23
22
  const geckodriver = await import('@sitespeed.io/geckodriver');
24
23
  geckodriverPath = geckodriver.default.binPath();
@@ -35,7 +34,7 @@ export async function configureBuilder(builder, baseDir, options) {
35
34
  }
36
35
 
37
36
  // https://firefox-source-docs.mozilla.org/testing/geckodriver/Flags.html
38
- let geckodriverArguments = get(firefoxConfig, 'geckodriverArgs');
37
+ let geckodriverArguments = getProperty(firefoxConfig, 'geckodriverArgs');
39
38
  if (geckodriverArguments) {
40
39
  if (!Array.isArray(geckodriverArguments))
41
40
  geckodriverArguments = [geckodriverArguments];
@@ -48,10 +47,10 @@ export async function configureBuilder(builder, baseDir, options) {
48
47
 
49
48
  const ffOptions = new Options();
50
49
 
51
- const profileTemplate = get(firefoxConfig, 'profileTemplate');
50
+ const profileTemplate = getProperty(firefoxConfig, 'profileTemplate');
52
51
  if (profileTemplate) {
53
- log.info('Using profile %s', get(firefoxConfig, 'profileTemplate'));
54
- ffOptions.setProfile(get(firefoxConfig, 'profileTemplate'));
52
+ log.info('Using profile %s', getProperty(firefoxConfig, 'profileTemplate'));
53
+ ffOptions.setProfile(getProperty(firefoxConfig, 'profileTemplate'));
55
54
  }
56
55
 
57
56
  if (options.userAgent) {
@@ -135,10 +134,10 @@ export async function configureBuilder(builder, baseDir, options) {
135
134
  }
136
135
 
137
136
  let firefoxTypes = [
138
- get(firefoxConfig, 'binaryPath') ?? undefined,
139
- get(firefoxConfig, 'nightly') ? Channel.NIGHTLY : undefined,
140
- get(firefoxConfig, 'beta') ? Channel.BETA : undefined,
141
- get(firefoxConfig, 'developer') ? Channel.AURORA : undefined
137
+ getProperty(firefoxConfig, 'binaryPath') ?? undefined,
138
+ getProperty(firefoxConfig, 'nightly') ? Channel.NIGHTLY : undefined,
139
+ getProperty(firefoxConfig, 'beta') ? Channel.BETA : undefined,
140
+ getProperty(firefoxConfig, 'developer') ? Channel.AURORA : undefined
142
141
  ];
143
142
 
144
143
  firefoxTypes = firefoxTypes.filter(function (n) {
@@ -211,7 +210,10 @@ export async function configureBuilder(builder, baseDir, options) {
211
210
 
212
211
  builder
213
212
  .getCapabilities()
214
- .set('pageLoadStrategy', get(options, 'pageLoadStrategy', 'normal'));
213
+ .set(
214
+ 'pageLoadStrategy',
215
+ getProperty(options, 'pageLoadStrategy', 'normal')
216
+ );
215
217
 
216
218
  if (isAndroidConfigured(options)) {
217
219
  // Monkey patch to avoid changing `selenium-webdriver` before the
@@ -2,13 +2,15 @@ import { rename as _rename } from 'node:fs';
2
2
  import { promisify } from 'node:util';
3
3
  import path from 'node:path';
4
4
  import { getLogger } from '@sitespeed.io/log';
5
- import get from 'lodash.get';
6
5
  import { adapters } from 'ff-test-bidi-har-export';
7
6
  import { getEmptyHAR, mergeHars } from '../../support/har/index.js';
8
7
  import { loadUsbPowerProfiler } from '../../support/usbPower.js';
9
8
  import { pathToFolder } from '../../support/pathToFolder.js';
10
9
  import { isAndroidConfigured, Android } from '../../android/index.js';
11
- import { adjustVisualProgressTimestamps } from '../../support/util.js';
10
+ import {
11
+ adjustVisualProgressTimestamps,
12
+ getProperty
13
+ } from '../../support/util.js';
12
14
  import { findFiles } from '../../support/fileUtil.js';
13
15
  import { GeckoProfiler } from '../geckoProfiler.js';
14
16
  import { FirefoxBidi } from '../firefoxBidi.js';
@@ -233,7 +235,7 @@ export class Firefox {
233
235
  result.memory = await memoryReport.collect(index, url);
234
236
  }
235
237
 
236
- const useFirefoxAppConstants = get(
238
+ const useFirefoxAppConstants = getProperty(
237
239
  this.options || {},
238
240
  'firefox.appconstants',
239
241
  false
@@ -2,8 +2,6 @@ import { format } from 'node:util';
2
2
  import { readFileSync, statSync } from 'node:fs';
3
3
  import yargs from 'yargs';
4
4
  import { hideBin } from 'yargs/helpers';
5
- import get from 'lodash.get';
6
- import set from 'lodash.set';
7
5
  import {
8
6
  framerate,
9
7
  crf,
@@ -14,6 +12,7 @@ import {
14
12
  import { screenshotDefaults } from '../screenshot/defaults.js';
15
13
  import { geckoProfilerDefaults } from '../firefox/settings/geckoProfilerDefaults.js';
16
14
  import { findUpSync } from './fileUtil.js';
15
+ import { setProperty, getProperty } from './util.js';
17
16
  import { execaSync } from 'execa';
18
17
 
19
18
  const configPath = findUpSync(['.browsertime.json']);
@@ -1352,13 +1351,13 @@ export function parseCommandLine() {
1352
1351
  argv.browser = 'firefox';
1353
1352
  if (argv.android.enabled) {
1354
1353
  // TODO add support for Firefox dev
1355
- set(
1354
+ setProperty(
1356
1355
  argv,
1357
1356
  'firefox.android.package',
1358
1357
  argv.firefox.nightly ? 'org.mozilla.fenix' : 'org.mozilla.firefox_beta'
1359
1358
  );
1360
1359
 
1361
- set(
1360
+ setProperty(
1362
1361
  argv,
1363
1362
  'firefox.android.activity',
1364
1363
  'org.mozilla.fenix.IntentReceiverActivity'
@@ -1367,36 +1366,36 @@ export function parseCommandLine() {
1367
1366
  }
1368
1367
 
1369
1368
  if (argv.ios) {
1370
- set(argv, 'safari.ios', true);
1369
+ setProperty(argv, 'safari.ios', true);
1371
1370
  } else if (argv.android.enabled) {
1372
1371
  if (argv.browser === 'chrome') {
1373
1372
  // Default to Chrome Android.
1374
- set(
1373
+ setProperty(
1375
1374
  argv,
1376
1375
  'chrome.android.package',
1377
- get(argv, 'chrome.android.package', 'com.android.chrome')
1376
+ getProperty(argv, 'chrome.android.package', 'com.android.chrome')
1378
1377
  );
1379
1378
  } else if (argv.browser === 'edge') {
1380
- set(
1379
+ setProperty(
1381
1380
  argv,
1382
1381
  'chrome.android.package',
1383
- get(argv, 'chrome.android.package', 'com.microsoft.emmx')
1382
+ getProperty(argv, 'chrome.android.package', 'com.microsoft.emmx')
1384
1383
  );
1385
- set(
1384
+ setProperty(
1386
1385
  argv,
1387
1386
  'chrome.android.activity',
1388
- get(argv, 'chrome.android.activity', 'com.microsoft.ruby.Main')
1387
+ getProperty(argv, 'chrome.android.activity', 'com.microsoft.ruby.Main')
1389
1388
  );
1390
1389
  }
1391
1390
  }
1392
1391
 
1393
1392
  if (argv.safari && argv.safari.useSimulator) {
1394
- set(argv, 'connectivity.engine', 'throttle');
1393
+ setProperty(argv, 'connectivity.engine', 'throttle');
1395
1394
  }
1396
1395
 
1397
1396
  // Always use hash by default when you configure spa
1398
1397
  if (argv.spa) {
1399
- set(argv, 'useHash', true);
1398
+ setProperty(argv, 'useHash', true);
1400
1399
  }
1401
1400
 
1402
1401
  if (argv.urlAlias) {
@@ -1408,28 +1407,32 @@ export function parseCommandLine() {
1408
1407
  for (const [index, url] of urls.entries()) {
1409
1408
  urlMetaData[url] = argv.urlAlias[index];
1410
1409
  }
1411
- set(argv, 'urlMetaData', urlMetaData);
1410
+ setProperty(argv, 'urlMetaData', urlMetaData);
1412
1411
  }
1413
1412
 
1414
1413
  // Simplest way to just to get CPU metrics
1415
1414
  if (argv.cpu) {
1416
1415
  if (argv.browser === 'chrome' || argv.browser === 'edge') {
1417
- set(argv, 'chrome.timeline', true);
1416
+ setProperty(argv, 'chrome.timeline', true);
1418
1417
  } else if (argv.browser === 'firefox') {
1419
- set(argv, 'firefox.geckoProfiler', true);
1418
+ setProperty(argv, 'firefox.geckoProfiler', true);
1420
1419
  }
1421
1420
  }
1422
1421
 
1423
1422
  if (argv.docker) {
1424
- set(argv, 'video', get(argv, 'video', true));
1425
- set(argv, 'visualMetrics', get(argv, 'visualMetrics', true));
1423
+ setProperty(argv, 'video', getProperty(argv, 'video', true));
1424
+ setProperty(
1425
+ argv,
1426
+ 'visualMetrics',
1427
+ getProperty(argv, 'visualMetrics', true)
1428
+ );
1426
1429
  }
1427
1430
 
1428
1431
  // Set the timeouts to a maximum while debugging
1429
1432
  if (argv.debug) {
1430
- set(argv, 'timeouts.pageload', 2_147_483_647);
1431
- set(argv, 'timeouts.script', 2_147_483_647);
1432
- set(argv, 'timeouts.pageCompleteCheck', 2_147_483_647);
1433
+ setProperty(argv, 'timeouts.pageload', 2_147_483_647);
1434
+ setProperty(argv, 'timeouts.script', 2_147_483_647);
1435
+ setProperty(argv, 'timeouts.pageCompleteCheck', 2_147_483_647);
1433
1436
  }
1434
1437
 
1435
1438
  return {
@@ -1,6 +1,6 @@
1
- import get from 'lodash.get';
2
1
  import { getLogger } from '@sitespeed.io/log';
3
2
  import { isAndroidConfigured } from '../android/index.js';
3
+ import { getProperty } from '../support/util.js';
4
4
  const log = getLogger('browsertime');
5
5
 
6
6
  // Most use screen size as of 2018
@@ -40,13 +40,17 @@ export function getViewPort(options) {
40
40
  if (isAndroidConfigured(options)) {
41
41
  return;
42
42
  }
43
- const emulatedWidth = get(options, 'chrome.mobileEmulation.width');
44
- const emulatedHeight = get(options, 'chrome.mobileEmulation.height');
43
+ const emulatedWidth = getProperty(options, 'chrome.mobileEmulation.width');
44
+ const emulatedHeight = getProperty(options, 'chrome.mobileEmulation.height');
45
45
  if (emulatedWidth && emulatedHeight) {
46
46
  return `${emulatedWidth}x${emulatedHeight}`;
47
47
  }
48
48
 
49
- const deviceName = get(options, 'chrome.mobileEmulation.deviceName', false);
49
+ const deviceName = getProperty(
50
+ options,
51
+ 'chrome.mobileEmulation.deviceName',
52
+ false
53
+ );
50
54
  if (deviceName) {
51
55
  // TODO if it don't map ...
52
56
  const viewPort = sizeMap[deviceName];
@@ -1,13 +1,12 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import merge from 'lodash.merge';
3
- import get from 'lodash.get';
4
3
  import { pathToFolder } from '../pathToFolder.js';
5
4
  import { localTime } from '../util.js';
6
5
  import { getLogger } from '@sitespeed.io/log';
7
6
  const log = getLogger('browsertime');
8
7
  const require = createRequire(import.meta.url);
9
8
  const version = require('../../../package.json').version;
10
- import { pick, isEmpty } from '../../support/util.js';
9
+ import { pick, isEmpty, getProperty } from '../../support/util.js';
11
10
 
12
11
  function generateUniquePageId(baseId, existingIdMap) {
13
12
  let newId = baseId;
@@ -119,8 +118,12 @@ function addExtrasToHAR(
119
118
 
120
119
  function addMetaToHAR(index, harPage, url, browserScript, options) {
121
120
  const _meta = (harPage._meta = {});
122
- _meta.connectivity = get(options, 'connectivity.profile', 'native');
123
- _meta.connectivity = get(options, 'connectivity.alias', _meta.connectivity);
121
+ _meta.connectivity = getProperty(options, 'connectivity.profile', 'native');
122
+ _meta.connectivity = getProperty(
123
+ options,
124
+ 'connectivity.alias',
125
+ _meta.connectivity
126
+ );
124
127
 
125
128
  if (options.resultURL) {
126
129
  const base = options.resultURL.endsWith('/')
@@ -1,7 +1,8 @@
1
- import get from 'lodash.get';
2
- import set from 'lodash.set';
3
1
  import { Stats } from 'fast-stats';
4
2
  import { getLogger } from '@sitespeed.io/log';
3
+
4
+ import { getProperty, setProperty } from './util.js';
5
+
5
6
  const log = getLogger('browsertime');
6
7
 
7
8
  function validateType(value, type, message) {
@@ -29,9 +30,9 @@ function isNumeric(n) {
29
30
  }
30
31
 
31
32
  function addNumber(target, key, value) {
32
- let stats = get(target, key, new Stats());
33
+ let stats = getProperty(target, key, new Stats());
33
34
  stats.push(value);
34
- set(target, key, stats);
35
+ setProperty(target, key, stats);
35
36
  }
36
37
 
37
38
  export class Statistics {
@@ -128,7 +129,7 @@ export class Statistics {
128
129
  // eslint-disable-next-line unicorn/no-array-reduce
129
130
  return Object.keys(this.data).reduce((results, key) => {
130
131
  let decimals = options.decimals || 0;
131
- let stats = get(this.data, [key]);
132
+ let stats = getProperty(this.data, [key]);
132
133
  if (options.iqr && stats.median() !== 0) {
133
134
  // https://en.wikipedia.org/wiki/Interquartile_range
134
135
  stats = stats.iqr();
@@ -147,7 +148,7 @@ export class Statistics {
147
148
  let name = percentileName(p);
148
149
  node[name] = Number.parseFloat(stats.percentile(p).toFixed(decimals));
149
150
  }
150
- set(results, [key], node);
151
+ setProperty(results, [key], node);
151
152
 
152
153
  return results;
153
154
  }, {});
@@ -199,7 +200,7 @@ export class Statistics {
199
200
 
200
201
  function summarizeRecursive(target, keyPrefix, data) {
201
202
  if (data instanceof Stats) {
202
- set(target, keyPrefix, summarize(data));
203
+ setProperty(target, keyPrefix, summarize(data));
203
204
  } else if (typeof data === 'object') {
204
205
  for (const key of Object.keys(data)) {
205
206
  const path = [...keyPrefix, key];
@@ -335,3 +335,56 @@ function groupBy(array, property) {
335
335
  }
336
336
  return grouped;
337
337
  }
338
+
339
+ export function setProperty(object, path, value) {
340
+ if (typeof path === 'string') {
341
+ path = path.split('.');
342
+ }
343
+
344
+ if (!Array.isArray(path) || path.length === 0) {
345
+ return;
346
+ }
347
+
348
+ let current = object;
349
+
350
+ for (let index = 0; index < path.length - 1; index++) {
351
+ const key = path[index];
352
+
353
+ if (current[key] === undefined) {
354
+ current[key] = {};
355
+ }
356
+
357
+ current = current[key];
358
+ }
359
+
360
+ current[path.at(-1)] = value;
361
+ }
362
+
363
+ /**
364
+ * A replacement for lodash.get(object, path, [defaultValue]).
365
+ *
366
+ */
367
+ export function getProperty(object, path, defaultValue) {
368
+ // eslint-disable-next-line unicorn/no-null
369
+ if (object == null) {
370
+ return defaultValue;
371
+ }
372
+
373
+ if (!Array.isArray(path)) {
374
+ path = path
375
+ .replaceAll(/\[(.*?)\]/g, '.$1')
376
+ .split('.')
377
+ .filter(Boolean);
378
+ }
379
+
380
+ let result = object;
381
+ for (const key of path) {
382
+ // eslint-disable-next-line unicorn/no-null
383
+ if (result == null) {
384
+ return defaultValue;
385
+ }
386
+ result = result[key];
387
+ }
388
+
389
+ return result === undefined ? defaultValue : result;
390
+ }
@@ -1,7 +1,7 @@
1
1
  import { execa } from 'execa';
2
- import get from 'lodash.get';
3
2
  import { xvfbDisplay } from '../video/defaults.js';
4
3
  import { getViewPort } from '../support/getViewPort.js';
4
+ import { getProperty } from '../support/util.js';
5
5
  import { isAndroidConfigured } from '../android/index.js';
6
6
 
7
7
  function buildXvfbCommand({ display, screen = 0, size, silent }) {
@@ -29,7 +29,7 @@ export async function startXvfb({ size, options }) {
29
29
  xvfbSize = `${Number(width) + extraSizeInFirefox}x${height}`;
30
30
  }
31
31
 
32
- const display = get(options, 'xvfbParams.display', xvfbDisplay);
32
+ const display = getProperty(options, 'xvfbParams.display', xvfbDisplay);
33
33
  const silent = options.verbose >= 2 ? false : true;
34
34
 
35
35
  const { command, args } = buildXvfbCommand({
@@ -43,7 +43,7 @@ export async function startXvfb({ size, options }) {
43
43
  detached: true
44
44
  });
45
45
 
46
- const waitToSettle = get(options, 'xvfbParams.waitToSettle', 500);
46
+ const waitToSettle = getProperty(options, 'xvfbParams.waitToSettle', 500);
47
47
  await new Promise(resolve => {
48
48
  setTimeout(resolve, waitToSettle);
49
49
  });
@@ -76,7 +76,7 @@ export class XVFB {
76
76
  }
77
77
 
78
78
  async start() {
79
- const useXvfb = get(this.options, 'xvfb', false);
79
+ const useXvfb = getProperty(this.options, 'xvfb', false);
80
80
 
81
81
  if (
82
82
  (useXvfb === true || useXvfb === 'true') &&
@@ -6,12 +6,12 @@ import {
6
6
  } from 'node:fs';
7
7
  import { promisify } from 'node:util';
8
8
  import { getLogger } from '@sitespeed.io/log';
9
- import get from 'lodash.get';
10
9
  import { addTextToVideo } from './addTextToVideo.js';
11
10
  import { removeOrange } from './removeOrange.js';
12
11
  import { convert } from './convertFps.js';
13
12
  import { convert as _convert } from '../../defaults.js';
14
13
  import { isAndroidConfigured } from '../../../android/index.js';
14
+ import { getProperty } from '../../../support/util.js';
15
15
  const rename = promisify(_rename);
16
16
  const copyFile = promisify(_copyFile);
17
17
  const unlink = promisify(_unlink);
@@ -28,7 +28,7 @@ export async function finetuneVideo(
28
28
  const newStart = videoMetrics.videoRecordingStart / 1000;
29
29
  let temporaryFile = path.join(videoDir, 'tmp.mp4');
30
30
 
31
- if (get(options, 'videoParams.keepOriginalVideo', false)) {
31
+ if (getProperty(options, 'videoParams.keepOriginalVideo', false)) {
32
32
  const originalFile = path.join(videoDir, index + '-original.mp4');
33
33
  await copyFile(videoPath, originalFile);
34
34
  }
@@ -57,7 +57,7 @@ export async function finetuneVideo(
57
57
 
58
58
  if (
59
59
  isAndroidConfigured(options) &&
60
- get(options, 'videoParams.convert', _convert)
60
+ getProperty(options, 'videoParams.convert', _convert)
61
61
  ) {
62
62
  const temporaryFile2 = path.join(videoDir, 'tmp-60fps.mp4');
63
63
  await convert(temporaryFile, temporaryFile2, 60);
@@ -2,8 +2,9 @@ import { fileURLToPath } from 'node:url';
2
2
  import path from 'node:path';
3
3
  import { execa } from 'execa';
4
4
  import { getLogger } from '@sitespeed.io/log';
5
- import get from 'lodash.get';
6
5
  import { readFile, removeFile, copyFile } from '../../../support/fileUtil.js';
6
+ import { getProperty } from '../../../support/util.js';
7
+
7
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
9
  const log = getLogger('browsertime.video');
9
10
 
@@ -48,15 +49,23 @@ export async function run(
48
49
  visitedPageNumber,
49
50
  options
50
51
  ) {
51
- const filmstripQuality = get(options, 'videoParams.filmstripQuality', 75);
52
- const createFilmstrip = get(options, 'videoParams.createFilmstrip', true);
53
- const fullSizeFilmstrip = get(
52
+ const filmstripQuality = getProperty(
53
+ options,
54
+ 'videoParams.filmstripQuality',
55
+ 75
56
+ );
57
+ const createFilmstrip = getProperty(
58
+ options,
59
+ 'videoParams.createFilmstrip',
60
+ true
61
+ );
62
+ const fullSizeFilmstrip = getProperty(
54
63
  options,
55
64
  'videoParams.filmstripFullSize',
56
65
  false
57
66
  );
58
67
 
59
- const thumbsize = get(options, 'videoParams.thumbsize', 400);
68
+ const thumbsize = getProperty(options, 'videoParams.thumbsize', 400);
60
69
 
61
70
  const scriptArguments = [
62
71
  '--video',
@@ -1,17 +1,22 @@
1
1
  import { promisify } from 'node:util';
2
2
  import { unlink as _unlink } from 'node:fs';
3
- import get from 'lodash.get';
4
3
  import { getLogger } from '@sitespeed.io/log';
5
4
  import { framerate } from '../../defaults.js';
6
5
  import { Android } from '../../../android/index.js';
6
+ import { getProperty } from '../../../support/util.js';
7
+
7
8
  const unlink = promisify(_unlink);
8
9
  const log = getLogger('browsertime.video');
9
10
  const delay = ms => new Promise(res => setTimeout(res, ms));
10
11
 
11
12
  export class AndroidRecorder {
12
13
  constructor(options) {
13
- this.waitTime = get(options, 'videoParams.androidVideoWaitTime', 5000);
14
- this.framerate = get(options, 'videoParams.framerate', framerate);
14
+ this.waitTime = getProperty(
15
+ options,
16
+ 'videoParams.androidVideoWaitTime',
17
+ 5000
18
+ );
19
+ this.framerate = getProperty(options, 'videoParams.framerate', framerate);
15
20
  this.options = options;
16
21
  }
17
22
 
@@ -1,7 +1,6 @@
1
1
  import { promisify } from 'node:util';
2
2
  import { unlink as _unlink, rename as _rename } from 'node:fs';
3
3
  import { getLogger } from '@sitespeed.io/log';
4
- import get from 'lodash.get';
5
4
  import { start as _start, stop as _stop } from './ffmpegRecorder.js';
6
5
  import { convert } from './convert.js';
7
6
  import {
@@ -12,20 +11,22 @@ import {
12
11
  threads as _threads
13
12
  } from '../../defaults.js';
14
13
  import { getViewPort } from '../../../support/getViewPort.js';
14
+ import { getProperty } from '../../../support/util.js';
15
+
15
16
  const unlink = promisify(_unlink);
16
17
  const rename = promisify(_rename);
17
18
  const log = getLogger('browsertime.video');
18
19
 
19
20
  export class DesktopRecorder {
20
21
  constructor(options) {
21
- this.display = get(options, 'xvfbParams.display', xvfbDisplay);
22
- this.framerate = get(options, 'videoParams.framerate', _framerate);
23
- this.nice = get(options, 'videoParams.nice', 0);
24
- this.crf = get(options, 'videoParams.crf', _crf);
25
- this.convert = get(options, 'videoParams.convert', _convert);
26
- this.threads = get(options, 'videoParams.threads', _threads);
22
+ this.display = getProperty(options, 'xvfbParams.display', xvfbDisplay);
23
+ this.framerate = getProperty(options, 'videoParams.framerate', _framerate);
24
+ this.nice = getProperty(options, 'videoParams.nice', 0);
25
+ this.crf = getProperty(options, 'videoParams.crf', _crf);
26
+ this.convert = getProperty(options, 'videoParams.convert', _convert);
27
+ this.threads = getProperty(options, 'videoParams.threads', _threads);
27
28
  this.viewPort = getViewPort(options);
28
- this.taskset = get(options, 'videoParams.taskset');
29
+ this.taskset = getProperty(options, 'videoParams.taskset');
29
30
  this.origin = '0,0';
30
31
  this.offset = { x: 0, y: 0 };
31
32
  this.options = options;
@@ -1,11 +1,10 @@
1
1
  import path from 'node:path';
2
- import get from 'lodash.get';
3
2
  import { getRecorder } from './screenRecording/recorder.js';
4
3
  import { getVideoMetrics } from './postprocessing/visualmetrics/getVideoMetrics.js';
5
4
  import { finetuneVideo } from './postprocessing/finetune/index.js';
6
5
  import { rename, removeFile, removeDirAndFiles } from '../support/fileUtil.js';
7
6
  import { pathToFolder } from '../support/pathToFolder.js';
8
-
7
+ import { getProperty } from '../support/util.js';
9
8
  /**
10
9
  * Create a new Video that handles everything with the video
11
10
  * @class
@@ -74,7 +73,7 @@ export class Video {
74
73
 
75
74
  async cleanup() {
76
75
  // Keep or remove the original file
77
- if (get(this.options, 'videoParams.keepOriginalVideo', false)) {
76
+ if (getProperty(this.options, 'videoParams.keepOriginalVideo', false)) {
78
77
  const originalFile = path.join(
79
78
  this.videoDir,
80
79
  this.index + '-original.mp4'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "browsertime",
3
3
  "description": "Get performance metrics from your web page using Browsertime.",
4
- "version": "24.0.0-alpha.3",
4
+ "version": "24.0.0-alpha.4",
5
5
  "bin": "./bin/browsertime.js",
6
6
  "type": "module",
7
7
  "types": "./types/scripting.d.ts",
@@ -10,7 +10,7 @@
10
10
  "@sitespeed.io/chromedriver": "131.0.6778-69",
11
11
  "@sitespeed.io/edgedriver": "131.0.2903-112",
12
12
  "@sitespeed.io/geckodriver": "0.35.0-1",
13
- "@sitespeed.io/log": "0.2.3",
13
+ "@sitespeed.io/log": "0.2.4",
14
14
  "@sitespeed.io/throttle": "5.0.1",
15
15
  "@sitespeed.io/tracium": "0.3.3",
16
16
  "chrome-har": "1.0.1",
@@ -18,9 +18,7 @@
18
18
  "execa": "9.5.2",
19
19
  "fast-stats": "0.0.7",
20
20
  "ff-test-bidi-har-export": "0.0.17",
21
- "lodash.get": "4.4.2",
22
21
  "lodash.merge": "4.6.2",
23
- "lodash.set": "4.3.2",
24
22
  "selenium-webdriver": "4.27.0",
25
23
  "yargs": "17.7.2"
26
24
  },
@@ -1 +1 @@
1
- {"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../../../../lib/core/engine/command/measure.js"],"names":[],"mappings":"AAgCA;;;;;;GAMG;AACH;IACE,gQAuGC;IAxFC;;OAEG;IACH,gBAAsB;IACtB;;OAEG;IACH,0BAA0C;IAC1C;;OAEG;IACH,cAAkB;IAClB;;OAEG;IACH,eAAoB;IACpB;;OAEG;IACH,uBAAoC;IACpC;;OAEG;IACH,gBAAsB;IACtB;;OAEG;IACH,0BAA0C;IAC1C;;OAEG;IACH,uBAAoC;IACpC;;OAEG;IACH,oBAAyD;IACzD;;OAEG;IACH,eAAoB;IACpB;;OAEG;IACH,0BAA0C;IAC1C;;OAEG;IACH,+BAAoD;IACpD;;OAEG;IACH,uBAAoC;IACpC;;OAEG;IACH,gBAAsB;IACtB;;OAEG;IACH,8BAA8B;IAC9B;;OAEG;IACH,6BAA6B;IAC7B;;OAEG;IACH,uBAA2B;IAC3B;;OAEG;IACH,mBAAoB;IACpB;;OAEG;IACH,gBAA6D;IAC7D;;OAEG;IACH,2BAAsE;IACtE;;OAEG;IACH,uBAA8D;IAC9D;;OAEG;IACH,2BAAqE;IAGvE;;;OAGG;IACH,oBAsBC;IArBC,aAAuE;IAuBzE;;;OAGG;IACH,mBAKC;IAED;;;OAGG;IACH,eAyBC;IAED;;;OAGG;IACH,iBAQC;IAED;;;;;;;;;;;;OAYG;IACH,kBAmBC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,kBALW,MAAM,kBACN,MAAM,GAEJ,OAAO,CAAC,IAAI,CAAC,CAmFzB;IAED;;;;;;;;OAQG;IACH,0BAJW,MAAM,gBAiBhB;IACD;;;;;;;;;OASG;IACH,qBAJW,MAAM,gBAyEhB;IAED;;;;;;;OAOG;IACH,UAJW,MAAM,SACN,GAAC,QAWX;IAED;;;;;;;OAOG;IACH,6BAOC;IAED;;;OAGG;IACH,gBAoLC;CACF;sBA7qBqB,yBAAyB"}
1
+ {"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../../../../lib/core/engine/command/measure.js"],"names":[],"mappings":"AAgCA;;;;;;GAMG;AACH;IACE,gQA+GC;IAhGC;;OAEG;IACH,gBAAsB;IACtB;;OAEG;IACH,0BAA0C;IAC1C;;OAEG;IACH,cAAkB;IAClB;;OAEG;IACH,eAAoB;IACpB;;OAEG;IACH,uBAAoC;IACpC;;OAEG;IACH,gBAAsB;IACtB;;OAEG;IACH,0BAA0C;IAC1C;;OAEG;IACH,uBAAoC;IACpC;;OAEG;IACH,oBAAyD;IACzD;;OAEG;IACH,eAAoB;IACpB;;OAEG;IACH,0BAA0C;IAC1C;;OAEG;IACH,+BAAoD;IACpD;;OAEG;IACH,uBAAoC;IACpC;;OAEG;IACH,gBAAsB;IACtB;;OAEG;IACH,8BAA8B;IAC9B;;OAEG;IACH,6BAA6B;IAC7B;;OAEG;IACH,uBAA2B;IAC3B;;OAEG;IACH,mBAAoB;IACpB;;OAEG;IACH,gBAA6D;IAC7D;;OAEG;IACH,2BAIC;IACD;;OAEG;IACH,uBAAsE;IACtE;;OAEG;IACH,2BAIC;IAGH;;;OAGG;IACH,oBAsBC;IArBC,aAAuE;IAuBzE;;;OAGG;IACH,mBAKC;IAED;;;OAGG;IACH,eAyBC;IAED;;;OAGG;IACH,iBAQC;IAED;;;;;;;;;;;;OAYG;IACH,kBAmBC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,kBALW,MAAM,kBACN,MAAM,GAEJ,OAAO,CAAC,IAAI,CAAC,CAmFzB;IAED;;;;;;;;OAQG;IACH,0BAJW,MAAM,gBAiBhB;IACD;;;;;;;;;OASG;IACH,qBAJW,MAAM,gBAyEhB;IAED;;;;;;;OAOG;IACH,UAJW,MAAM,SACN,GAAC,QAWX;IAED;;;;;;;OAOG;IACH,6BAOC;IAED;;;OAGG;IACH,gBAwLC;CACF;sBA1rBqB,yBAAyB"}
@@ -1 +1 @@
1
- {"version":3,"file":"getViewPort.d.ts","sourceRoot":"","sources":["../../lib/support/getViewPort.js"],"names":[],"mappings":"AAqCA,+CAkCC"}
1
+ {"version":3,"file":"getViewPort.d.ts","sourceRoot":"","sources":["../../lib/support/getViewPort.js"],"names":[],"mappings":"AAqCA,+CAsCC"}
@@ -7,4 +7,10 @@ export function adjustVisualProgressTimestamps(visualProgress: any, profilerStar
7
7
  export function localTime(): string;
8
8
  export function pick(obj: any, keys: any): {};
9
9
  export function isEmpty(value: any): boolean;
10
+ export function setProperty(object: any, path: any, value: any): void;
11
+ /**
12
+ * A replacement for lodash.get(object, path, [defaultValue]).
13
+ *
14
+ */
15
+ export function getProperty(object: any, path: any, defaultValue: any): any;
10
16
  //# sourceMappingURL=util.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../lib/support/util.js"],"names":[],"mappings":"AAGA,oGAeC;AACD,qDA2LC;AACD,+CAQC;AACD,gEAeC;AACD,gEAsBC;AACD,0HAWC;AAED,oCAmBC;AAED,8CAWC;AAED,6CAsBC"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../lib/support/util.js"],"names":[],"mappings":"AAGA,oGAeC;AACD,qDA2LC;AACD,+CAQC;AACD,gEAeC;AACD,gEAsBC;AACD,0HAWC;AAED,oCAmBC;AAED,8CAWC;AAED,6CAsBC;AAcD,sEAsBC;AAED;;;GAGG;AACH,4EAuBC"}
@@ -1 +1 @@
1
- {"version":3,"file":"visualMetrics.d.ts","sourceRoot":"","sources":["../../../../lib/video/postprocessing/visualmetrics/visualMetrics.js"],"names":[],"mappings":"AAqCA,qFAEC;AACD,0KA6HC"}
1
+ {"version":3,"file":"visualMetrics.d.ts","sourceRoot":"","sources":["../../../../lib/video/postprocessing/visualmetrics/visualMetrics.js"],"names":[],"mappings":"AAsCA,qFAEC;AACD,0KAqIC"}
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../lib/video/screenRecording/android/recorder.js"],"names":[],"mappings":"AAUA;IACE,0BAIC;IAHC,cAAsE;IACtE,eAAiE;IACjE,aAAsB;IAGxB,sBAGC;IAFC,iBAAwC;IAI1C,qCAgBC;CACF;wBAlCuB,2BAA2B"}
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../lib/video/screenRecording/android/recorder.js"],"names":[],"mappings":"AAWA;IACE,0BAQC;IAPC,cAIC;IACD,eAAyE;IACzE,aAAsB;IAGxB,sBAGC;IAFC,iBAAwC;IAI1C,qCAgBC;CACF;wBAxCuB,2BAA2B"}
@@ -1 +1 @@
1
- {"version":3,"file":"desktopRecorder.d.ts","sourceRoot":"","sources":["../../../../lib/video/screenRecording/desktop/desktopRecorder.js"],"names":[],"mappings":"AAkBA;IACE,0BAYC;IAXC,aAA8D;IAC9D,eAAkE;IAClE,UAA+C;IAC/C,SAAgD;IAChD,aAA4D;IAC5D,aAA4D;IAC5D,cAAoC;IACpC,aAAkD;IAClD,eAAmB;IACnB;;;MAA4B;IAC5B,aAAsB;IAGxB;;;OAiBC;IAhBC,cAAoB;IAEpB;;;OAWE;IAKJ,sCAoCC;CACF"}
1
+ {"version":3,"file":"desktopRecorder.d.ts","sourceRoot":"","sources":["../../../../lib/video/screenRecording/desktop/desktopRecorder.js"],"names":[],"mappings":"AAmBA;IACE,0BAYC;IAXC,aAAsE;IACtE,eAA0E;IAC1E,UAAuD;IACvD,SAAwD;IACxD,aAAoE;IACpE,aAAoE;IACpE,cAAoC;IACpC,aAA0D;IAC1D,eAAmB;IACnB;;;MAA4B;IAC5B,aAAsB;IAGxB;;;OAiBC;IAhBC,cAAoB;IAEpB;;;OAWE;IAKJ,sCAoCC;CACF"}