diff options
Diffstat (limited to 'viewer/tests/measure.test.mjs')
| -rw-r--r-- | viewer/tests/measure.test.mjs | 82 |
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)); +}); |
