Files
Webklar-Kundenbereich/test/kundenansichtNeuModulAnalyse.test.js
T

136 lines
8.2 KiB
JavaScript

import { test, after } from 'node:test'
import assert from 'node:assert/strict'
import { analyseSel } from '../public/analyse-view.js'
import { KEIN_PREIS, htmlWurzel } from './support/modulAbo.js'
// Analyse im Abo-Modell (P4 Task 8): Linkzeile A1 und Zielgruppen-Häkchen A2 statt des langen Zustimmungssatzes, Text A3, audienceConfirmed im
// Körper, die Reiter Übersicht, Heatmap und Einstellungen in der Etage (kn:reiter). Ohne modell === 'abo' bleibt alles wie heute.
const { initAnalyse } = await import('../public/analyse.js')
globalThis.document = { querySelectorAll: () => [] } // pickTab ruft hideChartTip
after(() => { delete globalThis.document })
const LEGAL = { avv: '/rechtliches/avv-website-analyse-2026-10-02.html', lb: '/rechtliches/leistungsbeschreibung-website-analyse-2026-10-02.html' }
const CTRL = { ok: true, company: 'Muster GmbH', address: 'Weg 1, 12345 Ort', email: 'info@muster.de' }
const offProject = (id = 'p1', analyse = {}) => ({ projectId: id, projectName: `Muster ${id}`, analyse: { state: 'off', eligible: true, accentColor: '#111111', avvVersion: '2026-10-02', legal: LEGAL, controller: CTRL, ...analyse } })
const aktivProjekt = (id = 'p1') => offProject(id, { state: 'active' })
const KEIN_ABO = { eligible: false, reason: 'kein_abo', reasonText: 'Alter Satz des Servers' }
async function lauf({ modell, projects = [offProject()], dataset = {}, projectId = 'p1', tab = 'uebersicht' } = {}) {
const posts = []
globalThis.fetch = async (url, opts) => {
if (opts?.method === 'POST') posts.push({ url: String(url), body: JSON.parse(opts.body) })
if (String(url).startsWith('/api/funktionen/analyse/stats')) return { ok: false, status: 503, json: async () => ({}) } // das Cockpit zeigt die Fehlerkarte; mehr braucht der Reitertest nicht
return { ok: true, status: 200, json: async () => ({ ...(modell ? { modell } : {}), projects }) }
}
const { root, stage, kn } = htmlWurzel('#an-stage')
Object.assign(root.dataset, dataset)
analyseSel.tab = tab
await initAnalyse(root, { projectId })
return { root, stage, posts, kn }
}
test('Abo: Linkzeile A1 mit beiden Links, ein Häkchen mit A2, kein langer Zustimmungssatz, keine Preise', async () => {
const { stage } = await lauf({ modell: 'abo' })
const html = stage.innerHTML
assert.match(html, /Es gelten <a href="[^"]*"[^>]*>Leistungsbeschreibung<\/a> und <a href="[^"]*"[^>]*>Auftragsverarbeitung<\/a> \(Fassung 2026-10-02\)\./)
assert.ok(html.includes(`href="${LEGAL.lb}"`) && html.includes(`href="${LEGAL.avv}"`))
assert.match(html, /<input type="checkbox" id="an-zielgruppe"><span>Meine Website richtet sich nicht gezielt an Kinder unter 16 Jahren\.<\/span>/)
assert.doesNotMatch(html, /Ich beauftrage WEBklar|id="an-avv"/)
assert.doesNotMatch(html, KEIN_PREIS)
assert.match(html, /Muster GmbH/, 'Verantwortlicher und Banner-Vorschau bleiben')
assert.match(html, /id="an-color"/)
})
test('Abo: Knopf gesperrt ohne Zielgruppen-Häkchen und bei unvollständigem Verantwortlichen; Körper mit audienceConfirmed', async () => {
const { stage, posts } = await lauf({ modell: 'abo' })
const box = stage.els['#an-zielgruppe']
const knopf = stage.els['#an-on']
box.checked = true
box.handlers.change()
assert.equal(knopf.disabled, false)
box.checked = false
box.handlers.change()
assert.equal(knopf.disabled, true)
box.checked = true
box.handlers.change()
await knopf.handlers.click()
assert.deepEqual(posts, [{ url: '/api/funktionen/analyse/activate', body: { projectId: 'p1', accentColor: '#111111', consent: { accepted: true, avvVersion: '2026-10-02' }, audienceConfirmed: true } }])
const ohne = await lauf({ modell: 'abo', projects: [offProject('p1', { controller: { ok: false, company: '', address: '', email: '', missing: ['firma'] } })] })
ohne.stage.els['#an-zielgruppe'].checked = true
ohne.stage.els['#an-zielgruppe'].handlers.change()
assert.equal(ohne.stage.els['#an-on'].disabled, true)
})
test('Abo: kein_abo nennt Hosting- und Wartungs-Abo mit Link zu Verträge (A3), nicht den Satz des Servers', async () => {
const { stage } = await lauf({ modell: 'abo', projects: [offProject('p1', KEIN_ABO)] })
assert.match(stage.innerHTML, /Die Analyse ist im Hosting- und im Wartungs-Abo enthalten\./)
assert.match(stage.innerHTML, /data-view="vertraege"/)
assert.doesNotMatch(stage.innerHTML, /Alter Satz des Servers/)
})
test('Abo: ein anderer Grund zeigt weiter den Satz des Servers, ohne Link', async () => {
const { stage } = await lauf({ modell: 'abo', projects: [offProject('p1', { eligible: false, reason: 'keine_website', reasonText: 'Es fehlt die Website.' })] })
assert.match(stage.innerHTML, /Es fehlt die Website\./)
assert.doesNotMatch(stage.innerHTML, /data-view="vertraege"/)
})
test('Ohne modell (fehlt, „Abo“, „abo “): der lange Zustimmungssatz mit #an-avv, Körper ohne audienceConfirmed, bei kein_abo der Satz des Servers', async () => {
for (const modell of [undefined, 'Abo', 'abo ']) {
const a = await lauf({ modell })
assert.match(a.stage.innerHTML, /id="an-avv"/)
assert.match(a.stage.innerHTML, /Ich beauftrage WEBklar/)
assert.doesNotMatch(a.stage.innerHTML, /an-zielgruppe/)
a.stage.els['#an-avv'].checked = true
a.stage.els['#an-avv'].handlers.change()
await a.stage.els['#an-on'].handlers.click()
assert.deepEqual(a.posts[0].body, { projectId: 'p1', accentColor: '#111111', consent: { accepted: true, avvVersion: '2026-10-02' } })
const b = await lauf({ modell, projects: [offProject('p1', KEIN_ABO)] })
assert.match(b.stage.innerHTML, /Alter Satz des Servers/)
}
})
test('wunschProjekt aus dem Behälter wählt das Projekt und wird entfernt', async () => {
const { root, stage } = await lauf({ modell: 'abo', projects: [offProject('p1'), offProject('p2')], dataset: { wunschProjekt: 'p2' }, projectId: '' })
assert.match(stage.innerHTML, /Analyse für Muster p2 einschalten/)
assert.equal(root.dataset.wunschProjekt, undefined)
})
test('Abo, aktive Analyse: keine eigene Leiste, kn:reiter mit den drei Reitern, Panel mit aria-label; bei() wechselt und meldet den neuen aktiven Reiter', async () => {
assert.equal(typeof CustomEvent, 'function', 'meldeReiter braucht CustomEvent (Node 20: global)')
const { root, stage, kn } = await lauf({ modell: 'abo', projects: [aktivProjekt()] })
assert.doesNotMatch(root.innerHTML, /role="tablist"|an-tabbtn|aria-labelledby/)
assert.match(root.innerHTML, /id="an-stage" role="tabpanel" aria-label="Übersicht"/)
const erst = kn.reiter.at(-1)
assert.deepEqual(erst.eintraege, [{ key: 'uebersicht', label: 'Übersicht' }, { key: 'heatmap', label: 'Heatmap' }, { key: 'einstellungen', label: 'Einstellungen' }])
assert.equal(erst.aktiv, 'uebersicht')
erst.bei('heatmap')
await new Promise((r) => setImmediate(r))
assert.equal(kn.reiter.at(-1).aktiv, 'heatmap')
assert.equal(stage.attribute['aria-label'], 'Heatmap')
assert.equal(analyseSel.tab, 'heatmap')
})
test('Abo, aktive Analyse mit gemerktem Reiter: das Panel nennt ihn, die Meldung nennt ihn als aktiv', async () => {
const { root, kn } = await lauf({ modell: 'abo', projects: [aktivProjekt()], tab: 'heatmap' })
assert.match(root.innerHTML, /id="an-stage" role="tabpanel" aria-label="Heatmap"/)
assert.equal(kn.reiter.at(-1).aktiv, 'heatmap')
})
test('Abo ohne Cockpit (Einschalten, abgeschaltet, nicht verfügbar, keine Website): leere Reiterliste, „Zurück“ füllt die Zeile', async () => {
for (const projects of [[offProject()], [offProject('p1', { state: 'disabled' })], [offProject('p1', KEIN_ABO)], []]) {
const { kn } = await lauf({ modell: 'abo', projects })
assert.deepEqual(kn.reiter.at(-1)?.eintraege, [], JSON.stringify(projects.map((p) => p.analyse.state)))
}
})
test('Ohne modell (fehlt, „Abo“): eigene Leiste mit denselben drei Labels, aria-labelledby, kein kn:reiter', async () => {
for (const modell of [undefined, 'Abo']) {
const { root, kn } = await lauf({ modell, projects: [aktivProjekt()] })
assert.deepEqual([...root.innerHTML.matchAll(/role="tab"[^>]*>([^<]+)</g)].map((m) => m[1]), ['Übersicht', 'Heatmap', 'Einstellungen'])
assert.match(root.innerHTML, /role="tablist"/)
assert.match(root.innerHTML, /id="an-stage" role="tabpanel" aria-labelledby="an-tab-uebersicht"/)
assert.equal(kn.reiter.length, 0)
}
})