Commit 2cdf2542915 for woocommerce
commit 2cdf2542915ddd3784a55d99b21914a9740d29bb
Author: Luigi Teschio <gigitux@gmail.com>
Date: Thu Jul 16 09:24:02 2026 +0200
Add editor asset size performance metrics (#66606)
* Add editor asset size performance metrics
* Add changelog entry for editor asset metrics
* add comment
* improve quality
* fix logic
diff --git a/plugins/woocommerce/changelog/performance-editor-asset-metrics b/plugins/woocommerce/changelog/performance-editor-asset-metrics
new file mode 100644
index 00000000000..8a9275f27e4
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-editor-asset-metrics
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Add editor asset count and network transfer size performance metrics.
diff --git a/plugins/woocommerce/tests/metrics/config/performance-reporter.ts b/plugins/woocommerce/tests/metrics/config/performance-reporter.ts
index d24f7f2f0b7..801c8f1d11c 100644
--- a/plugins/woocommerce/tests/metrics/config/performance-reporter.ts
+++ b/plugins/woocommerce/tests/metrics/config/performance-reporter.ts
@@ -12,6 +12,11 @@ import type {
} from '@playwright/test/reporter';
/* eslint-enable @typescript-eslint/no-unused-vars */
+/**
+ * Internal dependencies
+ */
+import { formatMetricValue } from '../utils.js';
+
export type WPPerformanceResults = Record< string, number >;
class PerformanceReporter implements Reporter {
@@ -66,7 +71,9 @@ class PerformanceReporter implements Reporter {
const printableResults: Record< string, { value: string } > = {};
for ( const [ key, value ] of Object.entries( results ) ) {
- printableResults[ key ] = { value: `${ value } ms` };
+ printableResults[ key ] = {
+ value: formatMetricValue( key, value ),
+ };
}
// eslint-disable-next-line no-console
diff --git a/plugins/woocommerce/tests/metrics/specs/editor.spec.js b/plugins/woocommerce/tests/metrics/specs/editor.spec.js
index 01a0b4559c0..6a0ae19a4d4 100644
--- a/plugins/woocommerce/tests/metrics/specs/editor.spec.js
+++ b/plugins/woocommerce/tests/metrics/specs/editor.spec.js
@@ -9,7 +9,11 @@ import { test, Metrics } from '@wordpress/e2e-test-utils-playwright';
* Internal dependencies
*/
import { PerfUtils } from '../fixtures';
-import { getTotalBlockingTime, median } from '../utils';
+import {
+ getTotalBlockingTime,
+ median,
+ startWooEditorAssetMetrics,
+} from '../utils';
// See https://github.com/WordPress/gutenberg/issues/51383#issuecomment-1613460429
const BROWSER_IDLE_WAIT = 1000;
@@ -56,54 +60,77 @@ test.describe( 'Editor Performance', () => {
perfUtils,
metrics,
} ) => {
- // Open the test draft.
- await admin.editPost( draftId );
- const canvas = await perfUtils.getCanvas();
+ const stopWooEditorAssetMetrics =
+ await startWooEditorAssetMetrics( page );
- // Wait for the first block.
- await canvas.locator( '.wp-block' ).first().waitFor();
+ try {
+ // Open the test draft.
+ await admin.editPost( draftId );
+ const canvas = await perfUtils.getCanvas();
- // Get the durations.
- const loadingDurations = await metrics.getLoadingDurations();
+ // Wait for the first block.
+ await canvas.locator( '.wp-block' ).first().waitFor();
- // Measure CLS
- const cumulativeLayoutShift =
- await metrics.getCumulativeLayoutShift();
+ // Get the durations.
+ const loadingDurations =
+ await metrics.getLoadingDurations();
- // Measure LCP
- const largestContentfulPaint =
- await metrics.getLargestContentfulPaint();
+ // Measure CLS
+ const cumulativeLayoutShift =
+ await metrics.getCumulativeLayoutShift();
- // Measure TBT
- const totalBlockingTime = await getTotalBlockingTime(
- page,
- BROWSER_IDLE_WAIT
- );
+ // Measure LCP
+ const largestContentfulPaint =
+ await metrics.getLargestContentfulPaint();
- // Save the results.
- if ( i > throwaway ) {
- results.totalBlockingTime = results.tbt || [];
- results.totalBlockingTime.push( totalBlockingTime );
- results.cumulativeLayoutShift =
- results.cumulativeLayoutShift || [];
- results.cumulativeLayoutShift.push( cumulativeLayoutShift );
- results.largestContentfulPaint =
- results.largestContentfulPaint || [];
- results.largestContentfulPaint.push(
- largestContentfulPaint
+ // Measure TBT
+ const totalBlockingTime = await getTotalBlockingTime(
+ page,
+ BROWSER_IDLE_WAIT
);
- Object.entries( loadingDurations ).forEach(
- ( [ metric, duration ] ) => {
- const metricKey =
- metric === 'timeSinceResponseEnd'
- ? 'firstBlock'
- : metric;
- if ( ! results[ metricKey ] ) {
- results[ metricKey ] = [];
+
+ // Measure WooCommerce editor assets.
+ const wooEditorAssetMetrics =
+ await stopWooEditorAssetMetrics();
+
+ // Save the results.
+ if ( i > throwaway ) {
+ results.totalBlockingTime =
+ results.totalBlockingTime || [];
+ results.totalBlockingTime.push( totalBlockingTime );
+ results.cumulativeLayoutShift =
+ results.cumulativeLayoutShift || [];
+ results.cumulativeLayoutShift.push(
+ cumulativeLayoutShift
+ );
+ results.largestContentfulPaint =
+ results.largestContentfulPaint || [];
+ results.largestContentfulPaint.push(
+ largestContentfulPaint
+ );
+ Object.entries( loadingDurations ).forEach(
+ ( [ metric, duration ] ) => {
+ const metricKey =
+ metric === 'timeSinceResponseEnd'
+ ? 'firstBlock'
+ : metric;
+ if ( ! results[ metricKey ] ) {
+ results[ metricKey ] = [];
+ }
+ results[ metricKey ].push( duration );
}
- results[ metricKey ].push( duration );
- }
- );
+ );
+ Object.entries( wooEditorAssetMetrics ).forEach(
+ ( [ metric, value ] ) => {
+ if ( ! results[ metric ] ) {
+ results[ metric ] = [];
+ }
+ results[ metric ].push( value );
+ }
+ );
+ }
+ } finally {
+ await stopWooEditorAssetMetrics();
}
} );
}
diff --git a/plugins/woocommerce/tests/metrics/utils.js b/plugins/woocommerce/tests/metrics/utils.js
index fc90c57e838..f5fca03e170 100644
--- a/plugins/woocommerce/tests/metrics/utils.js
+++ b/plugins/woocommerce/tests/metrics/utils.js
@@ -3,17 +3,14 @@
*/
import { existsSync, readFileSync } from 'fs';
-export function median( array ) {
- if ( ! array || ! array.length ) return undefined;
-
- const numbers = [ ...array ].sort( ( a, b ) => a - b );
- const middleIndex = Math.floor( numbers.length / 2 );
-
- if ( numbers.length % 2 === 0 ) {
- return ( numbers[ middleIndex - 1 ] + numbers[ middleIndex ] ) / 2;
- }
- return numbers[ middleIndex ];
-}
+/**
+ * Internal dependencies
+ */
+export {
+ median,
+ getMetricUnit,
+ formatMetricValue,
+} from '../../../../tools/compare-perf/metric-utils.js';
export function readFile( filePath ) {
if ( ! existsSync( filePath ) ) {
@@ -23,6 +20,117 @@ export function readFile( filePath ) {
return readFileSync( filePath, 'utf8' ).trim();
}
+const WOO_BLOCKS_ASSETS_PATH =
+ '/wp-content/plugins/woocommerce/assets/client/blocks/';
+const KB = 1024;
+
+function roundSize( value ) {
+ return Math.round( value * 100 ) / 100;
+}
+
+function getWooEditorAssetUrl( rawUrl, baseUrl ) {
+ const url = new URL( rawUrl, baseUrl );
+
+ if (
+ ! url.pathname.includes( WOO_BLOCKS_ASSETS_PATH ) ||
+ ! /\.(js|css)$/.test( url.pathname )
+ ) {
+ return null;
+ }
+
+ return {
+ normalizedUrl: url.origin + url.pathname,
+ isScript: url.pathname.endsWith( '.js' ),
+ isStyle: url.pathname.endsWith( '.css' ),
+ };
+}
+
+function getEmptyWooEditorAssetMetrics() {
+ return {
+ wooEditorAssetCount: 0,
+ wooEditorNetworkTransferAssetSize: 0,
+ wooEditorNetworkTransferScriptSize: 0,
+ wooEditorNetworkTransferStyleSize: 0,
+ };
+}
+
+function formatWooEditorAssetMetrics( metrics ) {
+ return {
+ wooEditorAssetCount: metrics.wooEditorAssetCount,
+ wooEditorNetworkTransferAssetSize: roundSize(
+ metrics.wooEditorNetworkTransferAssetSize / KB
+ ),
+ wooEditorNetworkTransferScriptSize: roundSize(
+ metrics.wooEditorNetworkTransferScriptSize / KB
+ ),
+ wooEditorNetworkTransferStyleSize: roundSize(
+ metrics.wooEditorNetworkTransferStyleSize / KB
+ ),
+ };
+}
+
+export async function startWooEditorAssetMetrics( page ) {
+ const client = await page.context().newCDPSession( page );
+ const { frameTree } = await client.send( 'Page.getFrameTree' );
+ const mainFrameId = frameTree.frame.id;
+ const requests = new Map();
+ const assetUrls = new Set();
+ const metrics = getEmptyWooEditorAssetMetrics();
+ let isCollecting = true;
+
+ const onRequestWillBeSent = ( event ) => {
+ // Exclude assets loaded within editor iframes.
+ if ( event.frameId !== mainFrameId ) {
+ return;
+ }
+
+ const assetUrl = getWooEditorAssetUrl( event.request.url, page.url() );
+
+ if ( assetUrl ) {
+ requests.set( event.requestId, assetUrl );
+ }
+ };
+
+ const onLoadingFinished = ( event ) => {
+ const assetUrl = requests.get( event.requestId );
+
+ if ( ! assetUrl || assetUrls.has( assetUrl.normalizedUrl ) ) {
+ return;
+ }
+
+ assetUrls.add( assetUrl.normalizedUrl );
+ metrics.wooEditorAssetCount += 1;
+
+ const size = event.encodedDataLength || 0;
+ metrics.wooEditorNetworkTransferAssetSize += size;
+
+ if ( assetUrl.isScript ) {
+ metrics.wooEditorNetworkTransferScriptSize += size;
+ }
+
+ if ( assetUrl.isStyle ) {
+ metrics.wooEditorNetworkTransferStyleSize += size;
+ }
+ };
+
+ client.on( 'Network.requestWillBeSent', onRequestWillBeSent );
+ client.on( 'Network.loadingFinished', onLoadingFinished );
+ await client.send( 'Network.enable' );
+
+ return async () => {
+ if ( ! isCollecting ) {
+ return formatWooEditorAssetMetrics( metrics );
+ }
+
+ isCollecting = false;
+ client.off( 'Network.requestWillBeSent', onRequestWillBeSent );
+ client.off( 'Network.loadingFinished', onLoadingFinished );
+ await client.detach();
+
+ return formatWooEditorAssetMetrics( metrics );
+ };
+}
+
export async function getTotalBlockingTime( page, idleWait ) {
const totalBlockingTime = await page.evaluate( async ( waitTime ) => {
return new Promise( ( resolve ) => {
diff --git a/tools/compare-perf/metric-utils.js b/tools/compare-perf/metric-utils.js
new file mode 100644
index 00000000000..3fdeb374468
--- /dev/null
+++ b/tools/compare-perf/metric-utils.js
@@ -0,0 +1,40 @@
+/**
+ * Computes the median number from an array of numbers.
+ *
+ * @param {number[]} array
+ *
+ * @return {number|undefined} Median value or undefined if array empty.
+ */
+function median( array ) {
+ if ( ! array || ! array.length ) return undefined;
+
+ const numbers = [ ...array ].sort( ( a, b ) => a - b );
+ const middleIndex = Math.floor( numbers.length / 2 );
+
+ if ( numbers.length % 2 === 0 ) {
+ return ( numbers[ middleIndex - 1 ] + numbers[ middleIndex ] ) / 2;
+ }
+ return numbers[ middleIndex ];
+}
+
+function getMetricUnit( metric ) {
+ if ( metric.endsWith( 'Size' ) ) {
+ return 'KB';
+ }
+
+ if ( metric.endsWith( 'Count' ) ) {
+ return 'count';
+ }
+
+ return 'ms';
+}
+
+function formatMetricValue( metric, value ) {
+ return `${ value } ${ getMetricUnit( metric ) }`;
+}
+
+module.exports = {
+ median,
+ getMetricUnit,
+ formatMetricValue,
+};
diff --git a/tools/compare-perf/process-reports.ts b/tools/compare-perf/process-reports.ts
index ec2684161e9..89831c8206b 100644
--- a/tools/compare-perf/process-reports.ts
+++ b/tools/compare-perf/process-reports.ts
@@ -13,8 +13,8 @@ const {
readJSONFile,
logAtIndent,
sanitizeBranchName,
- median
-} = require( './utils' ) ;
+} = require( './utils' );
+const { median, formatMetricValue } = require( './metric-utils' );
const formats = {
success: bold.green,
@@ -104,7 +104,10 @@ async function processPerformanceReports(
) ) {
for ( const [ metric, value ] of Object.entries( metrics ) ) {
invertedResult[ metric ] = invertedResult[ metric ] || {};
- invertedResult[ metric ][ branch ] = `${ value } ms`;
+ invertedResult[ metric ][ branch ] = formatMetricValue(
+ metric,
+ value
+ );
}
}
diff --git a/tools/compare-perf/utils.js b/tools/compare-perf/utils.js
index 3eafe7fdd89..86a2f122e01 100644
--- a/tools/compare-perf/utils.js
+++ b/tools/compare-perf/utils.js
@@ -124,25 +124,6 @@ function sanitizeBranchName( branch ) {
return branch.replace( /[^a-zA-Z0-9-]/g, '-' );
}
-/**
- * Computes the median number from an array numbers.
- *
- * @param {number[]} array
- *
- * @return {number|undefined} Median value or undefined if array empty.
- */
-function median( array ) {
- if ( ! array || ! array.length ) return undefined;
-
- const numbers = [ ...array ].sort( ( a, b ) => a - b );
- const middleIndex = Math.floor( numbers.length / 2 );
-
- if ( numbers.length % 2 === 0 ) {
- return ( numbers[ middleIndex - 1 ] + numbers[ middleIndex ] ) / 2;
- }
- return numbers[ middleIndex ];
-}
-
module.exports = {
askForConfirmation,
readJSONFile,
@@ -150,5 +131,4 @@ module.exports = {
getFilesFromDir,
logAtIndent,
sanitizeBranchName,
- median
};