Files
Webklar-Kundenbereich/test/knBestellBausteine.test.js
T
ClaudeandClaude Sonnet 5.5 d4050f870a Kundenansicht neu E-Fix F1: Kerninfo und Knopf der Bestellseiten passen in den nutzbaren Bereich (390x844, 375x667)
Die Messung im Werkzeug nahm innerHeight; nutzbar ist am Handy nur der Bereich zwischen Kopf und fester Leiste (735 px bei 844, 558 px bei 667).
Gemessen vor der Korrektur (Blockanfang bis Knopfunterkante, 375x667): Abo bestellen privat 799 px, Wechsel privat 888 px, Zusatzspeicher 567 px.

- Block knapp: Leistung als eine Zeile mit Link "Leistungen ansehen" auf die Leistungsbeschreibung, Preis, Fällig, Beginn, Laufzeit und Zahlung in je ein bis zwei Zeilen.
- Die ausführlichen Sätze stehen unter dem Knopf und der Kundenart-Zeile (baueEinzelheiten), ohne Klappe.
- Handy: Beschriftung läuft in der Zeile mit, kleinere Überschrift, knappere Abstände.
- Die Wortlaute des Blocks sind gekürzt und gehören in die Rechtsrunde.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-16 21:25:48 +00:00

89 lines
6.8 KiB
JavaScript

import { test } from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { mitDom } from './support/knDaten.js'
import { TERMS_URL, meMit, aboNeu } from './support/knBestellDaten.js'
import { kundenartVon, sicherLink, linkTeile, baueLinkzeile, baueKundenartZeile, baueBlock, baueEinzelheiten, leistungLink, hatWortlaut } from '../public/neu/bestell-bausteine.js'
test('kundenartVon: nur exakt unternehmen ist Betrieb (E4)', () => {
for (const m of [undefined, null, {}, { customer: {} }, meMit(''), meMit('privat'), { customer: { kundenartWirksam: 'Unternehmen' } }, { customer: { kundenart: 'unternehmen' } }]) {
assert.equal(kundenartVon(m), 'privat')
}
assert.equal(kundenartVon(meMit('unternehmen')), 'unternehmen')
assert.equal(kundenartVon({}, { kundenart: 'unternehmen' }), 'unternehmen')
assert.equal(kundenartVon({}, { kundenart: 'Unternehmen' }), 'privat')
})
test('sicherLink', () => {
for (const ok of ['/datenschutz.html', `${TERMS_URL}#zusatzspeicher`, '#chat', '#gutschrift?bedingungen=1', 'https://example.invalid/x']) assert.equal(sicherLink(ok), ok)
for (const schlecht of ['javascript:alert(1)', '//evil.example/x', '/\\evil.example', 'data:text/html,x', 'http:/x', 'abo.html', '', undefined, null, 42, '/a b', '#', '# chat', 'https://', 'https://user:pw@example.invalid/']) {
assert.equal(sicherLink(schlecht), '', String(schlecht))
}
})
test('linkTeile: Markierungen werden der Reihe nach Links, Unsicheres bleibt Text', () => {
assert.deepEqual(linkTeile('Es gilt [A] und [B].', ['/a', '/b']), [{ text: 'Es gilt ' }, { text: 'A', href: '/a' }, { text: ' und ' }, { text: 'B', href: '/b' }, { text: '.' }])
assert.deepEqual(linkTeile('Siehe [A].', ['javascript:x']), [{ text: 'Siehe ' }, { text: 'A' }, { text: '.' }])
assert.deepEqual(linkTeile('Nur Text', []), [{ text: 'Nur Text' }])
assert.deepEqual(linkTeile('Offen [A', ['/a']), [{ text: 'Offen [A' }])
assert.deepEqual(linkTeile('[A] [B]', ['/a']), [{ text: 'A', href: '/a' }, { text: ' ' }, { text: 'B' }], 'fehlende Adresse: Text')
assert.deepEqual(linkTeile(undefined, undefined), [])
})
test('baueLinkzeile, baueKundenartZeile, baueBlock', () =>
mitDom((fx) => {
const p = baueLinkzeile({ text: 'Siehe [A] und [B].', urls: ['/a', '#chat'] }, { id: 'x' })
const [a, b] = fx.alle(p, 'a')
assert.deepEqual([p.id, fx.text(p), a.getAttribute('target'), a.getAttribute('rel'), b.getAttribute('target')], ['x', 'Siehe A und B.', '_blank', 'noopener', null])
const T = aboNeu().bestelltexte.kundenart
assert.equal(fx.text(baueKundenartZeile('unternehmen', T, { id: 'k' })), 'Du bestellst als Betrieb · Stimmt nicht? Schreib uns im Chat')
assert.equal(fx.text(baueKundenartZeile('privat', T, { id: 'k' })), 'Du bestellst privat · Stimmt nicht? Schreib uns im Chat')
assert.equal(fx.text(baueKundenartZeile('kaputt', T, { id: 'k' })), 'Du bestellst privat · Stimmt nicht? Schreib uns im Chat')
assert.equal(fx.finde(baueKundenartZeile('privat', T, { id: 'k' }), 'a').getAttribute('href'), '#chat')
const dl = baueBlock([{ id: 'r1', label: 'Preis', text: '<b>50</b>' }], { id: 'blk' })
assert.deepEqual([fx.alle(dl, 'dt').length, fx.finde(dl, 'dd').textContent, fx.finde(dl, 'b'), fx.finde(dl, '#r1').textContent], [1, '<b>50</b>', null, '<b>50</b>'])
assert.deepEqual([dl.tagName, dl.id, dl.className], ['DL', 'blk', 'kn-block'])
const neuerTab = baueLinkzeile({ text: 'Siehe [A].', urls: ['#gutschrift?bedingungen=1'] }, { id: 'z', neuerTab: true })
assert.deepEqual([fx.finde(neuerTab, 'a').getAttribute('target'), fx.finde(neuerTab, 'a').getAttribute('rel')], ['_blank', 'noopener'])
assert.equal(baueLinkzeile({ text: 'Siehe [A].', urls: ['#chat'] }, { id: 'z', klasse: 'x-klasse' }).className, 'x-klasse')
// eine unsichere Adresse macht keinen Link
assert.equal(fx.alle(baueLinkzeile({ text: 'Siehe [A].', urls: ['javascript:alert(1)'] }, { id: 'y' }), 'a').length, 0)
// keine h1 in den Bausteinen
for (const el of [p, dl, baueKundenartZeile('privat', T, { id: 'k' })]) assert.equal(fx.alle(el, 'h1').length, 0)
}))
test('baueBlock: Link in der Zeile, nur sichere Adressen; baueEinzelheiten steht unter dem Knopf mit eigener Überschrift', () =>
mitDom((fx) => {
const dl = baueBlock([{ label: 'Leistung', text: 'Wartungs-Abo', link: { text: 'Leistungen ansehen', href: `${TERMS_URL}#wartung` } }, { label: 'Preis', text: '50', link: { text: 'x', href: 'javascript:alert(1)' } }], { id: 'blk' })
const [erste, zweite] = fx.alle(dl, 'dd')
assert.equal(fx.text(erste), 'Wartungs-Abo · Leistungen ansehen')
assert.deepEqual([fx.finde(erste, 'a').getAttribute('href'), fx.finde(erste, 'a').getAttribute('target'), fx.finde(erste, 'a').getAttribute('rel')], [`${TERMS_URL}#wartung`, '_blank', 'noopener'])
assert.equal(fx.text(zweite), '50', 'unsichere Adresse: kein Link, auch kein Trenner')
assert.equal(fx.alle(zweite, 'a').length, 0)
const e = baueEinzelheiten([{ label: 'Zahlung', text: 'Karte' }], { id: 'ein' })
assert.deepEqual([e.tagName, e.id, fx.text(fx.finde(e, 'h3')), fx.alle(e, 'dt').length, fx.alle(e, 'details, dialog, h1').length], ['SECTION', 'ein', 'Im Einzelnen', 1, 0])
// Adresse der Leistungsbeschreibung: die Bedingungen mit Anker; unsichere oder fehlende Adresse ergibt ''
assert.equal(leistungLink({ urls: [TERMS_URL, '/datenschutz.html'] }, 'wartung'), `${TERMS_URL}#wartung`)
for (const schlecht of [undefined, {}, { urls: [] }, { urls: ['javascript:alert(1)'] }, { urls: ['//evil.example/x'] }]) assert.equal(leistungLink(schlecht, 'wartung'), '')
}))
test('hatWortlaut', () => {
assert.equal(hatWortlaut(aboNeu()), true)
for (const d of [null, undefined, {}, { bestelltexte: {} }, { bestelltexte: { status: 'entwurf', knopf: 'x' } }, { bestelltexte: { status: 'entwurf', abo: { linkzeile: { text: 'a' } } } }]) assert.equal(hatWortlaut(d), false)
for (const art of ['abo', 'wechsel', 'speicher', 'gutschrift']) assert.equal(hatWortlaut(aboNeu(), art), true, art)
// jede Seite prüft ihre eigenen Sätze
const ohne = (teil) => { const d = aboNeu(); delete d.bestelltexte[teil]; return d }
assert.deepEqual(['abo', 'wechsel', 'speicher', 'gutschrift'].map((art) => hatWortlaut(ohne(art), art)), [false, false, false, false])
assert.equal(hatWortlaut(ohne('wechsel'), 'abo'), true)
const ohneGesperrt = aboNeu()
ohneGesperrt.bestelltexte.wechsel.gesperrt = ''
assert.equal(hatWortlaut(ohneGesperrt, 'wechsel'), false)
})
test('Quelltext: keine HTML-Einsetzung, Du-Form, keine h1', () => {
const q = readFileSync(new URL('../public/neu/bestell-bausteine.js', import.meta.url), 'utf8').replace(/\/\*[\s\S]*?\*\//g, '')
assert.doesNotMatch(q, /innerHTML|insertAdjacentHTML|outerHTML|document\.write/)
assert.doesNotMatch(q, /\b(Sie|Ihr(e[nmrs]?)?|Ihnen)\b/)
assert.doesNotMatch(q, /'h1'/)
})