feat: v0.11.0 — map interactions per PLAN.md: expanded airport popup (approx local time + deterministic generated weather), animated plane markers from /api/state polls with status popups, active contract route lines (pending dashed vs in-flight solid); pure libs lib/weather.ts, lib/localTime.ts, lib/mapData.ts

This commit is contained in:
2026-09-18 07:40:39 +00:00
parent 4c06ed26ff
commit 0208673da7
11 changed files with 874 additions and 14 deletions
+41
View File
@@ -0,0 +1,41 @@
import { describe, expect, it } from 'vitest';
import { approxLocalTime, approxZoneForLon } from './localTime';
describe('approxZoneForLon', () => {
it('maps longitude to the Etc/GMT±N zone with the POSIX sign flip', () => {
// UTC+1 (lon +15) is "Etc/GMT-1"; UTC−2 (lon −30) is "Etc/GMT2".
expect(approxZoneForLon(15)).toBe('Etc/GMT-1');
expect(approxZoneForLon(-30)).toBe('Etc/GMT2');
expect(approxZoneForLon(0)).toBe('Etc/GMT');
});
it('rounds to the nearest 15° band and clamps at ±12', () => {
expect(approxZoneForLon(10)).toBe('Etc/GMT-1'); // Frankfurt ≈ UTC+1
expect(approxZoneForLon(-6.2)).toBe('Etc/GMT'); // Lisbon: round(-0.41) = 0 → plain Etc/GMT (UTC)
expect(approxZoneForLon(179)).toBe('Etc/GMT-12');
expect(approxZoneForLon(-179)).toBe('Etc/GMT12');
});
});
describe('approxLocalTime', () => {
const noonUtc = Date.UTC(2026, 8, 17, 12, 0); // 2026-09-17 12:00 UTC
it('formats HH:MM in the approximate zone (deterministic)', () => {
expect(approxLocalTime(15, noonUtc)).toBe('13:00'); // east of Greenwich → UTC+1
expect(approxLocalTime(-30, noonUtc)).toBe('10:00'); // west of Greenwich → UTC−2
expect(approxLocalTime(0, noonUtc)).toBe('12:00'); // UTC
});
it('wraps across midnight', () => {
const lateUtc = Date.UTC(2026, 8, 17, 23, 30);
expect(approxLocalTime(45, lateUtc)).toBe('02:30'); // UTC+3 → next day
const earlyUtc = Date.UTC(2026, 8, 17, 1, 30);
expect(approxLocalTime(-45, earlyUtc)).toBe('22:30'); // UTC−3 → previous day
});
it('always yields a two-digit HH:MM string', () => {
for (const lon of [-170, -6.2, 8.56, 45, 179]) {
expect(approxLocalTime(lon, noonUtc)).toMatch(/^\d{2}:\d{2}$/);
}
});
});