import { test } from 'node:test' import assert from 'node:assert/strict' import { zeigeStunden, vorschussZeile } from '../public/neu/ansicht-stunden.js' import { mit, me, abo, aboAntwort, ledger, neueWurzel } from './support/knDaten.js' const L = ledger({ balanceMinutes: 90, usedMinutes: 120, vorschussMinutes: 150, nextExpiry: { minutes: 60, at: '2027-03-01T00:00:00.000Z' }, credits: [{ grantedAt: '2026-10-01T00:00:00.000Z', expiresAt: '2027-10-01T00:00:00.000Z', minutes: 60, remaining: 60, expired: false }], usage: [{ date: '2026-10-10T12:00:00.000Z', topic: 'Einrichtung Terminplaner', woid: '100150', minutes: 150, overageMinutes: 0, vorschussMinutes: 150, einrichtung: true }, { date: '2026-10-05T12:00:00.000Z', topic: 'Textänderung neu', woid: '100140', minutes: 30, overageMinutes: 30, vorschussMinutes: 0, einrichtung: false }] }) test('vorschussZeile', () => { assert.equal(vorschussZeile(150, false), 'Vorschuss für Einrichtung: 2,5 Std., wird mit den nächsten Monaten verrechnet') assert.equal(vorschussZeile(150, true), 'Vorschuss für Einrichtung: 2,5 Std., verfällt, wird nicht berechnet'); assert.equal(vorschussZeile(0, false), '') }) test('Stunden: Statistik, Vorschuss nie als Mehraufwand, Mehraufwand bleibt Mehraufwand', mit({}, async (fx) => { const w = neueWurzel(fx); await zeigeStunden(w, { route: { bereich: 'vertraege', reiter: 'stunden', query: '' }, me: me(), nav: 'tab', daten: aboAntwort({ subscription: abo(), ledger: L }) }) const t = fx.text(w) for (const s of ['1,5 Std.', '2 Std.', 'verfallen am 01.03.2027', 'Vorschuss für Einrichtung: 2,5 Std., wird mit den nächsten Monaten verrechnet', 'Monatskontingent', '+1 Std.', 'nutzbar bis 01.10.2027', '#100150']) assert.ok(t.includes(s), s) const [vorschuss, mehr] = fx.alle(w, 'tbody tr'); assert.match(vorschuss.textContent, /Vorschuss 2,5 Std\./); assert.doesNotMatch(vorschuss.textContent, /extra/); assert.match(mehr.textContent, /\+0,5 Std\. extra/) assert.equal(fx.finde(w, 'b'), null) })) test('Stunden: gekündigtes Abo verfällt den Vorschuss, Hosting und fehlendes Konto sagen es', mit({}, async (fx) => { const zeige = async (daten) => { const w = neueWurzel(fx); await zeigeStunden(w, { route: { bereich: 'vertraege', query: '' }, me: me(), nav: 'tab', daten }); await fx.ruhe(); return fx.text(w) } assert.match(await zeige(aboAntwort({ subscription: abo({ cancelAtPeriodEnd: true }), ledger: L })), /verfällt, wird nicht berechnet/) assert.match(await zeige(aboAntwort({ subscription: abo({ produkt: 'hosting' }) })), /Ein Stundenkonto gibt es im Wartungs-Abo\./) assert.match(await zeige(aboAntwort({ subscription: abo(), ledger: null })), /Ein Stundenkonto gibt es im Wartungs-Abo\./) })) test('Stunden lädt selbst, wenn keine Daten übergeben werden, und überlebt einen Ausfall', mit({ 'GET /api/abo': () => ({ status: 500, body: { error: 'x' } }) }, async (fx) => { const w = neueWurzel(fx); await zeigeStunden(w, { route: { bereich: 'vertraege', query: '' }, me: me(), nav: 'tab' }); await fx.ruhe(); assert.match(fx.text(w), /Deine Verträge konnten gerade nicht geladen werden\./) })) test('Stunden lädt ohne Daten das Abo; Leerer Verlauf, Satz und Link zu den Bedingungen; Warnungen', mit({ 'GET /api/abo': aboAntwort({ subscription: abo({ paymentProblem: true, termsUrl: '/vertragsunterlagen/v4/abo-bedingungen.html' }), ledger: ledger({ usage: [], credits: [] }) }) }, async (fx) => { const w = neueWurzel(fx); await zeigeStunden(w, { route: { bereich: 'vertraege', query: '' }, me: me(), nav: 'tab' }); await fx.ruhe() assert.match(fx.text(w), /Noch keine Buchungen\./); assert.match(fx.text(w), /1 Stunde pro bezahltem Monat\. Angerechnet wird in 15-Minuten-Schritten, die ältesten Stunden zuerst\./) assert.match(fx.text(w), /Bitte aktualisiere deine Zahlungsart/) const a = fx.finde(w, 'a[href="/vertragsunterlagen/v4/abo-bedingungen.html"]'); assert.equal(a.textContent, 'Leistungen & Bedingungen') }))