mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
The harness conversion replaced pauses that stood in for real conditions with camera readiness: the cables and infrastructure idle checks started measuring while the overlay was still fading in after moveEnd, directions sampled before its maneuver dots had ground cells, three scripts clicked before the loading cover was gone, the credits check hit-tested during UI transitions, and the oblique traffic flight lost its height cap. Each now waits for its condition; idle checks observe without requesting frames and record the reasons of the frames they count; readiness waits for the startup cover and no longer requests frames while polling.
159 lines
5.3 KiB
JavaScript
159 lines
5.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/** Persistent inline attribution through actual OSM layer display lifetimes. */
|
|
import { whenSettled, whenUiSettled } from './qa/ready.mjs';
|
|
import { launchBrowser, closeBrowser } from './qa/browser.mjs';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs/promises';
|
|
import { moveCamera } from './qa-journey-recorder.mjs';
|
|
const url = process.argv.find((a) => /^https?:/.test(a));
|
|
if (!url) throw new Error('Supply the running server URL');
|
|
const dir = process.env.QA_POLISH_DIR || 'qa-shots/final-polish';
|
|
await fs.mkdir(dir, { recursive: true });
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
const result = { states: [], errors: [] };
|
|
const browser = await launchBrowser({
|
|
headless: true,
|
|
args: [],
|
|
});
|
|
try {
|
|
const page = await browser.newPage();
|
|
await page.setViewport({ width: 1440, height: 900 });
|
|
page.on('pageerror', (e) => result.errors.push(e.message));
|
|
page.on('console', (m) => {
|
|
if (m.type() === 'error') result.errors.push(m.text());
|
|
});
|
|
await page.evaluateOnNewDocument(() =>
|
|
sessionStorage.setItem('gev:first-run-mission-session:v1', 'dismissed'),
|
|
);
|
|
await page.goto(`${url}/?welcome=0`, { waitUntil: 'domcontentloaded' });
|
|
await page.waitForFunction(() => window.__godsEyeView?.dataManager, {
|
|
timeout: 120000,
|
|
});
|
|
await whenSettled(page);
|
|
await page.evaluate(
|
|
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
);
|
|
await page.keyboard.press('Escape');
|
|
await page.evaluate(async () => {
|
|
const g = window.__godsEyeView;
|
|
for (const [id] of g.dataManager.layers)
|
|
await g.dataManager.setEnabled(id, false);
|
|
g.styleManager.setDetection({ enabled: false });
|
|
});
|
|
await moveCamera(
|
|
page,
|
|
{ lon: -97.765, lat: 30.3125, height: 3500, pitch: -80 },
|
|
0,
|
|
);
|
|
const toggle = (id, value) =>
|
|
page.evaluate(
|
|
(id, value) => window.__godsEyeView.dataManager.setEnabled(id, value),
|
|
id,
|
|
value,
|
|
);
|
|
const shot = async (name, osm, tiles) => {
|
|
await whenSettled(page);
|
|
await whenUiSettled(page);
|
|
const state = await page.$eval('#cesium-credits', (e) => {
|
|
const nodes = [
|
|
...e.querySelectorAll('a,img,.cesium-credit-expand-link'),
|
|
].filter((n) =>
|
|
n.checkVisibility({ visibilityProperty: true, opacityProperty: true }),
|
|
);
|
|
return {
|
|
text: e.innerText,
|
|
rects: nodes
|
|
.map((n) => {
|
|
const b = n.getBoundingClientRect();
|
|
return {
|
|
text: n.textContent || n.alt || 'image',
|
|
x: b.x,
|
|
y: b.y,
|
|
width: b.width,
|
|
height: b.height,
|
|
reachable: n.contains(
|
|
document.elementFromPoint(
|
|
b.x + b.width / 2,
|
|
b.y + b.height / 2,
|
|
),
|
|
),
|
|
};
|
|
})
|
|
.filter((b) => b.width > 0 && b.height > 0),
|
|
};
|
|
});
|
|
assert.equal(state.text.includes('© OpenStreetMap'), osm);
|
|
assert.equal(state.text.includes('© OpenMapTiles'), tiles);
|
|
assert.equal(
|
|
(state.text.match(/© OpenStreetMap/g) || []).length,
|
|
osm ? 1 : 0,
|
|
);
|
|
assert.ok(state.rects.length >= 3);
|
|
const centers = state.rects.map((b) => b.y + b.height / 2);
|
|
assert.ok(
|
|
Math.max(...centers) - Math.min(...centers) < 12,
|
|
`${name}: credits stay on one desktop row`,
|
|
);
|
|
assert.ok(
|
|
state.rects.every(
|
|
(b) => b.x >= 0 && b.x + b.width <= page.viewport().width,
|
|
),
|
|
'no credit clipped',
|
|
);
|
|
assert.ok(
|
|
state.rects.every((b) => b.reachable),
|
|
'all credits and the attribution link remain clear of controls',
|
|
);
|
|
await page.screenshot({ path: `${dir}/after-${name}.png` });
|
|
await page.screenshot({
|
|
path: `${dir}/after-${name}-row.png`,
|
|
clip: { x: 16, y: 750, width: 1150, height: 150 },
|
|
});
|
|
result.states.push({ name, ...state });
|
|
};
|
|
await whenSettled(page);
|
|
await shot('nothing', false, false);
|
|
await toggle('alpr-cameras', true);
|
|
await page.waitForFunction(
|
|
() =>
|
|
document
|
|
.querySelector('#cesium-credits')
|
|
.innerText.includes('© OpenStreetMap'),
|
|
{ timeout: 60000 },
|
|
);
|
|
await shot('alpr', true, false);
|
|
await sleep(6000);
|
|
await shot('alpr-persistent', true, false);
|
|
await toggle('traffic', true);
|
|
await page.waitForFunction(
|
|
() =>
|
|
document
|
|
.querySelector('#cesium-credits')
|
|
.innerText.includes('© OpenMapTiles'),
|
|
{ timeout: 60000 },
|
|
);
|
|
await shot('alpr-and-traffic', true, true);
|
|
await toggle('alpr-cameras', false);
|
|
await shot('traffic', true, true);
|
|
for (const width of [1280, 1920]) {
|
|
await page.setViewport({ width, height: 900 });
|
|
await whenSettled(page);
|
|
await shot(`traffic-${width}`, true, true);
|
|
}
|
|
await page.setViewport({ width: 1440, height: 900 });
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.styleManager.setCleanView(true),
|
|
);
|
|
await shot('traffic-clean-ui', true, true);
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.styleManager.setCleanView(false),
|
|
);
|
|
await toggle('traffic', false);
|
|
await shot('nothing-restored', false, false);
|
|
assert.deepEqual(result.errors, []);
|
|
console.log(JSON.stringify(result, null, 2));
|
|
} finally {
|
|
await fs.writeFile(`${dir}/credits.json`, JSON.stringify(result, null, 2));
|
|
await closeBrowser(browser);
|
|
}
|