Add Playwright performance tests to Product editor (#47590)
* Add product editor tests * Fix post editor lint * Add changelog * Fix lint error * Fix Typing test * Add log to resultsFiles * Fix comment * Remove PerfUtils
This commit is contained in:
parent
30c449e749
commit
c4e1cebe84
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Add Playwright performance tests to Product editor #47590
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable playwright/no-conditional-in-test, playwright/expect-expect */
|
/* eslint-disable @woocommerce/dependency-group, jest/expect-expect, jest/no-test-callback, array-callback-return, jest/no-identical-title */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WordPress dependencies
|
* WordPress dependencies
|
||||||
|
@ -16,18 +16,6 @@ const BROWSER_IDLE_WAIT = 1000;
|
||||||
|
|
||||||
const results = {};
|
const results = {};
|
||||||
|
|
||||||
async function editPost( admin, page, postId ) {
|
|
||||||
const query = new URLSearchParams();
|
|
||||||
query.set( 'post', String( postId ) );
|
|
||||||
query.set( 'action', 'edit' );
|
|
||||||
|
|
||||||
await admin.visitAdminPage( 'post.php', query.toString() );
|
|
||||||
await setPreferences( page, 'core/edit-post', {
|
|
||||||
welcomeGuide: false,
|
|
||||||
fullscreenMode: false,
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setPreferences( page, context, preferences ) {
|
async function setPreferences( page, context, preferences ) {
|
||||||
await page.waitForFunction( () => window?.wp?.data );
|
await page.waitForFunction( () => window?.wp?.data );
|
||||||
|
|
||||||
|
@ -45,6 +33,18 @@ async function setPreferences( page, context, preferences ) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function editPost( admin, page, postId ) {
|
||||||
|
const query = new URLSearchParams();
|
||||||
|
query.set( 'post', String( postId ) );
|
||||||
|
query.set( 'action', 'edit' );
|
||||||
|
|
||||||
|
await admin.visitAdminPage( 'post.php', query.toString() );
|
||||||
|
await setPreferences( page, 'core/edit-post', {
|
||||||
|
welcomeGuide: false,
|
||||||
|
fullscreenMode: false,
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
test.describe( 'Editor Performance', () => {
|
test.describe( 'Editor Performance', () => {
|
||||||
test.use( {
|
test.use( {
|
||||||
perfUtils: async ( { page }, use ) => {
|
perfUtils: async ( { page }, use ) => {
|
||||||
|
@ -125,12 +125,7 @@ test.describe( 'Editor Performance', () => {
|
||||||
console.log( draftId );
|
console.log( draftId );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'Run the test', async ( {
|
test( 'Run the test', async ( { admin, page, perfUtils, metrics } ) => {
|
||||||
admin,
|
|
||||||
page,
|
|
||||||
perfUtils,
|
|
||||||
metrics,
|
|
||||||
} ) => {
|
|
||||||
await editPost( admin, page, draftId );
|
await editPost( admin, page, draftId );
|
||||||
await perfUtils.disableAutosave();
|
await perfUtils.disableAutosave();
|
||||||
const canvas = await perfUtils.getCanvas();
|
const canvas = await perfUtils.getCanvas();
|
||||||
|
@ -175,4 +170,4 @@ test.describe( 'Editor Performance', () => {
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
/* eslint-enable playwright/no-conditional-in-test, playwright/expect-expect */
|
/* eslint-enable @woocommerce/dependency-group, jest/expect-expect, jest/no-test-callback, array-callback-return, jest/no-identical-title */
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
/* eslint-disable @woocommerce/dependency-group, jest/expect-expect, jest/no-test-callback, array-callback-return, jest/no-identical-title */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WordPress dependencies
|
||||||
|
*/
|
||||||
|
import { test, Metrics } from '@wordpress/e2e-test-utils-playwright';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { median } from '../utils';
|
||||||
|
import { toggleBlockProductEditor } from '../../e2e-pw/utils/simple-products';
|
||||||
|
|
||||||
|
// See https://github.com/WordPress/gutenberg/issues/51383#issuecomment-1613460429
|
||||||
|
const BROWSER_IDLE_WAIT = 1000;
|
||||||
|
const NEW_EDITOR_ADD_PRODUCT_URL =
|
||||||
|
'wp-admin/admin.php?page=wc-admin&path=%2Fadd-product';
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
totalBlockingTime: [],
|
||||||
|
cumulativeLayoutShift: [],
|
||||||
|
largestContentfulPaint: [],
|
||||||
|
type: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getTotalBlockingTime( page, idleWait ) {
|
||||||
|
const totalBlockingTime = await page.evaluate( async ( waitTime ) => {
|
||||||
|
return new Promise( ( resolve ) => {
|
||||||
|
const longTaskEntries = [];
|
||||||
|
// Create a performance observer to observe long task entries
|
||||||
|
new PerformanceObserver( ( list ) => {
|
||||||
|
const entries = list.getEntries();
|
||||||
|
// Store each long task entry in the longTaskEntries array
|
||||||
|
entries.forEach( ( entry ) => longTaskEntries.push( entry ) );
|
||||||
|
} ).observe( { type: 'longtask', buffered: true } );
|
||||||
|
|
||||||
|
// Give some time to collect entries
|
||||||
|
setTimeout( () => {
|
||||||
|
// Calculate the total blocking time by summing the durations of all long tasks
|
||||||
|
const tbt = longTaskEntries.reduce(
|
||||||
|
( acc, entry ) => acc + entry.duration,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
resolve( tbt );
|
||||||
|
}, waitTime );
|
||||||
|
} );
|
||||||
|
}, idleWait );
|
||||||
|
return totalBlockingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe( 'Product editor performance', () => {
|
||||||
|
test.use( {
|
||||||
|
metrics: async ( { page }, use ) => {
|
||||||
|
await use( new Metrics( { page } ) );
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
|
||||||
|
test.beforeEach( async ( { page } ) => {
|
||||||
|
await toggleBlockProductEditor( 'enable', page );
|
||||||
|
} );
|
||||||
|
|
||||||
|
test.afterAll( async ( {}, testInfo ) => {
|
||||||
|
const medians = {};
|
||||||
|
Object.keys( results ).map( ( metric ) => {
|
||||||
|
medians[ metric ] = median( results[ metric ] );
|
||||||
|
} );
|
||||||
|
await testInfo.attach( 'results', {
|
||||||
|
body: JSON.stringify( medians, null, 2 ),
|
||||||
|
contentType: 'application/json',
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
test.describe( 'Loading', () => {
|
||||||
|
const samples = 2;
|
||||||
|
const throwaway = 1;
|
||||||
|
const iterations = samples + throwaway;
|
||||||
|
for ( let i = 1; i <= iterations; i++ ) {
|
||||||
|
test( `Run the test (${ i } of ${ iterations })`, async ( {
|
||||||
|
page,
|
||||||
|
metrics,
|
||||||
|
} ) => {
|
||||||
|
await page.goto( NEW_EDITOR_ADD_PRODUCT_URL );
|
||||||
|
|
||||||
|
// Wait for the Preview button.
|
||||||
|
await page
|
||||||
|
.locator( '[aria-label="Preview in new tab"]' )
|
||||||
|
.first()
|
||||||
|
.waitFor();
|
||||||
|
|
||||||
|
// Get the durations.
|
||||||
|
const loadingDurations = await metrics.getLoadingDurations();
|
||||||
|
|
||||||
|
// Measure CLS
|
||||||
|
const cumulativeLayoutShift =
|
||||||
|
await metrics.getCumulativeLayoutShift();
|
||||||
|
|
||||||
|
// Measure LCP
|
||||||
|
const largestContentfulPaint =
|
||||||
|
await metrics.getLargestContentfulPaint();
|
||||||
|
|
||||||
|
// Measure TBT
|
||||||
|
const totalBlockingTime = await getTotalBlockingTime(
|
||||||
|
page,
|
||||||
|
BROWSER_IDLE_WAIT
|
||||||
|
);
|
||||||
|
|
||||||
|
// Save the results.
|
||||||
|
if ( i > throwaway ) {
|
||||||
|
Object.entries( loadingDurations ).forEach(
|
||||||
|
( [ metric, duration ] ) => {
|
||||||
|
const metricKey =
|
||||||
|
metric === 'timeSinceResponseEnd'
|
||||||
|
? 'firstBlock'
|
||||||
|
: metric;
|
||||||
|
if ( ! results[ metricKey ] ) {
|
||||||
|
results[ metricKey ] = [];
|
||||||
|
}
|
||||||
|
results[ metricKey ].push( duration );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
results.totalBlockingTime = results.tbt || [];
|
||||||
|
results.totalBlockingTime.push( totalBlockingTime );
|
||||||
|
results.cumulativeLayoutShift =
|
||||||
|
results.cumulativeLayoutShift || [];
|
||||||
|
results.cumulativeLayoutShift.push( cumulativeLayoutShift );
|
||||||
|
results.largestContentfulPaint =
|
||||||
|
results.largestContentfulPaint || [];
|
||||||
|
results.largestContentfulPaint.push(
|
||||||
|
largestContentfulPaint
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
test.describe( 'Typing', () => {
|
||||||
|
test( 'Run the test', async ( { page, metrics } ) => {
|
||||||
|
await page.goto( NEW_EDITOR_ADD_PRODUCT_URL );
|
||||||
|
|
||||||
|
// Wait for the Preview button.
|
||||||
|
await page
|
||||||
|
.locator( '[aria-label="Preview in new tab"]' )
|
||||||
|
.first()
|
||||||
|
.waitFor();
|
||||||
|
|
||||||
|
const input = page.getByPlaceholder( 'e.g. 12 oz Coffee Mug' );
|
||||||
|
|
||||||
|
// The first character typed triggers a longer time (isTyping change).
|
||||||
|
// It can impact the stability of the metric, so we exclude it. It
|
||||||
|
// probably deserves a dedicated metric itself, though.
|
||||||
|
const samples = 10;
|
||||||
|
const throwaway = 1;
|
||||||
|
const iterations = samples + throwaway;
|
||||||
|
|
||||||
|
// Start tracing.
|
||||||
|
await metrics.startTracing();
|
||||||
|
|
||||||
|
// Type the testing sequence into the empty input.
|
||||||
|
await input.type( 'x'.repeat( iterations ), {
|
||||||
|
delay: BROWSER_IDLE_WAIT,
|
||||||
|
// The extended timeout is needed because the typing is very slow
|
||||||
|
// and the `delay` value itself does not extend it.
|
||||||
|
timeout: iterations * BROWSER_IDLE_WAIT * 2, // 2x the total time to be safe.
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Stop tracing.
|
||||||
|
await metrics.stopTracing();
|
||||||
|
|
||||||
|
// Get the durations.
|
||||||
|
const [ keyDownEvents, keyPressEvents, keyUpEvents ] =
|
||||||
|
metrics.getTypingEventDurations();
|
||||||
|
|
||||||
|
// Save the results.
|
||||||
|
results.type = [];
|
||||||
|
for ( let i = throwaway; i < iterations; i++ ) {
|
||||||
|
results.type.push(
|
||||||
|
keyDownEvents[ i ] + keyPressEvents[ i ] + keyUpEvents[ i ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
/* eslint-enable @woocommerce/dependency-group, jest/expect-expect, jest/no-test-callback, array-callback-return, jest/no-identical-title */
|
|
@ -10,6 +10,10 @@ const resultsFiles = [
|
||||||
file: 'editor.performance-results.json',
|
file: 'editor.performance-results.json',
|
||||||
metricsPrefix: 'editor-',
|
metricsPrefix: 'editor-',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
file: 'product-editor.performance-results.json',
|
||||||
|
metricsPrefix: 'product-editor-',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const performanceResults = resultsFiles.map( ( { file } ) =>
|
const performanceResults = resultsFiles.map( ( { file } ) =>
|
||||||
|
|
Loading…
Reference in New Issue