Terminplaner: Abschluss – Client (Formular-Token, Widerruf prominent, Telefonlink, Einbau-Code, G02)
- R53: Das Widget wartet vor jedem Absenden, bis das Formular-Token formMinSekunden plus 0,5 s alt
ist ("Einen Moment …", Knopf gesperrt); gilt nach dem Neuladen der Konfiguration und beim Wiederholen
nach "neu_laden". Kein stilles Erfolgs-201 ohne Buchung mehr.
- R54: "Vertrag widerrufen" im Fuß ist ein zweiter Knopf (44 px). Im Knopfmodus lädt das Widget die
Konfiguration beim Laden der Seite (ein Abruf) und zeigt bei Widerrufsrecht dauerhaft einen zweiten
Knopf "Vertrag widerrufen" neben dem Buchungsknopf, der den Dialog beim Widerrufsformular öffnet.
- Telefonlink im Widget und auf der Gästeseite: Text mit Buchstaben oder Mehrfachangaben wird nie zum
tel:-Link, "(0)" hinter der Landesvorwahl entfällt.
- Portal, Einstellungen: Kasten "Einbau auf deiner Website" mit Einbau-Code und Kopierknopf; Hinweistexte
zum Widerruf passen zu R55 (auch Anfragen).
- G02: Widerrufssatz nennt, dass der Knopf bis zum Beginn des Termins gilt und danach das Formular auf der
Website oder die E-Mail an den Betrieb.
- Einbau-Anleitung angepasst (siteKey und Code im Portal, Knopfmodus, Checkliste); Tests und Playwright.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bd65c9dc58
commit
794a63c0d7
12 files changed
+536
-64
No files matched your search
@@ -87,6 +87,66 @@ test('Ausweichtext wird nie verbraucht (nur Kopien zurueck auf die Seite), Link
|
||||
assert.match(OHNE_KOMMENTARE, /httpsUrl\(c\.betrieb\.agbUrl\)/)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------- Telefonnummer, Formular-Token, Widerrufsknoepfe (Abschlussrunde)
|
||||
|
||||
/** Die reine Funktion telefonLink des Skripts (zwischen den Marken @telefon-anfang und @telefon-ende) einzeln ausfuehren. */
|
||||
function telefonFunktion() {
|
||||
const m = /\/\/ @telefon-anfang[^\n]*\n([\s\S]*?)\/\/ @telefon-ende/.exec(QUELLE)
|
||||
assert.ok(m, 'Marken @telefon-anfang/@telefon-ende fehlen')
|
||||
return vm.runInNewContext(`${m[1]}; telefonLink`, {})
|
||||
}
|
||||
|
||||
test('telefonLink: Nummern werden gewaehlt, freier Text nie (Buchstaben, Listen), "(0)" hinter der Landesvorwahl entfaellt', () => {
|
||||
const telefonLink = telefonFunktion()
|
||||
const faelle = [
|
||||
['06301 1234', 'tel:063011234'],
|
||||
['+49 6301 1234', 'tel:+4963011234'],
|
||||
['+49 (0) 6301-1234', 'tel:+4963011234'],
|
||||
['+49(0)6301 1234', 'tel:+4963011234'],
|
||||
['+49 (0)6301/12 34 5', 'tel:+49630112345'],
|
||||
['0049 (0) 6301 1234', 'tel:004963011234'],
|
||||
['(06301) 1234', 'tel:063011234'],
|
||||
['06301/1234-0', 'tel:0630112340'],
|
||||
['06301.1234', 'tel:063011234'],
|
||||
[' +49 160 1234567 ', 'tel:+491601234567'],
|
||||
['(0)6301 1234', 'tel:063011234'],
|
||||
// Text mit Buchstaben oder Mehrfachangaben: kein Link
|
||||
['06301 1234 (ab 9 Uhr)', ''],
|
||||
['Tel. 06301 1234', ''],
|
||||
['06301 1234 oder 0171 555', ''],
|
||||
['0171 1, 0171 2', ''],
|
||||
['06301 1234; 0171 2', ''],
|
||||
['ruf an', ''],
|
||||
['+49 (0) 6301 1234 ext. 5', ''],
|
||||
// keine Nummer
|
||||
['', ''], [' ', ''], ['12', ''], ['+', ''], [undefined, ''], [null, ''], [1234567, ''], [{}, ''],
|
||||
]
|
||||
for (const [eingabe, soll] of faelle) assert.equal(telefonLink(eingabe), soll, JSON.stringify(eingabe))
|
||||
})
|
||||
|
||||
test('R53: das Widget wartet vor dem Absenden, bis das Formular-Token formMinSekunden plus Puffer alt ist (Einen Moment …), Messung mit performance.now', () => {
|
||||
for (const noetig of ['formMinSekunden', 'FORM_PUFFER_MS', 'FORM_MIN_VORGABE_S', 'warteAufFormToken', 'Einen Moment …']) {
|
||||
assert.ok(QUELLE.includes(noetig), `${noetig} fehlt im Skript`)
|
||||
}
|
||||
assert.match(OHNE_KOMMENTARE, /FORM_PUFFER_MS\s*=\s*500\b/, 'Puffer 0,5 s')
|
||||
assert.match(OHNE_KOMMENTARE, /FORM_MIN_VORGABE_S\s*=\s*3\b/, 'Vorgabe 3 s wie der Server')
|
||||
// der Abruf der Buchung steht erst nach dem Warten
|
||||
const sende = OHNE_KOMMENTARE.slice(OHNE_KOMMENTARE.indexOf('function sende ('))
|
||||
assert.ok(sende.indexOf('warteAufFormToken(senden)') > 0 && sende.indexOf("request('POST', '/buchungen'") > sende.indexOf('warteAufFormToken(senden)'), 'POST /buchungen nur nach warteAufFormToken')
|
||||
assert.equal([...OHNE_KOMMENTARE.matchAll(/request\('POST', '\/buchungen'/g)].length, 1, 'genau eine Stelle sendet Buchungen')
|
||||
})
|
||||
|
||||
test('R54: Widerruf im Fuss ist ein zweiter Knopf (wkt-btn wkt-btn-sek, 44 px), kein Textlink; im Knopfmodus ein Abruf beim Laden und ein dauerhafter Knopf', () => {
|
||||
assert.ok(OHNE_KOMMENTARE.includes("knopf('wkt-btn wkt-btn-sek', 'Vertrag widerrufen'"), 'Fuss: sekundaerer Knopf')
|
||||
assert.doesNotMatch(OHNE_KOMMENTARE, /wkt-link/, 'kein Textlink-Knopf')
|
||||
assert.match(QUELLE, /\.wkt-btn,\.wkt-knopf\{[^}]*min-height:44px/, 'Knoepfe mindestens 44 px hoch')
|
||||
// Knopfmodus: neben dem Buchungsknopf, oeffnet den Dialog gleich beim Widerruf
|
||||
assert.ok(OHNE_KOMMENTARE.includes("knopf('wkt-knopf wkt-btn-sek', 'Vertrag widerrufen'"), 'dauerhafter Knopf im Knopfmodus')
|
||||
assert.ok(OHNE_KOMMENTARE.includes('oeffne(true)'), 'der Knopf oeffnet den Dialog beim Widerrufsformular')
|
||||
const start = OHNE_KOMMENTARE.slice(OHNE_KOMMENTARE.indexOf('function startKnopf'))
|
||||
assert.ok(start.includes('lade()') && start.indexOf('lade()') < start.indexOf('widerrufKnopf = knopf'), 'startKnopf holt die Konfiguration beim Laden')
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------- Textfarbe auf der Betriebsfarbe (Kontrast)
|
||||
|
||||
/** Die reinen Farbfunktionen des Skripts (zwischen den Marken @farben-anfang und @farben-ende) einzeln ausfuehren. */
|
||||
|
||||
Reference in new issue
Block a user