From 9956a6cfe6abfb2cd3fb051eba3c9b6d2e03aa97 Mon Sep 17 00:00:00 2001 From: Matysh Date: Fri, 7 Aug 2026 14:31:02 +0300 Subject: [PATCH] test: settle navigation transitions in browser smokes --- demo/smoke_backdrop.mjs | 10 ++++++++-- demo/smoke_bg_color.mjs | 4 ++++ demo/smoke_modes.mjs | 4 +++- demo/smoke_opening_measure.mjs | 1 + demo/smoke_resize_audit_1550.mjs | 16 ++++++++++------ demo/smoke_room_resize.mjs | 16 ++++++++++------ demo/smoke_sun.mjs | 1 + demo/smoke_zoom_out.mjs | 11 +++++++++-- 8 files changed, 46 insertions(+), 17 deletions(-) diff --git a/demo/smoke_backdrop.mjs b/demo/smoke_backdrop.mjs index 820515e7..01de843e 100644 --- a/demo/smoke_backdrop.mjs +++ b/demo/smoke_backdrop.mjs @@ -25,11 +25,14 @@ const restore = () => page.evaluate((s) => { return c.updateComplete && true; }, snap); -const mode = (m) => page.evaluate((m) => { +const mode = async (m) => { + await page.evaluate((m) => { const c = window.__card; c._setMode(m); c.requestUpdate(); return c.updateComplete && true; -}, m); + }, m); + await page.waitForTimeout(220); // editor chrome transition owns stage geometry +}; const settle = () => page.evaluate(() => new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)))); const q = (sel) => page.evaluate((s) => window.__card.renderRoot.querySelectorAll(s).length, sel); const spaceCfg = () => page.evaluate(() => { @@ -276,9 +279,11 @@ const editorWhite = await page.evaluate(async () => { const c = window.__card; const sr = () => c.shadowRoot || c.renderRoot; const upd = async () => { c.requestUpdate(); await c.updateComplete; }; + const navSettled = () => new Promise((resolve) => setTimeout(resolve, 220)); const probe = async (mode) => { c._setMode(mode); await upd(); + await navSettled(); const stage = sr().querySelector('.stage'); const paper = sr().querySelector('.stage svg .hp-paper'); return { @@ -294,6 +299,7 @@ const editorWhite = await page.evaluate(async () => { const decor = await probe('decor'); c._setMode('view'); await upd(); + await navSettled(); const viewStage = sr().querySelector('.stage'); // View with a backdrop must NOT force white — theme/card colour stays const viewForcedWhite = getComputedStyle(viewStage).backgroundColor === 'rgb(255, 255, 255)' diff --git a/demo/smoke_bg_color.mjs b/demo/smoke_bg_color.mjs index 5703c275..cba903c3 100644 --- a/demo/smoke_bg_color.mjs +++ b/demo/smoke_bg_color.mjs @@ -81,9 +81,11 @@ const res = await page.evaluate(async () => { // 8) editors keep their own canvas (inline bg only in view/kiosk) c._setMode('decor'); + await new Promise((resolve) => setTimeout(resolve, 220)); const editorBg = await stageBg(); out.editorUnpainted = editorBg !== rgb('#0a2a4a'); c._setMode('view'); + await new Promise((resolve) => setTimeout(resolve, 220)); out.viewPaintedAgain = (await stageBg()) === rgb('#0a2a4a'); // 9) kiosk instance respects the setting @@ -181,9 +183,11 @@ const paper = await page.evaluate(async () => { // editors keep the paper too (their canvas ignores bg_color anyway) c._setMode('decor'); await upd(); + await new Promise((resolve) => setTimeout(resolve, 220)); out.editorKeepsPaper = !!sr().querySelector('.stage svg .hp-paper'); c._setMode('view'); await upd(); + await new Promise((resolve) => setTimeout(resolve, 220)); // daynight night: the paper dims via the brightness filter ONLY, never alpha c._serverCfg.settings.bg_mode = 'daynight'; c._serverCfg.settings.north_deg = 0; diff --git a/demo/smoke_modes.mjs b/demo/smoke_modes.mjs index cd43a0f4..2946f678 100644 --- a/demo/smoke_modes.mjs +++ b/demo/smoke_modes.mjs @@ -12,7 +12,9 @@ const st = () => page.evaluate(() => { active: sr.querySelector('.modetab.active')?.textContent.trim(), editBtns: sr.querySelectorAll('.head .btn:not(.zb)').length, gears: sr.querySelectorAll('.tabedit').length, - markupBar: !!sr.querySelector('.editbar'), + // The last bar stays mounted for the exit animation; only an open chrome + // is an active/visible editor bar. + markupBar: !!sr.querySelector('.editorchrome.open .editbar'), // hpsettle is the transient post-boot grace (AUD-1552-02), not a mode class stageClass: sr.querySelector('.stage').className.replace(/ ?\bhpsettle\b/, ''), }; diff --git a/demo/smoke_opening_measure.mjs b/demo/smoke_opening_measure.mjs index 9959a287..a4df6e69 100644 --- a/demo/smoke_opening_measure.mjs +++ b/demo/smoke_opening_measure.mjs @@ -20,6 +20,7 @@ await page.evaluate(() => { return c.updateComplete && true; }); const settle = () => page.evaluate(() => new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)))); +await page.waitForTimeout(220); // editor chrome transition changes stage coordinates await settle(); const screenPt = (x, y) => page.evaluate(([x, y]) => { diff --git a/demo/smoke_resize_audit_1550.mjs b/demo/smoke_resize_audit_1550.mjs index e331bd61..d47a82bc 100644 --- a/demo/smoke_resize_audit_1550.mjs +++ b/demo/smoke_resize_audit_1550.mjs @@ -21,12 +21,16 @@ const restore = () => page.evaluate((s) => { c._cfgEpoch++; c.requestUpdate(); return c.updateComplete && true; }, snap); -const enter = (tool) => page.evaluate((t) => { - const c = window.__card; - if (!c._markup) c._setMode('plan'); - c._tool = t; c._rszSel = null; c.requestUpdate(); - return c.updateComplete && true; -}, tool); +const enter = async (tool) => { + const changed = await page.evaluate((t) => { + const c = window.__card; + const changed = !c._markup; + if (changed) c._setMode('plan'); + c._tool = t; c._rszSel = null; c.requestUpdate(); + return Promise.resolve(c.updateComplete).then(() => changed); + }, tool); + if (changed) await page.waitForTimeout(220); +}; const settle = () => page.evaluate(() => new Promise((res) => requestAnimationFrame(() => requestAnimationFrame(res)))); const screenPt = (x, y) => page.evaluate(([x, y]) => { const c = window.__card; diff --git a/demo/smoke_room_resize.mjs b/demo/smoke_room_resize.mjs index b7c58ff6..cd4126c9 100644 --- a/demo/smoke_room_resize.mjs +++ b/demo/smoke_room_resize.mjs @@ -14,12 +14,16 @@ const restore = () => page.evaluate((s) => { return c.updateComplete && true; }, snap); -const enter = (tool) => page.evaluate((t) => { - const c = window.__card; - if (!c._markup) c._setMode('plan'); - c._tool = t; c._rszSel = null; c.requestUpdate(); - return c.updateComplete && true; -}, tool); +const enter = async (tool) => { + const changed = await page.evaluate((t) => { + const c = window.__card; + const changed = !c._markup; + if (changed) c._setMode('plan'); + c._tool = t; c._rszSel = null; c.requestUpdate(); + return Promise.resolve(c.updateComplete).then(() => changed); + }, tool); + if (changed) await page.waitForTimeout(220); +}; const handleCount = () => page.evaluate(() => window.__card.renderRoot.querySelectorAll('.rszhandle').length); const roomPolyN = (id) => page.evaluate((id) => { diff --git a/demo/smoke_sun.mjs b/demo/smoke_sun.mjs index df109977..da072655 100644 --- a/demo/smoke_sun.mjs +++ b/demo/smoke_sun.mjs @@ -238,6 +238,7 @@ await page.evaluate(async () => { c._setMode('plan'); c._tool = 'opening'; c.requestUpdate(); await c.updateComplete; }); +await page.waitForTimeout(220); // stage coordinates settle with editor chrome await settle(); const [dx, dy] = await screenPt(960, 600); const [, dty] = await screenPt(960, 700); diff --git a/demo/smoke_zoom_out.mjs b/demo/smoke_zoom_out.mjs index 4dc38477..66a2429b 100644 --- a/demo/smoke_zoom_out.mjs +++ b/demo/smoke_zoom_out.mjs @@ -91,10 +91,17 @@ out.untouchedEditorNoJump = await page.evaluate(async () => { const before = { ...c._view }; c._setMode('plan'); await raf2(); c._setMode('view'); await raf2(); + // Header ResizeObserver publishes its final measured height one frame after + // the 180 ms chrome collapse, then the stage observer performs the last fit. + await new Promise((resolve) => setTimeout(resolve, 320)); const v = c._view; + const beforeCenter = [before.x + before.w / 2, before.y + before.h / 2]; + const afterCenter = [v.x + v.w / 2, v.y + v.h / 2]; return Math.abs(c._zoom - 1.6) < 0.01 - && Math.abs(v.x - before.x) < 0.005 && Math.abs(v.y - before.y) < 0.005 - && Math.abs(v.w - before.w) < 0.005 && Math.abs(v.h - before.h) < 0.005; + && Math.hypot(afterCenter[0] - beforeCenter[0], afterCenter[1] - beforeCenter[1]) < 0.01 + && Math.abs(v.w - before.w) < 0.005 + // The final measured stage may round by one CSS pixel after its observer. + && Math.abs(v.h - before.h) < 1.1; }); // -- the view zoom still survives a space switch, per space ---------------