browsertime 17.0.0-beta.3 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -12
- package/bin/browsertime.js +5 -8
- package/bin/browsertimeWebPageReplay.js +2 -2
- package/index.js +2 -2
- package/lib/android/gnirehtet.js +1 -2
- package/lib/android/root.js +57 -16
- package/lib/chrome/chromeDevtoolsProtocol.js +2 -3
- package/lib/chrome/har.js +1 -1
- package/lib/chrome/longTaskMetrics.js +1 -1
- package/lib/chrome/parseCpuTrace.js +2 -2
- package/lib/chrome/settings/chromeAndroidOptions.js +1 -1
- package/lib/chrome/settings/chromeDesktopOptions.js +1 -1
- package/lib/chrome/settings/traceCategories.js +1 -1
- package/lib/chrome/traceCategoriesParser.js +3 -3
- package/lib/chrome/webdriver/builder.js +1 -1
- package/lib/chrome/webdriver/chromium.js +13 -17
- package/lib/chrome/webdriver/setupChromiumOptions.js +18 -13
- package/lib/connectivity/humble.js +10 -12
- package/lib/connectivity/index.js +1 -1
- package/lib/core/engine/collector.js +2 -4
- package/lib/core/engine/command/addText.js +1 -2
- package/lib/core/engine/command/android.js +1 -1
- package/lib/core/engine/command/cache.js +1 -2
- package/lib/core/engine/command/chromeDevToolsProtocol.js +1 -2
- package/lib/core/engine/command/click.js +1 -2
- package/lib/core/engine/command/debug.js +1 -3
- package/lib/core/engine/command/javaScript.js +1 -2
- package/lib/core/engine/command/measure.js +13 -15
- package/lib/core/engine/command/meta.js +1 -1
- package/lib/core/engine/command/navigation.js +1 -1
- package/lib/core/engine/command/screenshot.js +1 -1
- package/lib/core/engine/command/scroll.js +1 -1
- package/lib/core/engine/command/select.js +1 -1
- package/lib/core/engine/command/set.js +1 -1
- package/lib/core/engine/command/stopWatch.js +1 -1
- package/lib/core/engine/command/switch.js +44 -1
- package/lib/core/engine/command/util/lcpHighlightScript.js +1 -1
- package/lib/core/engine/command/util/lsHighlightScript.js +1 -1
- package/lib/core/engine/command/wait.js +1 -1
- package/lib/core/engine/index.js +33 -15
- package/lib/core/engine/iteration.js +35 -42
- package/lib/core/engine/run.js +1 -1
- package/lib/core/pageCompleteChecks/defaultPageCompleteCheck.js +1 -1
- package/lib/core/pageCompleteChecks/pageCompleteCheckByInactivity.js +1 -1
- package/lib/core/pageCompleteChecks/spaInactivity.js +1 -1
- package/lib/core/seleniumRunner.js +9 -11
- package/lib/edge/webdriver/builder.js +1 -1
- package/lib/extensionserver/index.js +2 -4
- package/lib/extensionserver/setup.js +1 -1
- package/lib/firefox/geckoProfiler.js +46 -16
- package/lib/firefox/getHAR.js +1 -1
- package/lib/firefox/memoryReport.js +1 -1
- package/lib/firefox/settings/disableSafeBrowsingPreferences.js +1 -1
- package/lib/firefox/settings/disableTrackingProtectionPreferences.js +1 -1
- package/lib/firefox/settings/firefoxPreferences.js +1 -1
- package/lib/firefox/webdriver/builder.js +9 -9
- package/lib/firefox/webdriver/firefox.js +24 -6
- package/lib/safari/webdriver/safari.js +1 -1
- package/lib/screenshot/defaults.js +1 -1
- package/lib/screenshot/index.js +3 -5
- package/lib/support/cli.js +11 -5
- package/lib/support/dns.js +1 -1
- package/lib/support/engineUtils.js +60 -66
- package/lib/support/getViewPort.js +1 -1
- package/lib/support/har/index.js +6 -8
- package/lib/support/images/index.js +1 -1
- package/lib/support/logging.js +1 -1
- package/lib/support/pathToFolder.js +4 -4
- package/lib/support/preURL.js +1 -1
- package/lib/support/processes.js +1 -1
- package/lib/support/setResourceTimingBufferSize.js +1 -1
- package/lib/support/stop.js +1 -1
- package/lib/support/storageManager.js +1 -3
- package/lib/support/tcpdump.js +2 -2
- package/lib/support/xvfb.js +2 -4
- package/lib/video/postprocessing/finetune/addTextToVideo.js +3 -3
- package/lib/video/postprocessing/finetune/convertFps.js +1 -1
- package/lib/video/postprocessing/finetune/getFont.js +1 -1
- package/lib/video/postprocessing/finetune/getTimingMetrics.js +2 -2
- package/lib/video/postprocessing/finetune/index.js +4 -4
- package/lib/video/postprocessing/finetune/removeOrange.js +1 -1
- package/lib/video/postprocessing/visualmetrics/extraMetrics.js +1 -1
- package/lib/video/postprocessing/visualmetrics/getVideoMetrics.js +2 -2
- package/lib/video/screenRecording/android/recorder.js +1 -1
- package/lib/video/screenRecording/desktop/convert.js +1 -1
- package/lib/video/screenRecording/desktop/desktopRecorder.js +3 -3
- package/lib/video/screenRecording/desktop/ffmpegRecorder.js +6 -6
- package/lib/video/screenRecording/desktop/osx/getSPDisplaysDataType.js +1 -1
- package/lib/video/screenRecording/desktop/osx/getScreen.js +1 -1
- package/lib/video/screenRecording/firefox/firefoxWindowRecorder.js +1 -1
- package/lib/video/screenRecording/ios/convertToMp4.js +1 -1
- package/lib/video/screenRecording/ios/iosRecorder.js +2 -2
- package/lib/video/screenRecording/iosSimulator/convertToMp4.js +1 -1
- package/lib/video/screenRecording/iosSimulator/recorder.js +2 -2
- package/lib/video/screenRecording/recorder.js +6 -6
- package/lib/video/screenRecording/setOrangeBackground.js +1 -1
- package/lib/video/video.js +9 -11
- package/package.json +17 -17
|
@@ -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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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;
|
package/lib/firefox/getHAR.js
CHANGED
|
@@ -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
|
|
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));
|
|
@@ -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 (
|
|
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 =
|
|
148
|
-
?
|
|
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
|
|
5
|
+
export class Safari {
|
|
6
6
|
constructor(storageManager, options) {
|
|
7
7
|
this.options = options;
|
|
8
8
|
this.safariOptions = options.safari || {};
|
package/lib/screenshot/index.js
CHANGED
|
@@ -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
|
|
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({},
|
|
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;
|
package/lib/support/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
threads,
|
|
14
14
|
xvfbDisplay
|
|
15
15
|
} from '../video/defaults.js';
|
|
16
|
-
import
|
|
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:
|
|
843
|
+
default: screenshotDefaults.type,
|
|
844
844
|
group: 'Screenshot'
|
|
845
845
|
})
|
|
846
846
|
.option('screenshotParams.png.compressionLevel', {
|
|
847
847
|
describe: 'zlib compression level',
|
|
848
|
-
default:
|
|
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:
|
|
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:
|
|
858
|
+
default: screenshotDefaults.maxSize,
|
|
859
859
|
group: 'Screenshot'
|
|
860
860
|
})
|
|
861
861
|
.option('pageCompleteCheck', {
|
|
@@ -1194,6 +1194,12 @@ export function parseCommandLine() {
|
|
|
1194
1194
|
type: 'boolean',
|
|
1195
1195
|
default: false
|
|
1196
1196
|
})
|
|
1197
|
+
.option('cjs', {
|
|
1198
|
+
describe:
|
|
1199
|
+
'Load scripting files that ends with .js as common js. Default (false) loads files as esmodules.',
|
|
1200
|
+
type: 'boolean',
|
|
1201
|
+
default: false
|
|
1202
|
+
})
|
|
1197
1203
|
.option('browserRestartTries', {
|
|
1198
1204
|
type: 'number',
|
|
1199
1205
|
default: 3,
|
package/lib/support/dns.js
CHANGED
|
@@ -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
|
|
6
|
+
export async function flushDNS(options) {
|
|
7
7
|
if (isAndroidConfigured(options)) {
|
|
8
8
|
return;
|
|
9
9
|
}
|
|
@@ -1,83 +1,62 @@
|
|
|
1
|
-
import { resolve,
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import { resolve, dirname, join } from 'node:path';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { writeFile as _writeFile, unlink as _unlink } from 'node:fs';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
4
5
|
import dayjs from 'dayjs';
|
|
5
6
|
import intel from 'intel';
|
|
6
7
|
import { toArray } from '../support/util.js';
|
|
7
|
-
import { copyFile, copyFileSync } from './fileUtil.js';
|
|
8
|
-
import { realpathSync, mkdtempSync, rmdirSync } from 'node:fs';
|
|
9
|
-
const require = createRequire(import.meta.url);
|
|
10
8
|
const log = intel.getLogger('browsertime');
|
|
9
|
+
const writeFile = promisify(_writeFile);
|
|
10
|
+
const unlink = promisify(_unlink);
|
|
11
11
|
|
|
12
12
|
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
|
|
13
13
|
|
|
14
|
-
function
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function loadPrePostScriptsSync(scripts) {
|
|
19
|
-
return toArray(scripts).map(script => {
|
|
20
|
-
// if the script is an AsyncFunction, return it.
|
|
21
|
-
if (script instanceof AsyncFunction) {
|
|
22
|
-
return script;
|
|
23
|
-
}
|
|
24
|
-
try {
|
|
25
|
-
if (script.endsWith('.js')) {
|
|
26
|
-
const dir = getTmp();
|
|
27
|
-
const name = join(dir, 'tmp.cjs');
|
|
28
|
-
copyFileSync(script, name);
|
|
29
|
-
const data = require(resolve(name));
|
|
30
|
-
rmdirSync(dir, { recursive: true });
|
|
31
|
-
return data;
|
|
32
|
-
} else {
|
|
33
|
-
return require(resolve(script));
|
|
34
|
-
}
|
|
35
|
-
} catch (error) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
"Couldn't run pre/post script file: " + resolve(script) + ' ' + error
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function loadScriptSync(script) {
|
|
14
|
+
async function loadFile(script, options, throwError) {
|
|
15
|
+
// if the script is an AsyncFunction, return it.
|
|
44
16
|
if (script instanceof AsyncFunction) {
|
|
45
17
|
return script;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (script.endsWith('.js')) {
|
|
49
|
-
const dir = getTmp();
|
|
50
|
-
const name = join(dir, 'tmp.cjs');
|
|
51
|
-
copyFileSync(script, name);
|
|
52
|
-
const data = require(resolve(name));
|
|
53
|
-
rmdirSync(dir, { recursive: true });
|
|
54
|
-
return data;
|
|
55
|
-
} else {
|
|
56
|
-
return require(resolve(script));
|
|
57
|
-
}
|
|
58
|
-
} catch {
|
|
59
|
-
// assume the script is a valid inline script by default
|
|
18
|
+
} else if (!script.endsWith('js')) {
|
|
19
|
+
// This is an inline script
|
|
60
20
|
return script;
|
|
61
21
|
}
|
|
62
|
-
|
|
22
|
+
// If we follow correct naming for modules (.mjs) or commonjs (.cjs)
|
|
23
|
+
// we just loads it. .js files is loaded as commonjs if you set
|
|
24
|
+
// --cjs true
|
|
25
|
+
if (
|
|
26
|
+
options.cjs === false ||
|
|
27
|
+
script.endsWith('.mjs') ||
|
|
28
|
+
script.endsWith('.cjs')
|
|
29
|
+
) {
|
|
30
|
+
let myFunction = await import(pathToFileURL(resolve(script)));
|
|
31
|
+
return myFunction.default ?? myFunction;
|
|
32
|
+
} else {
|
|
33
|
+
// Hack a way! Try to add a package.json file in the same folder as the
|
|
34
|
+
// script file. That way, the .js file will be treated as commonjs =
|
|
35
|
+
// working as before we moved Browsertime to module
|
|
63
36
|
|
|
64
|
-
|
|
65
|
-
if (script) {
|
|
66
|
-
// if the script is an AsyncFunction, return it.
|
|
67
|
-
if (script instanceof AsyncFunction) {
|
|
68
|
-
return script;
|
|
69
|
-
}
|
|
37
|
+
let createdPackageJson = false;
|
|
70
38
|
try {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
39
|
+
const packageJson = join(dirname(resolve(script)), 'package.json');
|
|
40
|
+
try {
|
|
41
|
+
await writeFile(packageJson, '{}');
|
|
42
|
+
createdPackageJson = true;
|
|
43
|
+
} catch {
|
|
44
|
+
log.error(
|
|
45
|
+
'Could not create package.json file, the %s file will be treated as a esmodule',
|
|
46
|
+
script
|
|
47
|
+
);
|
|
80
48
|
}
|
|
49
|
+
const myFunction = await import(resolve(script));
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
if (createdPackageJson) {
|
|
53
|
+
await unlink(packageJson);
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
log.error(error);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return myFunction.default ?? myFunction;
|
|
81
60
|
} catch (error) {
|
|
82
61
|
// assume the script is a valid inline script by default
|
|
83
62
|
if (throwError) {
|
|
@@ -90,6 +69,21 @@ export async function loadScript(script, throwError) {
|
|
|
90
69
|
}
|
|
91
70
|
}
|
|
92
71
|
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function loadPrePostScripts(scripts, options) {
|
|
75
|
+
const readScripts = [];
|
|
76
|
+
for (let script of toArray(scripts)) {
|
|
77
|
+
const loadedScript = await loadFile(script, options, true);
|
|
78
|
+
readScripts.push(loadedScript);
|
|
79
|
+
}
|
|
80
|
+
return readScripts;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function loadScript(script, options, throwError) {
|
|
84
|
+
if (script) {
|
|
85
|
+
return loadFile(script, options, throwError);
|
|
86
|
+
}
|
|
93
87
|
return script;
|
|
94
88
|
}
|
|
95
89
|
export function timestamp() {
|
|
@@ -29,7 +29,7 @@ const sizeMap = {
|
|
|
29
29
|
'Samsung Galaxy S8+': '412x846'
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
export
|
|
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;
|
package/lib/support/har/index.js
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
204
|
-
|
|
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 =
|
|
220
|
+
pageEntries = pageEntries.filter(entry => entry.pageref === pageId);
|
|
223
221
|
|
|
224
222
|
let pageEnd = 0;
|
|
225
223
|
for (let entry of pageEntries) {
|
package/lib/support/logging.js
CHANGED
|
@@ -9,7 +9,7 @@ function toSafeKey(key) {
|
|
|
9
9
|
return key.replace(/[ %&()+,./:?|~–]|%7C/g, '-');
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export
|
|
12
|
+
export function pathToFolder(url, options) {
|
|
13
13
|
if (options.useSameDir) {
|
|
14
14
|
return '';
|
|
15
15
|
} else {
|
|
@@ -40,9 +40,7 @@ export default function (url, options) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// This is used from sitespeed.io to match URLs on Graphite
|
|
43
|
-
if (
|
|
44
|
-
pathSegments.push(...urlSegments);
|
|
45
|
-
} else {
|
|
43
|
+
if (options.storeURLsAsFlatPageOnDisk) {
|
|
46
44
|
const folder = toSafeKey(urlSegments.join('_').concat('_'));
|
|
47
45
|
if (folder.length > 255) {
|
|
48
46
|
log.info(
|
|
@@ -52,6 +50,8 @@ export default function (url, options) {
|
|
|
52
50
|
} else {
|
|
53
51
|
pathSegments.push(folder);
|
|
54
52
|
}
|
|
53
|
+
} else {
|
|
54
|
+
pathSegments.push(...urlSegments);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
package/lib/support/preURL.js
CHANGED
|
@@ -2,7 +2,7 @@ import intel from 'intel';
|
|
|
2
2
|
const log = intel.getLogger('browsertime');
|
|
3
3
|
const delay = ms => new Promise(res => setTimeout(res, ms));
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export async function preURL(browser, options) {
|
|
6
6
|
log.info('Accessing preURL %s', options.preURL);
|
|
7
7
|
await browser.loadAndWait(options.preURL);
|
|
8
8
|
if (!options.preURLDisableWhiteBackground) {
|
package/lib/support/processes.js
CHANGED
package/lib/support/stop.js
CHANGED
|
@@ -2,7 +2,7 @@ import { execa } from 'execa';
|
|
|
2
2
|
import intel from 'intel';
|
|
3
3
|
const log = intel.getLogger('browsertime');
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export async function stop(processName) {
|
|
6
6
|
const scriptArguments = ['-9', processName];
|
|
7
7
|
|
|
8
8
|
log.debug('Kill all processes ' + processName);
|
|
@@ -49,7 +49,7 @@ async function streamToString(stream) {
|
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
class StorageManager {
|
|
52
|
+
export class StorageManager {
|
|
53
53
|
constructor(url, { resultDir, prettyPrint = false } = {}) {
|
|
54
54
|
this.baseDir = resultDir
|
|
55
55
|
? _resolve(resultDir)
|
|
@@ -134,5 +134,3 @@ class StorageManager {
|
|
|
134
134
|
return this.baseDir;
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
-
|
|
138
|
-
export default StorageManager;
|
package/lib/support/tcpdump.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { execa } from 'execa';
|
|
3
|
-
import pathToFolder from './pathToFolder.js';
|
|
3
|
+
import { pathToFolder } from './pathToFolder.js';
|
|
4
4
|
import { rename } from './fileUtil.js';
|
|
5
5
|
|
|
6
|
-
export
|
|
6
|
+
export class TCPDump {
|
|
7
7
|
constructor(directory, options) {
|
|
8
8
|
this.baseDir = directory;
|
|
9
9
|
this.options = options;
|