Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ Loads the provided URLs in a headless browser several times to measure median We
* `--number` (`-n`): Total number of requests to send.
* `--file` (`-f`): File with URLs (one URL per line) to run benchmark tests for.
* `--diff` (`-d`): Add diffs in terms of milliseconds and percentages when two URLs are provided via `--file`.
* `--metrics` (`-m`): Which metrics to include; by default these are "FCP", "LCP", "TTFB" and "LCP-TTFB".
* `--metrics` (`-m`): Which metrics to include; by default these are "FCP", "LCP", "TTFB", "TTLB", and "LCP-TTFB".
* `--output` (`-o`): The output format: Either "table", "csv", or "md".
* `--show-percentiles` (`-p`): Whether to show more granular percentiles instead of only the median.
* `--throttle-cpu` (`-t`): Enable CPU throttling to emulate slow CPUs.
Expand Down
84 changes: 57 additions & 27 deletions cli/commands/benchmark-web-vitals.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export const options = [
{
argname: '-m, --metrics <metrics...>',
description:
'Which metrics to include; by default these are "FCP", "LCP", "TTFB" and "LCP-TTFB".',
'Which metrics to include; by default these are "FCP", "LCP", "TTFB", "TTLB", and "LCP-TTFB".',
},
{
argname: '-o, --output <output>',
Expand Down Expand Up @@ -158,9 +158,9 @@ export const options = [

/**
* @typedef {Object} MetricsDefinitionEntry
* @property {string} type Either 'webVitals', 'serverTiming', or 'aggregate'.
* @property {string} type Either 'webVitals', 'serverTiming', 'performanceTiming', or 'aggregate'.
* @property {?string} listen Which event to listen to (only relevant for type 'webVitals').
* @property {?string} global Which JS global to find the metric in (only relevant for type 'webVitals').
* @property {?string} global Which JS global to find the metric in (only relevant for type 'webVitals' or 'performanceTiming').
* @property {?string[]} add Which other metrics to add (only relevant for type 'aggregate').
* @property {?string[]} subtract Which other metrics to subtract (only relevant for type 'aggregate').
* @property {?string} name Name of the Server-Timing metric (only relevant for type 'serverTiming').
Expand Down Expand Up @@ -197,7 +197,7 @@ function getParamsFromOptions( opt ) {
metrics:
opt.metrics && opt.metrics.length
? opt.metrics
: [ 'FCP', 'LCP', 'TTFB', 'LCP-TTFB' ],
: [ 'FCP', 'LCP', 'TTFB', 'TTLB', 'LCP-TTFB' ],
output: opt.output,
showPercentiles: Boolean( opt.showPercentiles ),
showVariance: Boolean( opt.showVariance ),
Expand Down Expand Up @@ -347,6 +347,10 @@ function getMetricsDefinition( metrics ) {
listen: 'onTTFB',
global: 'webVitalsTTFB',
},
TTLB: {
type: 'performanceTiming',
global: 'wppResearchTTLB',
},
'LCP-TTFB': {
type: 'aggregate',
add: [ 'LCP' ],
Expand Down Expand Up @@ -513,6 +517,19 @@ async function benchmarkURL( url, metricsDefinition, params, logProgress ) {
} );
}

if ( groupedMetrics.performanceTiming ) {
if ( scriptTag ) {
scriptTag += ';';
} else {
scriptTag = '';
}
Object.values( groupedMetrics.performanceTiming ).forEach(
( value ) => {
scriptTag += `const entry = performance.getEntriesByType('navigation')[0]; if (entry) { window.${ value.global } = entry.responseEnd; }`;
}
);
}

/** @type {Browser} */
let browser;

Expand Down Expand Up @@ -609,34 +626,42 @@ async function benchmarkURL( url, metricsDefinition, params, logProgress ) {
throw new Error( `Bad response code ${ response.status() }.` );
}

if ( groupedMetrics.webVitals ) {
const pageMetrics = {
...( groupedMetrics.webVitals || {} ),
...( groupedMetrics.performanceTiming || {} ),
};

if ( Object.keys( pageMetrics ).length > 0 ) {
await Promise.all(
Object.values( groupedMetrics.webVitals ).map(
async ( value ) => {
// Wait until global is populated.
await page.waitForFunction(
`window.${ value.global } !== undefined`
);
Object.values( pageMetrics ).map( async ( value ) => {
// Wait until global is populated.
await page.waitForFunction(
`window.${ value.global } !== undefined`
);

/*
* Do a random click, since only that triggers certain metrics
* like LCP, as only a user interaction stops reporting new LCP
* entries. See https://web.dev/lcp/.
*
* Click off screen to prevent clicking a link by accident and navigating away.
*/
/*
* Do a random click, since only that triggers certain metrics
* like LCP, as only a user interaction stops reporting new LCP
* entries. See https://web.dev/lcp/.
*
* Click off screen to prevent clicking a link by accident and navigating away.
*
* This is only needed for web vitals that have a 'listen' property.
*/
if ( value.listen ) {
await page.click( 'body', {
offset: { x: -500, y: -500 },
} );
// Get the metric value from the global.
const metric =
/** @type {number} */ await page.evaluate(
( global ) => window[ global ],
value.global
);
value.results.push( metric );
}
)

// Get the metric value from the global.
const metric =
/** @type {number} */ await page.evaluate(
( global ) => window[ global ],
value.global
);
value.results.push( metric );
} )
).catch( () => {
/* Ignore errors. */
} );
Expand Down Expand Up @@ -693,9 +718,14 @@ async function benchmarkURL( url, metricsDefinition, params, logProgress ) {

// Retrieve all base metric values.
const metricResults = {};
if ( groupedMetrics.webVitals || groupedMetrics.serverTiming ) {
if (
groupedMetrics.webVitals ||
groupedMetrics.performanceTiming ||
groupedMetrics.serverTiming
) {
const baseMetrics = {
...( groupedMetrics.webVitals || {} ),
...( groupedMetrics.performanceTiming || {} ),
...( groupedMetrics.serverTiming || {} ),
};
Object.entries( baseMetrics ).forEach( ( [ key, value ] ) => {
Expand Down