- public/neu/glas-logo.js: das Bauteil „liquid-glass-logo-reveal“ (21st.dev, Shader und Zeitleiste aus zanwei/glass-icon-reveal, MIT, Hinweis im Kopf) in reines JavaScript übertragen: WEBklar-Zeichen als Zeichenfläche (Stern und Punkt ausgestanzt), weiß, keine Schleife, Textur 512 px (auf dem Handy ~3× schneller bereit), Hintergrund läuft zum Rand aus, kein Rauschen auf reinem Weiß, ohne WebGL2 das ruhende Logo als CSS-Maske. - Anmeldung (Handy und PC): Glas-Logo statt App-Icon, der Name erscheint, sobald das Glas losgelassen hat; ein Klick spielt es erneut; reduzierte Bewegung zeigt das ruhende Logo. - Installierte App: startbild.js im Kopf von Übersicht und Anmeldung deckt beim ersten Öffnen je App-Start weiß ab, spielt das Glas-Logo (Tempo 1,25) und blendet dann weich aus; ein Tipp überspringt, spätestens nach 6,5 s ist es weg. Danach ruht das Logo der Anmeldung. - Tests: knGlasLogo (Zeitleiste, Glasbreite < 1,3 Kacheln, MIT-Hinweis, Startbild in der VM), Seitenprüfungen der Anmeldung und der Übersicht kennen die neuen Skripte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
54 lines
5.0 KiB
JavaScript
54 lines
5.0 KiB
JavaScript
import { test } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { readFileSync } from 'node:fs'
|
|
import { sichtbarerText } from './support/knWortlautOrakel.js'
|
|
|
|
const lese = (p) => readFileSync(new URL(`../public/${p}`, import.meta.url), 'utf8')
|
|
const PAARE = ['login', 'kuendigen', 'widerrufen', 'auftrag', 'offline'].map((n) => [`${n}.html`, `neu/oeffentlich/${n}.html`])
|
|
const koerper = (html) => html.slice(html.search(/<body/i)) // Metas und Links im Kopf ändern sich bewusst (Stil, theme-color), geprüft werden sie unten
|
|
const attribute = (html) => [...koerper(html).matchAll(/\s(name|id|value|type|autocomplete|maxlength|required|min|max|step|inputmode|placeholder|enterkeyhint|data-text|data-label|data-label-for)(?:="([^"]*)")?/g)].map((m) => `${m[1]}=${m[2] ?? ''}`).sort()
|
|
const skripte = (html) => [...html.matchAll(/<script[^>]*>[\s\S]*?<\/script>/g)].map((m) => m[0].replace(/\s+/g, ' '))
|
|
// Eigene Zusätze der neuen Anmeldung, die die alte Seite nicht hat: Glas-Logo (login-logo.js) und Startbild der App (startbild.js)
|
|
const ZUSAETZE = ['<script src="/neu/startbild.js"></script>', '<script type="module" src="/neu/login-logo.js"></script>']
|
|
const ohneZusaetze = (html) => ZUSAETZE.reduce((h, z) => h.replace(z, ''), html)
|
|
for (const [alt, neu] of PAARE) {
|
|
test(`${neu}: gleicher sichtbarer Text, gleiche Felder, gleiche Skripte wie ${alt}`, () => {
|
|
// Kündigen und Widerrufen sprechen seit P7 du (Fassung 6, C14): ihr Text steht in test/knOeffentlichDu.test.js; Felder und Skripte werden weiter verglichen
|
|
const n = ohneZusaetze(lese(neu))
|
|
if (!['kuendigen.html', 'widerrufen.html'].includes(alt)) assert.equal(sichtbarerText(n), sichtbarerText(lese(alt)))
|
|
assert.deepEqual(attribute(n), attribute(lese(alt))); assert.deepEqual(skripte(n), skripte(lese(alt)))
|
|
})
|
|
test(`${neu}: neuer Stil, hell, ohne Verläufe, Lila und Dunkelmodus`, () => {
|
|
const n = lese(neu)
|
|
assert.match(n, /<meta name="theme-color" content="#ffffff">/); assert.equal((n.match(/name="theme-color"/g) || []).length, 1)
|
|
assert.doesNotMatch(n, /aurora|dot-background|#9d3dfe|gradient|\/login\.css|\/legal-form\.css|\/auftrag\.css|prefers-color-scheme|content="light dark"/)
|
|
if (neu.endsWith('offline.html')) assert.doesNotMatch(n, /<link|(?:src|href)="https?:/); else assert.match(n, /href="\/neu\/tokens\.css"[\s\S]*href="\/neu\/oeffentlich\/oeffentlich\.css"/)
|
|
})
|
|
}
|
|
test('Login: Skript lädt app.js und startet initLoginPage, Statusleiste hell', () => {
|
|
const n = lese('neu/oeffentlich/login.html'); assert.match(n, /<script src="\/app\.js"><\/script>/); assert.match(n, /initLoginPage\(\)/); assert.match(n, /apple-mobile-web-app-status-bar-style" content="default"/)
|
|
})
|
|
test('Login: Glas-Logo statt App-Icon (Handy und PC), Startbild der App als letztes im Kopf, Logo-Skript nach initLoginPage', () => {
|
|
const n = lese('neu/oeffentlich/login.html')
|
|
assert.match(n, /<script src="\/neu\/startbild\.js"><\/script>\s*<\/head>/)
|
|
assert.match(n, /<div class="kn-login-marke">\s*<div class="kn-login-glas"><\/div>\s*<span>WEBklar Kundenbereich<\/span>/)
|
|
assert.doesNotMatch(n, /app-icon-192\.png" alt=""/)
|
|
assert.match(n, /initLoginPage\(\);\s*<\/script>\s*<script type="module" src="\/neu\/login-logo\.js"><\/script>\s*<\/body>/)
|
|
const css = lese('neu/oeffentlich/oeffentlich.css')
|
|
assert.doesNotMatch(css, /\.kn-login-marke\s*\{\s*display:\s*none/, 'die Marke steht jetzt auch am PC')
|
|
assert.match(css, /\.kn-login-glas\s*\{[^}]*height:\s*220px/); assert.match(css, /\.kn-login-glas\s*\{\s*height:\s*190px;\s*\}/)
|
|
// Der Name erscheint erst nach dem Glas, sofort wenn das Logo ruht (nach dem Startbild) oder bei reduzierter Bewegung
|
|
assert.match(css, /\.kn-login-marke > span\s*\{[^}]*animation:\s*kn-login-name[^}]*\}/)
|
|
assert.match(css, /\.kn-login-marke\.kn-login-ruht > span\s*\{\s*animation:\s*none;\s*\}/)
|
|
assert.match(css, /@media \(prefers-reduced-motion: reduce\)\s*\{\s*\.kn-login-marke > span\s*\{\s*animation:\s*none;\s*\}\s*\}/)
|
|
})
|
|
test('Auftragsseite: Token bleibt geschützt (noindex, kein Referrer), Rückweg, Wurzel und Modul bleiben, hell, jede Klasse hat eine Regel', () => {
|
|
const n = lese('neu/oeffentlich/auftrag.html')
|
|
assert.match(n, /<meta name="robots" content="noindex, nofollow">/); assert.match(n, /<meta name="referrer" content="no-referrer">/); assert.match(n, /<meta name="color-scheme" content="light">/)
|
|
assert.match(n, /<body class="ab-seite">/); assert.match(n, /<a class="ab-app-back" href="\/dashboard\.html">/); assert.match(n, /id="ab-wurzel"/); assert.match(n, /id="ab-ansage"/); assert.match(n, /<script type="module" src="\/auftrag\.js"><\/script>/)
|
|
const klassen = new Set([...lese('auftrag.css').replace(/\/\*[\s\S]*?\*\//g, '').matchAll(/\.ab-seite[^{,]*?\.(ab-[a-z0-9-]+)/g)].map((m) => m[1])); klassen.add('ab-app-back') // Alle Klassen, die die alte Seite stylt (ohne Kommentare)
|
|
const css = lese('neu/oeffentlich/oeffentlich.css')
|
|
for (const k of klassen) assert.match(css, new RegExp(`\\.${k}(?![\\w-])`), `Klasse ohne Regel: ${k}`)
|
|
assert.match(css, /body\.ab-seite[^{]*\{[^}]*--ab-knopf:/)
|
|
})
|