mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-09 21:02:19 +00:00
Give the contract suite a clear pointer path and real canvases in the app
In the app the world overlay draws on a canvas of its own above the globe, so letting every canvas take the pointer sent the suite's clicks there. Only the engine's canvas takes it now. Without makeCanvas the pick check's sprite atlas was a plain object no real engine draws, so its sprites could not be picked; the page now hands the suite real 2D canvases. --only=prefix,... runs a subset of the checks. With both, Cesium passes 63 of 63 checks and Navara 66 of 66.
This commit is contained in:
1 parent
35e70ec280
commit
3e261d1baf
2 files changed
+68
-51
No files matched your search
@@ -181,14 +181,12 @@ one application, and only the globe-bound parts move to the next engine.
|
||||
in for tests, and `src/globe/testing/contractSuite.mjs` holds the checks
|
||||
every engine must pass. `npm test` runs them on the fake engine;
|
||||
`node scripts/qa-contract-suite.mjs <origin>` runs them in headless Chrome
|
||||
on the real Cesium and Navara of a keyless dev server. On November 7, 2026
|
||||
Cesium passed 57 of 57 and Navara 64 of 67: a resting move raised no
|
||||
`moveEnd`, a click payload never came, and contact sonar lost an angle-only
|
||||
change inside the app. `movers` and `lines` are required of every engine:
|
||||
Cesium draws them with billboards, points, models and polyline primitives
|
||||
(`src/globe/cesium/movers.js`, `lines.js`). The 57 Cesium checks above ran
|
||||
without them; in Node their checks pass on Cesium without pixels or a
|
||||
pointer, and the browser run with them is still to come.
|
||||
on the real Cesium and Navara of a keyless dev server, with a real pointer
|
||||
and real canvases; checks of state the app drives itself (contact sonar)
|
||||
are skipped and named. On November 7, 2026 Cesium passed 63 of 63 and
|
||||
Navara 66 of 66. `movers` and `lines` are required of every engine: Cesium
|
||||
draws them with billboards, points, models and polyline primitives
|
||||
(`src/globe/cesium/movers.js`, `lines.js`).
|
||||
- **Engine coupling.** `node scripts/report-engine-coupling.mjs` counts the
|
||||
engine-specific code outside the engines: renderer lines per engine, Cesium
|
||||
imports, `viewer` mentions, engine-name checks, raw Navara reads, Navara
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
* are refused here anyway, so neither engine draws photoreal tiles. The page
|
||||
* imports the suite from the dev server, so it shares the app's modules (an
|
||||
* engine event is recognised by the module that made it). It gets a real
|
||||
* pointer (the pick checks) and no map sources (the maps checks would ask for
|
||||
* photoreal tiles). Exits 1 when any check fails on any engine.
|
||||
* pointer (the pick checks), real 2D canvases for sprite atlases, and no map
|
||||
* sources (the maps checks would ask for photoreal tiles). Exits 1 when any check fails on any engine.
|
||||
*/
|
||||
import fs from 'node:fs';
|
||||
import puppeteer from 'puppeteer';
|
||||
@@ -26,11 +26,13 @@ const option = (name) =>
|
||||
args.find((arg) => arg.startsWith(`--${name}=`))?.slice(name.length + 3);
|
||||
if (!origin) {
|
||||
console.error(
|
||||
'Usage: node scripts/qa-contract-suite.mjs <origin> [--engine=cesium,navara] [--json=FILE]',
|
||||
'Usage: node scripts/qa-contract-suite.mjs <origin> [--engine=cesium,navara] [--only=prefix,...] [--json=FILE]',
|
||||
);
|
||||
process.exit(2);
|
||||
}
|
||||
const engines = (option('engine') ?? 'cesium,navara').split(',');
|
||||
// --only=prefix,prefix runs just the checks whose names start with one.
|
||||
const only = option('only')?.split(',') ?? null;
|
||||
|
||||
// Checks of state the running app drives itself, by name prefix, with the
|
||||
// owner. The app sets contact sonar on every frame (src/cyberSonarEngine.js),
|
||||
@@ -68,9 +70,17 @@ async function runOn(browser, kind) {
|
||||
() => !window.__godsEyeView.engine.camera.flightActive(),
|
||||
{ timeout: 60_000, polling: 250 },
|
||||
);
|
||||
// Only the engine's canvas takes the pointer: the world overlay draws on a
|
||||
// canvas of its own above it.
|
||||
await page.evaluate(() =>
|
||||
window.__godsEyeView.engine.host.canvas.setAttribute(
|
||||
'data-contract-target',
|
||||
'',
|
||||
),
|
||||
);
|
||||
await page.addStyleTag({
|
||||
content:
|
||||
'* { pointer-events: none !important; } canvas { pointer-events: auto !important; }',
|
||||
'* { pointer-events: none !important; } [data-contract-target] { pointer-events: auto !important; }',
|
||||
});
|
||||
// A real left click and pointer move at a canvas position.
|
||||
await page.exposeFunction('__contractPointer', async (type, x, y) => {
|
||||
@@ -86,47 +96,56 @@ async function runOn(browser, kind) {
|
||||
await page.mouse.move(...at);
|
||||
}
|
||||
});
|
||||
const results = await page.evaluate(async (appOwned) => {
|
||||
const { engine } = window.__godsEyeView;
|
||||
const waitFrames = (count) =>
|
||||
new Promise((resolve) => {
|
||||
let left = count;
|
||||
const remove = engine.frames.postRender.addEventListener(() => {
|
||||
if (--left > 0) return;
|
||||
remove();
|
||||
resolve();
|
||||
const results = await page.evaluate(
|
||||
async (appOwned, only) => {
|
||||
const { engine } = window.__godsEyeView;
|
||||
const waitFrames = (count) =>
|
||||
new Promise((resolve) => {
|
||||
let left = count;
|
||||
const remove = engine.frames.postRender.addEventListener(() => {
|
||||
if (--left > 0) return;
|
||||
remove();
|
||||
resolve();
|
||||
});
|
||||
engine.render.request('contract-suite');
|
||||
});
|
||||
engine.render.request('contract-suite');
|
||||
});
|
||||
const pointer = {
|
||||
click: (x, y) => window.__contractPointer('click', x, y),
|
||||
move: (x, y) => window.__contractPointer('move', x, y),
|
||||
};
|
||||
const { createContractSuite } =
|
||||
await import('/src/globe/testing/contractSuite.mjs');
|
||||
const outcome = [];
|
||||
for (const { name, run } of createContractSuite(engine, {
|
||||
waitFrames,
|
||||
pointer,
|
||||
})) {
|
||||
const owner = appOwned.find(([prefix]) => name.startsWith(prefix));
|
||||
if (owner) {
|
||||
outcome.push({ name, skipped: owner[1] });
|
||||
continue;
|
||||
const pointer = {
|
||||
click: (x, y) => window.__contractPointer('click', x, y),
|
||||
move: (x, y) => window.__contractPointer('move', x, y),
|
||||
};
|
||||
const { createContractSuite } =
|
||||
await import('/src/globe/testing/contractSuite.mjs');
|
||||
const outcome = [];
|
||||
// A real 2D canvas: a sprite atlas the engines can draw and pick.
|
||||
const makeCanvas = (width, height) =>
|
||||
Object.assign(document.createElement('canvas'), { width, height });
|
||||
for (const { name, run } of createContractSuite(engine, {
|
||||
waitFrames,
|
||||
pointer,
|
||||
makeCanvas,
|
||||
})) {
|
||||
if (only && !only.some((prefix) => name.startsWith(prefix))) continue;
|
||||
const owner = appOwned.find(([prefix]) => name.startsWith(prefix));
|
||||
if (owner) {
|
||||
outcome.push({ name, skipped: owner[1] });
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
await run();
|
||||
outcome.push({ name, ok: true });
|
||||
} catch (error) {
|
||||
outcome.push({
|
||||
name,
|
||||
ok: false,
|
||||
error: String(error?.message ?? error),
|
||||
});
|
||||
}
|
||||
}
|
||||
try {
|
||||
await run();
|
||||
outcome.push({ name, ok: true });
|
||||
} catch (error) {
|
||||
outcome.push({
|
||||
name,
|
||||
ok: false,
|
||||
error: String(error?.message ?? error),
|
||||
});
|
||||
}
|
||||
}
|
||||
return outcome;
|
||||
}, APP_OWNED);
|
||||
return outcome;
|
||||
},
|
||||
APP_OWNED,
|
||||
only,
|
||||
);
|
||||
await page.close();
|
||||
return { engine: kind, results, pageErrors };
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user