Terminplaner: Einbau-Skript – veraltetes Angebot

Fehler an leistung/ressource (beim Absenden und bei Tage/Zeiten) laden zuerst die Konfiguration
neu (ladeNeu): gilt die Auswahl nicht mehr, beginnt die Strecke von vorn mit Hinweis; sonst
steht die Meldung des Servers. Knopfmodus: Aufbaufehler zeigt den Ausweichtext statt "Wird
geladen".

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-03 08:17:44 +00:00
1 parent 4465ac7b94
commit 781eccb2d6
2 files changed
+94 -1

No files matched your search

+24 -1
View File
@@ -1275,6 +1275,12 @@
}, function (e2) { fehlerAllgemein(e2, function () { zeichne(true) }) })
return
}
if (angebotFehler(err)) {
// Leistung oder Person unbekannt: das Angebot hat sich vermutlich geaendert. Erst frisch laden; gilt die Auswahl nicht mehr, beginnt
// ladeNeu() von vorn (mit Hinweis), sonst steht die Meldung des Servers.
ladeNeu().then(function (weiter) { if (weiter) zeigeServerFehler(err.fehler) }, function (e2) { fehlerAllgemein(e2, function () { zeichne(true) }) })
return
}
mitMeldung429(err)
if (err && !err.netz && err.fehler && Object.keys(err.fehler).length) {
zeigeServerFehler(err.fehler)
@@ -1307,11 +1313,24 @@
return (err.fehler && err.fehler._) || T('Zu viele Anfragen. Bitte versuchen Sie es später noch einmal.', 'Zu viele Anfragen. Bitte versuche es später noch einmal.')
}
// 400 des Servers mit Fehler an leistung oder ressource (nicht aus dem Netz): die Auswahl passt nicht mehr zum Angebot
function angebotFehler (err) {
return !!(err && !err.netz && err.fehler && (err.fehler.leistung || err.fehler.ressource))
}
// Fehler eines lesenden Abrufs (Konfiguration, Tage, Zeiten) oder Netzfehler: keine Wiederholung von selbst (429 wuerde sonst
// in einer Schleife weiterzaehlen), nur Meldung und "Erneut versuchen".
function fehlerAllgemein (err, wiederhole) {
if (err && err.inaktiv) { zeigeAusweich(); return }
if (err && err.status === 429) { ausfall(wiederhole, meldung429(err)); return }
if (angebotFehler(err)) {
// Veraltetes Angebot (z. B. Leistung entfernt): frisch laden, ladeNeu() beginnt bei Aenderung von vorn mit Hinweis. Gilt die
// Auswahl noch, steht die Meldung mit "Erneut versuchen" (kein Neuaufbau, der denselben Abruf von selbst wiederholen wuerde).
ladeNeu().then(function (weiter) {
if (weiter) ausfall(wiederhole, Object.keys(err.fehler).map(function (k) { return err.fehler[k] }).join(' '))
}, function (e2) { fehlerAllgemein(e2, wiederhole) })
return
}
ausfall(wiederhole)
}
@@ -1448,7 +1467,11 @@
ui.body.appendChild(l)
l.focus()
lade().then(function () {
starte(true)
try {
starte(true)
} catch (e) {
zeigeAusweich() // Aufbau scheitert: der Ausweichtext statt "Wird geladen …"
}
}, function (err) {
if (err && err.inaktiv) { zeigeAusweich(); return }
ui.body.textContent = ''
+70
View File
@@ -1038,6 +1038,76 @@ async function pruefungen() {
eq(await fokus(), { body: false, label: 'Nächster Monat', imWidget: true }, 'Fokus nach dem gesperrten "Vorheriger Monat" beim anderen Pfeil')
})
// ---------------------------------------------------------------- 14. Fix-Runde 2: veraltetes Angebot, Aufbaufehler im Knopfmodus
// Die Auswahl wird gewaehlt, der Betrieb aendert das Angebot, dann antwortet der Server auf /tage mit 400 (leistung/ressource):
// das Widget muss frisch laden und von vorn beginnen (Hinweis, neue Liste), nicht "Gerade nicht erreichbar" zeigen.
const veraltetVorTage = (wahl) => async (page) => {
await oeffne(page, seitenHtml(eingebettet('data-modus="inline"')))
const wd = w(page)
let freigeben = null
const tor = new Promise((resolve) => { freigeben = resolve })
await page.route(/\/tage\?/, async (route) => { await tor; await route.continue() })
await karte(wd, 'Damenschnitt').click()
await warteTitel(wd, 'Bei wem')
const eingabe = salonEingabe()
if (wahl === 'leistung') {
eingabe.leistungen = eingabe.leistungen.filter((l) => l.id !== S_DAMEN)
await karte(wd, 'Egal').click() // /tage ist unterwegs und wird festgehalten ...
} else {
eingabe.ressourcen = eingabe.ressourcen.map((r) => (r.id === R_ANNA ? { ...r, aktiv: false } : r))
await karte(wd, 'Anna').click()
}
await srv.speichere(eingabe) // ... der Betrieb aendert das Angebot ...
freigeben() // ... und der Abruf erreicht den Server erst jetzt
await wd.locator('.wkt-meldung').waitFor()
eq(await wd.locator('.wkt-meldung').innerText(), 'Das Angebot hat sich geändert. Bitte wählen Sie neu.', 'Hinweis')
eq(await titel(wd).innerText(), 'Welche Leistung möchten Sie buchen?', 'Neubeginn bei der Leistungswahl')
eq(await wd.locator('.wkt-ausfall').count(), 0, 'kein "Gerade nicht erreichbar"')
eq(await karte(wd, 'Herrenschnitt').count(), 1, 'frische Liste')
if (wahl === 'leistung') {
eq(await karte(wd, 'Damenschnitt').count(), 0, 'entfernte Leistung nicht mehr in der Liste')
} else {
// mit Anna ausser Betrieb bleibt nur Ben: kein Personenschritt mehr, die Strecke geht gleich zum Termin
await karte(wd, 'Damenschnitt').click()
await warteTitel(wd, 'Wann passt es Ihnen?')
eq(await wd.locator('.wkt-pfad').innerText(), 'Schritt 2 von 4', 'ohne Personenschritt')
await wd.locator('td.wkt-tag[aria-disabled="false"]').first().waitFor()
}
await shot(page, `angebot-geaendert-${wahl}`)
}
await check('angebot_geaendert_leistung_vor_tage', veraltetVorTage('leistung'))
await check('angebot_geaendert_person_vor_tage', veraltetVorTage('person'))
await check('angebot_geaendert_400_beim_absenden', async (page) => {
await oeffne(page, seitenHtml(eingebettet('data-modus="inline"')))
const wd = await bisZusammenfassung(page, { leistung: 'Damenschnitt', tag: '2026-10-26' })
await wd.locator('input[name=storno]').check()
const eingabe = salonEingabe()
eingabe.leistungen = eingabe.leistungen.filter((l) => l.id !== S_DAMEN)
await srv.speichere(eingabe) // das Token ist noch gueltig: der Server antwortet mit 400 fuer die Leistung, nicht mit neu_laden
await wd.getByRole('button', { name: 'Zahlungspflichtig buchen' }).click()
await wd.locator('.wkt-meldung').waitFor()
eq(await wd.locator('.wkt-meldung').innerText(), 'Das Angebot hat sich geändert. Bitte wählen Sie neu.', 'Hinweis')
eq(await titel(wd).innerText(), 'Welche Leistung möchten Sie buchen?', 'Neubeginn')
eq(await karte(wd, 'Damenschnitt').count(), 0, 'entfernte Leistung nicht mehr in der Liste')
eq(srv.buchungen().length, 0, 'nichts gebucht')
})
await check('knopf_render_fehler_zeigt_ausweichtext', async (page) => {
await page.addInitScript(() => {
const echt = document.createElement.bind(document)
document.createElement = (tag, opt) => { if (String(tag).toLowerCase() === 'ul') throw new Error('Test: Aufbau scheitert'); return echt(tag, opt) }
})
await oeffne(page, seitenHtml(eingebettet('data-modus="knopf" data-knopf="Termin buchen"')))
await page.getByRole('button', { name: 'Termin buchen' }).click()
const dlg = page.locator('dialog.wkt-dialog')
await dlg.locator('#ausweich').waitFor()
assert(await dlg.locator('#ausweich').isVisible(), 'Ausweichtext im Dialog')
assert(!(await dlg.innerText()).includes('Wird geladen'), '"Wird geladen" darf nicht stehen bleiben')
eq(await dlg.locator('.wkt-karte').count(), 0, 'kein halbes Widget')
})
// Zum Schluss: Server gestoppt. Seite und Skript liefert ein eigener kleiner Server, die Schnittstelle ist weg (Verbindung abgelehnt).
await check('server_gestoppt_ausweichtext', async (page) => {
const skript = await readFile(WIDGET_JS, 'utf8')