sitespeed.io
Version:
sitespeed.io is an open-source tool for comprehensive web performance analysis, enabling you to test, monitor, and optimize your website’s speed using real browsers in various environments.
233 lines (212 loc) • 7 kB
JavaScript
#!/usr/bin/env node
import { readFileSync } from 'node:fs';
import merge from 'lodash.merge';
import set from 'lodash.set';
import get from 'lodash.get';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { BrowsertimeEngine, configureLogging } from 'browsertime';
import { getURLs } from '../lib/cli/util.js';
import { findUpSync } from '../lib/support/fileUtil.js';
import {config as browsertimeConfig} from '../lib/plugins/browsertime/index.js';
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';
const configPath = findUpSync(['.sitespeed.io.json']);
let config;
try {
config = configPath ? JSON.parse(readFileSync(configPath)) : {};
} catch (e) {
if (e instanceof SyntaxError) {
/* eslint no-console: off */
console.error(
'Could not parse the config JSON file ' +
configPath +
'. Is the file really valid JSON?'
);
}
throw e;
}
async function testURLs(engine, urls, isMulti) {
try {
await engine.start();
if(isMulti) {
const result = await engine.runMultiple(urls);
for (let errors of result[0].errors) {
if (errors.length > 0) {
process.exitCode = 1;
}
}
} else {
for (let url of urls) {
const result = await engine.run(url);
for (let errors of result[0].errors) {
if (errors.length > 0) {
process.exitCode = 1;
}
}
}
}
} finally {
engine.stop();
}
}
async function runBrowsertime() {
let yargsInstance = yargs(hideBin(process.argv));
let parsed = yargsInstance
.env('SITESPEED_IO')
.require(1, 'urlOrFile')
.option('browsertime.browser', {
alias: ['b', 'browser'],
default: browsertimeConfig.browser,
describe: 'Choose which Browser to use when you test.',
choices: ['chrome', 'firefox'],
group: 'Browser'
})
.option('mobile', {
describe:
'Access pages as mobile a fake mobile device. Set UA and width/height. For Chrome it will use device Apple iPhone 6.',
default: false,
type: 'boolean'
})
.option('browsertime.pageCompleteCheckPollTimeout', {
alias: 'pageCompleteCheckPollTimeout',
type: 'number',
default: 200,
describe:
'The time in ms to wait for running the page complete check the next time.'
})
.option('browsertime.pageCompleteCheckStartWait', {
alias: 'pageCompleteCheckStartWait',
type: 'number',
default: 500,
describe:
'The time in ms to wait for running the page complete check for the first time. Use this when you have a pageLoadStrategy set to none'
})
.option('browsertime.pageLoadStrategy', {
alias: 'pageLoadStrategy',
type: 'string',
default: 'normal',
choices: ['eager', 'none', 'normal'],
describe:
'Set the strategy to waiting for document readiness after a navigation event. After the strategy is ready, your pageCompleteCheck will start runninhg. This only for Firefox and Chrome and please check which value each browser implements.'
})
.option('browsertime.cpu', {
alias: 'cpu',
type: 'boolean',
describe: 'Easy way to enable both chrome.timeline and CPU long tasks.',
group: 'chrome'
})
.option('browsertime.chrome.CPUThrottlingRate', {
alias: 'chrome.CPUThrottlingRate',
type: 'number',
describe:
'Enables CPU throttling to emulate slow CPUs. Throttling rate as a slowdown factor (1 is no throttle, 2 is 2x slowdown, etc)',
group: 'chrome'
})
.option('config', {
describe:
'Path to JSON config file. You can also use a .browsertime.json file that will automatically be found by Browsertime using find-up.',
config: 'config'
})
.option('browsertime.viewPort', {
alias: 'viewPort',
default: browsertimeConfig.viewPort,
describe: 'The browser view port size WidthxHeight like 400x300',
group: 'Browser'
})
.option('browsertime.android', {
alias: 'android',
type: 'boolean',
default: false,
describe:
'Short key to use Android. Will automatically use com.android.chrome for Chrome and stable Firefox. If you want to use another Chrome version, use --chrome.android.package'
})
.option('chrome.enableChromeDriverLog', {
describe: 'Log Chromedriver communication to a log file.',
type: 'boolean',
group: 'chrome'
})
.option('chrome.enableVerboseChromeDriverLog', {
describe: 'Log verboose Chromedriver communication to a log file.',
type: 'boolean',
group: 'chrome'
})
.option('verbose', {
alias: ['v'],
describe:
'Verbose mode prints progress messages to the console. Enter up to three times (-vvv)' +
' to increase the level of detail.',
type: 'count'
})
.parserConfiguration({ 'camel-case-expansion': false, 'deep-merge-config': true })
.config(config);
const defaultConfig = {
iterations: 1,
connectivity: {
profile: 'native',
downstreamKbps: undefined,
upstreamKbps: undefined,
latency: undefined,
engine: 'external'
},
viewPort: '1366x708',
delay: 0,
video: false,
visualMetrics: false,
resultDir: '/tmp/browsertime',
chrome: {
ignoreCertificateErrors: true
}
};
const btOptions = merge({}, parsed.argv.browsertime, defaultConfig);
// hack to keep backward compability to --android
if (parsed.argv.android[0] === true) {
set(btOptions, 'android.enabled', true);
}
configureLogging(parsed.argv);
// We have a special hack in sitespeed.io when you set --mobile
if (parsed.argv.mobile) {
btOptions.viewPort = '360x640';
btOptions['view-port'] = '360x640';
if (btOptions.browser === 'chrome') {
const emulation = get(
btOptions,
'chrome.mobileEmulation.deviceName',
'Moto G4'
);
btOptions.chrome.mobileEmulation = {
deviceName: emulation
};
} else {
btOptions.userAgent = iphone6UserAgent;
}
}
if (parsed.argv.android) {
if (parsed.argv.browser === 'chrome') {
// Default to Chrome Android.
set(
btOptions,
'chrome.android.package',
get(btOptions, 'chrome.android.package', 'com.android.chrome')
);
}
else if (parsed.argv.browser === 'firefox') {
set(
btOptions,
'firefox.android.package',
get(btOptions, 'firefox.android.package', 'org.mozilla.firefox')
);
}
}
const engine = new BrowsertimeEngine(btOptions);
const urls = parsed.argv.multi ? parsed.argv._ : getURLs(parsed.argv._);
try {
await testURLs(engine, urls, parsed.argv.multi);
} catch (e) {
console.error('Could not run ' + e);
process.exit(1);
}
process.exit();
}
runBrowsertime();