Files
gods-eye-view/src/tooling/transitQa.test.mjs
T

704 lines
22 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { getTransitFeed } from '../data/transitFeeds.js';
import {
chooseLiveCities,
selectStyle,
detectOn,
} from '../../scripts/qa-transit-controls.mjs';
import { createStyleParameters } from '../ui/styleParameters.js';
import { bindDisplayControls } from '../ui/displayControls.js';
import { thermalShader } from '../styles/thermal.js';
import {
cycleMode,
setMode,
getMode,
setDetectionTuning,
} from '../data/detection.js';
const pause = async () => {};
class Element extends EventTarget {
constructor() {
super();
this.children = [];
this.attributes = new Map();
this.classList = { contains: () => false };
}
setAttribute(name, value) {
this.attributes.set(name, value);
}
getAttribute(name) {
return this.attributes.get(name);
}
append(...children) {
this.children.push(...children);
}
appendChild(child) {
this.children.push(child);
}
replaceChildren() {
this.children = [];
}
scrollIntoView() {}
getBoundingClientRect() {
return { width: 100 };
}
}
function pageFixture(t) {
const prior = { document: globalThis.document, window: globalThis.window };
const elements = new Map();
const document = {
documentElement: { dataset: { gevStyle: 'normal' } },
createElement: () => new Element(),
getElementById: (id) => elements.get(id),
querySelector: () => null,
};
globalThis.document = document;
globalThis.window = {};
t.after(() => {
for (const [name, value] of Object.entries(prior)) {
if (value === undefined) delete globalThis[name];
else globalThis[name] = value;
}
});
const find = (selector) => {
if (selector.startsWith('#param-sliders ')) {
const label = selector.match(/aria-label="([^"]+)"/)[1];
return elements
.get('param-sliders')
.children.flatMap((row) => row.children)
.find((el) => el.getAttribute('aria-label') === label);
}
return elements.get(selector.slice(1));
};
const page = {
evaluate: async (fn, ...args) => fn(...args),
waitForFunction: async (fn, options, ...args) =>
assert.ok(fn(...args), 'UI condition must hold'),
waitForSelector: async (selector) =>
assert.ok(find(selector), `missing ${selector}`),
$eval: async (selector, fn, ...args) => {
const el = find(selector);
assert.ok(el, `missing ${selector}`);
return fn(el, ...args);
},
click: async (selector) =>
find(selector)?.dispatchEvent(new Event('click')),
};
return { page, elements, document };
}
test('live visual cities follow local operating hours in summer and winter', () => {
for (const date of ['2026-09-15T06:30:00Z', '2026-01-15T06:30:00Z']) {
assert.equal(chooseLiveCities(new Date(date))[0].id, 'helsinki');
}
assert.deepEqual(
chooseLiveCities(new Date('2026-09-15T17:00:00Z')).map((c) => c.id),
['boston', 'austin'],
);
for (let hour = 0; hour < 24; hour++) {
const now = new Date(`2026-09-15T${String(hour).padStart(2, '0')}:00:00Z`);
const cities = chooseLiveCities(now);
assert.ok(cities.length >= 1);
for (const city of cities) {
const feed = getTransitFeed(city.feedId);
assert.ok(feed, `${city.id} has a registered feed`);
const distanceKm =
111.32 *
Math.hypot(
feed.center.lat - city.lat,
(feed.center.lon - city.lon) * Math.cos((city.lat * Math.PI) / 180),
);
assert.ok(
distanceKm < feed.loadRadiusKm,
`${city.id} is inside its feed's admission region`,
);
const local = Number(
new Intl.DateTimeFormat('en', {
timeZone: city.zone,
hour: 'numeric',
hourCycle: 'h23',
}).format(now),
);
assert.ok(local >= 6 && local < 22);
}
}
});
test('thermal controls wait for activation and use the real generated parameter rows', async (t) => {
const { page, document, elements } = pageFixture(t);
const container = new Element();
container.ownerDocument = document;
elements.set('param-sliders', container);
const parameters = createStyleParameters({ container });
t.after(() => parameters.destroy());
const values = Object.fromEntries(
Object.entries(thermalShader.uniforms).map(([key, meta]) => [
key,
meta.default,
]),
);
window.__godsEyeView = {
styleManager: {
setPanelCollapsed() {},
setStyle(name) {
document.documentElement.dataset.gevStyle = name;
parameters.render({
uniforms: thermalShader.uniforms,
readValue: (key) => values[key],
writeValue: (key, value) => {
values[key] = value;
},
onChange() {},
});
},
},
};
// A swallowed click resolves normally; the helper must notice no activation.
page.$eval = async (selector, fn, ...args) => {
if (selector.startsWith('.style-btn')) return fn(new Element(), ...args);
const label = selector.match(/aria-label="([^"]+)"/)[1];
const slider = container.children
.flatMap((row) => row.children)
.find((el) => el.getAttribute('aria-label') === label);
assert.ok(slider);
return fn(slider, ...args);
};
const how = await selectStyle(
page,
'thermal',
{ 'WHOT/BHOT': 1, Ironbow: 1 },
pause,
);
assert.match(how, /button did not activate/);
assert.equal(values.mode, 1);
assert.equal(values.palette, 1);
});
test('DETECT uses the real OFF/restore button and density input to reach DENSE', async (t) => {
const { page, elements } = pageFixture(t);
const button = new Element(),
slider = new Element();
elements.set('detection-toggle', button);
elements.set('detection-density-slider', slider);
button.getAttribute = (name) =>
name === 'aria-pressed'
? String(getMode() !== 'OFF')
: `Detection overlay: ${getMode().toLowerCase()}`;
setMode('BALANCED');
setMode('OFF');
const modes = [];
const controls = bindDisplayControls({
elements: { detectionButton: button, densitySlider: slider },
actions: {
cycleDetection() {
cycleMode();
modes.push(getMode());
},
setDensity(value) {
setDetectionTuning({ densityPct: Number(value) });
},
},
});
t.after(() => {
controls.destroy();
setMode('OFF');
});
assert.equal(await detectOn(page, pause), 'DENSE');
assert.deepEqual(modes, ['BALANCED', 'OFF', 'DENSE']);
const template = readFileSync(
new URL('../ui/templates/display-controls.html', import.meta.url),
'utf8',
);
assert.match(template, /id="detection-density-slider"/);
assert.match(template, /id="detection-toggle"/);
});
test('city motion checks load deterministic playback before reading rendered samples', () => {
const source = readFileSync(
new URL('../../scripts/qa-transit.mjs', import.meta.url),
'utf8',
);
const fixture = source.indexOf('_loadTransitFleetForTest(16');
assert.ok(fixture >= 0);
assert.ok(
source.indexOf('const motionFirst = await sampleVisible(page)', fixture) >
fixture,
);
assert.ok(
source.indexOf('const glideA = await sampleVisible(page)', fixture) >
fixture,
);
assert.ok(
source.indexOf('const headingA = await sampleScreen(page)', fixture) >
fixture,
);
assert.match(source, /scene.renderError.addEventListener/);
});
const { boundPageEvaluations, restoreRetainedTransit, sampleTransitPixels } =
await import('../../scripts/qa-transit-browser.mjs');
test('page promise deadline rejects a stuck enable or teardown and preserves ordinary results', async () => {
const page = { evaluate: async (fn, ...args) => fn(...args) };
boundPageEvaluations(page, 15);
assert.equal(await page.evaluate((a, b) => a + b, 3, 4), 7);
await assert.rejects(
page.evaluate(() => new Promise(() => {})),
/Transit QA page wait timed out after 15 ms/,
);
});
test('reload picks a surviving primed vehicle and reports unavailable snapshots as unexercised', async (t) => {
const previous = globalThis.window;
t.after(() => {
globalThis.window = previous;
});
let selected;
const entry = { key: 'mbta:survivor', record: { lon: -71.06, lat: 42.36 } };
const vehicles = new Map([[entry.key, entry]]);
globalThis.window = {
__godsEyeView: {
viewer: {
camera: {
positionCartographic: {
constructor: { fromDegrees: (...v) => v, toCartesian: (v) => v },
},
setView() {},
},
},
dataManager: {
setEnabled: async () => {},
layers: new Map([
[
'transit',
{
module: {
update: async () => {},
_transitStateForTest: () => ({ _vehicles: vehicles }),
_transitPartsForTest: () => ({
selection: {
selectVehicle: (key) => {
selected = key;
},
},
}),
},
},
],
]),
},
},
};
const primed = [
{ vehicleId: 'departed', oldestT: 10 },
{ vehicleId: 'survivor', oldestT: 20 },
];
assert.deepEqual(await restoreRetainedTransit(primed), {
key: entry.key,
oldest: 20,
});
assert.equal(selected, entry.key);
vehicles.clear();
const manager = window.__godsEyeView.dataManager;
const layer = manager.layers.get('transit').module;
layer._transitStateForTest = () => ({
_vehicles: vehicles,
_activeFeeds: new Map([['mbta', {}]]),
});
manager.setEnabled = async () => {
setTimeout(() => vehicles.set(entry.key, entry), 5);
};
assert.equal(
(await restoreRetainedTransit(primed)).key,
entry.key,
'wait for discovery instead of declaring every vehicle departed',
);
manager.setEnabled = async () => {};
assert.equal(
(
await restoreRetainedTransit([
{ ...primed[1], feedId: 'mbta', primedAt: Date.now() - 60000 },
])
).unexercised,
true,
'expired priming cannot pass',
);
layer._transitStateForTest = () => ({ _vehicles: vehicles });
vehicles.clear();
assert.equal((await restoreRetainedTransit(primed)).unexercised, true);
assert.equal((await restoreRetainedTransit([])).unexercised, true);
});
test('sensor sampler waits for postRender and reads framebuffer pixels; stopped rendering rejects and removes listener', async (t) => {
const previousWindow = globalThis.window,
previousDocument = globalThis.document,
previousImage = globalThis.Image;
t.after(() => {
globalThis.window = previousWindow;
globalThis.document = previousDocument;
globalThis.Image = previousImage;
});
const identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
const readRows = [];
let listener,
reads = 0,
render = true;
const gl = {
RGBA: 1,
UNSIGNED_BYTE: 2,
readPixels(x, y, w, h, format, type, buffer) {
reads++;
readRows.push(y);
buffer.set([255, 255, 255, 255]);
},
};
const scene = {
camera: { viewMatrix: identity, frustum: { projectionMatrix: identity } },
canvas: {
width: 400,
height: 400,
clientWidth: 400,
clientHeight: 400,
getContext: () => gl,
},
postRender: {
addEventListener(fn) {
listener = fn;
return () => {
listener = null;
};
},
},
requestRender() {
if (render) queueMicrotask(() => listener?.());
},
};
const raster = new Uint8ClampedArray(16 * 16 * 4).fill(255);
globalThis.Image = class {
width = 16;
height = 16;
async decode() {}
};
globalThis.document = {
getElementById: () => null,
createElement: () => ({
getContext: () => ({
drawImage() {},
getImageData: () => ({ data: raster }),
}),
}),
};
globalThis.window = {
__godsEyeView: {
viewer: { scene },
dataManager: {
layers: new Map([
[
'transit',
{
module: {
_transitStateForTest: () => ({
_vehicles: new Map([
[
'bus',
{
key: 'bus',
mode: 'bus',
marker: {
show: true,
image: 'raster',
position: { x: 0, y: 0, z: 0 },
},
},
],
]),
}),
},
},
],
]),
},
},
};
const page = { evaluate: async (fn, ...args) => fn(...args) };
const sampledLayer =
window.__godsEyeView.dataManager.layers.get('transit').module;
const originalState = sampledLayer._transitStateForTest;
sampledLayer._transitStateForTest = () => {
const state = originalState();
state._vehicles.get('bus').marker.position.y = 0.0025; // Screen y = 199.5.
return state;
};
const pixels = await sampleTransitPixels(page, {});
sampledLayer._transitStateForTest = originalState;
assert.deepEqual(
readRows.slice(0, 9),
[201, 200, 199, 201, 200, 199, 201, 200, 199],
'fractional CSS coordinates must invert the floored framebuffer row',
);
assert.ok(
reads >= 13,
'centre, rings and background come from WebGL readPixels',
);
assert.ok(Math.abs(pixels[0].centre - 1) < 1e-6);
assert.equal(listener, null);
// An opaque window/panel is not a sensor core. Choose another solid patch,
// and reject a raster with no white interior even though alpha/picking pass.
for (let i = 0; i < raster.length; i += 4) raster.fill(0, i, i + 3);
assert.equal((await sampleTransitPixels(page, {})).length, 0);
raster.fill(255);
for (let y = 6; y <= 9; y++)
for (let x = 0; x < 16; x++) {
const i = (y * 16 + x) * 4;
raster.fill(0, i, i + 3);
}
assert.equal(
(await sampleTransitPixels(page, {})).length,
1,
'a body patch away from the central panel remains eligible',
);
// A narrow centre can be opaque yet put the framebuffer sample on its
// antialiased side. Prefer the wider solid band above it, before reading luma.
raster.fill(0);
for (let y = 0; y < 16; y++)
for (let x = 0; x < 16; x++) {
const wide = y >= 3 && y <= 5;
if ((wide && x >= 3 && x <= 12) || (x >= 7 && x <= 9))
raster.fill(255, (y * 16 + x) * 4, (y * 16 + x + 1) * 4);
}
const readStart = readRows.length;
assert.equal((await sampleTransitPixels(page, {})).length, 1);
assert.ok(
readRows[readStart] > 204,
'centre samples use the widest solid band',
);
raster.fill(255);
sampledLayer._transitStateForTest = () => {
const state = originalState();
state._vehicles.get('bus').mode = 'tram';
return state;
};
const tramStart = readRows.length;
assert.equal((await sampleTransitPixels(page, {})).length, 1);
assert.ok(
readRows[tramStart] > 204,
'equal-width tram bands prefer the upper solid body',
);
sampledLayer._transitStateForTest = originalState;
scene.pick = () => ({ id: 'overlapping-label' });
assert.equal(
(await sampleTransitPixels(page, {})).length,
0,
'a foreign pick across the sampled core disqualifies an obscured sprite',
);
scene.pick = ({ x }) => ({ id: x < 200 ? 'overlapping-sprite' : 'bus' });
assert.equal(
(await sampleTransitPixels(page, {})).length,
0,
'a clear centre cannot qualify a partially obscured core',
);
delete scene.pick;
const module = window.__godsEyeView.dataManager.layers.get('transit').module;
const getState = module._transitStateForTest;
module._transitStateForTest = () => {
const state = getState();
state._stylePreset = 'noir';
state._vehicles.get('bus').marker.position.x = 0.65;
return state;
};
assert.equal(
(await sampleTransitPixels(page, {})).length,
0,
'the intentional noir edge vignette is outside unobscured-core acceptance',
);
module._transitStateForTest = getState;
// The fixture returns fresh wrapper objects, so exercise raster and DOM
// eligibility independently of framebuffer brightness.
raster.fill(0);
assert.equal((await sampleTransitPixels(page, {})).length, 0);
raster.fill(255);
document.elementFromPoint = () => ({ tagName: 'DIV', closest: () => null });
assert.equal((await sampleTransitPixels(page, {})).length, 0);
delete document.elementFromPoint;
document.getElementById = () => ({
width: 400,
clientWidth: 400,
getContext: () => ({
getImageData: () => ({ data: new Uint8ClampedArray(144).fill(255) }),
}),
});
assert.equal((await sampleTransitPixels(page, {})).length, 0);
document.getElementById = () => null;
window.__godsEyeView.styleManager = {
getDetectionDiagnostics: () => ({
calloutRects: [{ x: 205, y: 195, w: 10, h: 10, alpha: 1 }],
}),
};
assert.equal(
(await sampleTransitPixels(page, {})).length,
0,
'plate over the edge excludes the whole sprite even with a clear centre',
);
delete window.__godsEyeView.styleManager;
document.getElementById = () => ({
width: 400,
clientWidth: 400,
getContext: () => ({
getImageData: (x, y, w = 1, h = 1) => ({
data:
w > 1
? new Uint8ClampedArray(w * h * 4)
: new Uint8ClampedArray(
x === 191 && [190, 193, 196].includes(y)
? [20, 30, 40, 255]
: x === 191 && y === 207
? [94, 240, 138, 255]
: [0, 0, 0, 0],
),
}),
}),
});
assert.deepEqual(
(await sampleTransitPixels(page, {}))[0].bracket,
[94, 240, 138],
'stroke centre is sampled away from the top label plate',
);
document.getElementById = () => null;
render = false;
t.mock.timers.enable({ apis: ['setTimeout'] });
const pending = sampleTransitPixels(page, {});
const rejected = assert.rejects(pending, /no postRender within 5000 ms/);
await new Promise((resolve) => setImmediate(resolve));
t.mock.timers.tick(5000);
await rejected;
assert.equal(listener, null);
});
test('preset baseline clears a previous city selection before comparing fleet sizes', async (t) => {
const { readTransitFleetStyle } =
await import('../../scripts/qa-transit-browser.mjs');
const prior = globalThis.window;
t.after(() => {
globalThis.window = prior;
});
const entry = {
key: 'mbta:tram',
marker: {
show: true,
width: 33.75,
image: 'raster-selected',
color: { toCssColorString: () => 'rgb(255,194,74)' },
},
};
const state = {
_selectedKey: entry.key,
_vehicles: new Map([[entry.key, entry]]),
};
globalThis.window = {
__godsEyeView: {
dataManager: {
layers: new Map([
[
'transit',
{
module: {
_transitStateForTest: () => state,
_transitPartsForTest: () => ({
selection: {
clearSelection() {
state._selectedKey = null;
entry.marker.width = 22.5;
entry.marker.image = 'raster-fleet';
},
},
}),
},
},
],
]),
},
},
};
const baseline = readTransitFleetStyle({ resetSelection: true });
assert.equal(baseline.width, 22.5);
assert.equal(baseline.selected, false);
assert.deepEqual(readTransitFleetStyle({ key: entry.key }), baseline);
});
test('allocation accounting includes Cesium render aliases and excludes QA helper subtrees', async () => {
const { allocationTotals } =
await import('../../scripts/qa-transit-scenes.mjs');
const node = (functionName, url, selfSize, children = []) => ({
callFrame: { functionName, url },
selfSize,
children,
});
const profile = {
head: node('(root)', '', 0, [
node('render2', 'cesium.js', 0, [
node('Scene4.render', 'cesium.js', 10, [
node('_drawOverlay', '/src/data/detection.js', 100),
node('', '/src/layers/transit/testing.js', 200, [
node('hypot', '', 300),
node('onPreRender', '/src/layers/transit/rendering.js', 20, [
node('sampleAt', '/src/data/contactPlayback.js', 30),
node('style', '/src/data/transitPresetStyle.js', 10),
]),
]),
]),
]),
]),
};
const { topAllocators, ...totals } = allocationTotals(profile);
assert.equal(topAllocators[0].bytes, 100);
assert.deepEqual(totals, {
frameBytes: 170,
layerFrameBytes: 60,
sharedOverlayBytes: 100,
qaBytes: 500,
});
});
test('public transit documentation uses courtesy attribution and the approved ground sentence', () => {
const data = readFileSync(
new URL('../../DATA_SOURCES.md', import.meta.url),
'utf8',
);
const rows = data
.split('\n')
.filter((line) =>
/^\| \*\*(MBTA|CapMetro|Metro Transit|OVapi|Entur|TransLink|HSL)/.test(
line,
),
);
assert.equal(rows.length, 7);
for (const row of rows) {
assert.doesNotMatch(
row,
/licen[cs]e|unlicensed|unstated|grants|permits|public.domain|CC BY|NLOD|terms|trademarks/i,
);
assert.match(row, /open realtime vehicle data/);
assert.match(row, /published for developer use and not against their use/);
}
const readme = readFileSync(
new URL('../../README.md', import.meta.url),
'utf8',
);
assert.ok(
readme.includes(
'Twenty layers and map sources. **Eighteen have a keyless path.**',
),
);
assert.ok(
readme.includes(
'**Sits on the real ground.** Entity heights are aligned to work with Google 3D tiles, so aircraft park on aprons and cameras stand on street corners instead of floating.',
),
);
assert.doesNotMatch(
readme,
/geoid-aware|sampled against the _rendered_ terrain mesh/,
);
});