import test from 'node:test'; import assert from 'node:assert/strict'; import { parseGpx, gpxWithWaypoints } from '../src/services/gpx.js'; import { SAMPLE_GPX } from '../test-support/gpx-fixtures.js'; test('parseGpx reads synthetic track data without repository example files', () => { const parsed = parseGpx(SAMPLE_GPX); assert.equal(parsed.name, 'Testweg'); assert.equal(parsed.points.length, 3); assert.ok(parsed.metrics.distanceM > 0); assert.equal(parsed.points[0].lat, 52.5200); assert.equal(parsed.points[2].lon, 13.4090); }); const point = (tag, lat, lon, content = '') => `<${tag} lat="${lat}" lon="${lon}">${content}`; const gpx = (waypoints, path = '', extra = '') => `${waypoints}${path}${extra}`; const track = points => `${points}`; const locations = points => points.map(({ lat, lon }) => [lat, lon]); test('waypoints alone become route points in station order and contribute to distance and bounds', () => { const parsed = parseGpx(gpx(point('wpt', 50, 10) + point('wpt', 50.01, 10.02))); assert.deepEqual(locations(parsed.points), [[50, 10], [50.01, 10.02]]); assert.equal(parsed.metrics.pointCount, 2); assert.ok(parsed.metrics.distanceM > 0); assert.equal(parsed.metrics.maxLon, 10.02); assert.match(parsed.normalizedXml, / { const wp = point('wpt', 50, 10.00001) + point('wpt', 50.005, 10.015); const recorded = point('trkpt', 50, 10) + point('trkpt', 50, 10.00002) + point('trkpt', 50, 10.01) + point('trkpt', 50, 10.02); const parsed = parseGpx(gpx(wp, track(recorded))); assert.deepEqual(locations(parsed.points), [[50, 10], [50, 10.00001], [50, 10.01], [50.005, 10.015], [50, 10.02]]); assert.equal(parsed.metrics.maxLat, 50.005); assert.equal(parsed.points.length, 5); assert.deepEqual(parsed.points.map(p => p.sequence), [0, 1, 2, 3, 4]); assert.deepEqual(locations(parseGpx(parsed.normalizedXml).points), locations(parsed.points)); }); test('duplicate station locations do not multiply route points and station order is retained on loops', () => { const stations = point('wpt', 50, 10.02) + point('wpt', 50, 10.02) + point('wpt', 50, 10.01); const recorded = [10, 10.01, 10.02, 10.01, 10].map(lon => point('trkpt', 50, lon)).join(''); const parsed = parseGpx(gpx(stations, track(recorded))); assert.deepEqual(locations(parsed.points), [[50, 10], [50, 10.01], [50, 10.02], [50, 10.01], [50, 10]]); assert.deepEqual(locations(parseGpx(parsed.normalizedXml).points), locations(parsed.points)); }); test('existing routes and replacement points incorporate stations, including stations before and after the path', () => { const stations = [9.99, 10.005, 10.02].map(lon => point('wpt', 50, lon)).join(''); const route = `${point('rtept', 50, 10)}${point('rtept', 50, 10.01)}`; const xml = gpx(stations, route); const parsed = parseGpx(xml); assert.deepEqual(locations(parsed.points), [[50, 9.99], [50, 10], [50, 10.005], [50, 10.01], [50, 10.02]]); assert.deepEqual(locations(parseGpx(xml, [{ lat: 50, lon: 10 }, { lat: 50, lon: 10.01 }]).points), locations(parsed.points)); }); test('missing and invalid station coordinates are excluded while GPS gaps remain available for editing', () => { const stations = 'Ohne Ort' + point('wpt', 91, 10) + point('wpt', 50, 181) + point('wpt', 50, 10); const extra = 'Später'; const parsed = parseGpx(gpx(stations, '', extra)); assert.deepEqual(locations(parsed.points), [[50, 10]]); assert.match(parsed.normalizedXml, /wegwichtel:gpsGap/); assert.match(parsed.normalizedXml, /wegwichtel:pendingWaypoint/); const empty = parseGpx(gpx('', '', extra)); assert.equal(empty.metrics.pointCount, 0); assert.equal(empty.metrics.minLat, null); }); test('moving or deleting a station replaces its generated route anchor without removing recorded points', () => { const original = parseGpx(gpx(point('wpt', 50.01, 10.005), track(point('trkpt', 50, 10) + point('trkpt', 50, 10.01)))); assert.match(original.normalizedXml, /wegwichtel:waypointRoutePoint/); const updated = parseGpx(gpxWithWaypoints(original.normalizedXml, [{ id: 1, title: 'Neu', lat: 50.02, lon: 10.006 }])); assert.equal(updated.points.length, 3); assert.equal(updated.metrics.maxLat, 50.02); assert.equal(updated.points.some(p => p.lat === 50.01), false); const removed = parseGpx(gpxWithWaypoints(updated.normalizedXml, [])); assert.deepEqual(locations(removed.points), [[50, 10], [50, 10.01]]); assert.deepEqual(locations(parseGpx(updated.normalizedXml).points), locations(updated.points)); });