136 lines
8.2 KiB
JavaScript
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)
|
|
}
|
|
})
|