mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
aircraft_in_area stats summarized altitude over airborne aircraft but speed over every match, so parked aircraft pulled the mean speed toward zero. Both figures now use the same airborne subset, and the on-ground fixture is stationary so the test can tell the difference. Reserved AIS ship-type codes 100 to 255 now read as unknown, as type 0 does, instead of showing up in the breakdown as their own type. Adds tests for an area where every aircraft is on the ground and for find_vessel type and destination normalization, and notes in the changelog fragment that every vessel answer is now normalized. Co-Authored-By: Claude Opus 5.5 <[email protected]>
99 lines
4.3 KiB
JavaScript
99 lines
4.3 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFile } from 'node:fs/promises';
|
|
import {
|
|
accentForVesselType,
|
|
applyVesselOverlayPolicy,
|
|
normalizeVesselType,
|
|
vesselOverlayCohortLimit,
|
|
vesselTypeCss,
|
|
} from './vesselLabels.js';
|
|
|
|
test('normalizeVesselType maps numeric AIS codes to type families', () => {
|
|
assert.equal(normalizeVesselType('30'), 'FISHING');
|
|
assert.equal(normalizeVesselType('31'), 'TOWING');
|
|
assert.equal(normalizeVesselType('35'), 'MILITARY');
|
|
assert.equal(normalizeVesselType('36'), 'SAILING');
|
|
assert.equal(normalizeVesselType('37'), 'PLEASURE');
|
|
assert.equal(normalizeVesselType('40'), 'HIGH-SPEED');
|
|
assert.equal(normalizeVesselType('50'), 'PILOT');
|
|
assert.equal(normalizeVesselType('51'), 'SAR');
|
|
assert.equal(normalizeVesselType('52'), 'TUG');
|
|
assert.equal(normalizeVesselType('60'), 'PASSENGER');
|
|
assert.equal(normalizeVesselType('71'), 'CARGO');
|
|
assert.equal(normalizeVesselType('84'), 'TANKER');
|
|
assert.equal(normalizeVesselType('90'), 'OTHER');
|
|
});
|
|
|
|
test('normalizeVesselType preserves text and degrades unknown codes', () => {
|
|
assert.equal(normalizeVesselType('Crude Oil Tanker'), 'Crude Oil Tanker');
|
|
assert.equal(normalizeVesselType('0'), '');
|
|
assert.equal(normalizeVesselType('25'), 'OTHER');
|
|
assert.equal(normalizeVesselType('100'), '');
|
|
assert.equal(normalizeVesselType('255'), '');
|
|
assert.equal(normalizeVesselType(undefined), '');
|
|
});
|
|
|
|
test('vessel type CSS and card accents stay paired', () => {
|
|
assert.equal(vesselTypeCss('Crude Oil Tanker'), '#ffb347');
|
|
assert.equal(vesselTypeCss('Container Ship'), '#39d5ff');
|
|
assert.equal(vesselTypeCss('Passenger/Ferry'), '#ff7adf');
|
|
assert.equal(vesselTypeCss('Fishing'), '#7cff9b');
|
|
assert.equal(vesselTypeCss('Tug'), '#f7f0a3');
|
|
assert.equal(accentForVesselType('Tanker'), '255, 179, 71');
|
|
assert.equal(accentForVesselType('Cargo'), '57, 213, 255');
|
|
assert.equal(accentForVesselType('Passenger'), '255, 122, 223');
|
|
assert.equal(accentForVesselType('Fishing'), '124, 255, 155');
|
|
assert.equal(accentForVesselType('Pilot Vessel'), '247, 240, 163');
|
|
assert.equal(accentForVesselType('Dredger'), '57, 213, 255');
|
|
assert.equal(accentForVesselType('84'), '255, 179, 71');
|
|
assert.equal(vesselTypeCss('62'), '#ff7adf');
|
|
});
|
|
|
|
test('vessel viewport cohort preserves the shipped 118px grid density', () => {
|
|
assert.equal(vesselOverlayCohortLimit(1600, 900), 112);
|
|
assert.equal(vesselOverlayCohortLimit(1920, 1080), 170);
|
|
assert.equal(vesselOverlayCohortLimit(1920, 1080, 80), 80);
|
|
assert.equal(vesselOverlayCohortLimit(10000, 10000), 900, 'the shipped row ceiling remains absolute');
|
|
assert.equal(vesselOverlayCohortLimit(0, 1080), 0);
|
|
assert.equal(vesselOverlayCohortLimit(1920, 1080, 0), 0);
|
|
});
|
|
|
|
test('vessel host policy uses always-on shared fade and protected selected lane', () => {
|
|
const position = { x: 1, y: 2, z: 3 };
|
|
const ambient = applyVesselOverlayPolicy({
|
|
id: 'vessel:1', position, title: 'AMBIENT', gapPx: 10, selected: false,
|
|
});
|
|
assert.equal(ambient.variant, 'card');
|
|
assert.equal(ambient.protected, false);
|
|
assert.equal(ambient.collisionGroup, 'ambient-card');
|
|
assert.equal(ambient.edgeFade, 'keyhole');
|
|
assert.equal(ambient.maxDistance, 5_000_000);
|
|
assert.equal(ambient.distanceFadeStartRatio, 0.7);
|
|
assert.equal(ambient.cardStyle, 'tactical');
|
|
assert.equal(ambient.verticalOnly, true);
|
|
|
|
const selected = applyVesselOverlayPolicy({
|
|
id: 'vessel:2', position, title: 'SELECTED', gapPx: 12, selected: true,
|
|
});
|
|
assert.equal(selected.variant, 'selected');
|
|
assert.equal(selected.verticalOnly, false, 'selected detail can clear panels on either side');
|
|
assert.equal(selected.protected, true);
|
|
assert.equal(selected.collisionGroup, 'ambient-card');
|
|
assert.equal(selected.maxDistance, Number.POSITIVE_INFINITY);
|
|
});
|
|
|
|
test('vesselLabels cannot resurrect a dedicated renderer', async () => {
|
|
const source = await readFile(new URL('./vesselLabels.js', import.meta.url), 'utf8');
|
|
for (const forbidden of [
|
|
'document.createElement',
|
|
"createElement('canvas')",
|
|
'postRender.addEventListener',
|
|
'worldToWindowCoordinates',
|
|
'requestAnimationFrame',
|
|
"id = 'vessel-labels'",
|
|
]) {
|
|
assert.equal(source.includes(forbidden), false, `dedicated renderer token returned: ${forbidden}`);
|
|
}
|
|
});
|