224 lines
6.6 KiB
JavaScript
224 lines
6.6 KiB
JavaScript
import path from 'node:path';
|
|
import merge from 'lodash.merge';
|
|
import set from 'lodash.set';
|
|
import get from 'lodash.get';
|
|
import coach from 'coach-core';
|
|
import { BrowsertimeEngine, browserScripts } from 'browsertime';
|
|
const { getDomAdvice } = coach;
|
|
import { getLogger } from '@sitespeed.io/log';
|
|
const log = getLogger('plugin.browsertime');
|
|
|
|
const defaultBrowsertimeOptions = {
|
|
statistics: true
|
|
};
|
|
|
|
const delay = ms => new Promise(res => setTimeout(res, ms));
|
|
|
|
const iphone6UserAgent =
|
|
'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_3 like Mac OS X) AppleWebKit/536.26 ' +
|
|
'(KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25';
|
|
|
|
async function preWarmServer(urls, options, scriptOrMultiple) {
|
|
const preWarmOptions = {
|
|
browser: options.browser,
|
|
iterations: 1,
|
|
xvfb: options.xvfb,
|
|
docker: options.docker,
|
|
headless: options.headless
|
|
};
|
|
|
|
if (options.requestheader) {
|
|
preWarmOptions['requestheader'] = options.requestheader;
|
|
}
|
|
|
|
if (options.cookie) {
|
|
preWarmOptions['cookie'] = options.cookie;
|
|
}
|
|
|
|
if (options.android.enabled) {
|
|
preWarmOptions.android = options.android;
|
|
const chromeDevice = get(options, 'chrome.android.deviceSerial');
|
|
const firefoxDevice = get(options, 'firefox.android.deviceSerial');
|
|
if (options.browser === 'chrome') {
|
|
set(preWarmOptions, 'chrome.android.package', 'com.android.chrome');
|
|
}
|
|
if (chromeDevice) {
|
|
set(preWarmOptions, 'chrome.android.deviceSerial', chromeDevice);
|
|
} else if (firefoxDevice) {
|
|
set(preWarmOptions, 'firefox.android.deviceSerial', firefoxDevice);
|
|
}
|
|
}
|
|
|
|
const safariIos = get(options, 'safari.ios');
|
|
const safariDeviceName = get(options, 'safari.deviceName');
|
|
const safariDeviceUDID = get(options, 'safari.deviceUDID ');
|
|
|
|
if (safariIos) {
|
|
set(preWarmOptions, 'safari.ios', true);
|
|
if (safariDeviceName) {
|
|
set(preWarmOptions, 'safari.deviceName', safariDeviceName);
|
|
}
|
|
if (safariDeviceUDID) {
|
|
set(preWarmOptions, 'safari.deviceUDID', safariDeviceUDID);
|
|
}
|
|
}
|
|
|
|
if (options.gnirehtet) {
|
|
preWarmOptions.gnirehtet = options.gnirehtet;
|
|
}
|
|
|
|
const engine = new BrowsertimeEngine(preWarmOptions);
|
|
|
|
await engine.start();
|
|
log.info('Start pre-testing/warming ' + urls);
|
|
await (scriptOrMultiple
|
|
? engine.runMultiple(urls, {})
|
|
: engine.run(urls, {}));
|
|
await engine.stop();
|
|
log.info('Pre-testing done, closed the browser.');
|
|
return delay(options.preWarmServerWaitTime || 5000);
|
|
}
|
|
|
|
async function extraProfileRun(urls, options, scriptOrMultiple) {
|
|
log.info('Make one extra run to collect trace/video information');
|
|
options.iterations = 1;
|
|
if (options.enableProfileRun) {
|
|
if (options.browser === 'firefox') {
|
|
options.firefox.geckoProfiler = true;
|
|
options.firefox.collectMozLog = true;
|
|
} else if (options.browser === 'chrome') {
|
|
options.chrome.enableTraceScreenshots = true;
|
|
options.chrome.traceCategory = ['disabled-by-default-v8.cpu_profiler'];
|
|
options.chrome.timeline = true;
|
|
}
|
|
}
|
|
if (options.enableVideoRun) {
|
|
options.video = true;
|
|
options.visualMetrics = true;
|
|
} else {
|
|
options.video = false;
|
|
options.visualMetrics = false;
|
|
}
|
|
const traceEngine = new BrowsertimeEngine(options);
|
|
const scriptCategories = await browserScripts.allScriptCategories();
|
|
let scriptsByCategory =
|
|
await browserScripts.getScriptsForCategories(scriptCategories);
|
|
|
|
await traceEngine.start();
|
|
await (scriptOrMultiple
|
|
? traceEngine.runMultiple(urls, scriptsByCategory)
|
|
: traceEngine.run(urls, scriptsByCategory));
|
|
await traceEngine.stop();
|
|
}
|
|
|
|
async function parseUserScripts(scripts) {
|
|
const { browserScripts } = await import('browsertime');
|
|
if (!Array.isArray(scripts)) scripts = [scripts];
|
|
const allUserScripts = {};
|
|
for (let script of scripts) {
|
|
let myScript = await browserScripts.findAndParseScripts(
|
|
path.resolve(script),
|
|
'custom'
|
|
);
|
|
if (!myScript['custom']) {
|
|
myScript = { custom: Object.values(myScript)[0] };
|
|
}
|
|
merge(allUserScripts, myScript);
|
|
}
|
|
return allUserScripts;
|
|
}
|
|
|
|
async function addCoachScripts(scripts) {
|
|
const coachAdvice = await getDomAdvice();
|
|
scripts.coach = {
|
|
coachAdvice: coachAdvice
|
|
};
|
|
return scripts;
|
|
}
|
|
|
|
function addExtraScripts(scriptsByCategory, pluginScripts) {
|
|
// For all different script in the array
|
|
for (let scripts of pluginScripts) {
|
|
// and then for all scripts in that category
|
|
for (const [name, script] of Object.entries(scripts.scripts)) {
|
|
set(scriptsByCategory, scripts.category + '.' + name, script);
|
|
}
|
|
}
|
|
return scriptsByCategory;
|
|
}
|
|
|
|
function setupAsynScripts(asyncScripts) {
|
|
const allAsyncScripts = {};
|
|
// For all different script in the array
|
|
for (let scripts of asyncScripts) {
|
|
// and then for all scripts in that category
|
|
for (const [name, script] of Object.entries(scripts.scripts)) {
|
|
set(allAsyncScripts, scripts.category + '.' + name, script);
|
|
}
|
|
}
|
|
return allAsyncScripts;
|
|
}
|
|
|
|
export async function analyzeUrl(
|
|
url,
|
|
scriptOrMultiple,
|
|
pluginScripts,
|
|
pluginAsyncScripts,
|
|
options
|
|
) {
|
|
const btOptions = merge({}, defaultBrowsertimeOptions, options);
|
|
|
|
// set mobile options
|
|
if (options.mobile) {
|
|
btOptions.viewPort = '360x640';
|
|
if (btOptions.browser === 'chrome' || btOptions.browser === 'edge') {
|
|
const emulation = get(
|
|
btOptions,
|
|
'chrome.mobileEmulation.deviceName',
|
|
'Moto G4'
|
|
);
|
|
btOptions.chrome.mobileEmulation = {
|
|
deviceName: emulation
|
|
};
|
|
} else {
|
|
btOptions.userAgent = iphone6UserAgent;
|
|
}
|
|
}
|
|
const scriptCategories = await browserScripts.allScriptCategories();
|
|
let scriptsByCategory =
|
|
await browserScripts.getScriptsForCategories(scriptCategories);
|
|
|
|
if (btOptions.script) {
|
|
const userScripts = await parseUserScripts(btOptions.script);
|
|
scriptsByCategory = merge(scriptsByCategory, userScripts);
|
|
}
|
|
|
|
if (btOptions.coach) {
|
|
scriptsByCategory = addCoachScripts(scriptsByCategory);
|
|
}
|
|
scriptsByCategory = await addExtraScripts(scriptsByCategory, pluginScripts);
|
|
|
|
if (btOptions.preWarmServer) {
|
|
await preWarmServer(url, btOptions, scriptOrMultiple);
|
|
}
|
|
|
|
const engine = new BrowsertimeEngine(btOptions);
|
|
|
|
const asyncScript =
|
|
pluginAsyncScripts.length > 0
|
|
? await setupAsynScripts(pluginAsyncScripts)
|
|
: undefined;
|
|
|
|
try {
|
|
await engine.start();
|
|
return await (scriptOrMultiple
|
|
? engine.runMultiple(url, scriptsByCategory, asyncScript)
|
|
: engine.run(url, scriptsByCategory, asyncScript));
|
|
} finally {
|
|
await engine.stop();
|
|
if (btOptions.enableProfileRun || btOptions.enableVideoRun) {
|
|
await extraProfileRun(url, btOptions, scriptOrMultiple);
|
|
}
|
|
}
|
|
}
|