mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
- src/geo drops the ported debug argument checks and the tests that bitExact already covers; package boundaries accept a directory entry, so src/geo/ is listed once per group. - Cesium no longer carries movers and lines nobody draws with: they are optional engine capabilities. The fake engine and the Cesium camera use the shared cameraMath; unused contract tables, clock.ticked, the 'finest' surface detail and Navara click statistics are gone. - ALPR keeps one overlay core and one presentation model for both engines, bikeshare one station bookkeeping, the Navara ground effects one definition helper; image atlases, view centres, mover banks and colours are shared. - The legacy switch route, the clock offset and rate controls and other unused exports are removed. - The acceptance runs the Navara draw, geo import and scope checks; scenarios use the harness switch and page helpers; the main chunk check keeps the size budget only and runs once in CI.
143 lines
5.6 KiB
JavaScript
143 lines
5.6 KiB
JavaScript
// Contract: a switch picked just before the tab is hidden finishes in the
|
|
// hidden tab without waiting for a frame (a hidden tab draws nothing) and
|
|
// without a rollback, keeps the session and adds one history entry; once the
|
|
// tab is shown again the new engine draws a non-blank first frame and the
|
|
// layers' timers poll again. Both directions, cold and then warm.
|
|
//
|
|
// node scripts/qa-strict-hidden-switch.mjs http://127.0.0.1:4208
|
|
|
|
import assert from 'node:assert/strict';
|
|
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
|
import {
|
|
assertSession,
|
|
installFirstFrameProbe,
|
|
installSessionFixtures,
|
|
openApp,
|
|
rememberEngine,
|
|
sessionState,
|
|
startSession,
|
|
} from './qa-strict-session.mjs';
|
|
|
|
const base = localPreview(process.argv[2]);
|
|
const strict = createStrict({ origin: base.origin, layers: [] });
|
|
await strict.run({}, async (b) => {
|
|
const p = await b.newPage();
|
|
await p.setViewport({ width: 1280, height: 800 });
|
|
const errors = [];
|
|
p.on('pageerror', (e) => errors.push(e.message));
|
|
await installSessionFixtures(p, { flights: 20 });
|
|
await installFirstFrameProbe(p);
|
|
await openApp(p, base.origin, 'navara');
|
|
await startSession(p, ['earthquakes', 'flights']);
|
|
const cdp = await p.createCDPSession();
|
|
// Another tab of the same window: bringing it to the front hides ours.
|
|
const other = await b.newPage();
|
|
await other.goto('about:blank');
|
|
await p.bringToFront();
|
|
const visibility = () => p.evaluate(() => document.visibilityState);
|
|
assert.equal(await visibility(), 'visible');
|
|
const results = [];
|
|
for (const [kind, temperature] of [
|
|
['cesium', 'cold'],
|
|
['navara', 'warm'],
|
|
['cesium', 'warm'],
|
|
]) {
|
|
const before = await sessionState(p);
|
|
await p.evaluate((kind) => {
|
|
window.__qaHidden = { frame: window.__qaFirstFrame(kind), shown: [] };
|
|
window.__qaHidden.frame.then((t) => (window.__qaHidden.drawn = t));
|
|
// When the tab is hidden, as the page sees it: the time and whether
|
|
// the switch (busy picker) still runs.
|
|
document.addEventListener('visibilitychange', () =>
|
|
window.__qaHidden.shown.push({
|
|
state: document.visibilityState,
|
|
at: performance.now(),
|
|
busy: !!document.querySelector('#globe-engine')?.disabled,
|
|
}),
|
|
);
|
|
}, kind);
|
|
// The person picks, then switches tabs before the globe changes: the
|
|
// other tab comes to the front right after the key. A slow CPU (20x)
|
|
// keeps the switch running past the tab change.
|
|
await cdp.send('Emulation.setCPUThrottlingRate', { rate: 20 });
|
|
await strict.pick(p, kind, {
|
|
settle: false,
|
|
afterInput: () => other.bringToFront(),
|
|
});
|
|
await cdp.send('Emulation.setCPUThrottlingRate', { rate: 1 });
|
|
// rAF does not run in a hidden tab: poll on a timer.
|
|
await p.waitForFunction(
|
|
(kind) =>
|
|
window.__godsEyeView?.engine?.kind === kind &&
|
|
!document.querySelector('#globe-engine')?.disabled &&
|
|
!document.querySelector('.engine-switch-frame') &&
|
|
document.querySelector('#loading-screen.hidden'),
|
|
{ polling: 250, timeout: 60000 },
|
|
kind,
|
|
);
|
|
const hidden = await p.evaluate(() => ({
|
|
state: document.visibilityState,
|
|
hide: window.__qaHidden.shown.find((e) => e.state === 'hidden') ?? null,
|
|
pick: { at: window.__qaPick.at, idleAt: window.__qaPick.idleAt },
|
|
drawn: window.__qaHidden.drawn ?? null,
|
|
}));
|
|
await assertSession(
|
|
p,
|
|
kind,
|
|
`${temperature} switch to ${kind} while hidden`,
|
|
);
|
|
const after = await sessionState(p);
|
|
// Shown again: the first frame arrives, the timers poll again.
|
|
const reads = await p.evaluate(() => ({ ...window.__qaReads }));
|
|
await p.bringToFront();
|
|
assert.equal(await visibility(), 'visible');
|
|
// A non-blank frame of the new engine once the tab shows again.
|
|
const shown = await p.evaluate(async (kind) => {
|
|
const at = performance.now();
|
|
const frame = await Promise.race([
|
|
window.__qaFirstFrame(kind),
|
|
new Promise((resolve) => setTimeout(() => resolve(null), 10000)),
|
|
]);
|
|
return frame && Math.round(frame.presented - at);
|
|
}, kind);
|
|
await p.waitForFunction(
|
|
(reads) => window.__qaReads.flights > reads.flights,
|
|
{ polling: 250, timeout: 30000 },
|
|
reads,
|
|
);
|
|
await rememberEngine(p);
|
|
const row = {
|
|
kind,
|
|
temperature,
|
|
hiddenWhileBusy: Boolean(hidden.hide?.busy),
|
|
settledWhileHidden: hidden.state === 'hidden',
|
|
// From the tab being hidden to the picker being idle again.
|
|
settleAfterHideMs:
|
|
hidden.hide && hidden.pick.idleAt
|
|
? Math.round(hidden.pick.idleAt - hidden.hide.at)
|
|
: null,
|
|
// Observed: an engine may draw while hidden (Navara renders a requested
|
|
// frame without an animation frame); not part of the contract.
|
|
frameWhileHidden: hidden.drawn !== null,
|
|
firstFrameAfterShownMs: shown,
|
|
history: after.history - before.history,
|
|
};
|
|
results.push(row);
|
|
assert.ok(row.hiddenWhileBusy, 'the tab is hidden while the switch runs');
|
|
assert.ok(row.settledWhileHidden, 'the switch settles in the hidden tab');
|
|
// A hidden tab draws nothing: no first-frame wait (10 s) there.
|
|
assert.ok(
|
|
row.settleAfterHideMs !== null && row.settleAfterHideMs < 9000,
|
|
JSON.stringify(row),
|
|
);
|
|
assert.equal(row.history, 1, 'one history entry');
|
|
assert.ok(
|
|
row.firstFrameAfterShownMs !== null,
|
|
'a non-blank frame within 10 s of the tab showing',
|
|
);
|
|
console.log('PASS', JSON.stringify(row));
|
|
}
|
|
strict.extra.contracts = results;
|
|
assert.deepEqual(errors, []);
|
|
});
|