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

105 lines
8.9 KiB
JavaScript

import { test } from 'node:test'
import assert from 'node:assert/strict'
import { installFakeDom } from './support/fakeDom.js'
import { balkenMasse, overlayPasst, schriftStufe, sichtbarMachen, anzeigeText, baueReiter } from '../public/neu/reiter.js'
test('Zahlenfunktionen: Balken, Overlay-Rückfall, Schriftstufe, Sichtbarmachen (C2)', () => {
const basis = [{ links: 0, breite: 90 }, { links: 90, breite: 100 }, { links: 190, breite: 60 }, { links: 250, breite: 90 }]
assert.deepEqual(balkenMasse({ basis, aktiv: 1, gesamtBreite: 340 }), { links: 90, breite: 100, overlayVersatz: -90, overlayBreite: 340 })
assert.deepEqual([overlayPasst([90, 100], [90, 100.4]), overlayPasst([90, 100], [90, 101])], [true, false])
assert.deepEqual([schriftStufe({ inhaltsBreite: 342, verfuegbar: 342 }), schriftStufe({ inhaltsBreite: 343, verfuegbar: 342 })], [15, 14])
const s = (scrollLinks, itemLinks) => sichtbarMachen({ scrollLinks, sichtbarBreite: 300, itemLinks, itemBreite: 80 })
assert.deepEqual([s(0, 420), s(200, 120), s(100, 150), s(50, 5)], [216, 104, 100, 0])
})
test('anzeigeText: Zahl, "!" und Deckel 99+ (Review Focus 3)', () => {
assert.deepEqual([0, 7, 99, 100, 1500].map((zahl) => anzeigeText({ zahl })), ['', '7', '99', '99+', '99+'])
assert.deepEqual([anzeigeText({ zahl: 3, ausruf: true }), anzeigeText({ zahl: 0, ausruf: true })], ['!', '!'])
})
test('baueReiter: Rollen, Auswahl, Kurzlabel, Pfeiltasten, Anzeige mit Beschriftung', async () => {
const fx = installFakeDom()
try {
const gew = []
const eintraege = [{ key: 'termine', label: 'Termine' }, { key: 'team', label: 'Team & Plätze', kurz: 'Team' }, { key: 'chat', label: 'Chat', einheit: 'ungelesen' }]
const r = baueReiter({ eintraege, aktiv: 'termine', art: 'balken', ariaLabel: 'Bereiche', bei: (k) => gew.push(k), warte: (fn) => fn() })
const tabs = fx.alle(r, '[role=tab]')
assert.deepEqual(tabs.map((t) => t.textContent), ['Termine', 'Team', 'Chat']); assert.equal(tabs[1].getAttribute('aria-label'), 'Team & Plätze')
assert.deepEqual(tabs.map((t) => t.getAttribute('aria-selected')), ['true', 'false', 'false']); assert.equal(tabs[0].getAttribute('aria-current'), 'page')
assert.equal(fx.alle(r, '.kn-overlay [role=tab]').length, 0)
await fx.klick(tabs[1]); await fx.sende(tabs[1], 'keydown', { key: 'ArrowRight' }); assert.deepEqual(gew, ['team', 'chat'])
r.setzeAktiv('chat'); assert.equal(tabs[2].getAttribute('aria-selected'), 'true')
r.setzeAnzeige('chat', { zahl: 2 })
assert.deepEqual([fx.finde(tabs[2], '.kn-zahl').textContent, tabs[2].getAttribute('aria-label')], ['2', 'Chat, 2 ungelesen'])
r.setzeAnzeige('chat', { zahl: 0 }); assert.deepEqual([fx.finde(tabs[2], '.kn-zahl'), tabs[2].hasAttribute('aria-label')], [null, false])
} finally { fx.restore() }
})
test('baueReiter: Randfälle (ohne aktiven Eintrag, Home/End/Umbruch, Unterstrich-Art, Spiegelung im Overlay, Zahl kehrt vor dem Entfernen zurück)', async () => {
assert.deepEqual(balkenMasse({ basis: [{ links: 0, breite: 90 }], aktiv: -1, gesamtBreite: 340 }), { links: 0, breite: 0, overlayVersatz: 0, overlayBreite: 340 })
assert.ok(Object.is(balkenMasse({ basis: [{ links: 0, breite: 90 }], aktiv: 0, gesamtBreite: 340 }).overlayVersatz, 0))
const fx = installFakeDom()
try {
const gew = []; const spaeter = []
const eintraege = [{ key: 'a', label: 'A' }, { key: 'b', label: 'B', einheit: 'offen', ausrufText: 'Zahlung fehlgeschlagen' }, { key: 'c', label: 'C' }]
const r = baueReiter({ eintraege, aktiv: 'zzz', art: 'balken', bei: (k) => gew.push(k), warte: (fn) => spaeter.push(fn) })
const tabs = fx.alle(r, '[role=tab]'); const kopien = fx.alle(r, '.kn-overlay .kn-t')
assert.deepEqual([tabs.map((t) => t.getAttribute('aria-selected')), tabs.map((t) => t.getAttribute('tabindex'))], [['false', 'false', 'false'], ['0', '-1', '-1']]) // ohne Treffer bleibt der erste Reiter per Tab erreichbar
assert.deepEqual([r.getAttribute('data-art'), fx.finde(r, '.kn-overlay').parentNode.getAttribute('aria-hidden'), kopien.map((k) => k.textContent)], ['balken', 'true', ['A', 'B', 'C']])
await fx.sende(tabs[0], 'keydown', { key: 'ArrowLeft' }); await fx.sende(tabs[2], 'keydown', { key: 'ArrowRight' }); await fx.sende(tabs[1], 'keydown', { key: 'Home' }); await fx.sende(tabs[1], 'keydown', { key: 'End' }); await fx.sende(tabs[1], 'keydown', { key: 'x' })
assert.deepEqual(gew, ['c', 'a', 'a', 'c'])
r.setzeAnzeige('b', { zahl: 150 }); assert.deepEqual([fx.finde(tabs[1], '.kn-zahl').textContent, fx.finde(kopien[1], '.kn-zahl').textContent, tabs[1].getAttribute('aria-label')], ['99+', '99+', 'B, 99+ offen'])
r.setzeAnzeige('b', { zahl: 5, ausruf: true }); assert.deepEqual([fx.finde(tabs[1], '.kn-zahl').textContent, tabs[1].getAttribute('aria-label')], ['!', 'B, Zahlung fehlgeschlagen'])
r.setzeAnzeige('b', { zahl: 0 }); assert.ok(fx.finde(tabs[1], '.kn-zahl').classList.contains('kn-zahl--weg')); assert.equal(spaeter.length, 2) // erst nach 260 ms weg
r.setzeAnzeige('b', { zahl: 3 }); spaeter.splice(0).forEach((fn) => fn()) // der alte Timer darf die neue Zahl nicht löschen
assert.deepEqual([fx.alle(tabs[1], '.kn-zahl').map((z) => z.textContent), fx.alle(tabs[1], '.kn-zahl--weg').length], [['3'], 0])
const u = baueReiter({ eintraege, aktiv: 'b', ariaLabel: 'Funktionen', bei: () => {} })
assert.deepEqual([u.className, u.getAttribute('role'), u.getAttribute('aria-label'), fx.alle(u, '.kn-reiter-t').length, fx.alle(u, '.kn-overlay').length], ['kn-reiter', 'tablist', 'Funktionen', 3, 0])
} finally { fx.restore() }
})
test('messen: Gleiter, Overlay-Gegenbewegung, Schriftstufe, Rückfall und Sichtbarmachen mit nachgestellter Geometrie', () => {
const fx = installFakeDom()
try {
const eintraege = ['a', 'b', 'c', 'd', 'e', 'f'].map((key) => ({ key, label: key.toUpperCase() }))
const r = baueReiter({ eintraege, aktiv: 'a', art: 'balken', bei: () => {} })
fx.document.body.append(r)
const basis = fx.finde(r, '.kn-balken-basis'); const gleiter = fx.finde(r, '.kn-gleiter'); const overlay = fx.finde(r, '.kn-overlay')
const tabs = fx.alle(r, '[role=tab]'); const kopien = fx.alle(overlay, '.kn-t')
for (const el of [gleiter, overlay]) el.style = {}
gleiter.offsetWidth = 0
r.clientWidth = 300; r.scrollLeft = 0; basis.scrollWidth = 600
r.getBoundingClientRect = () => ({ left: 10, width: 300 }); basis.getBoundingClientRect = () => ({ left: 10 - r.scrollLeft, width: 600 })
tabs.forEach((t, i) => { t.getBoundingClientRect = () => ({ left: 10 - r.scrollLeft + i * 100, width: 100 }) })
kopien.forEach((k) => { k.getBoundingClientRect = () => ({ left: 0, width: 100 }) })
r.messen()
assert.deepEqual([r.classList.contains('kn-schrift-klein'), r.classList.contains('kn-fb')], [true, false]) // 600 > 300: Schrift 14 px, alle Wörter gleich breit: Overlay bleibt
assert.deepEqual([gleiter.style.width, gleiter.style.transform, overlay.style.width, overlay.style.transform], ['100px', 'translateX(0px)', '600px', 'translateX(0px)'])
r.setzeAktiv('c')
assert.deepEqual([gleiter.style.transform, overlay.style.transform, gleiter.style.transition], ['translateX(200px)', 'translateX(-200px)', ''])
r.setzeAktiv('f'); assert.equal(r.scrollLeft, 316) // 500 + 100 + 16 - 300
r.setzeAktiv('a'); assert.equal(r.scrollLeft, 0)
basis.scrollWidth = 300; kopien[2].getBoundingClientRect = () => ({ left: 0, width: 103 }); r.messen()
assert.deepEqual([r.classList.contains('kn-schrift-klein'), r.classList.contains('kn-fb')], [false, true]) // passt, aber das Overlay weicht ab: Rückfall
r.setzeAktiv('zzz'); assert.equal(gleiter.style.width, '0px')
} finally { fx.restore() }
})
test('messen: die erste Platzierung springt, erst die folgenden Wechsel federn', () => {
const fx = installFakeDom()
try {
const r = baueReiter({ eintraege: ['a', 'b'].map((key) => ({ key, label: key })), aktiv: 'a', art: 'balken', bei: () => {} })
fx.document.body.append(r)
const gleiter = fx.finde(r, '.kn-gleiter'); const overlay = fx.finde(r, '.kn-overlay'); const basis = fx.finde(r, '.kn-balken-basis'); const tabs = fx.alle(r, '[role=tab]')
const verlauf = []
gleiter.style = new Proxy({}, { set(ziel, name, wert) { if (name === 'transition') verlauf.push(wert); ziel[name] = wert; return true } }); overlay.style = {}; gleiter.offsetWidth = 0
r.clientWidth = 200; r.scrollLeft = 0; basis.scrollWidth = 200
r.getBoundingClientRect = () => ({ left: 0, width: 200 }); basis.getBoundingClientRect = () => ({ left: 0, width: 200 })
tabs.forEach((t, i) => { t.getBoundingClientRect = () => ({ left: i * 100, width: 100 }) })
fx.alle(overlay, '.kn-t').forEach((k) => { k.getBoundingClientRect = () => ({ left: 0, width: 100 }) })
r.setzeAktiv('b'); assert.deepEqual(verlauf, ['none', '']) // erste Platzierung: ohne Übergang
r.setzeAktiv('a'); assert.deepEqual(verlauf, ['none', '']) // danach mit Übergang (die Feder des CSS läuft)
assert.equal(gleiter.style.transform, 'translateX(0px)')
} finally { fx.restore() }
})