Files
gods-eye-view/scripts/qa-space-engine-switch.mjs
T
German Khokhlov 7abf74518f Cut duplicated and dead code across the engines, the layers and the QA scripts
- 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.
2026-11-05 13:32:39 +04:00

229 lines
7.8 KiB
JavaScript

import assert from 'node:assert/strict';
import { launchFixture } from '../src/bench/launchFixture.js';
import {
satelliteFixture,
SATELLITE_FIXTURE_EPOCH,
} from '../src/bench/satelliteFixture.js';
import { createStrict, localPreview } from './qa-strict-harness.mjs';
const url = localPreview(process.argv[2]),
missions = process.argv.includes('--missions');
url.search = '?engine=navara&welcome=0';
url.hash = 'v=2&lat=5&lon=5&alt=18000000&pitch=-90&heading=0&map=sentinel2&l=';
const layer = missions ? 'rocket-launches' : 'satellites';
const strict = createStrict({
origin: url.origin,
layers: [missions ? 'rocket-launches' : 'satellites'],
});
await strict.run({}, async (b) => {
const p = await b.newPage();
const errors = [];
p.on('pageerror', (e) => {
errors.push(e.message);
console.log('error', e.message);
});
const groups = Object.fromEntries(
[
'stations',
'visual',
'gps-ops',
'glo-ops',
'galileo',
'geo',
'starlink',
].map((g) => [g, satelliteFixture(g)]),
);
await p.evaluateOnNewDocument(
(groups, launches, epoch) => {
const fetch = window.fetch.bind(window);
window.spaceReads = {};
const mono = performance.now();
Date.now = () => epoch + performance.now() - mono;
window.fetch = (input, opt) => {
const path = new URL(input.url ?? input, location.href).pathname;
if (path.startsWith('/api/celestrak/') || path === '/api/launches') {
window.spaceReads[path] = (window.spaceReads[path] || 0) + 1;
const g = path.split('/').pop();
return Promise.resolve(
g === 'launches'
? Response.json(launches)
: new Response(groups[g] ?? groups.stations),
);
}
return fetch(input, opt);
};
},
groups,
launchFixture(),
SATELLITE_FIXTURE_EPOCH,
);
await p.goto(url.href);
await p.waitForFunction(
() =>
window.__godsEyeView?.engine?.kind === 'navara' &&
document.querySelector('#loading-screen.hidden'),
);
await p.evaluate(async (missions) => {
const { dataManager, styleManager, tileset } = window.__godsEyeView;
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
throw Error('Expected keyless preview');
await styleManager.initialRestorePromise;
window.sa = dataManager.layers.get('satellites').module;
window.la = dataManager.layers.get('rocket-launches').module;
if (missions) {
const result = await styleManager.setContextMode('space-missions');
if (!result.ok) throw Error('Missions mode unavailable: ' + result.error);
} else await dataManager.setEnabled('satellites', true, { origin: 'user' });
}, missions);
if (!missions) {
await p.waitForFunction(() => window.sa.getStats().count === 839, {
timeout: 40000,
});
console.log('core catalog ready');
await p.evaluate(() => window.sa.setParams({ catalog: 'dense' }));
await p.waitForFunction(() => window.sa.getStats().count === 6839, {
timeout: 60000,
});
console.log('dense catalog ready');
assert.ok(
await p.evaluate(() => window.sa.trackById(25544, { origin: 'user' })),
);
} else {
await p.waitForFunction(() => window.la.getStats().count === 3, {
timeout: 40000,
});
await p.evaluate(() => {
window.la.selectMissionAt(0);
window.la.startSelectedReplay();
window.la.setReplaySpeed(4);
});
await p.waitForFunction(() => window.la.qaDrawn()?.replay?.active, {
timeout: 20000,
});
await p.waitForFunction(
() => window.la.qaDrawn()?.replay?.phaseProgress > 0.001,
{ timeout: 35000 },
);
await p.evaluate(() => window.la.toggleReplayPause());
}
const before = await p.evaluate(() => ({
reads: window.spaceReads,
stamp: (window.la.getStats().count ? window.la : window.sa).getStats()
.lastUpdate,
mission: window.la.qaDrawn()?.selected,
replay: window.la.qaDrawn()?.replay,
}));
console.log(
'before switch',
before.reads,
before.mission,
before.replay?.active,
);
// Each source read exactly once: the seven TLE groups of the dense
// catalog; in Space Missions the launch list and every TLE group the
// mission view asks for.
const groupPaths = Object.keys(groups).map((g) => '/api/celestrak/' + g);
const readsOnce = async () => {
const reads = await p.evaluate(() => ({ ...window.spaceReads }));
await strict.check(
p,
layer,
missions
? 'the launch list and each TLE group read exactly once'
: 'each of the seven TLE groups read exactly once',
Object.values(reads).every((n) => n === 1) &&
(missions
? reads['/api/launches'] === 1
: Object.keys(reads).sort().join() === [...groupPaths].sort().join()),
reads,
);
};
await readsOnce();
for (const kind of ['cesium', 'navara']) {
await strict.switchTo(p, kind);
const state = await p.evaluate(() => {
const { dataManager, engine, styleManager } = window.__godsEyeView;
return {
ids: [
dataManager.layers.get('satellites').module === window.sa,
dataManager.layers.get('rocket-launches').module === window.la,
],
reads: window.spaceReads,
sat: window.sa.getStats(),
params: window.sa.getParams(),
follow: !!engine.camera.following(),
ring: window.sa.qaDrawn()?.rings.length,
mission: window.la.qaDrawn(),
mode: styleManager.getContextModeState().mode,
missionStats: window.la.getStats(),
};
});
console.log(
kind,
state.ids,
state.sat.count,
state.params.selectedSatTrackingId,
state.follow,
state.mode,
state.mission?.selected,
state.mission?.replay?.phaseProgress,
);
assert.ok(state.ids.every(Boolean));
assert.deepEqual(state.reads, before.reads);
if (missions) {
assert.equal(state.mode, 'space-missions');
assert.equal(state.mission.selected, before.mission);
assert.ok(state.mission.paused && state.mission.replay.active);
assert.equal(state.mission.speed, 4);
assert.equal(state.missionStats.lastUpdate, before.stamp);
assert.equal(
state.mission.replay.phaseProgress,
before.replay.phaseProgress,
);
} else {
assert.equal(state.sat.count, 6839);
assert.equal(state.params.selectedSatTrackingId, 25544);
assert.ok(state.follow && state.ring > 0);
assert.equal(state.sat.lastUpdate, before.stamp);
}
await strict.passed(
p,
layer,
missions
? 'same models; Space Missions mode, the same mission paused at the same progress, speed 4'
: 'same models; 6 839 objects, the ISS tracked with its ring, same update stamp',
);
await readsOnce();
}
if (missions) {
await p.evaluate(() => window.la.toggleReplayPause());
await p.waitForFunction(() => window.la.qaDrawn()?.paused === false);
await p.waitForFunction(
(progress) => window.la.qaDrawn()?.replay?.phaseProgress > progress,
{},
before.replay.phaseProgress,
);
await strict.switchTo(p, 'cesium');
const moving = await p.evaluate(() => window.la.qaDrawn());
await strict.check(
p,
layer,
'a running replay keeps advancing through the switch',
moving.replay?.active &&
!moving.paused &&
moving.replay.phaseProgress > before.replay.phaseProgress,
);
const left = await p.evaluate(() =>
window.__godsEyeView.styleManager.setContextMode(null),
);
assert.ok(left.ok);
assert.equal(
await p.evaluate(() =>
window.__godsEyeView.dataManager.isEnabled('rocket-launches'),
),
false,
);
}
assert.deepEqual(errors, []);
console.log('PASS retained space data and active camera across both planets');
});