aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/viewer/tests/measure.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/measure.test.mjs
downloadworldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.tar.gz
worldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.zip
worldmap-viewer: initial public history
Diffstat (limited to 'viewer/tests/measure.test.mjs')
-rw-r--r--viewer/tests/measure.test.mjs82
1 files changed, 82 insertions, 0 deletions
diff --git a/viewer/tests/measure.test.mjs b/viewer/tests/measure.test.mjs
new file mode 100644
index 0000000..f8c2110
--- /dev/null
+++ b/viewer/tests/measure.test.mjs
@@ -0,0 +1,82 @@
+import './_planet.mjs';
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { MeasurePath } from '../js/measure.js';
+import { R_KM } from '../js/geo.js';
+import { FakeEl, installFakeDom } from './fakedom.mjs';
+
+const A = { lat: 0, lon: 0 }, B = { lat: 0, lon: 90 }, C = { lat: 90, lon: 0 };
+const near = (a, b, eps) => assert.ok(Math.abs(a - b) <= eps, `${a} vs ${b}`);
+
+test('open path: segments and total', () => {
+ const m = new MeasurePath();
+ [A, B, C].forEach(p => m.add(p));
+ assert.equal(m.segmentsKm().length, 2);
+ near(m.totalKm(), R_KM * Math.PI, 1e-6); // two quarter circles
+ assert.equal(m.areaKm2(), null);
+ assert.equal(m.lines()[0].length, 3);
+});
+
+test('closing needs three points; a closed path adds the return leg and an area', () => {
+ const m = new MeasurePath();
+ m.add(A);
+ m.add(B);
+ assert.equal(m.close(), false);
+ m.add(C);
+ assert.equal(m.close(), true);
+ assert.equal(m.segmentsKm().length, 3);
+ near(m.areaKm2(), (4 * Math.PI * R_KM ** 2) / 8, 1);
+ assert.deepEqual(m.lines()[0].at(-1), A);
+});
+
+test('undo reopens then removes; repeats ignored; adding after a close starts over', () => {
+ const m = new MeasurePath();
+ [A, B, C].forEach(p => m.add(p));
+ m.close();
+ m.undo();
+ assert.equal(m.closed, false);
+ assert.equal(m.points.length, 3);
+ m.undo();
+ assert.equal(m.points.length, 2);
+ m.add(B); // a double-click lands twice on the same spot
+ assert.equal(m.points.length, 2);
+ m.add(C);
+ m.close();
+ m.add(A);
+ assert.deepEqual(m.points, [A]);
+});
+
+test('panel never renders null/undefined children (DOM would print "null")', async () => {
+ installFakeDom();
+ const { renderMeasure } = await import('../js/measure.js');
+ const box = new FakeEl('section'), noop = () => {};
+ const m = new MeasurePath();
+ for (const pts of [[], [A], [A, B]]) {
+ m.clear();
+ pts.forEach(p => m.add(p));
+ renderMeasure(box, m, { undo: noop, close: noop, clear: noop });
+ assert.ok(box.kids.every(k => k !== null && k !== undefined), `${pts.length} points: ${box.kids.map(String)}`);
+ }
+});
+
+test('elevation chart: an svg profile; hovering reports the nearest sample', async () => {
+ installFakeDom();
+ const { renderProfile, renderMeasure } = await import('../js/measure.js');
+ const box = new FakeEl('div'), seen = [];
+ const prof = { samples: [{ d_km: 0, lat: 0, lon: 0, z_m: -100, water: true }, { d_km: 10, lat: 0, lon: 0.1, z_m: 500, water: false }],
+ stats: { min_m: -100, max_m: 500, ascent_m: 600, descent_m: 0, water_km: 5, length_km: 10 } };
+ renderProfile(box, prof, p => seen.push(p));
+ const chart = box.kids[0];
+ assert.equal(chart.tag, 'svg');
+ assert.ok(chart.find(e => e.tag === 'path'));
+ chart.fire('pointermove', { clientX: 300 });
+ assert.equal(seen.at(-1).d_km, 10);
+ assert.match(box.textContent, /Highest 500 m/);
+ chart.fire('pointerleave');
+ assert.equal(seen.at(-1), null);
+ const mbox = new FakeEl('section'), m = new MeasurePath();
+ m.add(A);
+ m.add(B);
+ const pbox = renderMeasure(mbox, m, { undo() {}, close() {}, clear() {} });
+ assert.ok(mbox.kids.includes(pbox));
+});