import { test } from 'node:test'
import assert from 'node:assert/strict'
import { installFakeDom } from './support/fakeDom.js'
import { h, request } from '../public/shop-dom.js'
import { REITER } from '../public/buchungen.js'
import { render, zeitraumText, naechteText } from '../public/buchungen-tab-anfragen.js'
import { summeLive } from '../public/buchungen-posten.js'
// Task 12: Gerüst und Reiter „Anfragen“ gegen eine Anfrage-Attrappe nach docs/funktionen/unterkunft-api.md.
const POSTEN = [
{ text: 'Übernachtung', menge: 7, einzelCent: 5500, ustProzent: 7 },
{ text: 'Reinigung', menge: 1, einzelCent: 6000, ustProzent: 7 },
{ text: 'Direktbuchungsrabatt 10 %', menge: 1, einzelCent: -3850, ustProzent: 7 },
]
const zeile = (nr, extra = {}) => ({ id: `a${nr}`, nummer: `A-${nr}`, von: '2026-11-12', bis: '2026-11-30', gast: { name: `Gast ${nr}` }, status: 'neu', summen: { bruttoCent: 40650 }, ...extra })
const anfrage = (extra = {}) => ({
id: 'a1', nummer: 'A-1', von: '2026-11-12', bis: '2026-11-19', personen: 2, status: 'neu', steuerart: 'regelbesteuert', unterkunftName: 'Haus am See',
gast: { name: 'Anna Muster', email: 'anna@example.com', telefon: '0151', nachricht: 'Hund dabei' }, ...extra,
})
const vorschlag = { naechte: 7, posten: POSTEN, summen: { bruttoCent: 40650 } }
const mit = (route, fn) => async () => {
const fx = installFakeDom({ antworten: Array.from({ length: 40 }, () => route) })
try { await fn(fx) } finally { fx.restore() }
}
function baueCtx() {
const ctx = { projectId: 'p1', status: { state: 'active' }, nurLesen: false, meldungen: [], aktuell: () => true, request, zeigeMeldung: (t, a) => ctx.meldungen.push({ text: t, art: a }), neuLaden: async () => ctx.status }
return ctx
}
async function zeige(fx) {
const ctx = baueCtx()
const panel = h('div')
fx.document.body.append(panel)
await render(panel, ctx)
await fx.ruhe()
return { panel, ctx }
}
const server = (o = {}) => (a) => {
const key = `${a.method} ${a.url.split('?')[0]}`
if (o[key]) return o[key](a)
if (a.method === 'GET' && a.url.startsWith('/api/unterkunft/anfragen?')) return { body: { anfragen: [zeile(1), zeile(2, { status: 'gebucht' })] } }
if (a.method === 'GET' && a.url.startsWith('/api/unterkunft/anfragen/')) return { body: { anfrage: anfrage(), vorschlag } }
return { body: {} }
}
const posts = (fx) => fx.fetchLog.filter((a) => a.method !== 'GET')
const aktion = (fx, el, n) => fx.finde(el, `[data-aktion="${n}"]`)
const oeffne = async (fx, panel, id = 'a1') => { await fx.klick(fx.finde(panel, `[data-anfrage="${id}"]`)); await fx.ruhe() }
test('REITER: fünf Reiter mit den Labels', () => {
assert.deepEqual(REITER.map((r) => r.key), ['anfragen', 'kalender', 'unterkuenfte', 'rechnungen', 'einstellungen'])
assert.deepEqual(REITER.map((r) => r.label), ['Anfragen', 'Kalender', 'Unterkünfte & Preise', 'Rechnungen', 'Einstellungen'])
})
test('summeLive spiegelt summiere: 40650 / 37991 / 2659', () => {
const s = summeLive(POSTEN, 'regelbesteuert')
assert.equal(s.bruttoCent, 40650)
assert.equal(s.nettoCent, 37991)
assert.equal(s.ust[0].steuerCent, 2659)
const k = summeLive(POSTEN, 'kleinunternehmer')
assert.deepEqual(k, { bruttoCent: 40650, nettoCent: 40650, ust: [] })
})
test('Zeitraum und Nächte', () => {
assert.equal(zeitraumText('2026-11-12', '2026-11-30'), '12.–30.11.2026')
assert.equal(zeitraumText('2026-10-28', '2026-11-03'), '28.10.–03.11.2026')
assert.equal(zeitraumText('2026-12-28', '2027-01-03'), '28.12.2026–03.01.2027')
assert.equal(naechteText('2026-11-12', '2026-11-30'), '18 Nächte')
assert.equal(naechteText('2026-11-12', '2026-11-13'), '1 Nacht')
})
test('Liste: Nummer, Zeitraum, Gast, Status-Pille, Betrag; Filter offen als Standard', mit(server(), async (fx) => {
const { panel } = await zeige(fx)
assert.equal(fx.fetchLog[0].url, '/api/unterkunft/anfragen?projectId=p1&filter=offen')
const z = fx.alle(panel, '[data-anfrage]')
assert.equal(z.length, 2)
const t = fx.text(z[0])
for (const re of [/A-1/, /12\.–30\.11\.2026 · 18 Nächte/, /Gast 1/, /Neu/, /406,50\s€/]) assert.match(t, re)
assert.match(fx.text(z[1]), /Gebucht/)
await fx.setze(fx.finde(panel, '#bk-a-filter'), 'alle')
await fx.ruhe()
assert.equal(fx.fetchLog.at(-1).url, '/api/unterkunft/anfragen?projectId=p1&filter=alle')
}))
test('Detail: Vorschlag editierbar, Summe aktualisiert sich, Freigeben sendet posten/fristTage/nachricht und zeigt den Link', mit(server({
'POST /api/unterkunft/anfragen/a1/freigeben': () => ({ body: { buchungsLink: 'https://x.test/buchen/a1?t=abc' } }),
}), async (fx) => {
const { panel, ctx } = await zeige(fx)
await oeffne(fx, panel)
assert.match(fx.text(panel), /Anna Muster<\/b>/)
assert.equal(fx.finde(panel, 'b'), null, 'Fremdtext bleibt Text')
assert.match(fx.text(fx.finde(panel, '[data-summen]')), /Gesamt 406,50\s€/)
const zeilen = fx.alle(panel, '[data-zeile]')
assert.equal(zeilen.length, 3)
const einzel = fx.finde(zeilen[1], '[data-posten="einzel"]')
await fx.setze(einzel, '70,00')
assert.match(fx.text(fx.finde(panel, '[data-summen]')), /Gesamt 416,50\s€/)
await fx.setze(einzel, '60,00')
await fx.setze(fx.finde(panel, '#bk-a-frist'), '5')
await fx.setze(fx.finde(panel, '#bk-a-nachricht'), 'Gern!')
await fx.klick(aktion(fx, panel, 'freigeben'))
await fx.ruhe()
const [p] = posts(fx)
assert.equal(p.url, '/api/unterkunft/anfragen/a1/freigeben?projectId=p1')
assert.deepEqual(p.body, { posten: POSTEN, fristTage: 5, nachricht: 'Gern!' })
assert.equal(fx.finde(panel, '[data-link]').getAttribute('value'), 'https://x.test/buchen/a1?t=abc')
assert.ok(aktion(fx, panel, 'link-kopieren'))
assert.equal(ctx.meldungen.at(-1).art, 'ok')
}))
test('Freigeben: 409 zeigt „Der Zeitraum ist inzwischen belegt.“', mit(server({
'POST /api/unterkunft/anfragen/a1/freigeben': () => ({ status: 409, body: { fehler: 'zeitraum_belegt' } }),
}), async (fx) => {
const { panel } = await zeige(fx)
await oeffne(fx, panel)
await fx.klick(aktion(fx, panel, 'freigeben'))
await fx.ruhe()
assert.match(fx.text(panel), /Der Zeitraum ist inzwischen belegt\./)
assert.equal(aktion(fx, panel, 'freigeben').disabled, false)
}))
test('Ablehnen sendet den Text', mit(server({ 'POST /api/unterkunft/anfragen/a1/ablehnen': () => ({ body: {} }) }), async (fx) => {
const { panel } = await zeige(fx)
await oeffne(fx, panel)
await fx.klick(aktion(fx, panel, 'ablehnen'))
await fx.setze(fx.finde(panel, '#bk-a-ablehn'), 'Leider belegt')
await fx.klick(aktion(fx, panel, 'ablehnen-senden'))
await fx.ruhe()
assert.deepEqual(posts(fx)[0].body, { text: 'Leider belegt' })
}))
const gebucht = (extra = {}) => ({ anfrage: anfrage({ status: 'gebucht', posten: POSTEN, summen: { bruttoCent: 40650 }, rechnungId: 'r1', rechnungNummer: 'R-2026-001', ...extra }) })
test('Gebucht: Rechnung-Link, „Als bezahlt (vor Ort)“, Storno ohne Erstattungsfeld bei Vor-Ort-Zahlung', mit(server({
'GET /api/unterkunft/anfragen/a1': () => ({ body: gebucht({ zahlart: 'vorort' }) }),
'POST /api/unterkunft/anfragen/a1/bezahlt': () => ({ body: {} }),
'POST /api/unterkunft/anfragen/a1/stornieren': () => ({ body: {} }),
}), async (fx) => {
const { panel } = await zeige(fx)
await oeffne(fx, panel)
assert.equal(aktion(fx, panel, 'rechnung').getAttribute('href'), '/api/unterkunft/rechnungen/r1.pdf?projectId=p1')
await fx.klick(aktion(fx, panel, 'bezahlt'))
await fx.ruhe()
assert.equal(posts(fx)[0].url, '/api/unterkunft/anfragen/a1/bezahlt?projectId=p1')
await fx.klick(aktion(fx, panel, 'stornieren'))
assert.equal(fx.finde(panel, '#bk-a-erstattung'), null)
await fx.setze(fx.finde(panel, '#bk-a-grund'), 'Gast krank')
await fx.klick(aktion(fx, panel, 'storno-senden'))
await fx.ruhe()
assert.deepEqual(posts(fx).at(-1).body, { grund: 'Gast krank', erstattungCent: 0 })
}))
test('Storno bei Online-Zahlung: Erstattungsbetrag wird gesendet', mit(server({
'GET /api/unterkunft/anfragen/a1': () => ({ body: gebucht({ status: 'bezahlt', zahlart: 'online', bezahltVia: 'online' }) }),
'POST /api/unterkunft/anfragen/a1/stornieren': () => ({ body: {} }),
}), async (fx) => {
const { panel } = await zeige(fx)
await oeffne(fx, panel)
assert.equal(aktion(fx, panel, 'bezahlt'), null)
await fx.klick(aktion(fx, panel, 'stornieren'))
assert.equal(fx.finde(panel, '#bk-a-erstattung').value, '406,50')
await fx.setze(fx.finde(panel, '#bk-a-erstattung'), '200,00')
await fx.klick(aktion(fx, panel, 'storno-senden'))
await fx.ruhe()
assert.equal(posts(fx)[0].body.erstattungCent, 20000)
}))
// Task 13: Reiter „Kalender“ und „Unterkünfte & Preise“ (Antwortformen: ui-erwartungen.md)
const UK = { id: 'u1', name: 'Haus am See', beschreibung: 'Schön', maxPersonen: 4, mindestNaechte: 2, checkIn: '15:00', checkOut: '10:00', sort: 0, aktiv: true, icalExportUrl: 'https://x.test/unterkunft/ical/tok.ics',
icalImporte: [{ id: 'i1', name: 'Booking.com', url: 'https://b.test/x.ics', letzterErfolgAt: '2026-10-07T10:00:00.000Z', fehler: '' }, { id: 'i2', name: 'Airbnb', url: 'https://a.test/x.ics', fehler: 'Zeitüberschreitung' }] }
const PREISE = { staffeln: [{ abNaechten: 1, preisProNachtCent: 5500 }, { abNaechten: 7, preisProNachtCent: 5000 }], reinigungCent: 6000, personenInklusive: 2, aufpreisPersonNachtCent: 1000, direktrabattProzent: 10, steuerart: 'regel', ustUebernachtung: 7, ustReinigung: 7, fristTage: 3, stornobedingungen: 'Kostenlos bis 14 Tage vorher.' }
const BEREICHE = [
{ von: '2026-11-03', bis: '2026-11-06', quelle: 'gebucht', anfrageNummer: 'A-1' }, { von: '2026-11-10', bis: '2026-11-12', quelle: 'reserviert' },
{ von: '2026-11-15', bis: '2026-11-17', quelle: 'ical:i1' }, { von: '2026-11-20', bis: '2026-11-22', quelle: 'manuell', sperreId: 's1', notiz: 'Eigenbedarf' },
]
const ukServer = (o = {}) => (a) => {
const key = `${a.method} ${a.url.split('?')[0]}`
if (o[key]) return o[key](a)
if (key === 'GET /api/unterkunft/unterkuenfte') return { body: { unterkuenfte: [UK] } }
if (key === 'GET /api/unterkunft/unterkuenfte/u1/kalender') return { body: { bereiche: BEREICHE } }
if (key === 'GET /api/unterkunft/unterkuenfte/u1/preise') return { body: { preise: PREISE } }
if (key === 'POST /api/unterkunft/preisvorschau') return { body: { naechte: 7, posten: [], summen: { bruttoCent: 40650 } } }
return { body: {} }
}
const zeigeTab = async (fx, modul) => {
const ctx = baueCtx()
const panel = h('div')
fx.document.body.append(panel)
await modul.render(panel, ctx)
await fx.ruhe()
return { panel, ctx }
}
const kal = await import('../public/buchungen-tab-kalender.js')
const ukTab = await import('../public/buchungen-tab-unterkuenfte.js')
const tag = (fx, panel, t) => fx.finde(panel, `[data-tag="${t}"]`)
const gehZuNovember = async (fx, panel) => { while (!/November 2026/.test(fx.text(fx.finde(panel, '[data-monat]')))) { await fx.klick(aktion(fx, panel, 'weiter')); await fx.ruhe() } }
test('rasterTage: November 2026 beginnt am Montag 26.10. und endet am Sonntag 29.11.... ', () => {
const t = kal.rasterTage(2026, 10)
assert.equal(t[0], '2026-10-26')
assert.equal(t.length % 7, 0)
assert.equal(t.at(-1), '2026-12-06')
assert.equal(kal.rasterTage(2026, 5)[0], '2026-06-01') // Juni 2026 beginnt am Montag
})
test('Kalender: Raster Montag zuerst, Farben nach Quelle, Legende', mit(ukServer(), async (fx) => {
const { panel } = await zeigeTab(fx, kal)
await gehZuNovember(fx, panel)
assert.equal(fx.text(fx.alle(panel, '[role="columnheader"]')[0]), 'Mo')
assert.equal(fx.alle(panel, '[data-tag]')[0].dataset.tag, '2026-10-26')
assert.ok(tag(fx, panel, '2026-11-03').classList.contains('ist-gebucht'))
assert.ok(!tag(fx, panel, '2026-11-06').classList.contains('ist-gebucht'), 'Abreisetag ist frei')
assert.ok(tag(fx, panel, '2026-11-10').classList.contains('ist-reserviert'))
assert.ok(tag(fx, panel, '2026-11-16').classList.contains('ist-ical'))
assert.ok(tag(fx, panel, '2026-11-20').classList.contains('ist-manuell'))
assert.ok(tag(fx, panel, '2026-11-08').classList.contains('ist-frei'))
for (const t of ['Direkt gebucht', 'Reserviert', 'Booking.com / Airbnb', 'Von dir gesperrt']) assert.match(fx.text(panel), new RegExp(t))
assert.ok(fx.fetchLog.some((a) => /kalender\?projectId=p1&von=2026-10-26&bis=2026-12-07$/.test(a.url)))
}))
test('Kalender: zwei Klicks auf freie Tage → Notiz → POST sperren', mit(ukServer({ 'POST /api/unterkunft/unterkuenfte/u1/sperren': () => ({ status: 201, body: {} }) }), async (fx) => {
const { panel } = await zeigeTab(fx, kal)
await gehZuNovember(fx, panel)
await fx.klick(tag(fx, panel, '2026-11-25'))
assert.equal(posts(fx).length, 0)
await fx.klick(tag(fx, panel, '2026-11-27'))
assert.match(fx.text(panel), /Zeitraum sperren/)
await fx.setze(fx.finde(panel, '#bk-k-notiz'), 'Familie')
await fx.klick(aktion(fx, panel, 'sperren-senden'))
await fx.ruhe()
const [p] = posts(fx)
assert.equal(p.url, '/api/unterkunft/unterkuenfte/u1/sperren?projectId=p1')
assert.deepEqual(p.body, { von: '2026-11-25', bis: '2026-11-28', notiz: 'Familie' })
}))
test('Kalender: Zeitraum über belegte Tage wird nicht angeboten; 409 vom Server wird gezeigt', mit(ukServer({ 'POST /api/unterkunft/unterkuenfte/u1/sperren': () => ({ status: 409, body: { fehler: 'zeitraum_belegt' } }) }), async (fx) => {
const { panel } = await zeigeTab(fx, kal)
await gehZuNovember(fx, panel)
await fx.klick(tag(fx, panel, '2026-11-08'))
await fx.klick(tag(fx, panel, '2026-11-13'))
assert.equal(aktion(fx, panel, 'sperren-senden'), null)
assert.match(fx.text(fx.finde(panel, '[data-hinweis]')), /belegte Tage/)
await fx.klick(tag(fx, panel, '2026-11-24'))
await fx.klick(tag(fx, panel, '2026-11-24'))
await fx.klick(aktion(fx, panel, 'sperren-senden'))
await fx.ruhe()
assert.match(fx.text(panel), /Der Zeitraum ist bereits belegt\./)
}))
test('Kalender: Klick auf manuelle Sperre bietet „Sperre aufheben“ (DELETE); andere Quellen nicht', mit(ukServer({ 'DELETE /api/unterkunft/sperren/s1': () => ({ body: {} }) }), async (fx) => {
const { panel } = await zeigeTab(fx, kal)
await gehZuNovember(fx, panel)
await fx.klick(tag(fx, panel, '2026-11-04'))
assert.equal(aktion(fx, panel, 'aufheben'), null)
await fx.klick(tag(fx, panel, '2026-11-21'))
assert.match(fx.text(panel), /Eigenbedarf/)
await fx.klick(aktion(fx, panel, 'aufheben'))
await fx.ruhe()
const [p] = posts(fx)
assert.equal(p.method, 'DELETE')
assert.equal(p.url, '/api/unterkunft/sperren/s1?projectId=p1')
}))
test('Unterkünfte: Liste mit Fremdtext als Text; Formular hat alle Felder; Speichern sendet PUT', mit(ukServer({ 'PUT /api/unterkunft/unterkuenfte/u1': () => ({ body: { unterkunft: UK } }) }), async (fx) => {
const { panel } = await zeigeTab(fx, ukTab)
assert.match(fx.text(panel), /Haus am<\/i> See/)
assert.equal(fx.finde(panel, 'i'), null)
await fx.klick(aktion(fx, panel, 'bearbeiten'))
await fx.ruhe()
for (const f of ['name', 'beschreibung', 'maxPersonen', 'mindestNaechte', 'checkIn', 'checkOut', 'aktiv']) assert.ok(fx.finde(panel, `[data-feld="${f}"]`), f)
await fx.setze(fx.finde(panel, '#bk-u-max'), '6')
await fx.klick(fx.finde(panel, '.bk-unterkuenfte form [data-aktion="speichern"]'))
await fx.ruhe()
const put = posts(fx).find((a) => a.method === 'PUT')
assert.equal(put.url, '/api/unterkunft/unterkuenfte/u1?projectId=p1')
assert.deepEqual(put.body, { name: UK.name, beschreibung: 'Schön', maxPersonen: 6, mindestNaechte: 2, checkIn: '15:00', checkOut: '10:00', sort: 0, aktiv: true })
}))
test('Unterkünfte: Feldfehler (400 mit felder) kommen am Feld an', mit(ukServer({ 'PUT /api/unterkunft/unterkuenfte/u1': () => ({ status: 400, body: { fehler: 'ungueltig', felder: { name: 'Name ist schon vergeben.' } } }) }), async (fx) => {
const { panel } = await zeigeTab(fx, ukTab)
await fx.klick(aktion(fx, panel, 'bearbeiten'))
await fx.ruhe()
await fx.klick(fx.finde(panel, '.bk-unterkuenfte form [data-aktion="speichern"]'))
await fx.ruhe()
assert.equal(fx.text(fx.finde(panel, '[data-fehler="name"]')), 'Name ist schon vergeben.')
}))
test('Unterkünfte: Neue Unterkunft prüft Felder lokal und sendet POST', mit(ukServer({ 'POST /api/unterkunft/unterkuenfte': () => ({ status: 201, body: { unterkunft: { ...UK, id: 'u2', name: 'Neu' } } }) }), async (fx) => {
const { panel } = await zeigeTab(fx, ukTab)
await fx.klick(aktion(fx, panel, 'neu'))
await fx.klick(aktion(fx, panel, 'speichern'))
assert.match(fx.text(fx.finde(panel, '[data-fehler="name"]')), /Namen/)
assert.equal(posts(fx).length, 0)
await fx.setze(fx.finde(panel, '#bk-u-name'), 'Neu')
await fx.klick(aktion(fx, panel, 'speichern'))
await fx.ruhe()
const [p] = posts(fx)
assert.equal(p.method, 'POST')
assert.equal(p.body.name, 'Neu')
assert.equal(p.body.sort, 1)
}))
test('Preis-Editor: Staffelzeilen +/−, Beispielrechnung über /preisvorschau, Speichern sendet Cent', mit(ukServer({ 'PUT /api/unterkunft/unterkuenfte/u1/preise': () => ({ body: {} }) }), async (fx) => {
const { panel } = await zeigeTab(fx, ukTab)
await fx.klick(aktion(fx, panel, 'bearbeiten'))
await fx.ruhe()
assert.match(fx.text(fx.finde(panel, '[data-vorschau]')), /7 Nächte, 2 Personen = 406,50\s€/)
const vor = fx.fetchLog.find((a) => a.url.startsWith('/api/unterkunft/preisvorschau'))
assert.equal(vor.method, 'POST')
assert.equal(vor.body.unterkunftId, 'u1')
assert.equal(vor.body.personen, 2)
assert.equal(fx.alle(panel, '[data-staffel-zeile]').length, 2)
await fx.klick(aktion(fx, panel, 'staffel-dazu'))
assert.equal(fx.alle(panel, '[data-staffel-zeile]').length, 3)
const neu = fx.alle(panel, '[data-staffel-zeile]')[2]
await fx.setze(fx.finde(neu, '[data-staffel="ab"]'), '14')
await fx.setze(fx.finde(neu, '[data-staffel="preis"]'), '45')
await fx.klick(fx.finde(fx.alle(panel, '[data-staffel-zeile]')[1], '[data-aktion="staffel-weg"]'))
assert.equal(fx.alle(panel, '[data-staffel-zeile]').length, 2)
await fx.klick(aktion(fx, panel, 'preise-speichern'))
await fx.ruhe()
const put = posts(fx).find((a) => a.url.includes('/preise'))
assert.equal(put.method, 'PUT')
assert.deepEqual(put.body.staffeln, [{ abNaechten: 1, preisProNachtCent: 5500 }, { abNaechten: 14, preisProNachtCent: 4500 }])
assert.equal(put.body.reinigungCent, 6000)
assert.equal(put.body.aufpreisPersonNachtCent, 1000)
assert.equal(put.body.steuerart, 'regel')
assert.equal(put.body.direktrabattProzent, 10)
assert.equal(put.body.stornobedingungen, 'Kostenlos bis 14 Tage vorher.')
}))
test('iCal: Status je Import, Fehlertext, Export-Link, Anleitungen', mit(ukServer(), async (fx) => {
const { panel } = await zeigeTab(fx, ukTab)
await fx.klick(aktion(fx, panel, 'bearbeiten'))
await fx.ruhe()
assert.match(fx.text(fx.finde(panel, '[data-import="i1"]')), /Zuletzt abgeglichen 07\.10\.2026/)
assert.match(fx.text(fx.finde(panel, '[data-import="i2"]')), /nicht geklappt: Zeitüberschreitung/)
assert.equal(fx.finde(panel, '[data-export]').getAttribute('value'), UK.icalExportUrl)
assert.ok(aktion(fx, panel, 'export-kopieren'))
assert.match(fx.text(fx.finde(panel, '[data-anleitung="booking"]')), /Unterkunft → Tarife & Verfügbarkeit → Kalender synchronisieren → Kalender importieren → Link einfügen/)
assert.ok(fx.finde(panel, '[data-anleitung="airbnb"]'))
}))
test('iCal: Import hinzufügen (POST), entfernen mit Rückfrage, Link erneuern mit Bestätigung', mit(ukServer({
'POST /api/unterkunft/unterkuenfte/u1/ical-importe': () => ({ status: 201, body: {} }),
'DELETE /api/unterkunft/unterkuenfte/u1/ical-importe/i1': () => ({ body: {} }),
'POST /api/unterkunft/unterkuenfte/u1/ical-export/erneuern': () => ({ body: {} }),
}), async (fx) => {
const { panel } = await zeigeTab(fx, ukTab)
await fx.klick(aktion(fx, panel, 'bearbeiten'))
await fx.ruhe()
await fx.setze(fx.finde(panel, '#bk-i-name'), 'Vrbo')
await fx.setze(fx.finde(panel, '#bk-i-url'), 'http://unsicher.test/x.ics')
const vorher = posts(fx).length
await fx.klick(aktion(fx, panel, 'import-dazu'))
assert.equal(posts(fx).length, vorher, 'nur https')
await fx.setze(fx.finde(panel, '#bk-i-url'), 'https://vrbo.test/x.ics')
await fx.klick(aktion(fx, panel, 'import-dazu'))
await fx.ruhe()
assert.deepEqual(posts(fx).find((a) => a.url.includes('ical-importe')).body, { name: 'Vrbo', url: 'https://vrbo.test/x.ics' })
await fx.klick(fx.finde(fx.finde(panel, '[data-import="i1"]'), '[data-aktion="import-weg"]'))
assert.equal(posts(fx).filter((a) => a.method === 'DELETE').length, 0)
await fx.klick(aktion(fx, panel, 'import-weg-ok'))
await fx.ruhe()
assert.equal(posts(fx).find((a) => a.method === 'DELETE').url, '/api/unterkunft/unterkuenfte/u1/ical-importe/i1?projectId=p1')
await fx.klick(aktion(fx, panel, 'export-erneuern'))
assert.equal(posts(fx).filter((a) => a.url.includes('erneuern')).length, 0)
await fx.klick(aktion(fx, panel, 'erneuern-ok'))
await fx.ruhe()
assert.equal(posts(fx).filter((a) => a.url.includes('erneuern')).length, 1)
}))