mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
As in the previous batches. Scripts launching Chrome themselves go from 99 to 8 (all outside this conversion), fixed pauses from 86 to 69 and Cesium-only reads from 75 to 46; the QA harness baseline records them.
217 lines
7.1 KiB
JavaScript
217 lines
7.1 KiB
JavaScript
#!/usr/bin/env node
|
|
// Cesium-only probes: the native bloom stage verifies the Cesium visual-effects adapter.
|
|
import { evaluateWithGeo } from './qa/geometry.mjs';
|
|
/** Rendered acceptance for style transitions and effect-state ownership. */
|
|
import { whenSettled, whenTilesLoaded } from './qa/ready.mjs';
|
|
import { launchBrowser, closeBrowser } from './qa/browser.mjs';
|
|
import fs from 'node:fs';
|
|
|
|
const browser = await launchBrowser({
|
|
headless: true,
|
|
args: [
|
|
'--no-sandbox',
|
|
...(process.platform === 'darwin' ? ['--enable-gpu'] : []),
|
|
],
|
|
});
|
|
const page = await browser.newPage();
|
|
const errors = [];
|
|
page.on('pageerror', (error) => errors.push(error.message));
|
|
let failures = 0;
|
|
function check(name, passed) {
|
|
console.log(`[${passed ? 'PASS' : 'FAIL'}] ${name}`);
|
|
if (!passed) failures++;
|
|
}
|
|
try {
|
|
await page.setViewport({ width: 1440, height: 900 });
|
|
const url = new URL(process.env.QA_BASE_URL || 'http://127.0.0.1:4173');
|
|
url.searchParams.set('welcome', '0');
|
|
await page.goto(url.href, { waitUntil: 'domcontentloaded' });
|
|
await page.waitForFunction(
|
|
() => window.__godsEyeView?.styleManager?._visualEffects,
|
|
{ timeout: 60_000 },
|
|
);
|
|
await whenSettled(page);
|
|
await evaluateWithGeo(page, async (qaGeo) => {
|
|
window.__godsEyeView.engine.camera.cancelFlight?.();
|
|
window.__godsEyeView.engine.camera.setView({
|
|
destination: qaGeo.cartographic.toCartesian({
|
|
longitude: (-97.7431 * Math.PI) / 180,
|
|
latitude: (30.2568 * Math.PI) / 180,
|
|
height: 1200,
|
|
}),
|
|
orientation: { heading: 0, pitch: -0.85, roll: 0 },
|
|
});
|
|
window.__godsEyeView.engine.render.request();
|
|
});
|
|
await page.evaluate(
|
|
() =>
|
|
new Promise((resolve) =>
|
|
requestAnimationFrame(() => requestAnimationFrame(resolve)),
|
|
),
|
|
);
|
|
const settled = await whenTilesLoaded(page, { timeoutMs: 60_000 }).then(
|
|
() => true,
|
|
() => false,
|
|
);
|
|
check('visible tile content settles before visual captures', settled);
|
|
fs.mkdirSync('qa-shots/visual-effects', { recursive: true });
|
|
for (const style of [
|
|
'normal',
|
|
'retro',
|
|
'surveillance',
|
|
'thermal',
|
|
'anime',
|
|
'noir',
|
|
'normal',
|
|
]) {
|
|
await page.evaluate(
|
|
(name) => window.__godsEyeView.styleManager.setStyle(name),
|
|
style,
|
|
);
|
|
await page.waitForFunction(
|
|
() => window.__godsEyeView.styleManager.transitions.size === 0,
|
|
{ timeout: 10_000 },
|
|
);
|
|
const state = await page.evaluate(() => {
|
|
const manager = window.__godsEyeView.styleManager;
|
|
const effects = manager._visualEffects;
|
|
const visible = Object.entries(effects.stages)
|
|
.filter(([, stage]) => stage.enabled)
|
|
.map(([name]) => name);
|
|
const animated = Object.values(effects.stages).some(
|
|
(stage) =>
|
|
stage.enabled &&
|
|
stage.uniforms.time !== undefined &&
|
|
stage.uniforms.intensity > 0.001,
|
|
);
|
|
return {
|
|
active: manager.activeStyle,
|
|
visible,
|
|
clock: effects.frameId !== null,
|
|
animated,
|
|
};
|
|
});
|
|
check(
|
|
`${style}: only the selected style remains visible`,
|
|
state.active === style &&
|
|
JSON.stringify(state.visible) ===
|
|
JSON.stringify(style === 'normal' ? [] : [style]),
|
|
);
|
|
check(
|
|
`${style}: the animation clock follows visible animated stages`,
|
|
state.clock === state.animated,
|
|
);
|
|
if (['normal', 'surveillance', 'thermal'].includes(style)) {
|
|
await page.screenshot({ path: `qa-shots/visual-effects/${style}.png` });
|
|
if (style === 'surveillance')
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.viewer.camera.lookRight(0.2),
|
|
);
|
|
}
|
|
}
|
|
await page.evaluate(() => {
|
|
const manager = window.__godsEyeView.styleManager;
|
|
manager.setStyle('noir');
|
|
manager.setStyle('retro');
|
|
manager.setStyle('normal');
|
|
});
|
|
await page.waitForFunction(
|
|
() => window.__godsEyeView.styleManager.transitions.size === 0,
|
|
{ timeout: 10_000 },
|
|
);
|
|
check(
|
|
'rapid switches settle to Normal without an old style or clock',
|
|
await page.evaluate(() => {
|
|
const effects = window.__godsEyeView.styleManager._visualEffects;
|
|
return (
|
|
effects.frameId === null &&
|
|
Object.values(effects.stages).every((stage) => !stage.enabled)
|
|
);
|
|
}),
|
|
);
|
|
check(
|
|
'bloom actions, effect state and saved snapshot agree',
|
|
await page.evaluate(() => {
|
|
const manager = window.__godsEyeView.styleManager;
|
|
const states = [];
|
|
const unsubscribe = manager.subscribeShareState((value) =>
|
|
states.push(value),
|
|
);
|
|
const result = manager.setBloom({ enabled: true, intensityPct: 98 });
|
|
unsubscribe();
|
|
const published = states.at(-1);
|
|
const snapshot = manager.getVisualState();
|
|
return (
|
|
states[0].initial &&
|
|
states.length > 1 &&
|
|
Object.isFrozen(published.state.options) &&
|
|
published.state.bloomEnabled &&
|
|
published.state.options.bloomIntensity === 98 &&
|
|
result.ok &&
|
|
result.bloom.intensityPct === 98 &&
|
|
snapshot.bloom.intensity === 98 &&
|
|
manager._visualEffects.bloomIntensity === 98 &&
|
|
window.__godsEyeView.viewer.scene.postProcessStages.bloom.enabled
|
|
);
|
|
}),
|
|
);
|
|
check(
|
|
'sharpen actions, effect state and saved snapshot agree',
|
|
await page.evaluate(() => {
|
|
const manager = window.__godsEyeView.styleManager;
|
|
const result = manager.setSharpen({ enabled: true, intensityPct: 61 });
|
|
return (
|
|
result.ok &&
|
|
result.sharpen.intensityPct === 61 &&
|
|
manager.getVisualState().sharpen.intensity === 61 &&
|
|
manager._visualEffects.sharpenIntensity === 0.61 &&
|
|
Math.abs(window.__godsEyeView.engine.render.sharpen().amount - 1.32) <
|
|
1e-9
|
|
);
|
|
}),
|
|
);
|
|
await page.setViewport({ width: 620, height: 900 });
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.styleManager.setPanelCollapsed('pp-toggles', false, {
|
|
persist: false,
|
|
syncShare: false,
|
|
}),
|
|
);
|
|
await page.screenshot({ path: 'qa-shots/visual-effects/narrow.png' });
|
|
const teardown = await page.evaluate(async () => {
|
|
const manager = window.__godsEyeView.styleManager;
|
|
const effects = manager._visualEffects;
|
|
manager.setStyle('retro');
|
|
const engine = window.__godsEyeView.engine;
|
|
const pending = manager.dispose();
|
|
const stoppedBeforeAwait =
|
|
effects.stopped &&
|
|
effects.frameId === null &&
|
|
effects.transitions.size === 0;
|
|
await pending;
|
|
return {
|
|
stoppedBeforeAwait,
|
|
disabled:
|
|
engine.render.visualStyles.every(
|
|
(name) => engine.render.style(name).intensity === 0,
|
|
) &&
|
|
!engine.render.bloom().enabled &&
|
|
!engine.render.sharpen().enabled,
|
|
};
|
|
});
|
|
check(
|
|
'UI teardown stops effect animation before its first await',
|
|
teardown.stoppedBeforeAwait,
|
|
);
|
|
check('UI teardown disables owned effects on the engine', teardown.disabled);
|
|
check(
|
|
'effects do not introduce uncaught browser errors',
|
|
errors.length === 0,
|
|
);
|
|
if (errors.length) console.log(JSON.stringify(errors));
|
|
} finally {
|
|
await closeBrowser(browser);
|
|
}
|
|
console.log(`RESULT: ${failures} failures`);
|
|
process.exitCode = failures ? 1 : 0;
|