Internationalization
This commit is contained in:
+24
-22
@@ -1,3 +1,4 @@
|
||||
import { loadI18n } from '../test-support/i18n.js';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
@@ -345,6 +346,7 @@ async function loadLocalRoutes(options = {}) {
|
||||
Wegwichtel: {}
|
||||
};
|
||||
context.globalThis = context;
|
||||
await loadI18n(context);
|
||||
vm.runInNewContext(source, context, { filename: sourcePath });
|
||||
return { api: context.Wegwichtel.LocalRoutes, events, indexedDB, localStorage };
|
||||
}
|
||||
@@ -394,7 +396,7 @@ test('route id generation fails when no secure crypto source is available', asyn
|
||||
|
||||
assert.throws(
|
||||
() => api.createRouteId(),
|
||||
/Keine sichere Zufallsquelle/
|
||||
/keine neue Strecke angelegt/
|
||||
);
|
||||
});
|
||||
|
||||
@@ -405,10 +407,10 @@ test('local route package stores GPX and JSON separately and reconstructs an app
|
||||
|
||||
const saved = api.save({
|
||||
id: routeId,
|
||||
gpx: api.emptyGpx('Neue Route'),
|
||||
gpx: api.emptyGpx('Neue Strecke'),
|
||||
route: {
|
||||
id: routeId,
|
||||
name: 'Neue Route',
|
||||
name: 'Neue Strecke',
|
||||
description: 'Lokal gespeichert',
|
||||
serverRouteId: 42,
|
||||
uploadedAt: '2026-09-27T18:00:00.000Z'
|
||||
@@ -436,7 +438,7 @@ test('local route package stores GPX and JSON separately and reconstructs an app
|
||||
]
|
||||
});
|
||||
|
||||
assert.equal(saved.route.name, 'Neue Route');
|
||||
assert.equal(saved.route.name, 'Neue Strecke');
|
||||
assert.equal(saved.pois.length, 2);
|
||||
assert.equal(events.at(-1).action, 'saved');
|
||||
assert.equal(events.at(-1).id, routeId);
|
||||
@@ -450,7 +452,7 @@ test('local route package stores GPX and JSON separately and reconstructs an app
|
||||
assert.match(gpx, /<wegwichtel:poiId>101<\/wegwichtel:poiId>/);
|
||||
assert.doesNotMatch(gpx, /<wpt/);
|
||||
assert.equal((gpx.match(/<trkpt /g) || []).length, 2);
|
||||
assert.equal(metadata.route.name, 'Neue Route');
|
||||
assert.equal(metadata.route.name, 'Neue Strecke');
|
||||
assert.equal(metadata.route.serverRouteId, 42);
|
||||
assert.equal(metadata.route.uploadedAt, '2026-09-27T18:00:00.000Z');
|
||||
assert.equal(metadata.pois[0].audio.name, 'start.mp3');
|
||||
@@ -482,19 +484,19 @@ test('GPX waypoint import reads stable POI ids and validates coordinates', async
|
||||
assert.equal(imported[0].lon, 10.1);
|
||||
|
||||
assert.match(api.emptyGpx('A & B'), /<name>A & B<\/name>/);
|
||||
assert.throws(() => api.parseWaypoints('<not-gpx></not-gpx>'), /GPX-Datei ist nicht gültig/);
|
||||
assert.throws(() => api.syncGpx(sampleGpx, { name: 'Fehler' }, [{ id: 1, title: 'Ohne Position' }]), /gültige Koordinaten/);
|
||||
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/);
|
||||
|
||||
const waypointOnly = api.syncGpx(
|
||||
`<gpx version="1.1" xmlns="${GPX_NAMESPACE}"></gpx>`,
|
||||
{ name: 'Nur Streckenpunkt' },
|
||||
[{ id: 2, title: 'Einzelner Streckenpunkt', lat: 50.2, lon: 10.2, sequence: 0 }]
|
||||
{ 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/);
|
||||
});
|
||||
|
||||
test('local points are stored directly as managed GPX Streckenpunkte and uploaded unchanged', async () => {
|
||||
test('local points are stored directly as managed GPX Stationen and uploaded unchanged', async () => {
|
||||
const { api } = await loadLocalRoutes();
|
||||
const saved = api.save({
|
||||
id: 'local-upload',
|
||||
@@ -514,10 +516,10 @@ test('local points are stored directly as managed GPX Streckenpunkte and uploade
|
||||
assert.equal(api.gpxForUpload(saved), saved.gpx, 'upload must use the already stored track without conversion');
|
||||
});
|
||||
|
||||
test('sync keeps imported foreign Streckenpunkte and upload rejects a route without Streckenpunkte', async () => {
|
||||
test('sync keeps imported foreign Stationen and upload rejects a route without Stationen', async () => {
|
||||
const { api } = await loadLocalRoutes();
|
||||
const synced = api.syncGpx(sampleGpx, { name: 'Import' }, [
|
||||
{ id: 200, title: 'Neuer Streckenpunkt', lat: 50.2, lon: 10.2, sequence: 0 }
|
||||
{ id: 200, title: 'Neuer Station', lat: 50.2, lon: 10.2, sequence: 0 }
|
||||
]);
|
||||
|
||||
assert.match(synced, /<ele>100<\/ele>/);
|
||||
@@ -529,7 +531,7 @@ test('sync keeps imported foreign Streckenpunkte and upload rejects a route with
|
||||
gpx: api.emptyGpx('Leer'),
|
||||
route: { name: 'Leer' },
|
||||
pois: []
|
||||
}), /noch keine Streckenpunkte im GPX-Track/);
|
||||
}), /noch keinen aufgezeichneten Weg/);
|
||||
});
|
||||
|
||||
test('remove updates index and dispatches a local-route change event', async () => {
|
||||
@@ -564,7 +566,7 @@ test('quota failure rolls back package writes and keeps the previous package rea
|
||||
gpx: sampleGpx,
|
||||
route: { name: 'Nachher' },
|
||||
pois: []
|
||||
}), /Speicher für Streckendaten ist voll/);
|
||||
}), /Platz für deine Strecken ist voll/);
|
||||
|
||||
assert.equal(api.get('local-quota').route.name, 'Vorher');
|
||||
});
|
||||
@@ -581,7 +583,7 @@ test('local media uses IndexedDB so route JSON does not consume Local Storage qu
|
||||
id: routeId,
|
||||
gpx: api.emptyGpx('Medientest'),
|
||||
route: { name: 'Medientest' },
|
||||
pois: [{ id: 1, title: 'Streckenpunkt 1', lat: 50.1, lon: 10.1, audio, images: [image] }]
|
||||
pois: [{ id: 1, title: 'Station 1', lat: 50.1, lon: 10.1, audio, images: [image] }]
|
||||
});
|
||||
|
||||
const metadata = localStorage.getItem(`${api.STORAGE_PREFIX}.${encodeURIComponent(routeId)}.json`);
|
||||
@@ -640,7 +642,7 @@ test('local media stores a File System Access handle instead of copying file byt
|
||||
id: 'route-handle',
|
||||
gpx: api.emptyGpx('Handle'),
|
||||
route: { name: 'Handle' },
|
||||
pois: [{ id: 7, title: 'Streckenpunkt', lat: 50.1, lon: 10.1, images: [media] }]
|
||||
pois: [{ id: 7, title: 'Station', lat: 50.1, lon: 10.1, images: [media] }]
|
||||
});
|
||||
const metadata = localStorage.getItem(`${api.STORAGE_PREFIX}.${encodeURIComponent('route-handle')}.json`);
|
||||
assert.match(metadata, /"storage":"file-handle"/);
|
||||
@@ -648,12 +650,12 @@ test('local media stores a File System Access handle instead of copying file byt
|
||||
|
||||
assert.equal(await api.mediaBlob(media), file);
|
||||
permission = 'prompt';
|
||||
await assert.rejects(api.mediaBlob(media), /erneut erlaubt/);
|
||||
await assert.rejects(api.mediaBlob(media), /Erlaube noch einmal/);
|
||||
assert.equal(await api.mediaBlob(media, { requestPermission: true }), file);
|
||||
assert.equal(permissionRequests, 1);
|
||||
});
|
||||
|
||||
test('multiple Streckenpunkte with large local media keep Local Storage metadata small', async () => {
|
||||
test('multiple Stationen with large local media keep Local Storage metadata small', async () => {
|
||||
const { api, localStorage } = await loadLocalRoutes();
|
||||
const routeId = 'multi-media';
|
||||
const large = new Uint8Array(1024 * 1024);
|
||||
@@ -664,11 +666,11 @@ test('multiple Streckenpunkte with large local media keep Local Storage metadata
|
||||
|
||||
api.save({
|
||||
id: routeId,
|
||||
gpx: api.emptyGpx('Mehrere Streckenpunkte'),
|
||||
route: { name: 'Mehrere Streckenpunkte' },
|
||||
gpx: api.emptyGpx('Mehrere Stationen'),
|
||||
route: { name: 'Mehrere Stationen' },
|
||||
pois: [
|
||||
{ id: 1, title: 'Erster Streckenpunkt', lat: 50.1, lon: 10.1, images: [first] },
|
||||
{ id: 2, title: 'Zweiter Streckenpunkt', lat: 50.2, lon: 10.2, images: [second] }
|
||||
{ id: 1, title: 'Erster Station', lat: 50.1, lon: 10.1, images: [first] },
|
||||
{ id: 2, title: 'Zweiter Station', lat: 50.2, lon: 10.2, images: [second] }
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user