Files
gods-eye-view/src/data/detectionHost.test.mjs
T
German KhokhlovandClaude Opus 5.5 3157888780 i18n: fix the repeat-review findings
- Status pieces translate in one place (src/ui/statusText.js): a Local
  ADS-B error with a single piece now translates, and the analyst card
  splits empty-layer notes into pieces (Transit, Local ADS-B).
- Translate the ALPR retry chip text, the ECCC error on the Air quality
  card, "Upstream response too large" and the "entry {n}" feed label.
- The unmarked-text check also sees error/loadingLabel/statusMessage/
  status assignments, `status:` properties and showToast?.( /
  this._showToast( calls; the scene import preview marks its pack
  statuses, data and model-facing text carry i18n-ignore with a reason.
- Tests check the real code: the HUD locality formatter moves to
  src/hudLocalityFormat.js, the unsaved-keys test pins the teardown.
- One copy each: city names come from src/cityPois.js, the GNSS HTTP
  template and wind "field unavailable" texts; dead fallbacks removed.
- Leftovers of the reverted vessel-label change and the reformatting of
  detectionHost.test.mjs / keySetup.test.mjs are back to main.

Catalogs: es/ru 2617 messages each, complete.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 19:28:59 +04:00

963 lines
36 KiB
JavaScript

import { readShellSource } from '../testSupport/readShellSource.mjs';
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import * as Cesium from 'cesium';
import {
countFadingRenderEntries,
destroyDetection,
detectionDebugRequested,
getDetectionDiagnostics,
getDetectionTheme,
getMode,
initDetection,
isDetectionSuspended,
resumeDetection,
setDetectionStyle,
setDetectionTuning,
setMode,
suspendDetection,
} from './detection.js';
import {
destroyWorldOverlay,
initWorldOverlay,
setOverlayEntries,
} from '../overlays/worldOverlay.js';
import { DETECTION_THEME_MAP } from '../overlays/worldOverlayTokens.js';
import { composeLabel, resolveTier } from './detectionDraw.js';
test('detection diagnostics count rendered fading rows instead of absent selected identities', () => {
assert.equal(countFadingRenderEntries([
{ selected: true },
{ selected: false },
{ selected: true },
{ selected: false },
]), 2);
assert.equal(countFadingRenderEntries([{ selected: true }, { selected: true }]), 0);
});
class MockEvent {
constructor() { this.listeners = new Set(); }
addEventListener(listener) {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
raise() {
for (const listener of [...this.listeners]) listener();
}
}
class MockPath2D {
moveTo() {}
lineTo() {}
roundRect() {}
arcTo() {}
closePath() {}
}
function mockContext(target, trace) {
const calls = [];
const record = (...call) => {
calls.push(call);
trace.push([target, ...call]);
};
let filter = 'none';
let strokeStyle = '';
let globalCompositeOperation = 'source-over';
return {
calls,
font: '',
get filter() { return filter; },
set filter(value) { filter = value; record('filter', value); },
fillStyle: '',
get strokeStyle() { return strokeStyle; },
set strokeStyle(value) { strokeStyle = value; record('strokeStyle', value); },
globalAlpha: 1,
get globalCompositeOperation() { return globalCompositeOperation; },
set globalCompositeOperation(value) { globalCompositeOperation = value; record('globalCompositeOperation', value); },
lineWidth: 1,
measureText(text) { return { width: String(text).length * 6 }; },
setTransform(...args) { record('setTransform', ...args); },
clearRect(...args) { record('clearRect', ...args); },
save() { record('save'); },
restore() { record('restore'); },
translate(...args) { record('translate', ...args); },
scale(...args) { record('scale', ...args); },
beginPath() { record('beginPath'); },
rect(...args) { record('rect', ...args); },
clip(...args) { record('clip', ...args); },
moveTo(...args) { record('moveTo', ...args); },
lineTo(...args) { record('lineTo', ...args); },
arc(...args) { record('arc', ...args); },
arcTo(...args) { record('arcTo', ...args); },
closePath() { record('closePath'); },
// The style is recorded AT fill time, not on assignment, so a pin can prove
// which plate token actually reached the canvas rather than which one was
// set at some point during the frame. globalAlpha rides along for the same
// reason: the backdrop feather is an alpha, not a colour.
fill(path) { record('fill', path, this.fillStyle, this.globalAlpha); },
stroke(path) { record('stroke', path, this.strokeStyle, this.lineWidth); },
fillRect(...args) { record('fillRect', ...args); },
fillText(...args) { record('fillText', ...args); },
drawImage(...args) { record('drawImage', ...args); },
};
}
// `search` seeds window.location.search. The detection mode banner is developer
// telemetry gated behind ?detectDebug=1, so any test that uses the banner as its
// "did this frame repaint" probe must opt in explicitly.
function installEnvironment({ width = 800, height = 600, dpr = 2, search = '' } = {}) {
const byId = new Map();
const paintTrace = [];
const ctx = mockContext('shared', paintTrace);
const detectionCtx = mockContext('detection', paintTrace);
const originalPerformance = Object.getOwnPropertyDescriptor(globalThis, 'performance');
const originalDateNow = Date.now;
const originalPath2D = globalThis.Path2D;
let currentTime = 1_000;
let performanceStep = 0;
Object.defineProperty(globalThis, 'performance', {
configurable: true,
value: { now: () => {
const value = currentTime;
currentTime += performanceStep;
return value;
} },
});
Date.now = () => currentTime;
globalThis.Path2D = MockPath2D;
class MockElement {
constructor(tagName) {
this.tagName = tagName.toUpperCase();
this.id = '';
this.children = [];
this.parentElement = null;
this.style = {};
this.dataset = {};
this.hidden = false;
this.width = 0;
this.height = 0;
this.clientWidth = width;
this.clientHeight = height;
this._rect = { left: 0, top: 0, width, height };
}
appendChild(child) {
child.parentElement = this;
this.children.push(child);
register(child);
return child;
}
insertBefore(child, before) {
child.parentElement = this;
const index = this.children.indexOf(before);
if (index < 0) this.children.push(child);
else this.children.splice(index, 0, child);
register(child);
return child;
}
setAttribute(name, value) {
if (name === 'id') this.id = String(value);
else this[name] = String(value);
register(this);
}
getBoundingClientRect() { return this._rect; }
getContext() {
if (this.tagName !== 'CANVAS') return null;
return this.id === 'world-overlay-detection-surface' ? detectionCtx : ctx;
}
querySelector(selector) {
const wanted = selector.slice(1);
return this.children.find((child) => child.id === wanted) || null;
}
remove() {
if (this.parentElement) {
this.parentElement.children = this.parentElement.children.filter((child) => child !== this);
}
unregister(this);
this.parentElement = null;
}
get nextSibling() { return null; }
}
function register(element) {
if (element.id) byId.set(element.id, element);
for (const child of element.children) register(child);
}
function unregister(element) {
if (element.id) byId.delete(element.id);
for (const child of element.children) unregister(child);
}
const body = new MockElement('body');
body.classList = { contains() { return false; } };
const document = {
body,
createElement(tagName) { return new MockElement(tagName); },
getElementById(id) { return byId.get(id) || null; },
querySelector(selector) { return byId.get(selector.slice(1)) || null; },
querySelectorAll(selector) {
const element = selector.startsWith('#') ? byId.get(selector.slice(1)) : null;
return element ? [element] : [];
},
};
const window = {
devicePixelRatio: dpr,
location: { search },
addEventListener() {},
removeEventListener() {},
getComputedStyle() { return { display: 'block', visibility: 'visible', opacity: '1' }; },
};
globalThis.ResizeObserver = class { observe() {} disconnect() {} };
globalThis.MutationObserver = class { observe() {} disconnect() {} };
globalThis.document = document;
globalThis.window = window;
const container = new MockElement('div');
container.id = 'cesiumContainer';
body.appendChild(container);
const root = new MockElement('div');
root.id = 'world-overlay-root';
body.appendChild(root);
const canvas = new MockElement('canvas');
canvas.id = 'world-overlay-canvas';
root.appendChild(canvas);
const postRender = new MockEvent();
const viewer = {
container,
canvas: { clientWidth: width, clientHeight: height },
camera: {
positionWC: new Cesium.Cartesian3(0, 0, 10_000_000),
positionCartographic: { height: 1_000_000 },
viewMatrix: Cesium.Matrix4.clone(Cesium.Matrix4.IDENTITY),
frustum: { projectionMatrix: Cesium.Matrix4.clone(Cesium.Matrix4.IDENTITY) },
moveEnd: new MockEvent(),
},
scene: { postRender, requestRender() {} },
};
return {
viewer,
postRender,
document,
ctx,
detectionCtx,
paintTrace,
advance(ms) { currentTime += ms; },
setPerformanceStep(ms) { performanceStep = ms; },
cleanup() {
destroyDetection();
destroyWorldOverlay();
Date.now = originalDateNow;
if (originalPath2D === undefined) delete globalThis.Path2D;
else globalThis.Path2D = originalPath2D;
delete globalThis.document;
delete globalThis.window;
delete globalThis.ResizeObserver;
delete globalThis.MutationObserver;
if (originalPerformance) Object.defineProperty(globalThis, 'performance', originalPerformance);
else delete globalThis.performance;
},
};
}
function detectableLayer() {
const positions = [
new Cesium.Cartesian3(0, 0, 6_356_752),
new Cesium.Cartesian3(10_000, 0, 6_356_740),
];
return {
id: 'flights',
getDetectableObjects() {
return positions.map((position, index) => ({
position,
sourceId: `flight-${index}`,
id: `TEST${index}`,
metric: `FL${300 + index}`,
type: 'AIR',
}));
},
};
}
function militaryLayer() {
return {
id: 'military',
getDetectableObjects() {
return [{
position: new Cesium.Cartesian3(0, 0, 6_356_752),
sourceId: 'military-pin',
id: 'MIL-PIN',
metric: 'FL450',
type: 'AIR',
tier: 'military',
}];
},
};
}
// One air contact and one space contact, both on screen. The mock camera
// projects through an identity view-projection, so a position's x/y ARE its
// normalized device coordinates; these two land ~320px apart and both survive
// the declutter.
function mixedTierLayer() {
return {
id: 'mixed',
getDetectableObjects() {
return [
{
position: new Cesium.Cartesian3(-0.4, 0.2, 6_356_752),
sourceId: 'air-1',
id: 'AIRONE',
metric: 'FL350',
type: 'AIR',
},
{
position: new Cesium.Cartesian3(0.4, -0.2, 6_356_752),
sourceId: 'sat-1',
id: 'SATONE',
metric: '412KM',
type: 'SAT',
},
];
},
};
}
/** Fill styles that actually reached the shared normal-blend canvas. */
function sharedFillStyles(env) {
return env.ctx.calls
.filter(([name]) => name === 'fill')
.map(([, , fillStyle]) => fillStyle);
}
/** Settle a frame so the arbiter has promoted its identities to render entries. */
function settleFrame(env) {
env.advance(250);
env.postRender.raise();
}
test('detection lifecycle re-hosts unchanged painters behind the sole host listener', () => {
// Opts into the telemetry banner: this test reads it as the repaint probe.
const env = installEnvironment({ search: '?detectDebug=1' });
const modes = [];
try {
initWorldOverlay(env.viewer);
initDetection(env.viewer, [detectableLayer()], (mode) => modes.push(mode));
assert.equal(env.postRender.listeners.size, 1);
assert.equal(env.document.getElementById('detection-overlay'), null);
assert.ok(env.document.getElementById('world-overlay-canvas'));
const surface = env.document.getElementById('world-overlay-detection-surface');
const root = env.document.getElementById('world-overlay-root');
const mainCanvas = env.document.getElementById('world-overlay-canvas');
assert.ok(surface);
// The surface must sit in the Cesium container (no stacking context
// between it and the WebGL canvas) or its `screen` blend is discarded;
// ordering under the card canvas is carried by z-index, not siblinghood.
assert.equal(surface.parentElement, env.viewer.container);
assert.deepEqual(root.children, [mainCanvas]);
assert.equal(surface.style.mixBlendMode, 'screen');
assert.equal(
surface.style.filter,
'contrast(1.05) saturate(1.05) drop-shadow(0 0 3px rgba(0, 244, 255, 0.4))',
);
assert.equal(surface.style.display, 'none');
setMode('DENSE');
env.advance(250);
env.postRender.raise();
const first = getDetectionDiagnostics();
assert.equal(first.profile, 'DENSE');
assert.ok(Array.isArray(first.calloutRects));
if (first.calloutRects.length) {
const x=first.calloutRects[0].x;
first.calloutRects[0].x=NaN;
assert.equal(getDetectionDiagnostics().calloutRects[0].x,x,'QA owns a copy, never a live plate');
}
assert.equal(first.observationCount, 2);
assert.equal(first.didSolve, true);
assert.ok(first.solveRevision > 0);
assert.ok(first.collectiveLabelBudget > 0);
assert.ok(first.bracketOpacityCounts.full + first.bracketOpacityCounts.partial > 0);
assert.ok(env.detectionCtx.calls.some(([name, text]) => name === 'fillText'
&& String(text).startsWith('DENSE VIS:')));
assert.ok(env.detectionCtx.calls.some(([name, path]) => name === 'stroke' && path instanceof MockPath2D));
assert.ok(env.detectionCtx.calls.some(([name]) => name === 'fillRect'), 'scanlines and banner still paint');
assert.equal(env.detectionCtx.globalCompositeOperation, 'source-over');
assert.equal(env.detectionCtx.filter, 'none');
assert.equal(surface.style.display, 'block');
const canvas = env.document.getElementById('world-overlay-canvas');
assert.equal(Number(canvas.dataset.solveRevision), first.solveRevision);
assert.equal(canvas.dataset.profile, 'DENSE');
// The existing 125 ms arbiter cadence remains authoritative.
env.advance(124);
env.postRender.raise();
assert.equal(getDetectionDiagnostics().solveRevision, first.solveRevision);
// Callsigns paint on the SHARED normal-blend canvas, never on the
// screen-blended sensor surface: `screen` can only lighten, so a callout's
// dark backing plate is a no-op over sunlit ground and the text dissolves
// into it. Brackets, banner and scanlines stay on the sensor surface.
assert.ok(env.ctx.calls.some(([name, text]) => name === 'fillText' && text === 'TEST0'));
assert.ok(!env.detectionCtx.calls.some(([name, text]) => name === 'fillText' && text === 'TEST0'));
env.advance(1);
env.postRender.raise();
assert.ok(getDetectionDiagnostics().solveRevision > first.solveRevision);
setDetectionTuning({ densityPct: 25, allocationStrategy: 'WEIGHTED' });
env.postRender.raise();
assert.equal(getMode(), 'SPARSE');
assert.equal(getDetectionDiagnostics().profile, 'SPARSE');
assert.equal(getDetectionDiagnostics().allocationStrategy, 'WEIGHTED');
assert.ok(env.detectionCtx.calls.some(([name]) => name === 'arc'), 'sparse focus ring remains available');
setMode('BALANCED');
env.postRender.raise();
assert.equal(getDetectionDiagnostics().profile, 'BALANCED');
setMode('DENSE');
env.postRender.raise();
assert.equal(getDetectionDiagnostics().profile, 'DENSE');
const bannerCount = () => env.detectionCtx.calls.filter(([name, text]) => (
name === 'fillText' && /^(SPARSE|BALANCED|DENSE) VIS:/.test(String(text))
)).length;
const beforeSuspend = bannerCount();
suspendDetection('intercity');
assert.equal(isDetectionSuspended(), true);
assert.equal(surface.style.opacity, '0');
env.postRender.raise();
assert.equal(bannerCount(), beforeSuspend);
assert.equal(env.postRender.listeners.size, 1);
resumeDetection();
env.postRender.raise();
assert.equal(isDetectionSuspended(), false);
assert.equal(surface.style.opacity, '1');
assert.ok(bannerCount() > beforeSuspend);
// These are the exact engine calls made by military style presets after
// their production UI gate chooses CRT/NVG/FLIR defaults.
const expectedThemes = {
retro: 'contrast(1.08) saturate(1.04) drop-shadow(0 0 3px rgba(255, 176, 56, 0.45))',
surveillance: 'contrast(1.12) saturate(1.12) drop-shadow(0 0 3px rgba(120, 255, 120, 0.42))',
thermal: 'contrast(1.1) saturate(1.08) drop-shadow(0 0 3px rgba(255, 224, 170, 0.42))',
};
for (const style of ['retro', 'surveillance', 'thermal']) {
setMode('OFF');
setDetectionStyle(style);
assert.equal(surface.style.mixBlendMode, 'screen');
assert.equal(surface.style.filter, expectedThemes[style]);
setDetectionTuning({ densityPct: 75 });
setMode('DENSE');
env.postRender.raise();
assert.equal(getDetectionDiagnostics().profile, 'DENSE');
assert.ok(getDetectionTheme().line);
assert.equal(env.postRender.listeners.size, 1);
}
setMode('OFF');
setDetectionStyle('normal');
assert.equal(surface.style.mixBlendMode, 'screen');
assert.equal(
surface.style.filter,
'contrast(1.05) saturate(1.05) drop-shadow(0 0 3px rgba(0, 244, 255, 0.4))',
);
env.postRender.raise();
assert.equal(getMode(), 'OFF');
assert.equal(surface.style.display, 'none');
assert.equal(env.postRender.listeners.size, 1);
assert.ok(modes.includes('SPARSE') && modes.includes('BALANCED') && modes.includes('DENSE'));
destroyDetection();
assert.equal(env.postRender.listeners.size, 1, 'detection never owns the listener');
setMode('DENSE');
env.postRender.raise();
assert.equal(env.document.getElementById('detection-overlay'), null);
destroyWorldOverlay();
assert.equal(env.postRender.listeners.size, 0);
assert.equal(env.document.getElementById('world-overlay-canvas'), null);
assert.equal(env.document.getElementById('world-overlay-detection-surface'), null);
} finally {
env.cleanup();
}
});
test('callouts stop painting the moment the last detectable object goes away', () => {
// The callout lane replays the previous solve on frames the sensor lane
// skips under load, which is what keeps plates from strobing against
// persistent brackets. The cost of that design is that an empty field MUST
// clear the replay buffer, or the final callsigns stay stranded on the
// shared canvas after the last data layer is switched off.
const env = installEnvironment();
let objects = [{
position: new Cesium.Cartesian3(0, 0, 6_356_752),
sourceId: 'lonely-1',
id: 'LASTONE',
metric: 'FL120',
type: 'AIR',
}];
try {
initWorldOverlay(env.viewer);
initDetection(env.viewer, [{ id: 'flights', getDetectableObjects: () => objects }], () => {});
setMode('DENSE');
env.advance(250);
env.postRender.raise();
// The first frame solves; the arbiter hands the identity back as a render
// entry on the frame after that.
env.advance(250);
env.postRender.raise();
assert.ok(
env.ctx.calls.some(([name, text]) => name === 'fillText' && text === 'LASTONE'),
'the callout paints on the shared canvas while the contact exists',
);
objects = [];
env.ctx.calls.length = 0;
env.advance(250);
env.postRender.raise();
env.advance(250);
env.postRender.raise();
assert.ok(
!env.ctx.calls.some(([name, text]) => name === 'fillText' && text === 'LASTONE'),
'an empty field must drop the replay buffer, not strand the last callsign',
);
} finally {
env.cleanup();
}
});
test('every style paints its OWN plate token through the production callout path', () => {
// The token table and the direct painter are both pinned elsewhere, but a
// pin on either one still passes if the runtime stops consulting the active
// theme and freezes on a single plate. This drives the real stash → replay
// path once per style and reads the fill style the shared canvas received.
const env = installEnvironment();
try {
initWorldOverlay(env.viewer);
initDetection(env.viewer, [mixedTierLayer()], () => {});
setMode('DENSE');
settleFrame(env);
settleFrame(env);
for (const style of ['surveillance', 'retro', 'thermal', 'normal']) {
setDetectionStyle(style);
settleFrame(env);
env.ctx.calls.length = 0;
settleFrame(env);
const theme = DETECTION_THEME_MAP[style] || DETECTION_THEME_MAP._default;
const fills = sharedFillStyles(env);
assert.ok(
fills.includes(theme.calloutPlate),
`${style} must paint its own callout plate (${theme.calloutPlate})`,
);
for (const [otherName, other] of Object.entries(DETECTION_THEME_MAP)) {
if (other.calloutPlate === theme.calloutPlate) continue;
assert.ok(
!fills.includes(other.calloutPlate),
`${style} must not paint the ${otherName} plate`,
);
}
}
} finally {
env.cleanup();
}
});
test('space-tier callouts take the heavier plate while air contacts keep the light one', () => {
// Satellites sit over the high-albedo lit Earth disc far more often than
// aircraft do, so the space tier carries the heavier backing. Losing the
// tier split is invisible to the token table — only a frame carrying BOTH
// tiers at once can show it.
const env = installEnvironment();
try {
initWorldOverlay(env.viewer);
initDetection(env.viewer, [mixedTierLayer()], () => {});
setDetectionStyle('surveillance');
setMode('DENSE');
settleFrame(env);
env.ctx.calls.length = 0;
settleFrame(env);
const theme = DETECTION_THEME_MAP.surveillance;
assert.notEqual(
theme.calloutPlate,
theme.calloutPlateSpace,
'the fixture is only meaningful while the two tiers differ',
);
const painted = (text) => env.ctx.calls.some(([name, value]) => name === 'fillText' && value === text);
assert.ok(painted('AIRONE') && painted('SATONE'), 'both tiers reached the frame under test');
const fills = sharedFillStyles(env);
assert.ok(fills.includes(theme.calloutPlate), 'the air contact keeps the light plate');
assert.ok(fills.includes(theme.calloutPlateSpace), 'the space contact takes the heavier plate');
} finally {
env.cleanup();
}
});
test('surveillance military tier reaches the dedicated render target as shipped red', () => {
const env = installEnvironment();
try {
initWorldOverlay(env.viewer);
initDetection(env.viewer, [militaryLayer()], () => {});
setDetectionStyle('surveillance');
setMode('DENSE');
env.advance(250);
env.postRender.raise();
assert.ok(
env.detectionCtx.calls.some(([name, value]) => name === 'strokeStyle' && value === '#ff5a47'),
'the production projection/tier/batched-bracket paint path emits surveillance military red',
);
assert.ok(env.detectionCtx.calls.some(([name, path]) => name === 'stroke'
&& path instanceof MockPath2D));
} finally {
env.cleanup();
}
});
test('pathological detection paint holds alternate frames without freezing shared lanes', () => {
// Opts into the telemetry banner: the held-frame assertions count banner paints.
const env = installEnvironment({ search: '?detectDebug=1' });
try {
initWorldOverlay(env.viewer);
initDetection(env.viewer, [detectableLayer()], () => {});
setOverlayEntries('valve-card', [{
id: 'ambient-card',
position: new Cesium.Cartesian3(0, 0, 0),
variant: 'label',
title: 'AMBIENT-CONTINUES',
selected: true,
protected: true,
horizonCull: false,
edgeFade: 'none',
}]);
setMode('DENSE');
env.advance(250);
env.setPerformanceStep(30);
env.postRender.raise();
env.postRender.raise();
const detectionClears = env.detectionCtx.calls.filter(([name]) => name === 'clearRect').length;
const detectionBanners = env.detectionCtx.calls.filter(([name, text]) => name === 'fillText'
&& String(text).startsWith('DENSE VIS:')).length;
const sharedClears = env.ctx.calls.filter(([name]) => name === 'clearRect').length;
const sharedCards = env.ctx.calls.filter(([name, text]) => name === 'fillText'
&& text === 'AMBIENT-CONTINUES').length;
env.postRender.raise();
assert.equal(env.detectionCtx.calls.filter(([name]) => name === 'clearRect').length, detectionClears,
'the host preserves detection pixels on the held odd frame');
assert.equal(env.detectionCtx.calls.filter(([name, text]) => name === 'fillText'
&& String(text).startsWith('DENSE VIS:')).length, detectionBanners);
assert.equal(env.ctx.calls.filter(([name]) => name === 'clearRect').length, sharedClears + 1,
'the shared surface still clears');
assert.equal(env.ctx.calls.filter(([name, text]) => name === 'fillText'
&& text === 'AMBIENT-CONTINUES').length, sharedCards + 1,
'the shared lane still repaints');
assert.equal(getDetectionDiagnostics().throttleSkipCount, 1);
env.postRender.raise();
assert.equal(env.detectionCtx.calls.filter(([name]) => name === 'clearRect').length,
detectionClears + 1, 'the next even frame repaints detection');
assert.equal(getDetectionDiagnostics().throttleSkipCount, 1);
} finally {
env.cleanup();
}
});
test('detection cannot resurrect a private canvas, listener, matrix, resize, clear, or UI inventory', () => {
const source = readFileSync(new URL('./detection.js', import.meta.url), 'utf8');
const uiSource = readShellSource()
+ '\n' + readFileSync(new URL('../ui/visualPresets.js', import.meta.url), 'utf8');
assert.doesNotMatch(source, /createElement\(\s*['"]canvas['"]\s*\)/);
assert.doesNotMatch(source, /postRender\.addEventListener/);
assert.doesNotMatch(source, /['"]detection-overlay['"]/);
assert.doesNotMatch(source, /devicePixelRatio/);
assert.doesNotMatch(source, /clearRect\(/);
assert.doesNotMatch(source, /Matrix4\.multiply/);
assert.doesNotMatch(source, /CALLOUT_OCCLUSION_SELECTORS/);
assert.doesNotMatch(source, /_ctx\.(?:filter|globalCompositeOperation)\s*=/);
assert.match(source, /_hostSurface\.style\.mixBlendMode = _theme\.blend;/);
assert.match(source, /_hostSurface\.style\.filter = `\$\{_theme\.filter\} drop-shadow\(0 0 \$\{GLOW_PX\}px \$\{_theme\.glow\}\)`;/);
assert.match(source, /const LABEL_SOLVE_INTERVAL_MS = 125;/);
assert.match(source, /const _labelArbiter = new LabelArbiter\(\);/);
assert.match(source, /const clipW = vp3 \* px \+ vp7 \* py \+ vp11 \* pz \+ vp15;/);
assert.match(source, /registerWorldOverlayPaintLane\('detection', _paintDetectionLane/);
assert.match(source, /target: 'detection'/);
assert.match(source, /shouldPaint: _shouldPaintDetectionLane/);
// The three military styles used to carry three copies of these numbers.
// They now share ONE frozen object, which Cockpit's force-on reuses too, so
// the pin moved from "three identical literals" to "one preset, referenced
// three times" — same guarantee, and the copies can no longer drift.
assert.match(
uiSource,
/const MILITARY_DETECTION_PRESET = Object\.freeze\(\{\s*mode: 'dense',\s*densityPct: 75,?\s*\}\);/,
'the tactical detection default is still Dense @ 75%',
);
assert.equal(
uiSource.match(/detection: MILITARY_DETECTION_PRESET,/g)?.length,
3,
'CRT, NVG, and FLIR retain their Dense auto-enable defaults',
);
assert.match(uiSource, /preset\.detection && !this\._detectionUserOverridden/);
});
// ── Developer telemetry gate (2026-08-20 QA hunt) ───────────────────────────
// The orange mode banner ("DENSE VIS:15 SRC:1036 DENS:100% ELASTIC 0.4ms")
// painted for every user: CRT/NVG/FLIR auto-enable detection, so engine
// telemetry was the first thing a visitor saw, overlapping the cockpit callsign
// block. It is kept as a debug affordance, but must default OFF.
test('detectionDebugRequested parses the query-string gate and nothing else', () => {
assert.equal(detectionDebugRequested('?detectDebug=1'), true);
assert.equal(detectionDebugRequested('?foo=bar&detectDebug=1'), true);
assert.equal(detectionDebugRequested(''), false);
assert.equal(detectionDebugRequested('?detectDebug=0'), false);
assert.equal(detectionDebugRequested('?detectDebug'), false);
assert.equal(detectionDebugRequested('?detectdebug=1'), false, 'the flag is case-sensitive');
assert.equal(detectionDebugRequested(undefined), false);
assert.equal(detectionDebugRequested(null), false);
});
test('the mode banner is absent by default and present behind the flag', () => {
const bannerPaints = (env) => env.detectionCtx.calls.filter(([name, text]) => (
name === 'fillText' && /^(SPARSE|BALANCED|DENSE) VIS:/.test(String(text))
)).length;
const painted = (search) => {
const env = installEnvironment({ search });
try {
initWorldOverlay(env.viewer);
initDetection(env.viewer, [detectableLayer()], () => {});
setMode('DENSE');
env.advance(250);
env.postRender.raise();
// The overlay is alive either way — brackets still stroke.
assert.ok(
env.detectionCtx.calls.some(([name, path]) => name === 'stroke' && path instanceof MockPath2D),
'detection still paints its contacts regardless of the debug gate',
);
return bannerPaints(env);
} finally {
destroyDetection();
destroyWorldOverlay();
env.cleanup();
}
};
assert.equal(painted(''), 0, 'users must never see the engine telemetry banner');
assert.ok(painted('?detectDebug=1') > 0, 'the flag brings the telemetry back');
});
test('detection telemetry stays reachable programmatically with the banner hidden', () => {
// Hiding the banner must not remove the numbers — getDetectionDiagnostics()
// is the supported way to read them, and is what the QA harnesses use.
const env = installEnvironment();
try {
initWorldOverlay(env.viewer);
initDetection(env.viewer, [detectableLayer()], () => {});
setMode('DENSE');
env.advance(250);
env.postRender.raise();
const diagnostics = getDetectionDiagnostics();
assert.equal(diagnostics.profile, 'DENSE');
assert.equal(typeof diagnostics.observationCount, 'number');
assert.equal(typeof diagnostics.visibleCount, 'number');
assert.equal(typeof diagnostics.densityPct, 'number');
} finally {
destroyDetection();
destroyWorldOverlay();
env.cleanup();
}
});
test('civilian and military AIR brackets cover front, left, and right at Sparse density', () => {
for (const layerId of ['flights', 'military']) {
const env = installEnvironment({ width: 900, height: 600, dpr: 1 });
try {
const objects = [-0.82, 0, 0.82].map((x, index) => ({
position: new Cesium.Cartesian3(x, 0, 6_356_752),
sourceId: `${layerId}-${index}`,
id: `${layerId.toUpperCase()}-${index}`,
metric: 'FL120',
type: 'AIR',
}));
initWorldOverlay(env.viewer);
initDetection(env.viewer, [{ id: layerId, getDetectableObjects: () => objects }], () => {});
setDetectionTuning({ densityPct: 25 });
setMode('SPARSE');
settleFrame(env);
const diagnostics = getDetectionDiagnostics();
assert.deepEqual(
diagnostics.aircraftBracketSectors,
{ left: 1, front: 1, right: 1 },
`${layerId} side brackets must not be starved by the central keyhole`,
);
assert.equal(diagnostics.visibleCount, 3);
assert.equal(diagnostics.densityPct, 25);
assert.equal(diagnostics.profile, 'SPARSE');
assert.ok(diagnostics.selectedCount <= diagnostics.collectiveLabelBudget,
'Sparse keeps its documented budget instead of silently becoming Dense');
} finally {
destroyDetection();
destroyWorldOverlay();
env.cleanup();
}
}
});
/**
* Two air contacts at the same screen radius, one with the planet behind it and
* one with sky. The mock camera sits at (0, 0, 10 000 km) and projects through
* an identity view-projection, so a position's x/y ARE its NDC and z is free:
* the pole-surface contact looks straight down the axis into the planet, while
* its mirror sits 10 000 km FURTHER out, so the view ray through it escapes.
* Equal screen radius keeps the keyhole's radial fade identical for both, which
* makes the two painted plate alphas directly comparable.
*/
function backdropLayer() {
return {
id: 'flights',
getDetectableObjects() {
return [
{
position: new Cesium.Cartesian3(-0.4, 0.2, 6_356_752),
sourceId: 'ground-1', id: 'GROUNDED', metric: 'FL100', type: 'AIR',
},
{
position: new Cesium.Cartesian3(0.4, -0.2, 20_000_000),
sourceId: 'sky-1', id: 'SKYBACK', metric: 'FL400', type: 'AIR',
},
];
},
};
}
test('the backdrop feather reaches the canvas as a lighter plate against sky', () => {
// The discriminator and the painter are pinned in their own modules, but both
// pins still pass if detection stops carrying the factor between them. This
// drives the real collect → solve → stash → replay path and reads the alpha
// the shared canvas actually received for each plate.
const env = installEnvironment();
try {
initWorldOverlay(env.viewer);
initDetection(env.viewer, [backdropLayer()], () => {});
setDetectionStyle('normal');
setMode('DENSE');
settleFrame(env);
env.ctx.calls.length = 0;
settleFrame(env);
const painted = (text) => env.ctx.calls.some(([name, value]) => name === 'fillText' && value === text);
assert.ok(painted('GROUNDED') && painted('SKYBACK'), 'both backdrops reached the frame under test');
const plate = DETECTION_THEME_MAP._default.calloutPlate;
const plateAlphas = env.ctx.calls
.filter(([name, , fillStyle]) => name === 'fill' && fillStyle === plate)
.map(([, , , globalAlpha]) => globalAlpha);
assert.equal(plateAlphas.length, 2, 'exactly one plate per contact');
const heaviest = Math.max(...plateAlphas);
const lightest = Math.min(...plateAlphas);
assert.ok(heaviest > 0, 'the grounded contact still gets a plate');
assert.ok(lightest > 0, 'the sky contact keeps a whisper rather than vanishing');
// Reverting the feature makes both plates equal, which fails here first.
assert.ok(
lightest < heaviest * 0.5,
`sky plate ${lightest} must be markedly lighter than ground plate ${heaviest}`,
);
} finally {
env.cleanup();
}
});
test('a transit vehicle is a contact: box, route, mode and operator on the shared canvas', () => {
// The owner's finding was that buses were "green dots moving around
// clumsily" — the layer rendered them and the detection overlay ignored
// them, because transit published no detectable objects at all. This drives
// the real host with the shape the layer now returns.
const env = installEnvironment();
const objects = [
{
position: new Cesium.Cartesian3(0, 0, 6_356_752),
sourceId: 'mbta:y1276',
tier: 'transit_bus',
id: '111',
type: 'VEH',
klass: 'MBTA',
metric: 'BUS 34 KM/H',
},
{
position: new Cesium.Cartesian3(0, 900, 6_356_752),
sourceId: 'mbta:R-548BBB14',
id: 'RED',
type: 'VEH',
klass: 'MBTA',
metric: 'METRO STOPPED',
skipLabel: true,
},
];
try {
initWorldOverlay(env.viewer);
initDetection(
env.viewer,
[{ id: 'transit', getDetectableObjects: () => objects }],
() => {},
);
setMode('DENSE');
env.advance(250);
env.postRender.raise();
env.advance(250);
env.postRender.raise();
const painted = env.ctx.calls
.filter(([name]) => name === 'fillText')
.map(([, text]) => text);
assert.ok(
painted.includes('111'),
`the route is the bright line; painted ${JSON.stringify(painted)}`,
);
// The host paints a single-line track label — bright id, one dim micro
// field — so the mode and what the vehicle is doing share that field.
assert.ok(
painted.some((text) => String(text).includes('BUS')),
`the mode rides along on the micro line; painted ${JSON.stringify(painted)}`,
);
assert.ok(
painted.some((text) => String(text).includes('34 KM/H')),
'and so does what the vehicle is doing',
);
assert.ok(env.ctx.calls.some(([name, , color, width]) => name === 'stroke' && color === '#5EF08A' && width === 1.25));
assert.ok(env.ctx.calls.some(([name, , color, width]) => name === 'stroke' && color === '#05080C' && width === 3.25));
assert.ok(!env.detectionCtx.calls.some(([name, , color]) => name === 'stroke' && color === '#5EF08A'));
// The operator travels on the object for the two-tier card form, which is
// where `klass` is read; the host's track label does not paint it.
assert.equal(composeLabel(objects[0]).secondary, 'MBTA · BUS 34 KM/H');
// The selected vehicle carries its own fuller card, so detection must not
// label it a second time.
assert.equal(
painted.includes('RED'),
false,
'skipLabel keeps the selected vehicle from being labelled twice',
);
} finally {
env.cleanup();
}
});
test('transit contacts take the vehicle tier, not the civil-air one', () => {
assert.equal(resolveTier({ type: 'VEH' }), 'vehicle');
assert.equal(resolveTier({ type: 'AIR' }), 'civil');
assert.equal(resolveTier({ type: 'SEA' }), 'sea');
});