Files
AfiS-Aktensystem/web/tests/design-tokens.test.ts
T
Harlekin e0b03ea336
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

103 lines
4.5 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
const css = readFileSync(join(process.cwd(), 'app/globals.css'), 'utf8')
describe('Design-Tokens', () => {
it('enthaelt die Grundpalette aus dem Design-System', () => {
for (const hex of ['#15181a', '#1d2124', '#262b2f', '#3a4147', '#e4e7e4', '#939c99', '#7a97a6']) {
expect(css).toContain(hex)
}
})
it('enthaelt alle sechs Klassifizierungsfarben', () => {
for (const hex of ['#8a918d', '#6d8a9c', '#4e8f72', '#b08637', '#b4543c', '#7e5e96']) {
expect(css).toContain(hex)
}
})
it('haelt Flaechen eckig und Bedienelemente rund', () => {
// Die Flaechenradien bleiben bei 2 px: eine Akte ist ein Blatt.
const flaechen = [...css.matchAll(/--radius-(?!field|control)[\w-]+:\s*([^;]+);/g)].map((m) =>
m[1]!.trim(),
)
expect(flaechen.length).toBeGreaterThan(0)
for (const value of flaechen) expect(['0px', '2px']).toContain(value)
// Und daneben genau zwei Radien fuer Bedienelemente.
expect(css).toMatch(/--radius-field:\s*10px;/)
expect(css).toMatch(/--radius-control:\s*999px;/)
})
it('gibt jedem Knopf Rahmen, Ueberfahren und Druckrueckmeldung', () => {
const knopf = css.slice(css.indexOf('.btn {'), css.indexOf('.btn-primary'))
expect(knopf).toContain('border: 1px solid var(--color-line)')
expect(knopf).toContain('border-radius: var(--radius-control)')
// Ueberfahren und Druecken sind beide beschrieben ...
expect(css).toMatch(/\.btn:hover/)
expect(css).toMatch(/\.btn:active/)
// ... und der Druck setzt ohne Verzoegerung ein, sonst bliebe ein
// schneller Klick unsichtbar.
const gedrueckt = css.slice(css.indexOf('.btn:active'))
expect(gedrueckt.slice(0, 400)).toContain('transition-duration: 0ms')
expect(gedrueckt.slice(0, 400)).toContain('inset 0 0 0')
})
it('haelt Beschriftungen weiter weg als die Polsterung des Feldes', () => {
// Die Regel, aus der die Zahlen folgen: eine Beschriftung, die naeher am
// Rahmen steht als der Inhalt des Rahmens an demselben Rahmen, wirkt
// angeklebt. Also muss der Abstand nach aussen groesser sein als die
// Polsterung nach innen. Geprueft wird die Regel, nicht der Wert - wer die
// Polsterung aendert, faellt hier auf.
const feld = css.slice(css.indexOf('.field {'), css.indexOf('.field:hover'))
const polsterung = /padding:\s*([\d.]+)rem\s+([\d.]+)rem/.exec(feld)
expect(polsterung, feld).not.toBeNull()
const [innenSenkrecht, innenWaagerecht] = [
Number(polsterung![1]),
Number(polsterung![2]),
]
const label = css.slice(css.indexOf('.field-label {'))
const unten = /margin-block-end:\s*([\d.]+)rem/.exec(label.slice(0, 300))
expect(unten, label.slice(0, 300)).not.toBeNull()
expect(Number(unten![1])).toBeGreaterThan(innenSenkrecht)
const reihe = css.slice(css.indexOf('.field-row {'))
const abstand = /gap:\s*([\d.]+)rem/.exec(reihe.slice(0, 300))
expect(abstand, reihe.slice(0, 300)).not.toBeNull()
expect(Number(abstand![1])).toBeGreaterThan(innenWaagerecht)
})
it('gibt jeder Spalte einer Listenzeile eine feste Breite', () => {
// Ohne feste Breiten bestimmt jede Zeile ihre Spalten selbst, und der
// Titel fängt in jeder Zeile woanders an. Geprüft wird, dass die Breiten
// an **einer** Stelle stehen und nicht an den Aufrufstellen.
for (const klasse of ['.record-key', '.record-label', '.record-meta']) {
const block = css.slice(css.indexOf(`${klasse} {`))
expect(block.slice(0, 400), klasse).toMatch(/width:\s*var\(--record-[a-z]+,/)
expect(block.slice(0, 400), klasse).toContain('flex: none')
}
// Die Stempelmarke ist überall gleich breit, sonst lesen sich
// unterschiedliche Namen als Unordnung statt als Unterschied.
const stempel = css.slice(css.indexOf('.stamp-mark {'))
expect(stempel.slice(0, 500)).toMatch(/width:\s*var\(--stamp-width,/)
// Und auf schmalen Schirmen fallen sie weg, statt überzulaufen.
expect(css).toMatch(/@media \(max-width: 40rem\)/)
})
it('kennt keine Schatten ausser dem Fokusring', () => {
const shadows = [...css.matchAll(/--shadow-[\w-]+:\s*([^;]+);/g)].map((m) => m[1]!.trim())
expect(shadows.length).toBeGreaterThan(0)
for (const value of shadows) expect(value).toBe('none')
})
it('respektiert prefers-reduced-motion bei der Schwaerzung', () => {
expect(css).toMatch(/@media \(prefers-reduced-motion: reduce\)/)
})
})