mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
704 lines
22 KiB
JavaScript
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/,
|
|
);
|
|
});
|