mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +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.
268 lines
9.3 KiB
JavaScript
268 lines
9.3 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { fromDegrees } from '../src/geo/cartesian3.js';
|
|
import {
|
|
createStrict,
|
|
localPreview,
|
|
openPage,
|
|
waitForEngine,
|
|
} from './qa-strict-harness.mjs';
|
|
|
|
const base = localPreview(process.argv[2]);
|
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
|
base.hash =
|
|
'v=2&lat=43.6500&lon=-79.3800&alt=46000&heading=0&pitch=-90&roll=0&map=sentinel2&l=4';
|
|
const strict = createStrict({
|
|
origin: base.origin,
|
|
layers: ['military-awareness', 'military-installations'],
|
|
});
|
|
await strict.run({}, async (b) => {
|
|
const { page: p, errors } = await openPage(b);
|
|
await p.evaluateOnNewDocument(() => {
|
|
const fetch = window.fetch.bind(window);
|
|
window.fetch = (input, options) => {
|
|
const { pathname } = new URL(input.url ?? input, document.baseURI);
|
|
if (pathname === '/api/vessels')
|
|
return Promise.resolve(
|
|
Response.json({
|
|
status: 'live',
|
|
rows: ['123456789', '223456789'].map((mmsi, i) => ({
|
|
mmsi,
|
|
name: 'Toronto ' + i,
|
|
lat: 43.65,
|
|
lon: -79.38 + i * 0.05,
|
|
last_position_epoch: Date.now() / 1000,
|
|
})),
|
|
}),
|
|
);
|
|
// Contacts reads air quality near the subject; no station here.
|
|
if (
|
|
new URL(input.url ?? input, document.baseURI).hostname ===
|
|
'api.weather.gc.ca'
|
|
)
|
|
return Promise.resolve(
|
|
Response.json({ type: 'FeatureCollection', features: [] }),
|
|
);
|
|
if (pathname === '/api/vessels/track')
|
|
return Promise.resolve(Response.json({ samples: [] }));
|
|
if (pathname === '/api/flights')
|
|
return Promise.resolve(
|
|
Response.json({ time: Date.now() / 1000, states: [] }),
|
|
);
|
|
if (pathname === '/api/military')
|
|
return Promise.resolve(Response.json({ now: Date.now(), ac: [] }));
|
|
// One mapped station next to the Contacts subject, with a footprint.
|
|
if (pathname === '/api/military-installations') {
|
|
window.qaInstallationReads = (window.qaInstallationReads ?? 0) + 1;
|
|
const ring = [
|
|
[43.665, -79.345],
|
|
[43.665, -79.315],
|
|
[43.68, -79.315],
|
|
[43.68, -79.345],
|
|
[43.665, -79.345],
|
|
];
|
|
return Promise.resolve(
|
|
Response.json({
|
|
status: 'ok',
|
|
elements: [
|
|
{
|
|
type: 'way',
|
|
id: 424242,
|
|
tags: { military: 'naval_base', name: 'QA Harbour Station' },
|
|
center: { lat: 43.6725, lon: -79.33 },
|
|
geometry: ring.map(([lat, lon]) => ({ lat, lon })),
|
|
},
|
|
],
|
|
}),
|
|
);
|
|
}
|
|
return fetch(input, options);
|
|
};
|
|
window.qaSelected = [];
|
|
for (const type of ['gev:entity-selected', 'gev:entity-selection-cleared'])
|
|
window.addEventListener(type, ({ detail }) =>
|
|
window.qaSelected.push({
|
|
type,
|
|
id: detail?.id,
|
|
layerId: detail?.layerId,
|
|
}),
|
|
);
|
|
});
|
|
await p.goto(base.href);
|
|
await waitForEngine(p, 'navara');
|
|
await p.evaluate(async () => {
|
|
const { dataManager, styleManager, tileset } = window.__godsEyeView;
|
|
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
|
throw Error('Require keyless preview');
|
|
await dataManager.setEnabled('ais-live-vessels', true, { origin: 'user' });
|
|
window.qaBefore = [...dataManager.getEnabledLayerIds()].sort();
|
|
const outcome = await styleManager.setContextMode('contacts');
|
|
if (!outcome.ok) throw Error(JSON.stringify(outcome));
|
|
window.qaContext = dataManager.layers.get('military-awareness').module;
|
|
window.qaSites = dataManager.layers.get('military-installations').module;
|
|
const vessels = dataManager.layers.get('ais-live-vessels').module;
|
|
if (!vessels.selectById('123456789')) throw Error('Missing first vessel');
|
|
if (!window.qaContext.navigateNext({ targetLayer: 'ais-live-vessels' }))
|
|
throw Error('Cannot navigate to the second vessel');
|
|
});
|
|
const sample = () =>
|
|
p.evaluate(() => {
|
|
const { dataManager, styleManager, engine } = window.__godsEyeView;
|
|
return {
|
|
context:
|
|
window.qaContext ===
|
|
dataManager.layers.get('military-awareness').module,
|
|
sites:
|
|
window.qaSites ===
|
|
dataManager.layers.get('military-installations').module,
|
|
mode: styleManager.getContextModeState().mode,
|
|
subject: window.qaContext.getContextSnapshot()?.subject?.id,
|
|
history:
|
|
window.qaContext._getAwarenessNavigationStateForTest().historyKeys,
|
|
enabled: [...dataManager.getEnabledLayerIds()].sort(),
|
|
ring:
|
|
engine.kind === 'navara'
|
|
? engine.lines
|
|
.list()
|
|
.filter((line) => line.id === 'gev-awareness-rim').length
|
|
: null,
|
|
};
|
|
});
|
|
// The station zone: the fixture's installation is the one mapped site
|
|
// within the Contacts radius of the subject, and the engine draws it where
|
|
// a pointer can reach it (the engine's own picker returns it at an
|
|
// uncovered canvas point at its marker or inside its footprint).
|
|
const inside = [fromDegrees(-79.33, 43.6725)];
|
|
for (let i = 1; i < 6; i++)
|
|
for (let k = 1; k < 6; k++)
|
|
inside.push(fromDegrees(-79.345 + 0.005 * k, 43.665 + 0.0025 * i));
|
|
const zone = async () => {
|
|
await p.waitForFunction(
|
|
() =>
|
|
!window.qaSites.getStats().loading &&
|
|
window.qaSites
|
|
.getAnalystRecords()
|
|
.some((r) => r.name === 'QA Harbour Station'),
|
|
{ timeout: 30000 },
|
|
);
|
|
const drawn = await p
|
|
.waitForFunction(
|
|
(points) => {
|
|
const { engine } = window.__godsEyeView;
|
|
const canvas = engine.host.canvas;
|
|
const box = canvas.getBoundingClientRect();
|
|
for (const world of points) {
|
|
const centre = engine.projector.worldToWindow(world, {});
|
|
if (!centre) continue;
|
|
for (const [dx, dy] of [
|
|
[0, 0],
|
|
[0, -8],
|
|
[8, 0],
|
|
[-8, 0],
|
|
[0, 8],
|
|
]) {
|
|
const at = { x: centre.x + dx, y: centre.y + dy };
|
|
if (
|
|
!canvas.contains(
|
|
document.elementFromPoint(box.left + at.x, box.top + at.y),
|
|
)
|
|
)
|
|
continue;
|
|
const id = engine.picker.pick(at)?.id;
|
|
if (String(id?.id ?? id) === 'osm:way:424242')
|
|
return [Math.round(at.x), Math.round(at.y)];
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
{ timeout: 20000 },
|
|
inside,
|
|
)
|
|
.then((handle) => handle.jsonValue())
|
|
.catch(() => null);
|
|
return p.evaluate(
|
|
(drawn) => ({
|
|
drawn,
|
|
records: window.qaSites.getAnalystRecords().map((r) => r.name),
|
|
coverage: window.qaSites.getStats().coverage,
|
|
count: window.qaSites.getStats().count,
|
|
reads: window.qaInstallationReads,
|
|
subject: window.qaContext.getContextSnapshot()?.subject?.id,
|
|
}),
|
|
drawn,
|
|
);
|
|
};
|
|
const checkZone = async () => {
|
|
const station = await zone();
|
|
await strict.check(
|
|
p,
|
|
'military-installations',
|
|
'the station zone holds the fixture station, drawn and pickable',
|
|
station.count === 1 &&
|
|
station.records.length === 1 &&
|
|
station.records[0] === 'QA Harbour Station' &&
|
|
station.coverage?.kind === 'subject' &&
|
|
Array.isArray(station.drawn),
|
|
station,
|
|
);
|
|
return station;
|
|
};
|
|
const firstZone = await checkZone();
|
|
const baseline = await sample();
|
|
console.log('baseline', JSON.stringify(baseline));
|
|
assert.equal(baseline.mode, 'flights');
|
|
assert.equal(baseline.subject, '223456789');
|
|
assert.ok(baseline.history.includes('ais-live-vessels:123456789'));
|
|
assert.ok(baseline.ring >= 1);
|
|
await strict.passed(
|
|
p,
|
|
'military-awareness',
|
|
'Contacts subject, navigation history and awareness rim',
|
|
baseline,
|
|
);
|
|
for (const kind of ['cesium', 'navara']) {
|
|
await strict.switchTo(p, kind);
|
|
const current = await sample();
|
|
console.log('handoff', kind, JSON.stringify(current));
|
|
assert.deepEqual(current, {
|
|
...baseline,
|
|
ring: kind === 'navara' ? baseline.ring : null,
|
|
});
|
|
await strict.passed(
|
|
p,
|
|
'military-awareness',
|
|
'same awareness model, Contacts mode, subject, history and enabled layers',
|
|
);
|
|
await strict.passed(
|
|
p,
|
|
'military-installations',
|
|
'same installations model and enabled through the switch',
|
|
);
|
|
const station = await checkZone();
|
|
await strict.check(
|
|
p,
|
|
'military-installations',
|
|
'the switch reads no installation snapshot',
|
|
station.reads === firstZone.reads,
|
|
{ before: firstZone.reads, after: station.reads },
|
|
);
|
|
}
|
|
await checkZone();
|
|
const restored = await p.evaluate(async () => {
|
|
const { dataManager, styleManager } = window.__godsEyeView;
|
|
const result = await styleManager.setContextMode('off');
|
|
return (
|
|
result.ok &&
|
|
JSON.stringify([...dataManager.getEnabledLayerIds()].sort()) ===
|
|
JSON.stringify(window.qaBefore)
|
|
);
|
|
});
|
|
assert.ok(
|
|
restored,
|
|
'Restore layers enabled by the user before entering Contacts',
|
|
);
|
|
assert.deepEqual(errors, []);
|
|
console.log(
|
|
'PASS Contacts history, dependency ownership and exit restore across both globes',
|
|
);
|
|
});
|