test: settle navigation transitions in browser smokes

This commit is contained in:
Matysh
2026-08-07 14:31:02 +03:00
parent fe5f5b6a24
commit 9956a6cfe6
8 changed files with 46 additions and 17 deletions
+8 -2
View File
@@ -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)'
+4
View File
@@ -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;
+3 -1
View File
@@ -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/, ''),
};
+1
View File
@@ -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]) => {
+10 -6
View File
@@ -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;
+10 -6
View File
@@ -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) => {
+1
View File
@@ -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);
+9 -2
View File
@@ -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 ---------------