aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/viewer/tests/nav.test.mjs
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-07 00:14:38 +0200
committergodosa <godosa@godosa.eu>2026-10-07 00:14:38 +0200
commit3443c1c65e9f1753e1e656b35d08416c1fa298f2 (patch)
tree4e43236f460145a4d75d1b4616dcb7aa6ef08f51 /viewer/tests/nav.test.mjs
downloadworldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.tar.gz
worldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.zip
worldmap-viewer: initial public history
Diffstat (limited to 'viewer/tests/nav.test.mjs')
-rw-r--r--viewer/tests/nav.test.mjs64
1 files changed, 64 insertions, 0 deletions
diff --git a/viewer/tests/nav.test.mjs b/viewer/tests/nav.test.mjs
new file mode 100644
index 0000000..4efde7f
--- /dev/null
+++ b/viewer/tests/nav.test.mjs
@@ -0,0 +1,64 @@
+import './_planet.mjs';
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { keyAction, exportName } from '../js/nav.js';
+
+const k = (key, extra = {}) => keyAction({ key, target: { tagName: 'CANVAS' }, ...extra });
+
+test('keys map to actions', () => {
+ assert.deepEqual(k('ArrowLeft'), ['pan', -0.2, 0]);
+ assert.deepEqual(k('ArrowDown'), ['pan', 0, 0.2]);
+ assert.deepEqual(k('+'), ['zoom', 0.6]);
+ assert.deepEqual(k('='), ['zoom', 0.6]);
+ assert.deepEqual(k('-'), ['zoom', 1 / 0.6]);
+ assert.deepEqual(k('Home'), ['home']);
+ assert.deepEqual(k('f'), ['fullscreen']);
+ assert.deepEqual(k('/'), ['search']);
+ assert.equal(k('x'), null);
+});
+
+test('typing and modifier chords are left alone', () => {
+ for (const tagName of ['INPUT', 'TEXTAREA', 'SELECT']) assert.equal(keyAction({ key: 'ArrowLeft', target: { tagName } }), null);
+ assert.equal(k('f', { ctrlKey: true }), null);
+ assert.equal(k('ArrowUp', { metaKey: true }), null);
+ assert.equal(k('-', { altKey: true }), null);
+});
+
+test('export file names', () => {
+ assert.equal(exportName({ layer: 'relief', over: '', view: 'globe' }), 'world-relief-globe.png');
+ assert.equal(exportName({ layer: 'relief', over: 'biomes', view: 'equal_earth' }), 'world-relief+biomes-equal_earth.png');
+});
+
+test('exports note procedural detail when tiles are showing', async () => {
+ const { exportNotes } = await import('../js/nav.js');
+ assert.deepEqual(exportNotes({ view: { tiles: { active: true } } }), ['procedural detail — not data']);
+ assert.deepEqual(exportNotes({ view: { tiles: { active: false } } }), []);
+ assert.deepEqual(exportNotes({ view: {} }), []);
+});
+
+test('Shift+arrows turn and tilt; plain arrows still pan; N points north', () => {
+ const k = (key, o = {}) => keyAction({ key, target: { tagName: 'CANVAS' }, ...o });
+ assert.deepEqual(k('ArrowLeft', { shiftKey: true }), ['turn', -15, 0]);
+ assert.deepEqual(k('ArrowUp', { shiftKey: true }), ['turn', 0, 10]);
+ assert.deepEqual(k('ArrowLeft'), ['pan', -0.2, 0]);
+ assert.deepEqual(k('n'), ['north']);
+ assert.equal(k('n', { target: { tagName: 'INPUT' } }), null);
+});
+
+test('G goes to ground view, ? shows the controls', () => {
+ assert.deepEqual(k('g'), ['ground']);
+ assert.deepEqual(k('?', { shiftKey: true }), ['help']);
+ assert.equal(k('g', { target: { tagName: 'INPUT' } }), null);
+});
+
+test('ground view: the eye above the spot looked at, close and tilted, heading kept', async () => {
+ const { groundPose, GROUND_RANGE, GROUND_PITCH } = await import('../js/nav.js');
+ const ahead = GROUND_RANGE * Math.sin(GROUND_PITCH * Math.PI / 180) * 180 / Math.PI; // degrees of arc
+ const n = groundPose({ lat: 0, lon: 10, range: 0.5, heading: 0, pitch: 0 });
+ assert.ok(Math.abs(n.lat - ahead) < 1e-9 && Math.abs(n.lon - 10) < 1e-9, 'looking north: the target lies ahead');
+ assert.equal(n.range, GROUND_RANGE);
+ assert.equal(n.pitch, GROUND_PITCH);
+ const e = groundPose({ lat: 0, lon: 10, range: 0.5, heading: 90, pitch: 0 });
+ assert.ok(Math.abs(e.lat) < 1e-9 && Math.abs(e.lon - 10 - ahead) < 1e-9 && e.heading === 90);
+ assert.ok(GROUND_RANGE * 12742 < 5 && GROUND_PITCH >= 70);
+});