overhaul gpx creation and usage
This commit is contained in:
+34
-13
@@ -361,6 +361,27 @@ const sampleGpx = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
</trkseg></trk>
|
||||
</gpx>`;
|
||||
|
||||
test('missing GPS samples and stations survive storage and later manual coordinates', async () => {
|
||||
const { api } = await loadLocalRoutes();
|
||||
let gpx = api.appendTrackPoint(api.emptyGpx('Ohne Empfang'), { timestamp: 1000 }, 'Ohne Empfang');
|
||||
gpx = api.appendTrackPoint(gpx, { lat: 50, lon: 10, timestamp: 2000 }, 'Ohne Empfang');
|
||||
const saved = api.save({ id: 'missing-gps', gpx, route: { name: 'Ohne Empfang' }, pois: [
|
||||
{ id: 42, title: 'Drinnen', lat: null, lon: null, sequence: 0 }
|
||||
] });
|
||||
const positions = api.recordedPositions(saved.gpx);
|
||||
assert.equal(positions.length, 2);
|
||||
assert.equal(positions[0].lat, null);
|
||||
assert.equal(api.parseWaypoints(saved.gpx)[0].lat, null);
|
||||
assert.match(api.gpxForUpload(saved), /gpsGap/);
|
||||
positions[0].lat = 49.999;
|
||||
positions[0].lon = 9.999;
|
||||
const edited = api.replaceRecordedPositions(saved.gpx, positions, 'Ohne Empfang');
|
||||
assert.doesNotMatch(edited, /gpsGap/);
|
||||
assert.equal(api.recordedPositions(edited)[0].lat, 49.999);
|
||||
assert.equal(api.parseWaypoints(edited)[0].title, 'Drinnen');
|
||||
assert.equal(api.parseWaypoints(edited)[0].lat, null);
|
||||
});
|
||||
|
||||
test('route id generation uses randomUUID when available', async () => {
|
||||
const { api } = await loadLocalRoutes();
|
||||
|
||||
@@ -448,10 +469,10 @@ test('local route package stores GPX and JSON separately and reconstructs an app
|
||||
const gpx = localStorage.getItem(`${prefix}.${encodedId}.gpx`);
|
||||
const metadata = JSON.parse(localStorage.getItem(`${prefix}.${encodedId}.json`));
|
||||
|
||||
assert.match(gpx, /<trkpt lat="50\.1" lon="10\.1">/);
|
||||
assert.match(gpx, /<wpt lat="50\.1" lon="10\.1">/);
|
||||
assert.match(gpx, /<wegwichtel:poiId>101<\/wegwichtel:poiId>/);
|
||||
assert.doesNotMatch(gpx, /<wpt/);
|
||||
assert.equal((gpx.match(/<trkpt /g) || []).length, 2);
|
||||
assert.doesNotMatch(gpx, /<trkpt/);
|
||||
assert.equal((gpx.match(/<wpt /g) || []).length, 2);
|
||||
assert.equal(metadata.route.name, 'Neue Strecke');
|
||||
assert.equal(metadata.route.serverRouteId, 42);
|
||||
assert.equal(metadata.route.uploadedAt, '2026-09-27T18:00:00.000Z');
|
||||
@@ -465,8 +486,8 @@ test('local route package stores GPX and JSON separately and reconstructs an app
|
||||
assert.equal(appRoute.pois.length, 2);
|
||||
assert.equal(appRoute.pois[0].audioUrl, 'data:audio/mpeg;base64,QQ==');
|
||||
assert.equal(appRoute.pois[0].images[0].url, 'data:image/jpeg;base64,Qg==');
|
||||
assert.equal(appRoute.points.length, 2);
|
||||
assert.ok(appRoute.distanceM > 0);
|
||||
assert.equal(appRoute.points.length, 0);
|
||||
assert.equal(appRoute.distanceM, 0);
|
||||
assert.equal(Math.round(appRoute.elevationGainM), 0);
|
||||
const listed = api.listAppRoutes();
|
||||
assert.equal(listed.length, 1);
|
||||
@@ -485,15 +506,15 @@ test('GPX waypoint import reads stable POI ids and validates coordinates', async
|
||||
|
||||
assert.match(api.emptyGpx('A & B'), /<name>A & B<\/name>/);
|
||||
assert.throws(() => api.parseWaypoints('<not-gpx></not-gpx>'), /keinen lesbaren Weg/);
|
||||
assert.throws(() => api.syncGpx(sampleGpx, { name: 'Fehler' }, [{ id: 1, title: 'Ohne Position' }]), /Ort von Ohne Position fehlt/);
|
||||
assert.match(api.syncGpx(sampleGpx, { name: 'Fehler' }, [{ id: 1, title: 'Ohne Position' }]), /pendingWaypoint/);
|
||||
|
||||
const waypointOnly = api.syncGpx(
|
||||
`<gpx version="1.1" xmlns="${GPX_NAMESPACE}"></gpx>`,
|
||||
{ name: 'Nur Station' },
|
||||
[{ id: 2, title: 'Einzelner Station', lat: 50.2, lon: 10.2, sequence: 0 }]
|
||||
);
|
||||
assert.match(waypointOnly, /<trkpt lat="50.2" lon="10.2">/);
|
||||
assert.doesNotMatch(waypointOnly, /<wpt\b/);
|
||||
assert.match(waypointOnly, /<wpt lat="50.2" lon="10.2">/);
|
||||
assert.doesNotMatch(waypointOnly, /<trkpt\b/);
|
||||
});
|
||||
|
||||
test('local points are stored directly as managed GPX Stationen and uploaded unchanged', async () => {
|
||||
@@ -508,11 +529,11 @@ test('local points are stored directly as managed GPX Stationen and uploaded unc
|
||||
]
|
||||
});
|
||||
|
||||
assert.match(saved.gpx, /<trkpt lat="50\.123456789" lon="10\.987654321">/);
|
||||
assert.match(saved.gpx, /<trkpt lat="50\.223456789" lon="11\.087654321">/);
|
||||
assert.match(saved.gpx, /<wpt lat="50\.123456789" lon="10\.987654321">/);
|
||||
assert.match(saved.gpx, /<wpt lat="50\.223456789" lon="11\.087654321">/);
|
||||
assert.match(saved.gpx, /<wegwichtel:poiId>1<\/wegwichtel:poiId>/);
|
||||
assert.equal((saved.gpx.match(/<trkpt /g) || []).length, 2);
|
||||
assert.doesNotMatch(saved.gpx, /<wpt\b/);
|
||||
assert.equal((saved.gpx.match(/<wpt /g) || []).length, 2);
|
||||
assert.doesNotMatch(saved.gpx, /<trkpt\b/);
|
||||
assert.equal(api.gpxForUpload(saved), saved.gpx, 'upload must use the already stored track without conversion');
|
||||
});
|
||||
|
||||
@@ -525,7 +546,7 @@ test('sync keeps imported foreign Stationen and upload rejects a route without S
|
||||
assert.match(synced, /<ele>100<\/ele>/);
|
||||
assert.match(synced, /<ele>112<\/ele>/);
|
||||
assert.match(synced, /<wegwichtel:poiId>200<\/wegwichtel:poiId>/);
|
||||
assert.equal((synced.match(/<trkpt /g) || []).length, 3);
|
||||
assert.equal((synced.match(/<trkpt /g) || []).length, 2);
|
||||
assert.equal(api.gpxForUpload({ gpx: sampleGpx, route: { name: 'Import' }, pois: [] }), sampleGpx);
|
||||
assert.throws(() => api.gpxForUpload({
|
||||
gpx: api.emptyGpx('Leer'),
|
||||
|
||||
Reference in New Issue
Block a user