sitespeed.io/bin/browsertimeWebPageReplay.js

233 lines
6.8 KiB
JavaScript
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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 { findUpSync } from 'find-up';
import { BrowsertimeEngine, configureLogging } from 'browsertime';
import { getURLs } from '../lib/cli/util.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();