import './_planet.mjs'; import { test } from 'node:test'; import assert from 'node:assert/strict'; import { parse, serialize, DEFAULT, carryHash } from '../js/urlstate.js'; test('round trip', () => { const s = { ...DEFAULT, layer: 'biomes', view: 'equal_earth', lat: 40.5, lon: -10.25, alt: 0.5, zoom: 3, overlays: ['coast', 'pins'] }; assert.deepEqual(parse(serialize(s)), s); const t = { ...s, over: 'biomes', op: 0.35, sky: true, day: 100, mo: 12345, alt: 0.0004, zoom: 20000 }; assert.deepEqual(parse(serialize(t)), t); }); test('new keys clamp and default', () => { const s = parse('#op=7&day=300&mo=-5&sky=maybe&alt=0.00001&zoom=1e9'); assert.equal(s.op, 1); assert.equal(s.day, 215); assert.equal(s.mo, 0); assert.equal(s.sky, false); assert.equal(s.alt, 0.00001); // above the 20 m floor now assert.equal(s.zoom, 20000); assert.equal(s.over, ''); }); test('garbage and out-of-range values fall back or clamp', () => { const s = parse('#view=nope&lat=abc&lon=200&alt=99&zoom=0'); assert.equal(s.view, DEFAULT.view); assert.equal(s.lat, DEFAULT.lat); assert.equal(s.lon, -160); assert.equal(s.alt, 5); assert.equal(s.zoom, 1); assert.deepEqual(parse(''), DEFAULT); }); test('clamps come from the shared view limits', async () => { const { MIN_ALT, MAX_ALT, MAX_ZOOM } = await import('../js/limits.js'); const s = parse('#alt=1e-9&zoom=1e12'); assert.equal(s.alt, MIN_ALT); assert.equal(s.zoom, MAX_ZOOM); assert.equal(parse('#alt=1e9').alt, MAX_ALT); }); test('tilt and 3D: round trip, clamps, only written when they differ from the default', () => { const s = { ...DEFAULT, alt: 0.001, hd: 30.5, pt: 45, terrain: 2.5 }; assert.deepEqual(parse(serialize(s)), s); assert.deepEqual(parse(serialize({ ...DEFAULT, terrain: 0 })), { ...DEFAULT, terrain: 0 }); assert.ok(!/hd=|pt=|3d=/.test(serialize(DEFAULT))); assert.ok(!/hd=|pt=/.test(serialize({ ...DEFAULT, view: 'equal_earth', hd: 30, pt: 40 })), 'flat views have no tilt'); const c = parse('#hd=-90&pt=95&3d=9'); assert.deepEqual([c.hd, c.pt, c.terrain], [270, 80, 5]); assert.equal(parse('#3d=0.4').terrain, 1); assert.equal(parse('#3d=-2').terrain, 0); assert.equal(parse('#3d=abc').terrain, 1); }); test('era and water round-trip; defaults stay out of the URL', () => { const s = { ...DEFAULT, era: 'age-of-legends', water: false }; assert.deepEqual(parse(serialize(s)), s); assert.doesNotMatch(serialize(DEFAULT), /era=|water=/); assert.equal(parse('#water=1').water, true); assert.equal(parse('#era=').era, ''); }); test('the sea floor replaces relief only on the 3D globe with the water off', async () => { const { drawnLayer } = await import('../js/urlstate.js'); const s = { ...DEFAULT, water: false }; assert.equal(drawnLayer(s, 'globe'), 'seafloor'); assert.equal(drawnLayer({ ...s, terrain: 0 }, 'globe'), 'relief', '3D off: the water toggle is hidden'); assert.equal(drawnLayer(s, 'equal_earth'), 'relief'); assert.equal(drawnLayer({ ...s, layer: 'biomes' }, 'globe'), 'biomes'); assert.equal(drawnLayer(DEFAULT, 'globe'), 'relief'); }); test('carryHash keeps the view, drops era and time', () => { assert.equal(carryHash('#layer=ground&view=globe&lat=10&era=high-fantasy&sky=1&day=50&mo=9'), '#layer=ground&view=globe&lat=10&sky=1'); assert.equal(carryHash('#era=x'), ''); assert.equal(carryHash(''), ''); });