Skip to content

Commit c26d870

Browse files
committed
Build/Test Tools: Harden visual regression test stability.
Add network-idle wait with timeout fallback, jQuery animation guard, mask selector validation warnings, and teardown error logging. Update README to reflect the current local-only workflow.
1 parent 17a5616 commit c26d870

2 files changed

Lines changed: 64 additions & 11 deletions

File tree

tests/visual-regression/README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
# Visual Regression Tests in WordPress Core
22

3-
These tests make use of Playwright, with a setup very similar to that of the e2e tests.
3+
These tests use Playwright to capture full-page screenshots of WordPress admin
4+
screens and compare them against baseline snapshots.
45

56
## How to Run the Tests Locally
67

7-
1. Check out trunk.
8-
2. Run `npm run test:visual` to generate some base snapshots.
9-
3. Check out the feature branch to be tested.
10-
4. Run `npm run test:visual` again. If any tests fail, the diff images can be found in `artifacts/`
11-
8+
1. Start the local environment: `npm run env:start`
9+
2. Generate baseline snapshots: `npm run test:visual -- --update-snapshots`
10+
3. Make your changes.
11+
4. Run `npm run test:visual` — any visual differences will fail and produce
12+
diff images in `artifacts/`.
13+
5. Open the HTML report for a side-by-side comparison (opens automatically
14+
after a local run, or see `artifacts/visual-report/`).

tests/visual-regression/specs/visual-snapshots.test.js

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,44 @@
1919
import { test, expect } from '@wordpress/e2e-test-utils-playwright';
2020

2121
/**
22-
* Waits for resources and fonts to finish loading.
22+
* Waits for network activity, fonts, and jQuery animations to settle.
2323
*
2424
* @param {import('@playwright/test').Page} page
2525
*/
2626
async function waitForPageReady( page ) {
2727
await page.waitForLoadState( 'load' );
28+
29+
// Wait for in-flight requests (AJAX heartbeat, dashboard widgets) to
30+
// finish. The 5 s timeout keeps the suite moving when a long-poll
31+
// endpoint (e.g. heartbeat-tick) holds the connection open.
32+
await page
33+
.waitForLoadState( 'networkidle', { timeout: 5000 } )
34+
.catch( () => {} );
35+
2836
// If a webfont fails to load (network issue, Docker DNS), this resolves
2937
// with fallback fonts and the diff will surface the discrepancy.
3038
await page.evaluate( () => document.fonts.ready );
39+
40+
// Wait for jQuery animations (e.g. dashboard widget slide-in) to
41+
// complete. CSS animations are already disabled in the Playwright config,
42+
// but jQuery .animate() bypasses that setting.
43+
await page.evaluate( () => {
44+
if ( typeof jQuery === 'undefined' ) {
45+
return;
46+
}
47+
return new Promise( ( resolve ) => {
48+
if ( jQuery.active === 0 && jQuery( ':animated' ).length === 0 ) {
49+
resolve();
50+
return;
51+
}
52+
const interval = setInterval( () => {
53+
if ( jQuery.active === 0 && jQuery( ':animated' ).length === 0 ) {
54+
clearInterval( interval );
55+
resolve();
56+
}
57+
}, 100 );
58+
} );
59+
} );
3160
}
3261

3362
/**
@@ -247,9 +276,21 @@ test.describe( 'Admin Visual Snapshots', () => {
247276

248277
let screenshotOptions = {};
249278
if ( Array.isArray( masks ) ) {
250-
screenshotOptions = {
251-
mask: masks.map( ( s ) => page.locator( s ) ),
252-
};
279+
const locators = masks.map( ( s ) => page.locator( s ) );
280+
281+
// Warn when a mask selector matches nothing — the volatile
282+
// element may have been removed or renamed, causing false diffs.
283+
for ( let i = 0; i < locators.length; i++ ) {
284+
const count = await locators[ i ].count();
285+
if ( count === 0 ) {
286+
// eslint-disable-next-line no-console
287+
console.warn(
288+
`[${ name }] mask selector "${ masks[ i ] }" matched 0 elements`
289+
);
290+
}
291+
}
292+
293+
screenshotOptions = { mask: locators };
253294
} else if ( typeof masks === 'function' ) {
254295
screenshotOptions = { mask: masks( page ) };
255296
}
@@ -260,7 +301,16 @@ test.describe( 'Admin Visual Snapshots', () => {
260301
);
261302
} finally {
262303
if ( teardown ) {
263-
await teardown( requestUtils, data );
304+
try {
305+
await teardown( requestUtils, data );
306+
} catch ( err ) {
307+
// Log but don't mask the original assertion failure.
308+
// eslint-disable-next-line no-console
309+
console.error(
310+
`[${ name }] teardown failed:`,
311+
err.message
312+
);
313+
}
264314
}
265315
}
266316
} );

0 commit comments

Comments
 (0)