import test from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { fingerprint, entries, progress, setText, confirmText, setPageValue, FIELDS } from '../public/assets/translate.js'; import { EditorHistory } from '../public/assets/editor-model.js'; const page = () => ({ schemaVersion: 26, title: 'Leistungen', description: 'Was wir tun', blocks: [{ id: 's1', type: 'section', children: [ { id: 'h1', type: 'heading', props: { text: 'Leistungen', level: 1 } }, { id: 'b1', type: 'button', props: { text: 'Kontakt', href: '/kontakt' } }, { id: 'i1', type: 'image', props: { src: '/x.svg', alt: '', decorative: true, width: 1, height: 1 } }, { id: 'q1', type: 'quote', props: { text: 'Sehr gut', author: 'Anna', role: '', rating: 5 } }, ] }], }); test('the fingerprint matches hash("fnv1a32") in PHP, also for umlauts and markup', () => { // Werte aus PHP 8.4: hash('fnv1a32', …) assert.equal(fingerprint('Leistungen'), '16a48bdd'); assert.equal(fingerprint('Über uns – ß'), '80c36c2f'); }); test('the translatable fields mirror Translations::FIELDS on the server', () => { const php = readFileSync(new URL('../src/Content/Translations.php', import.meta.url), 'utf8'); const server = Object.fromEntries([...php.matchAll(/'([a-z]+)' => \[((?:'[a-zA-Z]+'(?:, )?)+)\],/g)].map(([, type, list]) => [type, [...list.matchAll(/'([a-zA-Z]+)'/g)].map(match => match[1])])); assert.deepEqual(Object.fromEntries(Object.entries(FIELDS).map(([type, fields]) => [type, fields.map(field => field.prop)])), server); }); test('translating writes only into translations, notes the original and can be undone', () => { const original = page(); const history = new EditorHistory(original); assert.deepEqual(progress(history.document, 'en'), { total: 6, done: 0, stale: 0 }, 'Leere und dekorative Felder zählen nicht.'); history.change(doc => setText(doc, 'en', 'h1.text', 'Services')); history.change(doc => setText(doc, 'en', 'title', 'Services')); history.change(doc => setPageValue(doc, 'en', 'slug', ' Services ')); const doc = history.document; assert.equal(doc.schemaVersion, 27); assert.equal(doc.blocks[0].children[0].props.text, 'Leistungen', 'Die Hauptsprache bleibt unberührt.'); assert.deepEqual(doc.translations.en, { blocks: { h1: { text: 'Services' } }, source: { 'h1.text': '16a48bdd', title: '16a48bdd' }, title: 'Services', slug: 'services' }); assert.deepEqual(progress(doc, 'en'), { total: 6, done: 2, stale: 0 }); // Das Original ändert sich: die Übersetzung gilt als veraltet, bis sie bestätigt wird. history.change(content => { content.blocks[0].children[0].props.text = 'Unsere Leistungen'; }); assert.equal(entries(history.document, 'en').find(entry => entry.key === 'h1.text').state, 'stale'); history.change(content => confirmText(content, 'en', 'h1.text')); assert.equal(entries(history.document, 'en').find(entry => entry.key === 'h1.text').state, 'done'); // Leeren entfernt den Text samt Vermerk; eine leere Sprache verschwindet ganz. history.change(content => { setText(content, 'en', 'h1.text', ''); setText(content, 'en', 'title', ''); setPageValue(content, 'en', 'slug', ''); }); assert.equal(history.document.translations, undefined); while (history.past.length) history.undo(); assert.deepEqual(history.document, original); });