mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
Move QA scripts to the shared harness, batch 2 of 4
Each script launches and closes Chrome through scripts/qa/browser.mjs, waits on the application through scripts/qa/ready.mjs instead of the loading screen and fixed pauses, and reads the contract instead of Cesium where the check is not about Cesium itself.
This commit is contained in:
1 parent
ee697422c9
commit
373edf2989
26 files changed
+526
-803
No files matched your search
+60
-59
@@ -1,4 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
// Cesium-only probes: native primitive failure injection tests the Cesium paint path.
|
||||
import { evaluateWithGeo } from './qa/geometry.mjs';
|
||||
import { whenSettled, whenFramesDrawn, whenTilesLoaded } from './qa/ready.mjs';
|
||||
/**
|
||||
* qa-ghostmaps.mjs — headless proof for the S2 Underground GhostMaps layer
|
||||
* (id `s2-ghostmaps`): S2's public KMZ intel products, kept current from
|
||||
@@ -39,7 +42,7 @@
|
||||
* Exits non-zero on any FAIL. Does not commit anything.
|
||||
*/
|
||||
|
||||
import puppeteer from 'puppeteer';
|
||||
import { launchBrowser, closeBrowser } from './qa/browser.mjs';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
@@ -64,7 +67,6 @@ function record(name, ok, detail) {
|
||||
const tag = ok ? '\x1b[32mPASS\x1b[0m' : '\x1b[31mFAIL\x1b[0m';
|
||||
console.log(` [${tag}] ${name}${detail ? ` — ${detail}` : ''}`);
|
||||
}
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
async function boot(page) {
|
||||
await page.goto(`${APP_URL}/?welcome=0`, {
|
||||
@@ -72,10 +74,11 @@ async function boot(page) {
|
||||
timeout: TIMEOUT_MS,
|
||||
});
|
||||
await page.waitForFunction(
|
||||
() => window.__godsEyeView?.viewer && window.__godsEyeView?.dataManager,
|
||||
() => window.__godsEyeView?.engine && window.__godsEyeView?.dataManager,
|
||||
{ timeout: TIMEOUT_MS },
|
||||
);
|
||||
await sleep(5000);
|
||||
await whenSettled(page);
|
||||
await whenFramesDrawn(page);
|
||||
await page.keyboard.press('Escape');
|
||||
}
|
||||
|
||||
@@ -186,14 +189,14 @@ async function showRow(page) {
|
||||
.querySelector(`[data-layer-id="${id}"]`)
|
||||
?.scrollIntoView({ block: 'center' });
|
||||
}, LAYER_ID);
|
||||
await sleep(400);
|
||||
await whenFramesDrawn(page);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
fs.mkdirSync(SHOTS_DIR, { recursive: true });
|
||||
const browser = await puppeteer.launch({
|
||||
headless: !HEADFUL,
|
||||
args: ['--use-angle=swiftshader', '--enable-unsafe-swiftshader'],
|
||||
const browser = await launchBrowser({
|
||||
headless: true,
|
||||
args: ['--enable-unsafe-swiftshader'],
|
||||
defaultViewport: { width: 1440, height: 900 },
|
||||
});
|
||||
try {
|
||||
@@ -330,6 +333,7 @@ async function main() {
|
||||
await page.waitForFunction(() => window.__godsEyeView?.dataManager, {
|
||||
timeout: TIMEOUT_MS,
|
||||
});
|
||||
await whenSettled(page);
|
||||
await enableAndSettle(page);
|
||||
const persistedAge = await stats(page);
|
||||
record(
|
||||
@@ -351,41 +355,38 @@ async function main() {
|
||||
|
||||
// ---- (ii) PICK ----------------------------------------------------------
|
||||
await installPins(page);
|
||||
const target = await page.evaluate((id) => {
|
||||
const gev = window.__godsEyeView;
|
||||
const pin = window.__qaGhostPins(`${id}:cip:`)[0];
|
||||
const carto = gev.viewer.scene.globe.ellipsoid.cartesianToCartographic(
|
||||
pin.position,
|
||||
);
|
||||
try {
|
||||
gev.viewer.camera.cancelFlight();
|
||||
} catch {
|
||||
/* no flight */
|
||||
}
|
||||
gev.viewer.camera.setView({
|
||||
destination: gev.viewer.scene.globe.ellipsoid.cartographicToCartesian({
|
||||
longitude: carto.longitude,
|
||||
latitude: carto.latitude,
|
||||
height: 40000,
|
||||
}),
|
||||
orientation: { heading: 0, pitch: -Math.PI / 2, roll: 0 },
|
||||
});
|
||||
gev.viewer.scene.requestRender?.();
|
||||
return pin.id;
|
||||
}, LAYER_ID);
|
||||
await page
|
||||
.waitForFunction(
|
||||
() => window.__godsEyeView.viewer.scene.globe.tilesLoaded,
|
||||
{ timeout: 30000 },
|
||||
)
|
||||
.catch(() => {});
|
||||
const target = await evaluateWithGeo(
|
||||
page,
|
||||
async (qaGeo, id) => {
|
||||
const pin = window.__qaGhostPins(`${id}:cip:`)[0];
|
||||
const carto = qaGeo.cartographic.fromCartesian(pin.position);
|
||||
try {
|
||||
window.__godsEyeView.engine.camera.cancelFlight();
|
||||
} catch {
|
||||
/* no flight */
|
||||
}
|
||||
window.__godsEyeView.engine.camera.setView({
|
||||
destination: qaGeo.cartographic.toCartesian({
|
||||
longitude: carto.longitude,
|
||||
latitude: carto.latitude,
|
||||
height: 40000,
|
||||
}),
|
||||
orientation: { heading: 0, pitch: -Math.PI / 2, roll: 0 },
|
||||
});
|
||||
window.__godsEyeView.engine.render.request?.();
|
||||
return pin.id;
|
||||
},
|
||||
LAYER_ID,
|
||||
);
|
||||
await whenTilesLoaded(page, { timeoutMs: 30000 }).catch(() => {});
|
||||
await whenSettled(page);
|
||||
// The horizon cull reveals the pin on the frame after the camera moves.
|
||||
await page
|
||||
.waitForFunction(
|
||||
() => {
|
||||
const { viewer: v, engine } = window.__godsEyeView;
|
||||
v.scene.requestRender?.();
|
||||
const c = v.scene.canvas;
|
||||
const { engine } = window.__godsEyeView;
|
||||
window.__godsEyeView.engine.render.request?.();
|
||||
const c = window.__godsEyeView.engine.host.canvas;
|
||||
return engine.picker
|
||||
.drillPick({ x: c.clientWidth / 2, y: c.clientHeight / 2 }, 8)
|
||||
.some((p) => String(p?.id ?? '').startsWith('ghostmaps:cip:'));
|
||||
@@ -394,8 +395,8 @@ async function main() {
|
||||
)
|
||||
.catch(() => {});
|
||||
const probe = await page.evaluate(() => {
|
||||
const { viewer: v, engine } = window.__godsEyeView;
|
||||
const c = v.scene.canvas;
|
||||
const { engine } = window.__godsEyeView;
|
||||
const c = window.__godsEyeView.engine.host.canvas;
|
||||
const box = c.getBoundingClientRect();
|
||||
const centre = { x: c.clientWidth / 2, y: c.clientHeight / 2 };
|
||||
const picked = engine.picker.drillPick(centre, 8);
|
||||
@@ -486,27 +487,26 @@ async function main() {
|
||||
// far side. A pin clearly behind the planet (sphere test with margin) must
|
||||
// be hidden, and pins facing the camera must still show.
|
||||
const horizonCounts = (lon, lat) =>
|
||||
page.evaluate(
|
||||
async (id, lo, la) => {
|
||||
const gev = window.__godsEyeView;
|
||||
evaluateWithGeo(
|
||||
page,
|
||||
async (qaGeo, id, lo, la) => {
|
||||
const d2r = Math.PI / 180;
|
||||
try {
|
||||
gev.viewer.camera.cancelFlight();
|
||||
window.__godsEyeView.engine.camera.cancelFlight();
|
||||
} catch {
|
||||
/* no flight */
|
||||
}
|
||||
gev.viewer.camera.setView({
|
||||
destination:
|
||||
gev.viewer.scene.globe.ellipsoid.cartographicToCartesian({
|
||||
longitude: lo * d2r,
|
||||
latitude: la * d2r,
|
||||
height: 18_000_000,
|
||||
}),
|
||||
window.__godsEyeView.engine.camera.setView({
|
||||
destination: qaGeo.cartographic.toCartesian({
|
||||
longitude: lo * d2r,
|
||||
latitude: la * d2r,
|
||||
height: 18_000_000,
|
||||
}),
|
||||
orientation: { heading: 0, pitch: -Math.PI / 2, roll: 0 },
|
||||
});
|
||||
gev.viewer.scene.requestRender?.();
|
||||
window.__godsEyeView.engine.render.request?.();
|
||||
await new Promise((r) => setTimeout(r, 1200));
|
||||
const c = gev.viewer.camera.positionWC;
|
||||
const c = window.__godsEyeView.engine.camera.pose().position;
|
||||
const norm = (v) => Math.hypot(v.x, v.y, v.z);
|
||||
let far = 0;
|
||||
let nearShown = 0;
|
||||
@@ -583,7 +583,8 @@ async function main() {
|
||||
await page.waitForFunction(() => window.__godsEyeView?.dataManager, {
|
||||
timeout: TIMEOUT_MS,
|
||||
});
|
||||
await sleep(3000);
|
||||
await whenSettled(page);
|
||||
await whenFramesDrawn(page);
|
||||
await enableAndSettle(page);
|
||||
const kept = await stats(page);
|
||||
record(
|
||||
@@ -612,7 +613,7 @@ async function main() {
|
||||
(id) => window.__godsEyeView.dataManager.setEnabled(id, false),
|
||||
LAYER_ID,
|
||||
);
|
||||
await sleep(500);
|
||||
await whenFramesDrawn(page);
|
||||
record(
|
||||
'DISABLE: every GhostMaps data source leaves the viewer',
|
||||
(await sources(page)).length === 0,
|
||||
@@ -679,7 +680,7 @@ async function main() {
|
||||
{ timeout: 20000, polling: 100 },
|
||||
LAYER_ID,
|
||||
);
|
||||
await sleep(500);
|
||||
await whenFramesDrawn(page);
|
||||
const second = await identity();
|
||||
record(
|
||||
'PUSH: a new edition reloads only that product',
|
||||
@@ -698,7 +699,7 @@ async function main() {
|
||||
.filter((s) => !s.closed)
|
||||
.forEach((s) => s.emit(catalog));
|
||||
});
|
||||
await sleep(1000);
|
||||
await whenFramesDrawn(page);
|
||||
const third = await identity();
|
||||
record(
|
||||
'PUSH: a product leaving the catalog leaves the globe',
|
||||
@@ -738,7 +739,7 @@ async function main() {
|
||||
});
|
||||
await phone.close();
|
||||
} finally {
|
||||
await browser.close();
|
||||
await closeBrowser(browser);
|
||||
}
|
||||
const failed = results.filter((r) => !r.ok);
|
||||
console.log(
|
||||
|
||||
Reference in new issue
Block a user