Kundenansicht neu P7: Gutschrift-Antrag schlank, Bedingungen als eigene Ansicht statt Klappe
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9b00771d11
commit
d17520c4f5
5 files changed
+235
-71
No files matched your search
@@ -3,61 +3,187 @@ import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { zeigeGutschrift, sammleDateien, IO } from '../public/neu/ansicht-gutschrift.js'
|
||||
import { sichtbarerText, ladeAlteApp } from './support/knWortlautOrakel.js'
|
||||
import { klartext } from '../server/services/bestelltexte.js'
|
||||
import { mit, me, abo, aboAntwort, anfrageGutschrift, neueWurzel, LIMITS } from './support/knDaten.js'
|
||||
import { NEU, meMit, aboNeu } from './support/knBestellDaten.js'
|
||||
|
||||
const alt = ladeAlteApp(); const ABO = aboAntwort({ subscription: abo() })
|
||||
// Gutschrift-Antrag im schlanken Muster (Spec 5.6, Fassung 6): Satz mit Links über dem Knopf, keine Klappe, kein Häkchen;
|
||||
// die Bedingungen sind eine eigene Ansicht (#gutschrift?bedingungen=1), der Rechtstext bleibt unverändert
|
||||
const ABO = aboNeu({ subscription: abo() })
|
||||
const ANTRAEGE = { requests: [anfrageGutschrift({ id: 't1', status: 'rueckfrage', question: 'Bitte <b>Kündigungsbestätigung</b> hochladen' }), anfrageGutschrift({ id: 't2', status: 'genehmigt', amountCents: 4000, decisionNote: 'Passt.' }), anfrageGutschrift({ id: 't3', status: 'abgelehnt', decisionNote: 'Nicht erstattbar.' })], conditionsVersion: '2026-09-30', limits: LIMITS }
|
||||
const SRV = { 'GET /api/abo': ABO, 'GET /api/abo/takeover-credits': ANTRAEGE }
|
||||
const zeigeNeu = async (fx, m = me()) => { const w = neueWurzel(fx); await zeigeGutschrift(w, { route: { bereich: 'vertraege', etage: 'gutschrift', query: '' }, me: m, nav: 'push' }); await fx.ruhe(); return w }
|
||||
for (const [name, abo_, antwort] of [['mit Anträgen', ABO, ANTRAEGE], ['ohne Abo', aboAntwort(), { requests: [], limits: LIMITS }]]) {
|
||||
test(`Gutschrift (${name}): Karte wie in app.js`, mit({ 'GET /api/abo': abo_, 'GET /api/abo/takeover-credits': antwort }, async (fx) => {
|
||||
const w = await zeigeNeu(fx)
|
||||
assert.equal(sichtbarerText(fx.finde(w, '[data-karte="gutschrift"]')), sichtbarerText(await alt.gutschrift(abo_, antwort)))
|
||||
}))
|
||||
const zeigeNeu = async (fx, m = meMit('unternehmen'), query = '') => {
|
||||
const w = neueWurzel(fx)
|
||||
await zeigeGutschrift(w, { route: { bereich: 'vertraege', etage: 'gutschrift', query }, me: m, nav: 'push' })
|
||||
await fx.ruhe()
|
||||
return w
|
||||
}
|
||||
async function fuelleAntrag(fx, w) {
|
||||
await fx.klick(fx.finde(w, 'input[value="hosting"]'))
|
||||
await fx.setze(fx.finde(w, '#tk-provider'), 'IONOS')
|
||||
await fx.setze(fx.finde(w, '#tk-amount'), '119.88')
|
||||
await fx.setze(fx.finde(w, '#tk-months'), '12')
|
||||
await fx.setze(fx.finde(w, '#tk-paid-until'), '2027-03-31')
|
||||
await fx.setze(fx.finde(w, '#tk-message'), 'Der Vertrag dort läuft noch bis Ende März.')
|
||||
fx.finde(w, '#tk-files').files = [{ name: 'r.pdf', size: 10 }]
|
||||
}
|
||||
|
||||
test('Formular: Satz vor dem Knopf, keine Klappe, kein Häkchen, Links wie im Wortlaut', mit(SRV, async (fx) => {
|
||||
const w = await zeigeNeu(fx)
|
||||
await fx.klick(fx.finde(w, '#tk-start'))
|
||||
assert.ok(fx.finde(w, '#tk-consent') === null, 'kein Häkchen für die Zustimmung')
|
||||
assert.deepEqual([fx.alle(w, 'details').length, fx.finde(w, '#tk-submit').textContent], [0, 'Antrag absenden'])
|
||||
const ids = fx.alle(w, '[id]').map((e) => e.id)
|
||||
assert.ok(ids.indexOf('tk-satz') > -1 && ids.indexOf('tk-satz') < ids.indexOf('tk-submit'))
|
||||
assert.equal(fx.text(fx.finde(w, '#tk-satz')), klartext(ABO.bestelltexte.gutschrift.satz.text))
|
||||
const [bed, ds] = fx.alle(w, '#tk-satz a')
|
||||
assert.deepEqual([bed.textContent, bed.getAttribute('href'), bed.getAttribute('target'), ds.getAttribute('href'), ds.getAttribute('target')], ['Bedingungen der Übernahmegutschrift', '#gutschrift?bedingungen=1', '_blank', '/datenschutz.html#uebernahmegutschrift', '_blank'])
|
||||
assert.equal(fx.alle(w, (e) => e.tagName === 'INPUT' && e.getAttribute('type') === 'checkbox' && e.getAttribute('name') !== 'services').length, 0, 'nur die Leistungen sind Häkchen')
|
||||
}))
|
||||
|
||||
test('Absenden sendet consent und die Fassung, nie eine Kundenart', mit({ ...SRV, 'POST /api/abo/takeover-credits': { request: ANTRAEGE.requests[0] } }, async (fx) => {
|
||||
IO.lies = async () => 'UERGLTEuNA=='
|
||||
const w = await zeigeNeu(fx)
|
||||
await fx.klick(fx.finde(w, '#tk-start'))
|
||||
await fuelleAntrag(fx, w)
|
||||
await fx.klick(fx.finde(w, '#tk-submit'))
|
||||
await fx.ruhe()
|
||||
const b = fx.fetchLog.find((a) => a.method === 'POST' && a.url === '/api/abo/takeover-credits').body
|
||||
assert.deepEqual([b.consent, b.termsVersion, 'kundenart' in b, 'unternehmer' in b], [true, NEU, false, false])
|
||||
assert.deepEqual({ ...b, files: undefined }, { services: ['hosting'], provider: 'IONOS', domain: '', invoiceAmount: '119.88', billingMonths: 12, paidUntil: '2027-03-31', message: 'Der Vertrag dort läuft noch bis Ende März.', email: 'anna@example.com', consent: true, termsVersion: NEU, files: undefined })
|
||||
assert.deepEqual(b.files, [{ name: 'r.pdf', data: 'UERGLTEuNA==' }])
|
||||
assert.match(fx.text(w), /Vielen Dank! Dein Antrag ist eingegangen\. Die Eingangsbestätigung haben wir an anna@example\.com geschickt; unsere Antwort kommt ebenfalls per E-Mail\./)
|
||||
}))
|
||||
|
||||
test('Bedingungen-Ansicht: Titel als Ereignis, fünf Sätze aus dem Block, Rückweg, kein Formular', mit(SRV, async (fx) => {
|
||||
const w = neueWurzel(fx)
|
||||
const titel = []
|
||||
w.addEventListener('kn:titel', (e) => titel.push(e.detail.text))
|
||||
await zeigeGutschrift(w, { route: { bereich: 'vertraege', etage: 'gutschrift', query: 'bedingungen=1' }, me: meMit('unternehmen'), nav: 'push' })
|
||||
await fx.ruhe()
|
||||
assert.deepEqual(titel, ['Bedingungen der Übernahmegutschrift'])
|
||||
assert.equal(fx.alle(w, 'h1').length, 0)
|
||||
assert.equal(fx.alle(w, 'ol > li').length, 5)
|
||||
assert.equal(fx.finde(w, 'ol > li').textContent, 'Die Übernahmegutschrift gibt es für Leistungen, die Ihr Abo ersetzt (Domain, Hosting bzw. Betrieb der Website, E-Mail-Postfächer) und die Sie bei Ihrem bisherigen Anbieter bereits im Voraus bezahlt haben.')
|
||||
assert.equal(fx.finde(w, 'a[href="#gutschrift"]').textContent, 'Zurück zum Antrag')
|
||||
assert.ok(fx.finde(w, '#tk-form') === null)
|
||||
assert.equal(fx.fetchLog.length, 0, 'die Bedingungen brauchen keinen Abruf')
|
||||
}))
|
||||
|
||||
test('Andere Abfragen zeigen den Antrag, nicht die Bedingungen', mit(SRV, async (fx) => {
|
||||
for (const query of ['', 'bedingungen=0', 'x=1']) {
|
||||
const w = await zeigeNeu(fx, meMit('unternehmen'), query)
|
||||
assert.ok(fx.finde(w, '#tk-start'), query)
|
||||
assert.equal(fx.alle(w, 'ol > li').length, 0, query)
|
||||
}
|
||||
}))
|
||||
|
||||
test('Bedingungen der Gutschrift: Block gleich dem Pin in legalTexts.test.js (Fassung 2026-09-30)', () => {
|
||||
const q = readFileSync(new URL('../public/neu/ansicht-gutschrift.js', import.meta.url), 'utf8'); const i = q.indexOf('const TAKEOVER_CONDITIONS'); const block = q.slice(i, q.indexOf(']', i))
|
||||
const q = readFileSync(new URL('../public/neu/ansicht-gutschrift.js', import.meta.url), 'utf8')
|
||||
const i = q.indexOf('const TAKEOVER_CONDITIONS')
|
||||
const block = q.slice(i, q.indexOf(']', i))
|
||||
assert.equal(createHash('sha256').update(block).digest('hex'), '2b7a745cb599c0ab28f20615a745bd86da6faa8d120391ca56beac35a0473e62')
|
||||
})
|
||||
test('sammleDateien: Prüftexte wörtlich', async () => {
|
||||
const g = { maxFiles: 3, maxFileBytes: 8 * 1048576, maxTotalBytes: 10 * 1048576 }; const f = (name, size) => ({ name, size }); const lies = async () => 'QQ=='
|
||||
await assert.rejects(sammleDateien([], g, { erforderlich: true }, lies), { message: 'Bitte laden Sie die Rechnung Ihres bisherigen Anbieters hoch.' })
|
||||
|
||||
test('sammleDateien: Prüftexte in du', async () => {
|
||||
const g = { maxFiles: 3, maxFileBytes: 8 * 1048576, maxTotalBytes: 10 * 1048576 }
|
||||
const f = (name, size) => ({ name, size })
|
||||
const lies = async () => 'QQ=='
|
||||
await assert.rejects(sammleDateien([], g, { erforderlich: true }, lies), { message: 'Bitte lade die Rechnung deines bisherigen Anbieters hoch.' })
|
||||
await assert.rejects(sammleDateien([1, 2, 3, 4].map((n) => f(`${n}.pdf`, 1)), g, { erforderlich: false }, lies), { message: 'Bitte höchstens 3 Dateien auf einmal hochladen.' })
|
||||
await assert.rejects(sammleDateien([f('groß.pdf', 9 * 1048576)], g, { erforderlich: false }, lies), { message: '„groß.pdf“ ist größer als 8 MB.' })
|
||||
await assert.rejects(sammleDateien([f('a.pdf', 6 * 1048576), f('b.pdf', 6 * 1048576)], g, { erforderlich: false }, lies), { message: 'Die Dateien sind zusammen größer als 10 MB.' })
|
||||
assert.deepEqual(await sammleDateien([f('a.pdf', 5)], g, { erforderlich: true }, lies), [{ name: 'a.pdf', data: 'QQ==' }])
|
||||
})
|
||||
test('Antrag: Prüfungen wörtlich, Body mit denselben Schlüsseln wie früher, Meldung nach Erfolg', mit({ ...SRV, 'POST /api/abo/takeover-credits': {} }, async (fx) => {
|
||||
IO.lies = async () => 'QQ=='; const w = await zeigeNeu(fx); await fx.klick(fx.finde(w, (e) => e.textContent === 'Gutschrift beantragen'))
|
||||
await fx.klick(fx.finde(w, '#tk-submit')); assert.match(fx.text(w), /Bitte wählen Sie, welche Leistung Sie bisher woanders bezahlt haben\./)
|
||||
await fx.klick(fx.finde(w, 'input[value="domain"]')); await fx.setze(fx.finde(w, '#tk-provider'), 'IONOS'); await fx.setze(fx.finde(w, '#tk-amount'), '119.88'); await fx.setze(fx.finde(w, '#tk-months'), '12'); await fx.setze(fx.finde(w, '#tk-paid-until'), '2027-03-31')
|
||||
await fx.klick(fx.finde(w, '#tk-submit')); assert.match(fx.text(w), /Bitte bestätigen Sie Ihre Angaben und die Bedingungen\./)
|
||||
fx.finde(w, '#tk-files').files = [{ name: 'r.pdf', size: 10 }]; await fx.klick(fx.finde(w, '#tk-consent')); await fx.klick(fx.finde(w, '#tk-submit')); await fx.ruhe()
|
||||
assert.deepEqual(fx.fetchLog.find((a) => a.method === 'POST').body, { services: ['domain'], provider: 'IONOS', domain: '', invoiceAmount: '119.88', billingMonths: 12, paidUntil: '2027-03-31', message: '', email: 'anna@example.com', consent: true, files: [{ name: 'r.pdf', data: 'QQ==' }] })
|
||||
assert.match(fx.text(w), /Vielen Dank! Ihr Antrag ist eingegangen\. Die Eingangsbestätigung haben wir an anna@example\.com geschickt; unsere Antwort kommt ebenfalls per E-Mail\./)
|
||||
|
||||
test('Antrag: Prüfungen in du, Meldung nach Erfolg', mit({ ...SRV, 'POST /api/abo/takeover-credits': {} }, async (fx) => {
|
||||
IO.lies = async () => 'QQ=='
|
||||
const w = await zeigeNeu(fx)
|
||||
await fx.klick(fx.finde(w, (e) => e.textContent === 'Gutschrift beantragen'))
|
||||
await fx.klick(fx.finde(w, '#tk-submit'))
|
||||
assert.match(fx.text(w), /Bitte wähle, welche Leistung du bisher woanders bezahlt hast\./)
|
||||
await fx.klick(fx.finde(w, 'input[value="domain"]'))
|
||||
await fx.klick(fx.finde(w, '#tk-submit'))
|
||||
assert.match(fx.text(w), /Bitte nenne deinen bisherigen Anbieter\./)
|
||||
await fx.setze(fx.finde(w, '#tk-provider'), 'IONOS')
|
||||
await fx.klick(fx.finde(w, '#tk-submit'))
|
||||
assert.match(fx.text(w), /Bitte gib Rechnungsbetrag, Zeitraum und „Bezahlt bis“ an\./)
|
||||
await fx.setze(fx.finde(w, '#tk-amount'), '119.88')
|
||||
await fx.setze(fx.finde(w, '#tk-months'), '12')
|
||||
await fx.setze(fx.finde(w, '#tk-paid-until'), '2027-03-31')
|
||||
await fx.klick(fx.finde(w, '#tk-submit'))
|
||||
await fx.ruhe()
|
||||
assert.match(fx.text(w), /Bitte lade die Rechnung deines bisherigen Anbieters hoch\./)
|
||||
assert.ok(!fx.fetchLog.some((a) => a.method === 'POST'))
|
||||
fx.finde(w, '#tk-files').files = [{ name: 'r.pdf', size: 10 }]
|
||||
await fx.klick(fx.finde(w, '#tk-submit'))
|
||||
await fx.ruhe()
|
||||
assert.match(fx.text(w), /Vielen Dank! Dein Antrag ist eingegangen\./)
|
||||
}))
|
||||
|
||||
test('Rückfrage beantworten; Kundentext bleibt Text; Ansichtsmodus sperrt alles', mit({ ...SRV, 'POST /api/abo/takeover-credits/t1/reply': {} }, async (fx) => {
|
||||
const w = await zeigeNeu(fx); assert.equal(fx.finde(w, 'b'), null)
|
||||
await fx.setze(fx.finde(w, 'form[data-reply-for="t1"] textarea'), 'Anbei.'); await fx.klick(fx.finde(w, 'form[data-reply-for="t1"] button[type="submit"]')); await fx.ruhe()
|
||||
const w = await zeigeNeu(fx)
|
||||
assert.ok(fx.finde(w, 'b') === null)
|
||||
await fx.setze(fx.finde(w, 'form[data-reply-for="t1"] textarea'), 'Anbei.')
|
||||
await fx.klick(fx.finde(w, 'form[data-reply-for="t1"] button[type="submit"]'))
|
||||
await fx.ruhe()
|
||||
assert.deepEqual(fx.fetchLog.find((a) => a.url.endsWith('/reply')).body, { message: 'Anbei.', files: [] })
|
||||
const v = await zeigeNeu(fx, me({ viewAs: true })); assert.match(fx.text(v), /Im Ansichtsmodus kann kein Antrag gestellt werden\./)
|
||||
assert.match(fx.text(w), /Danke für deine Antwort! Wir prüfen deinen Antrag weiter und melden uns per E-Mail\./)
|
||||
const v = await zeigeNeu(fx, meMit('unternehmen', { viewAs: true }))
|
||||
assert.match(fx.text(v), /Im Ansichtsmodus kann kein Antrag gestellt werden\./)
|
||||
assert.ok(fx.alle(v, 'button').filter((b) => ['Gutschrift beantragen', 'Antwort senden'].includes(b.textContent)).every((b) => b.hasAttribute('disabled')))
|
||||
}))
|
||||
|
||||
test('Gutschrift ohne Abo: Satz und Link zum Abo über die Brücke', mit({ 'GET /api/abo': aboAntwort(), 'GET /api/abo/takeover-credits': { requests: [], limits: LIMITS } }, async (fx) => {
|
||||
const w = await zeigeNeu(fx); assert.match(fx.text(w), /Die Übernahmegutschrift können Sie beantragen, sobald Ihr Abo läuft\./)
|
||||
const a = fx.finde(w, 'a[href="#vertraege"]'); assert.deepEqual([a.textContent, a.getAttribute('data-view')], ['Zum Abo', 'vertraege'])
|
||||
}))
|
||||
test('Gutschrift: Antwort ohne Text und Nachweis wird abgewiesen, Abbrechen schließt das Formular, Ausfälle haben eigene Texte', mit({ ...SRV, 'POST /api/abo/takeover-credits/t1/reply': {} }, async (fx) => {
|
||||
const w = await zeigeNeu(fx)
|
||||
await fx.klick(fx.finde(w, 'form[data-reply-for="t1"] button[type="submit"]')); await fx.ruhe()
|
||||
assert.match(fx.text(w), /Bitte schreiben Sie eine Antwort oder laden Sie einen Nachweis hoch\./); assert.ok(!fx.fetchLog.some((a) => a.url.endsWith('/reply')))
|
||||
assert.equal(fx.finde(w, '#tk-form').hasAttribute('hidden'), true); await fx.klick(fx.finde(w, '#tk-start')); assert.equal(fx.finde(w, '#tk-form').hasAttribute('hidden'), false)
|
||||
await fx.klick(fx.finde(w, '#tk-cancel')); assert.equal(fx.finde(w, '#tk-form').hasAttribute('hidden'), true)
|
||||
assert.match(fx.text(w), /Antrag .* · Domain/); assert.match(fx.text(w), /Nicht genehmigt/); assert.match(fx.text(w), /Gutschrift: 40,00 € – wird mit Ihren nächsten Monatsgebühren verrechnet\./)
|
||||
assert.match(fx.text(w), /Die Übernahmegutschrift kannst du beantragen, sobald dein Abo läuft\./)
|
||||
const a = fx.finde(w, 'a[href="#vertraege"]')
|
||||
assert.deepEqual([a.textContent, a.getAttribute('data-view')], ['Zum Abo', 'vertraege'])
|
||||
}))
|
||||
|
||||
test('Gutschrift: Antwort ohne Text und Nachweis wird abgewiesen, Abbrechen schließt das Formular', mit({ ...SRV, 'POST /api/abo/takeover-credits/t1/reply': {} }, async (fx) => {
|
||||
const w = await zeigeNeu(fx)
|
||||
await fx.klick(fx.finde(w, 'form[data-reply-for="t1"] button[type="submit"]'))
|
||||
await fx.ruhe()
|
||||
assert.match(fx.text(w), /Bitte schreib eine Antwort oder lade einen Nachweis hoch\./)
|
||||
assert.ok(!fx.fetchLog.some((a) => a.url.endsWith('/reply')))
|
||||
assert.equal(fx.finde(w, '#tk-form').hasAttribute('hidden'), true)
|
||||
await fx.klick(fx.finde(w, '#tk-start'))
|
||||
assert.equal(fx.finde(w, '#tk-form').hasAttribute('hidden'), false)
|
||||
await fx.klick(fx.finde(w, '#tk-cancel'))
|
||||
assert.equal(fx.finde(w, '#tk-form').hasAttribute('hidden'), true)
|
||||
assert.match(fx.text(w), /Antrag .* · Domain/)
|
||||
assert.match(fx.text(w), /Nicht genehmigt/)
|
||||
assert.match(fx.text(w), /Gutschrift: 40,00\s€ – wird mit deinen nächsten Monatsgebühren verrechnet\./)
|
||||
}))
|
||||
|
||||
test('Gutschrift: Ausfall der Anträge und des Abos', mit({ 'GET /api/abo': ABO, 'GET /api/abo/takeover-credits': () => ({ status: 500, body: { error: 'Server kaputt' } }) }, async (fx) => {
|
||||
assert.match(fx.text(await zeigeNeu(fx)), /Übernahmegutschrift konnte nicht geladen werden: Server kaputt/)
|
||||
fx.tabelle['GET /api/abo'] = () => ({ status: 500, body: { error: 'x' } })
|
||||
assert.match(fx.text(await zeigeNeu(fx)), /Deine Verträge konnten gerade nicht geladen werden\./)
|
||||
}))
|
||||
|
||||
test('Ohne Wortlaut der Gutschrift: nur ein Satz, kein Formular, kein Knopf; unsichere Adressen werden Text', mit({ 'GET /api/abo': ABO, 'GET /api/abo/takeover-credits': ANTRAEGE }, async (fx) => {
|
||||
const ohne = aboNeu({ subscription: abo() })
|
||||
delete ohne.bestelltexte.gutschrift
|
||||
fx.tabelle['GET /api/abo'] = ohne
|
||||
const w = await zeigeNeu(fx)
|
||||
assert.ok(fx.finde(w, '#tk-start') === null)
|
||||
assert.ok(fx.finde(w, '#tk-form') === null)
|
||||
assert.match(fx.text(w), /Die Online-Bestellung wird gerade eingerichtet – schreib uns gern im Chat\./)
|
||||
const boese = aboNeu({ subscription: abo() })
|
||||
boese.bestelltexte.gutschrift.satz.urls = ['javascript:alert(1)', '//evil.example']
|
||||
fx.tabelle['GET /api/abo'] = boese
|
||||
const v = await zeigeNeu(fx)
|
||||
await fx.klick(fx.finde(v, '#tk-start'))
|
||||
assert.equal(fx.alle(v, '#tk-satz a[href]').length, 0)
|
||||
}))
|
||||
|
||||
test('Quelltext: der Block TAKEOVER_CONDITIONS ist die einzige Sie-Stelle, keine Klappe, kein Häkchen', () => {
|
||||
const q = readFileSync(new URL('../public/neu/ansicht-gutschrift.js', import.meta.url), 'utf8').replace(/\/\*[\s\S]*?\*\//g, '')
|
||||
const i = q.indexOf('const TAKEOVER_CONDITIONS')
|
||||
const ohneBlock = q.slice(0, i) + q.slice(q.indexOf(']', i))
|
||||
assert.doesNotMatch(ohneBlock, /\b(Sie|Ihr(e[nmrs]?)?|Ihnen)\b/)
|
||||
assert.doesNotMatch(ohneBlock, /details|dialog|innerHTML|tk-consent|tk-conditions/)
|
||||
assert.doesNotMatch(ohneBlock, /body:\s*\{[^}]*kundenart/s)
|
||||
})
|
||||
@@ -7,7 +7,9 @@ import { fileURLToPath } from 'node:url'
|
||||
const WURZEL = path.join(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const NEU = path.join(WURZEL, 'public', 'neu')
|
||||
const MODULE = ['ansicht-hilfen', 'rechnung-offen', 'install-hinweis', 'website-details', 'ansicht-website', 'ansicht-chat', 'ansicht-vertraege', 'vertraege-rechnungen', 'vertraege-abo', 'ansicht-stunden', 'ansicht-speicher', 'ansicht-bestellen', 'ansicht-wechsel', 'ansicht-gutschrift', 'ansicht-passwort', 'bestell-bausteine']
|
||||
const WORTLAUT = ['ansicht-speicher', 'ansicht-bestellen', 'ansicht-wechsel', 'ansicht-gutschrift']
|
||||
// Seit P7 sprechen auch die vier Bestellseiten „du“ (Fassung 6, C14); einzige Ausnahme ist der Rechtstext TAKEOVER_CONDITIONS (hash-gepinnt)
|
||||
const WORTLAUT = []
|
||||
const BESTELLSEITEN = ['ansicht-bestellen', 'ansicht-wechsel', 'ansicht-speicher', 'ansicht-gutschrift', 'bestell-bausteine']
|
||||
const vorhanden = MODULE.filter((n) => existsSync(path.join(NEU, `${n}.js`)))
|
||||
|
||||
const ohneKommentare = (q) => q.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^:'"`\\])\/\/[^\n]*/g, '$1')
|
||||
@@ -32,8 +34,24 @@ test('P2-Module: kein HTML-Einsetzen, nur relative Importe, keine Admin-Verweise
|
||||
}
|
||||
})
|
||||
|
||||
test('P2-Module: Du-Form, nur die vier Wortlaut-Dateien behalten die Sie-Form (C14)', () => {
|
||||
for (const n of vorhanden.filter((x) => !WORTLAUT.includes(x))) assert.doesNotMatch(lies(n), /\b(Sie|Ihr(e[nmrs]?)?|Ihnen)\b/, n)
|
||||
/** Quelltext ohne den Rechtstext TAKEOVER_CONDITIONS (er bleibt Zeichen für Zeichen in der Sie-Form). */
|
||||
const ohneRechtstext = (q) => {
|
||||
const i = q.indexOf('const TAKEOVER_CONDITIONS')
|
||||
return i < 0 ? q : q.slice(0, i) + q.slice(q.indexOf(']', i))
|
||||
}
|
||||
|
||||
test('P2-Module: Du-Form in allen Modulen, einzige Ausnahme der Block TAKEOVER_CONDITIONS (C14, P7)', () => {
|
||||
for (const n of vorhanden.filter((x) => !WORTLAUT.includes(x))) assert.doesNotMatch(ohneRechtstext(lies(n)), /\b(Sie|Ihr(e[nmrs]?)?|Ihnen)\b/, n)
|
||||
assert.match(lies('ansicht-gutschrift'), /Ihr Abo ersetzt/, 'der Rechtstext bleibt unverändert in der Sie-Form')
|
||||
})
|
||||
|
||||
test('Bestellseiten: kein kundenart im Body, keine eigene h1 (Titel meldet die Ansicht)', () => {
|
||||
for (const n of BESTELLSEITEN) {
|
||||
assert.ok(existsSync(path.join(NEU, `${n}.js`)), n)
|
||||
const q = lies(n)
|
||||
assert.doesNotMatch(q, /body:\s*\{[^}]*kundenart/s, n)
|
||||
assert.doesNotMatch(q, /h\(\s*['"]h1['"]/, n)
|
||||
}
|
||||
})
|
||||
|
||||
test('public/neu/: kein Lila in irgendeiner Textdatei (die Prüfung lila liest jedes geladene Stylesheet samt ungenutzter Regeln)', () => {
|
||||
|
||||
@@ -43,6 +43,9 @@ test('baueLinkzeile, baueKundenartZeile, baueBlock', () =>
|
||||
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
|
||||
|
||||
Reference in new issue
Block a user