mana-swift-core/Tests/ManaTokensTests/ThemeTests.swift
Till JS 39246a922f feat(themes): kollekta als 21. Signatur-Theme + frischgruen abgedunkelt
- ManaTheme.kollekta ergänzt (Label/Charakter/Signatur-App/-URL 1:1 aus
  dem Web-SOT packages/themes/src/index.ts), GeneratedThemes per Generator
  nachgezogen → 21 Apps ↔ 21 Themes
- frischgruen-Primary (Nutriphi) dunkler: light 142 62% 31% → 64% 25%,
  dark 142 60% 52% → 58% 43%
- ThemeTests-Snapshot auf 21 Variants aktualisiert (kollekta eingefügt)
- CHANGELOG [Unreleased] ergänzt

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 16:47:13 +02:00

121 lines
4.6 KiB
Swift

import SwiftUI
import Testing
@testable import ManaTokens
@Suite("ManaTheme Variants")
struct ThemeTests {
@Test("Alle 21 Variants sind im enum vorhanden")
func allCasesPresent() {
// Explizite Snapshot-Liste: fängt versehentliche Renames/Entfernungen.
// Eine neue Variant (z. B. Theme-Pass-Signatur-App) muss hier bewusst
// nachgetragen werden — gewollte Friktion. Seit 2026-06-03: N Apps ↔
// N Themes, keine Baseline-Variants mehr (forest/neutral/monochrome
// entfernt, schaenke = Who-Signatur ergänzt). 2026-06-08: kollekta =
// Kollekta-Signatur ergänzt → 21.
let cases = ManaTheme.allCases.map(\.rawValue).sorted()
#expect(cases == [
"bodensee",
"buchseite",
"buehne",
"frischgruen",
"garderobe",
"herbar",
"hoerweg",
"kollekta",
"kreisel",
"lume",
"mana",
"panel",
"paper",
"pinnwand",
"schaenke",
"skylight",
"spektrum",
"twilight",
"waldlichtung",
"wegspur",
"wolke",
])
}
@Test("Jede Variant liefert nicht-leere Colors")
func everyVariantHasColors() {
for variant in ManaTheme.allCases {
let colors = variant.colors
// Existenz-Smoke — Color hat keinen Equatable-Color-Vergleich,
// aber wir können die Properties gegen Optional/Crash absichern.
let _: Color = colors.background
let _: Color = colors.foreground
let _: Color = colors.surface
let _: Color = colors.surfaceHover
let _: Color = colors.muted
let _: Color = colors.mutedForeground
let _: Color = colors.border
let _: Color = colors.primary
let _: Color = colors.primaryForeground
let _: Color = colors.error
let _: Color = colors.success
let _: Color = colors.warning
}
}
@Test("Convenience-Accessor matcht colors-Accessor")
func convenienceMatchesColors() {
// Identitäts-Smoke — beide Pfade müssen denselben Color-Wert liefern.
// Color ist nicht Equatable, also testen wir, dass es überhaupt
// möglich ist, beide Pfade typkorrekt zu nutzen.
let theme = ManaTheme.schaenke
let direct = theme.background
let viaColors = theme.colors.background
let _: (Color, Color) = (direct, viaColors)
}
@Test("schaenke.primary entspricht schaenke.css (10, 72, 42)")
func schaenkePrimaryMatchesSpec() {
// Wir rekonstruieren den erwarteten Light-Wert aus HSL und
// vergleichen mit dem, was `Color.manaToken` für denselben Input
// liefert. Da `Color`-Vergleich nicht direkt möglich ist, prüfen
// wir indirekt über PlatformColor.fromHSL.
let expected = PlatformColor.fromHSL(10, 72, 42)
let components = rgbComponents(of: expected)
// Schänke-Primary Light: HSL(10, 72, 42) → ~RGB(0.722, 0.218, 0.118)
#expect(approxEqual(components.r, 0.722, tolerance: 0.02))
#expect(approxEqual(components.g, 0.218, tolerance: 0.02))
#expect(approxEqual(components.b, 0.118, tolerance: 0.02))
}
@Test("ManaTheme ist via rawValue rekonstruierbar")
func rawValueRoundtrip() {
for variant in ManaTheme.allCases {
let raw = variant.rawValue
let roundtrip = ManaTheme(rawValue: raw)
#expect(roundtrip == variant)
}
}
@Test("Environment-Default ist mana")
func environmentDefault() {
// Wir können das Environment hier nicht direkt anzapfen ohne
// einen Host-View, aber wir spiegeln das Default per Konstante:
// ManaThemeEnvironmentKey.defaultValue ist auf .mana gesetzt.
// Smoke-Test, der die Konvention dokumentiert.
#expect(ManaTheme.allCases.contains(.mana))
}
}
// MARK: - Helpers
private func rgbComponents(of color: PlatformColor) -> (r: Double, g: Double, b: Double) {
#if canImport(UIKit)
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
return (Double(r), Double(g), Double(b))
#else
let space = color.usingColorSpace(.deviceRGB) ?? color
return (Double(space.redComponent), Double(space.greenComponent), Double(space.blueComponent))
#endif
}
private func approxEqual(_ a: Double, _ b: Double, tolerance: Double = 0.001) -> Bool {
abs(a - b) < tolerance
}