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
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die README; an keiner Stelle Bedeutung, nur Zeichensetzung.
103 lines
4.5 KiB
TypeScript
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\)/)
|
|
})
|
|
})
|