import { localizedHtml } from '../test-support/i18n.js';
import { loadI18n } from '../test-support/i18n.js';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
import vm from 'node:vm';
import test from 'node:test';
const root = path.resolve(import.meta.dirname, '..');
async function messages() {
const context = {};
const filename = path.join(root, 'public/js/user-messages.js');
await loadI18n(context);
vm.runInNewContext(await fs.readFile(filename, 'utf8'), context, { filename });
return context.Wegwichtel.UserMessages;
}
test('visible errors explain file, permission and storage problems without exposing technical replies', async () => {
const ui = await messages();
assert.match(ui.error({ message: 'Die GPX-Datei ist kein gültiges XML.' }), /keinen lesbaren Weg/);
assert.match(ui.error({ message: JSON.stringify({ message: 'POI nicht gefunden.', details: 'internal' }) }), /Diese Station wurde nicht gefunden/);
assert.match(ui.error({ responseJSON: { message: 'Freigegebene Strecken dürfen nur durch einen direkten lokalen Serveraufruf gelöscht werden.' } }), /Person löschen/);
assert.match(ui.error({ responseJSON: { message: 'Freigegebene Strecken können nicht mehr verändert werden.' } }), /nicht mehr verändern/);
assert.match(ui.error({ message: 'Nicht unterstütztes Bildformat: image/unknown' }), /anderes Bild/);
assert.match(ui.error({ message: 'Nicht unterstütztes Audioformat: audio/unknown' }), /andere Aufnahme/);
assert.match(ui.error({ message: 'POI-Koordinaten sind ungültig.' }), /Ort dieser Station/);
assert.match(ui.error({ status: 413 }), /zu groß/);
for (const status of [401, 403]) assert.match(ui.error({ status }), /Du darfst/);
assert.match(ui.error({ status: 500 }), /später/);
assert.match(ui.error({ name: 'QuotaExceededError' }), /Platz ist voll/);
assert.match(ui.error({ cause: { name: 'QuotaExceededError' } }), /Platz ist voll/);
assert.equal(ui.error(new Error('Gib deiner Station einen Namen.')), 'Gib deiner Station einen Namen.');
for (const error of [new Error('Failed to fetch'), { message: 'sequence muss eine Ganzzahl sein.' }, { message: 'HTTP 500 /internal/path' }, {}, 'undefined', '{"details":"raw"}']) {
assert.equal(ui.error(error, 'Bitte noch einmal versuchen.'), 'Bitte noch einmal versuchen.');
}
});
test('public help and spoken labels omit implementation terms and preserve meaningful storage guidance', async () => {
const html = await localizedHtml(await fs.readFile(path.join(root, 'public/index.html'), 'utf8'));
const text = html.replace(/<[^>]*>/g, ' ');
const labels = [...html.matchAll(/(?:aria-label|title|placeholder)="([^"]+)"/g)].map(match => match[1]).join(' ');
assert.doesNotMatch(text + labels, /\b(?:GPX|JSON|Cache|API|OPFS|IndexedDB|Local Storage|Server|Token|HTTP|JavaScripts|jQuery)\b/i);
assert.match(text, /Für unterwegs speichern/);
assert.match(text, /Weg als Datei herunterladen/);
assert.match(text, /Bilder und Tonaufnahmen sind in dieser Datei nicht enthalten/);
assert.match(text, /Fortschritt.*gelöscht/s);
assert.match(text, /Meine Entwürfe/);
});