@@ -3,6 +3,7 @@ import BasicBlocksStaticApp from "@examples/05-interoperability/10-static-html-r
33import StaticApp from "@examples/02-backend/04-rendering-static-documents/src/App" ;
44import { describe , test } from "vite-plus/test" ;
55import { render } from "vitest-browser-react" ;
6+ import { page } from "../../utils/context.js" ;
67import { EDITOR_SELECTOR } from "../../utils/const.js" ;
78import { expectElement , sleep , waitForSelector } from "../../utils/editor.js" ;
89
@@ -16,39 +17,37 @@ describe("Check static rendering", () => {
1617
1718 // Renders two editors back-to-back and screenshots each against the same
1819 // baseline, asserting the static HTML export looks like the live editor.
19- // Heavy enough that even the suite-wide 30s testTimeout is tight when 3
20- // browsers contend in one Docker container — bump to 60s to absorb that .
20+ // Heavy (two full editors + two full-page screenshots), and Firefox is slow,
21+ // so under 3-browser-in- one- container contention even 60s is tight — 90s .
2122 test (
2223 "Check static rendering visually matches live editor" ,
23- { timeout : 60000 } ,
24+ { timeout : 90000 } ,
2425 async ( ) => {
25- // Screenshots the page body against the shared baseline. Mirrors the
26+ // Screenshots the whole page against the shared baseline. Mirrors the
2627 // options the original Playwright test used:
2728 // - Mask the regions that legitimately differ between the live editor and
2829 // the static export, or that aren't deterministic across runs. <video>/
2930 // <audio> render differently as they load (and the amount loaded varies
30- // per run) — unmasked, their per-frame changes also keep the page from
31- // ever stabilising, which hangs the screenshot matcher. Checkboxes and
32- // toggle buttons are interactive widgets in the live editor but plain
33- // markup in the export. Masks are resolved at call time so they pick up
34- // whichever of these elements the current render produced.
31+ // per run); checkboxes and toggle buttons are interactive widgets in the
32+ // live editor but plain markup in the export. The `mask` option only
33+ // accepts vitest Locators (NOT raw DOM elements — passing elements
34+ // silently masks nothing), so each matched element is wrapped with
35+ // `page.elementLocator`. Resolved at call time to pick up whichever of
36+ // these elements the current render produced.
37+ // https://vitest.dev/guide/browser/visual-regression-testing.html#handle-dynamic-content
3538 // - `allowedMismatchedPixels` is vite-plus's pixelmatch equivalent of
3639 // Playwright's `maxDiffPixels`: a small allowance for the image caption
3740 // text, which renders slightly differently (e.g. '×' vs 'x').
41+ const masks = ( ) =>
42+ [ "video" , "audio" , 'input[type="checkbox"]' , ".bn-toggle-button" ]
43+ . flatMap ( ( sel ) => [ ...document . querySelectorAll ( sel ) ] )
44+ . map ( ( el ) => page . elementLocator ( el ) ) ;
3845 const matchEquality = ( ) =>
3946 expectElement ( document . body ) . toMatchScreenshot (
4047 "static-rendering-equality" ,
4148 {
4249 comparatorOptions : { allowedMismatchedPixels : 200 } ,
43- screenshotOptions : {
44- scale : "css" ,
45- mask : [
46- ...document . querySelectorAll ( "video" ) ,
47- ...document . querySelectorAll ( "audio" ) ,
48- ...document . querySelectorAll ( 'input[type="checkbox"]' ) ,
49- ...document . querySelectorAll ( ".bn-toggle-button" ) ,
50- ] ,
51- } ,
50+ screenshotOptions : { scale : "css" , mask : masks ( ) } ,
5251 } ,
5352 ) ;
5453
@@ -61,6 +60,27 @@ describe("Check static rendering", () => {
6160 document . head . appendChild ( style ) ;
6261 await sleep ( 500 ) ;
6362
63+ // This document is taller than the suite-wide 1280x720 iframe. The matcher
64+ // captures the `document.body` element box (it can't `fullPage`
65+ // scroll-and-stitch), and a fixed-height iframe never paints what's below
66+ // its fold — so grow the iframe past the whole document first. No restore
67+ // is needed: every test file's `beforeAll` resets the iframe to 1280x720.
68+ //
69+ // The height is a FIXED constant (comfortably taller than the document),
70+ // NOT the measured `scrollHeight`: the latter drifts a few px between
71+ // runs/renders on Firefox, and since the capture is downscaled (below)
72+ // that drift changes the output dimensions and fails the dimension check.
73+ //
74+ // NB: the browser *window* stays 1280x720 (a taller window breaks
75+ // Firefox's native `<select>` handling — see customblocks), so the capture
76+ // of the now-taller iframe is downscaled to fit the window. The whole page
77+ // is captured, but the baseline is smaller than 1280xH (not 1:1).
78+ // Full-resolution would require running these tests in a separate
79+ // tall-window browser project.
80+ const PAGE_HEIGHT = 1800 ;
81+ await page . viewport ( 1280 , PAGE_HEIGHT ) ;
82+ await sleep ( 200 ) ;
83+
6484 await matchEquality ( ) ;
6585
6686 // Await the unmount: `render`/`unmount` run inside `act()`, and starting
@@ -72,6 +92,7 @@ describe("Check static rendering", () => {
7292
7393 await render ( < BasicBlocksStaticApp /> ) ;
7494 await waitForSelector ( EDITOR_SELECTOR ) ;
95+ await page . viewport ( 1280 , PAGE_HEIGHT ) ;
7596 await sleep ( 500 ) ;
7697 await matchEquality ( ) ;
7798 } ,
0 commit comments