111 lines
6.4 KiB
JavaScript
111 lines
6.4 KiB
JavaScript
import { test } from 'node:test'
|
||
import assert from 'node:assert/strict'
|
||
import { readFileSync } from 'node:fs'
|
||
import vm from 'node:vm'
|
||
import { installFakeDom } from './support/fakeDom.js'
|
||
|
||
// Ablauf des Widgets public/embed/unterkunft.js gegen den Vertrag docs/funktionen/unterkunft-api.md (Fake-fetch, Minimal-DOM).
|
||
const QUELLE = readFileSync(new URL('../public/embed/unterkunft.js', import.meta.url), 'utf8')
|
||
const CFG = { aktiv: true, anbieter: { name: 'Haus Test', ort: 'X', datenschutzUrl: 'https://kunde.example/datenschutz' }, akzentfarbe: '#0f766e', formToken: 'tok1', unterkuenfte: [{ id: 'u1', name: 'Ferienwohnung', maxPersonen: 3, mindestNaechte: 2, checkIn: '15:00', checkOut: '11:00' }] }
|
||
const BEL = { belegt: [{ von: '2026-11-20', bis: '2026-11-25' }], heute: '2026-11-01', bisMax: '2028-04-30' }
|
||
|
||
function start(antworten) {
|
||
const fx = installFakeDom({ antworten })
|
||
fx.document.readyState = 'complete'
|
||
fx.document.currentScript = { dataset: {}, src: 'https://project.webklar.com/embed/unterkunft.js' }
|
||
const el = fx.document.createElement('div')
|
||
el.setAttribute('data-webklar-unterkunft', 'abcdefghijklmnop')
|
||
fx.document.body.appendChild(el)
|
||
vm.runInThisContext(QUELLE, { filename: 'unterkunft.js' })
|
||
return { fx, el }
|
||
}
|
||
const tag = (fx, el, t) => fx.finde(el, `[data-tag="${t}"]`)
|
||
|
||
test('Ladefehler und aktiv:false zeigen nur den Ausweichsatz', async () => {
|
||
for (const antwort of [{ status: 200, body: { aktiv: false } }, { status: 500, body: {} }]) {
|
||
const { fx, el } = start([antwort])
|
||
try {
|
||
await fx.ruhe()
|
||
assert.equal(fx.text(el), 'Anfragen gerne per Telefon oder E-Mail.')
|
||
} finally { fx.restore() }
|
||
}
|
||
})
|
||
|
||
test('Kalender, Auswahl, Mindestnaechte, Absenden mit Vertragsfeldern, Danke-Text', async () => {
|
||
const { fx, el } = start([{ status: 200, body: CFG }, { status: 200, body: BEL }, { status: 201, body: { nummer: 'A-1234' } }, { status: 200, body: BEL }])
|
||
try {
|
||
await fx.ruhe(); await fx.ruhe()
|
||
assert.match(fx.fetchLog[0].url, /^https:\/\/project\.webklar\.com\/api\/public\/unterkunft\/v1\/abcdefghijklmnop$/)
|
||
assert.match(fx.fetchLog[1].url, /\/belegung\?unterkunft=u1&von=\d{4}-\d{2}-\d{2}&bis=\d{4}-\d{2}-\d{2}$/)
|
||
assert.equal(tag(fx, el, '2026-11-10').getAttribute('aria-label'), 'Dienstag, 10. November 2026, frei')
|
||
assert.equal(tag(fx, el, '2026-11-21').getAttribute('aria-label').includes('belegt'), true)
|
||
assert.ok(fx.finde(el, '[name="kontakt_url"]'), 'Honeypot')
|
||
// ohne Zeitraum
|
||
await fx.setze(fx.finde(el, '[name="name"]'), 'Max'); await fx.setze(fx.finde(el, '[name="email"]'), 'max@example.com')
|
||
await fx.klick(fx.finde(el, 'button[type="submit"]'))
|
||
assert.match(fx.text(el), /Bitte wählen Sie Anreise und Abreise im Kalender\./)
|
||
assert.equal(fx.fetchLog.length, 2)
|
||
// Mindestnaechte (2): 1 Nacht -> Hinweis
|
||
await fx.klick(tag(fx, el, '2026-11-10')); await fx.klick(tag(fx, el, '2026-11-11'))
|
||
assert.match(fx.text(el), /Mindestaufenthalt: 2 Nächte/)
|
||
await fx.klick(tag(fx, el, '2026-11-14'))
|
||
assert.match(fx.text(el), /10\.–14\. Nov\. · 4 Nächte/)
|
||
await fx.klick(fx.finde(el, 'button[type="submit"]'))
|
||
const post = fx.fetchLog[2]
|
||
assert.equal(post.method, 'POST')
|
||
assert.match(post.url, /\/anfrage$/)
|
||
assert.deepEqual(post.body, { unterkunft: 'u1', von: '2026-11-10', bis: '2026-11-14', personen: 1, name: 'Max', firma: '', email: 'max@example.com', telefon: '', nachricht: '', formToken: 'tok1', kontakt_url: '' })
|
||
assert.match(fx.text(el), /Danke! Ihre Anfrage A-1234 ist eingegangen\. Sie erhalten gleich eine Bestätigung per E-Mail\./)
|
||
} finally { fx.restore() }
|
||
})
|
||
|
||
test('Pfeiltasten bewegen den Fokus', async () => {
|
||
const { fx, el } = start([{ status: 200, body: CFG }, { status: 200, body: BEL }])
|
||
try {
|
||
await fx.ruhe(); await fx.ruhe()
|
||
await fx.klick(tag(fx, el, '2026-11-10'))
|
||
await fx.sende(tag(fx, el, '2026-11-10'), 'keydown', { key: 'ArrowRight' })
|
||
assert.equal(fx.document.activeElement.getAttribute('data-tag'), '2026-11-11')
|
||
await fx.sende(fx.document.activeElement, 'keydown', { key: 'ArrowDown' })
|
||
assert.equal(fx.document.activeElement.getAttribute('data-tag'), '2026-11-18')
|
||
} finally { fx.restore() }
|
||
})
|
||
|
||
test('Monatsnavigation: erster Monat hat aktiven Weiter-Knopf, Fokus bleibt, Tab-Stop im gezeigten Monat, Abreisetag-Label', async () => {
|
||
const { fx, el } = start([{ status: 200, body: CFG }, { status: 200, body: BEL }])
|
||
try {
|
||
await fx.ruhe(); await fx.ruhe()
|
||
const erster = fx.finde(el, '.wku-monat')
|
||
const weiter = fx.finde(erster, '[data-nav="vor1"]')
|
||
assert.ok(weiter && !weiter.hasAttribute('disabled'))
|
||
await fx.klick(weiter)
|
||
assert.match(fx.text(el), /Dezember 2026/)
|
||
assert.equal(fx.document.activeElement.getAttribute('data-nav'), 'vor1')
|
||
assert.equal(fx.finde(el, '[data-tag][tabindex="0"]').getAttribute('data-tag'), '2026-12-01')
|
||
await fx.klick(fx.finde(el, '[data-nav="zur"]'))
|
||
assert.equal(fx.document.activeElement.getAttribute('data-nav'), 'vor1') // Zurueck ist im ersten Monat gesperrt, Fokus wechselt zum Weiter-Knopf
|
||
assert.equal(fx.finde(el, '[data-tag][tabindex="0"]').getAttribute('data-tag'), '2026-11-01')
|
||
await fx.klick(tag(fx, el, '2026-11-18'))
|
||
assert.match(tag(fx, el, '2026-11-20').getAttribute('aria-label'), /belegt – als Abreisetag möglich/)
|
||
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() }
|
||
}
|
||
})
|