Files
gods-eye-view/src/ui/cockpitController.test.mjs
T

100 lines
3.8 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { CockpitViewController } from './cockpitController.js';
function environment() {
const original = { document: globalThis.document, window: globalThis.window };
const document = Object.assign(new EventTarget(), {
getElementById: () => null,
querySelectorAll: () => [],
body: { classList: { remove() {} } },
});
const window = Object.assign(new EventTarget(), { clearTimeout, setTimeout });
globalThis.document = document;
globalThis.window = window;
const callbacks = new Set();
const event = { addEventListener(fn) { callbacks.add(fn); return () => callbacks.delete(fn); } };
const viewer = {
scene: { preUpdate: event, screenSpaceCameraController: { enableInputs: false } },
trackedEntityChanged: event,
entities: { contains: () => false },
};
return { viewer, callbacks, restore() { Object.assign(globalThis, original); } };
}
test('Cockpit subscriptions are released once and retained actions cannot restart it', () => {
const env = environment();
try {
const owner = new CockpitViewController(env.viewer, { services: {} });
assert.equal(env.callbacks.size, 2);
owner.dispose();
owner.dispose();
assert.equal(env.callbacks.size, 0);
assert.equal(owner.enter(), false);
assert.equal(owner.navigateContext(1), false);
assert.equal(owner.toggleTrackedTr3b(), false);
assert.equal(owner.update(), false);
assert.equal(owner.active, false);
} finally { env.restore(); }
});
test('disposing an active Cockpit releases the supplied render owner without retracking', () => {
const env = environment();
try {
const released = [];
const owner = new CockpitViewController(env.viewer, {
services: { releaseContinuousRender: reason => released.push(reason) },
});
owner.active = true;
owner.dispose();
owner.dispose();
assert.deepEqual(released, ['cockpit']);
assert.equal(env.viewer.scene.screenSpaceCameraController.enableInputs, true);
assert.equal(env.viewer.trackedEntity, undefined);
assert.equal(owner.active, false);
} finally { env.restore(); }
});
test('aircraft identity is read from the supplied layer instances', () => {
const env = environment();
try {
const civilian = { icao24: 'abc123', layerId: 'flights', callsign: 'CIVIL' };
const military = { icao24: 'def456', layerId: 'military', callsign: 'MILITARY' };
const owner = new CockpitViewController(env.viewer, { services: {
flightsLayer: { getTrackedInfo: () => civilian },
militaryFlightsLayer: { getTrackedInfo: () => military },
} });
env.viewer.trackedEntity = { gevTrackedId: 'military:def456' };
assert.deepEqual(owner.readAircraftInfo(), military);
env.viewer.trackedEntity = { gevTrackedId: 'flights:abc123' };
assert.deepEqual(owner.readAircraftInfo(), civilian);
owner.dispose();
} finally { env.restore(); }
});
test('an obsolete regional briefing cannot publish after Cockpit disposal', async () => {
const env = environment();
try {
let finish;
let signal;
const owner = new CockpitViewController(env.viewer, { services: {
regionalDistanceM: () => Infinity,
fetchRegionalBrief: (_lat, _lon, options) => {
signal = options.signal;
return new Promise(resolve => { finish = resolve; });
},
releaseContinuousRender() {},
} });
const published = [];
owner.renderRegionalBrief = payload => published.push(payload);
owner.active = true;
owner.maybeRefreshRegionalBrief({ layerId: 'flights', icao24: 'abc123', latitude: 30, longitude: -97 });
owner.dispose();
assert.equal(signal.aborted, true);
finish({ articles: [] });
await new Promise(resolve => setImmediate(resolve));
assert.deepEqual(published, []);
assert.equal(owner.regionalBriefAbort, null);
} finally { env.restore(); }
});