Buchungskalender: Abschluss-Fixwelle B zusammenführen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
1c6b6bf8d6
10 files changed
+257
-71
No files matched your search
@@ -843,3 +843,28 @@ test('Posten-Editor: die Summenanzeige enthaelt bei Regelbesteuerung kein Text-"
|
||||
assert.doesNotMatch(fx.text(e.el), /null/)
|
||||
} finally { fx.restore() }
|
||||
})
|
||||
|
||||
// Abschluss F3: jeder Servercode hat einen deutschen Du-Satz mit Handlungshinweis
|
||||
test('Fehlertexte: alle Codes des Portals sind übersetzt (Du-Form, kein Standardtext)', async () => {
|
||||
const { fehlerFuerFormular } = await import('../public/buchungen-fehler.js')
|
||||
const standard = fehlerFuerFormular({ fehler: 'gibt_es_nicht' }).message
|
||||
for (const code of ['preise_unvollstaendig', 'einstellungen_unvollstaendig', 'status_ungueltig', 'limit_erreicht', 'kein_konto', 'nicht_aktiv', 'praefix_gesperrt', 'sperre_aus_ical', 'online_aus', 'zahlung_fehlgeschlagen']) {
|
||||
const m = fehlerFuerFormular({ fehler: code }).message
|
||||
assert.notEqual(m, standard, code)
|
||||
assert.ok(m.length > 30 && m.endsWith('.'), code)
|
||||
assert.doesNotMatch(m, /\bSie\b|\bIhre?\b/, code)
|
||||
}
|
||||
})
|
||||
|
||||
test('Preis-Editor: Platzhalter-Feldfehler verschwindet beim Tippen', mit(ukServer(), async (fx) => {
|
||||
const { panel } = await zeigeTab(fx, ukTab)
|
||||
await fx.klick(aktion(fx, panel, 'bearbeiten'))
|
||||
await fx.ruhe()
|
||||
const storno = fx.finde(panel, '#bk-p-storno')
|
||||
await fx.setze(storno, 'Bis zu [X] Tage vor Anreise kostenfrei stornierbar, danach 100 %.')
|
||||
await fx.klick(aktion(fx, panel, 'preise-speichern'))
|
||||
await fx.ruhe()
|
||||
assert.match(fehlerText(fx, panel, 'stornobedingungen'), /Platzhalter/)
|
||||
await fx.setze(storno, 'Bis zu 14 Tage vor Anreise kostenfrei stornierbar, danach 100 %.')
|
||||
assert.equal(fehlerText(fx, panel, 'stornobedingungen'), '')
|
||||
}))
|
||||
@@ -17,9 +17,8 @@ test('kein innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, new F
|
||||
assert.doesNotMatch(OHNE_KOMMENTARE, /innerHTML|outerHTML|insertAdjacentHTML|document\.write|\beval\s*\(|new Function/)
|
||||
})
|
||||
|
||||
test('API-Pfad, Praefix .wku-, @layer wku, Style-Id, kein Storage, keine Cookies, keine Fremdadressen', () => {
|
||||
test('API-Pfad, Praefix .wku-, Style-Id, kein Storage, keine Cookies, keine Fremdadressen', () => {
|
||||
assert.ok(OHNE_KOMMENTARE.includes('/api/public/unterkunft/v1/'))
|
||||
assert.ok(OHNE_KOMMENTARE.includes('@layer wku'))
|
||||
assert.ok(OHNE_KOMMENTARE.includes('wku-style'))
|
||||
assert.match(OHNE_KOMMENTARE, /\.wku-root/)
|
||||
assert.doesNotMatch(OHNE_KOMMENTARE, /localStorage|sessionStorage|document\.cookie|https?:\/\//)
|
||||
@@ -29,3 +28,18 @@ test('Pflichttexte und Honeypot', () => {
|
||||
for (const t of ['Anfragen gerne per Telefon oder E-Mail.', 'Bitte wählen Sie Anreise und Abreise im Kalender.', 'Anfrage senden', 'kontakt_url', 'formToken']) assert.ok(QUELLE.includes(t), t)
|
||||
assert.match(QUELLE, /Danke! Ihre Anfrage/)
|
||||
})
|
||||
|
||||
// F1 (Abschluss): kein @layer (Seitenregeln wuerden gewinnen), jede Regel unter .wku-root, Reset der Formularelemente.
|
||||
test('CSS-Isolation: kein @layer, jeder Selektor beginnt mit .wku-root, all:revert fuer Elemente, Fenster +600 Tage', () => {
|
||||
assert.doesNotMatch(OHNE_KOMMENTARE, /@layer/)
|
||||
const m = QUELLE.match(/var CSS = \[([\s\S]*?)\]\.join\(''\)/)
|
||||
assert.ok(m)
|
||||
const css = [...m[1].matchAll(/'((?:[^'\\]|\\.)*)'/g)].map((x) => x[1]).join('')
|
||||
const flach = css.replace(/@(container|media|supports)[^{]*\{/g, '')
|
||||
const regeln = [...flach.matchAll(/([^{}]+)\{[^{}]*\}/g)].map((x) => x[1])
|
||||
assert.ok(regeln.length > 40)
|
||||
for (const sel of regeln) for (const teil of sel.split(/,(?![^(]*\))/)) assert.match(teil.trim(), /^\.wku-root(?![\w-])/, teil)
|
||||
assert.match(css, /\.wku-root :is\(button,input,select,textarea[^)]*\)\{all:revert/)
|
||||
assert.match(css, /\.wku-root\{[^}]*width:100%/)
|
||||
assert.ok(OHNE_KOMMENTARE.includes('plusTage(von, 600)'))
|
||||
})
|
||||
@@ -106,7 +106,7 @@ test('freigegeben: Pflichtangaben in Reihenfolge direkt ueber dem Knopf, Abruf m
|
||||
const block = fx.finde(el, '[data-tag="pflichtangaben"]')
|
||||
assert.equal(kinder[kinder.indexOf(block) + 1], knopf, 'Block steht direkt ueber dem Knopf')
|
||||
const t = fx.text(block)
|
||||
const marken = ['Anbieter: Haus Test GmbH', 'Weg 1', 'Unterkunft: Ferienwohnung, 10.11.2026 bis 14.11.2026', 'Check-in ab 15:00 Uhr', 'Check-out bis 11:00 Uhr', 'Personen: 2', 'Übernachtung, 4 Nächte', 'Endreinigung', 'Gesamtpreis: 450,00', '7 % USt', 'Zahlarten:', 'Stornobedingungen: Bis 14 Tage vor Anreise kostenfrei.', 'Ein Widerrufsrecht besteht nicht (§ 312g Abs. 2 Nr. 9 BGB).', 'Datenschutz:', 'Reserviert bis 10.10.2026 14:30 Uhr']
|
||||
const marken = ['Anbieter: Haus Test GmbH', 'Weg 1', 'Unterkunft: Ferienwohnung, 10.11.2026 bis 14.11.2026', 'Check-in ab 15:00 Uhr', 'Check-out bis 11:00 Uhr', 'Personen: 2', 'Übernachtung, 4 Nächte', 'Endreinigung', 'Gesamtpreis: 450,00', '7 % USt', 'Zahlarten:', 'Stornobedingungen: Bis 14 Tage vor Anreise kostenfrei.', 'Ein Widerrufsrecht besteht nicht (§ 312g Abs. 2 Satz 1 Nr. 9 BGB).', 'Datenschutz:', 'Reserviert bis 10.10.2026 14:30 Uhr']
|
||||
let pos = -1
|
||||
for (const m of marken) {
|
||||
const i = t.indexOf(m, pos + 1)
|
||||
@@ -289,3 +289,61 @@ test('Fehler der Rechnungsadresse verschwinden, sobald der Gast die Felder ausfu
|
||||
assert.ok(meldung.hidden)
|
||||
})
|
||||
})
|
||||
|
||||
/* ----------------------------------------------------- Abschluss: 409-Codes, a11y --- */
|
||||
test('409-Codes beim Buchen: eigene Banner-Texte statt stillem Neuladen', async () => {
|
||||
const faelle = [
|
||||
['online_aus', /Online-Zahlung ist gerade nicht möglich – bitte wählen Sie „Vor Ort bezahlen“\./, false],
|
||||
['einstellungen_unvollstaendig', /Die Buchung ist gerade nicht möglich\. Bitte wenden Sie sich direkt an den Anbieter\./, false],
|
||||
['zustimmung_fehlt', /Die Buchung ist gerade nicht möglich\. Bitte wenden Sie sich direkt an den Anbieter\./, false],
|
||||
['status_ungueltig', /Der Stand Ihrer Buchung hat sich geändert\./, true],
|
||||
]
|
||||
for (const [code, muster, neuLaden] of faelle) {
|
||||
const antworten = [{ status: 200, body: DATEN }, { status: 409, body: { fehler: code } }]
|
||||
if (neuLaden) antworten.push({ status: 200, body: { ...DATEN, status: 'gebucht', zahlart: 'vorort' } })
|
||||
await mitFx(antworten, undefined, async (fx, el) => {
|
||||
await fuelle(fx, el)
|
||||
await fx.klick(fx.finde(el, 'button[type="submit"]'))
|
||||
assert.match(fx.text(el), muster, code)
|
||||
assert.equal(fx.fetchLog.length, neuLaden ? 3 : 2, code)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
test('409 bei /zahlung: status_ungueltig laedt neu mit Hinweis; online_aus zeigt Text', async () => {
|
||||
const d = { ...DATEN, status: 'gebucht', zahlart: 'online', bezahlt: false, rechnungNummer: 'R-1' }
|
||||
await mitFx([{ status: 200, body: d }, { status: 409, body: { fehler: 'status_ungueltig' } }, { status: 200, body: { ...d, bezahlt: true } }], undefined, async (fx, el) => {
|
||||
await fx.klick(fx.finde(el, '[data-tag="online-zahlen"]'))
|
||||
assert.match(fx.text(el), /Der Stand Ihrer Buchung hat sich geändert\./)
|
||||
assert.match(fx.text(el), /Bezahlt – vielen Dank\./)
|
||||
})
|
||||
await mitFx([{ status: 200, body: d }, { status: 409, body: { fehler: 'online_aus' } }], undefined, async (fx, el) => {
|
||||
await fx.klick(fx.finde(el, '[data-tag="online-zahlen"]'))
|
||||
assert.match(fx.text(el), /Online-Zahlung ist gerade nicht möglich/)
|
||||
})
|
||||
})
|
||||
|
||||
test('zahlungUrl nur bei https; Widerrufs-Zitat vollstaendig', async () => {
|
||||
await mitFx([{ status: 200, body: DATEN }, { status: 200, body: { status: 'gebucht', rechnungNummer: 'R-1', zahlungUrl: 'javascript:alert(1)' } }], undefined, async (fx, el) => {
|
||||
assert.match(fx.text(el), /§ 312g Abs\. 2 Satz 1 Nr\. 9 BGB/)
|
||||
await fuelle(fx, el)
|
||||
await fx.klick(fx.finde(el, 'input[value="online"]'))
|
||||
await fx.klick(fx.finde(el, 'button[type="submit"]'))
|
||||
assert.deepEqual(fx.assigned, [])
|
||||
})
|
||||
})
|
||||
|
||||
test('Feldfehler: aria-invalid, aria-describedby, Fokus auf erstes Fehlerfeld; Zahlart als fieldset/legend', async () => {
|
||||
await mitFx([{ status: 200, body: DATEN }], undefined, async (fx, el) => {
|
||||
const gruppe = fx.finde(el, 'fieldset[data-tag="zahlart"]')
|
||||
assert.ok(gruppe)
|
||||
assert.equal(fx.text(fx.finde(gruppe, 'legend')), 'Zahlart')
|
||||
await fx.klick(fx.finde(el, 'button[type="submit"]'))
|
||||
const strasse = fx.finde(el, '[name="strasse"]')
|
||||
assert.equal(strasse.getAttribute('aria-invalid'), 'true')
|
||||
assert.equal(strasse.getAttribute('aria-describedby'), 'uk-fehler-strasse')
|
||||
assert.equal(fx.document.activeElement, strasse)
|
||||
await fx.setze(strasse, 'Weg 1')
|
||||
assert.equal(strasse.getAttribute('aria-invalid'), null)
|
||||
})
|
||||
})
|
||||
@@ -90,3 +90,21 @@ test('Monatsnavigation: erster Monat hat aktiven Weiter-Knopf, Fokus bleibt, Tab
|
||||
assert.doesNotMatch(tag(fx, el, '2026-11-22').getAttribute('aria-label'), /Abreisetag möglich/)
|
||||
} finally { fx.restore() }
|
||||
})
|
||||
|
||||
test('Serverfehler: felder.zeitraum, neu_laden und mindestnaechte erscheinen mit eigenem Text', async () => {
|
||||
const faelle = [
|
||||
[{ status: 400, body: { fehler: 'ungueltig', felder: { zeitraum: 'Ungültiger Zeitraum.' } } }, /Ungültiger Zeitraum\./],
|
||||
[{ status: 400, body: { fehler: 'neu_laden' } }, /zu lange geöffnet/],
|
||||
[{ status: 400, body: { fehler: 'mindestnaechte', felder: { mindestNaechte: 3 } } }, /Mindestaufenthalt: 3 Nächte\./],
|
||||
]
|
||||
for (const [antwort, muster] of faelle) {
|
||||
const { fx, el } = start([{ status: 200, body: CFG }, { status: 200, body: BEL }, antwort, { status: 200, body: CFG }])
|
||||
try {
|
||||
await fx.ruhe(); await fx.ruhe()
|
||||
await fx.setze(fx.finde(el, '[name="name"]'), 'Max'); await fx.setze(fx.finde(el, '[name="email"]'), 'max@example.com')
|
||||
await fx.klick(tag(fx, el, '2026-11-10')); await fx.klick(tag(fx, el, '2026-11-14'))
|
||||
await fx.klick(fx.finde(el, 'button[type="submit"]'))
|
||||
assert.match(fx.text(el), muster)
|
||||
} finally { fx.restore() }
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user