Files
Termind/TFMTests/ColorSystemTests.swift
T
2026-09-16 23:10:52 +08:00

107 lines
3.4 KiB
Swift

import AppKit
import SwiftUI
import TermindApplication
import TermindDomain
import TermindInfrastructure
import TermindPersistence
import TermindPresentation
import TermindRemoteInfrastructure
import Testing
@testable import Termind
@MainActor
@Suite
struct ColorSystemTests {
@Test
func neutralPaletteAdaptsBetweenAquaAndDarkAqua() throws {
let providers: [() -> Color] = [
{ TermindColor.windowSurface },
{ TermindColor.contentSurface },
{ TermindColor.textSurface },
{ TermindColor.primaryForeground },
{ TermindColor.secondaryForeground },
{ TermindColor.separator },
]
for provider in providers {
let light = try resolved(provider(), appearance: .aqua)
let dark = try resolved(provider(), appearance: .darkAqua)
#expect(colorDistance(light, dark) > 0.04)
}
}
@Test
func primaryTextKeepsReadableContrastOnTheWindowSurface() throws {
for appearance in [NSAppearance.Name.aqua, .darkAqua] {
let foreground = try resolved(
TermindColor.primaryForeground,
appearance: appearance
)
let background = try resolved(
TermindColor.windowSurface,
appearance: appearance
)
#expect(contrastRatio(foreground, background) >= 4.5)
}
}
@Test
func markdownThemeIsConcreteForItsRequestedAppearance() throws {
let lightValue = TermindMarkdownTheme.make(
tone: .primary,
colorScheme: .light
)
let darkValue = TermindMarkdownTheme.make(
tone: .primary,
colorScheme: .dark
)
let light = try #require(
lightValue.theme.colors.body.usingColorSpace(.deviceRGB)
)
let dark = try #require(
darkValue.theme.colors.body.usingColorSpace(.deviceRGB)
)
#expect(lightValue.colorScheme == .light)
#expect(darkValue.colorScheme == .dark)
#expect(colorDistance(light, dark) > 0.5)
}
private func resolved(
_ color: Color,
appearance name: NSAppearance.Name
) throws -> NSColor {
let appearance = try #require(NSAppearance(named: name))
var result: NSColor?
appearance.performAsCurrentDrawingAppearance {
result = NSColor(color).usingColorSpace(.deviceRGB)
}
return try #require(result)
}
private func colorDistance(_ lhs: NSColor, _ rhs: NSColor) -> CGFloat {
let dr = lhs.redComponent - rhs.redComponent
let dg = lhs.greenComponent - rhs.greenComponent
let db = lhs.blueComponent - rhs.blueComponent
return sqrt(dr * dr + dg * dg + db * db)
}
private func contrastRatio(_ lhs: NSColor, _ rhs: NSColor) -> CGFloat {
let lighter = max(luminance(lhs), luminance(rhs))
let darker = min(luminance(lhs), luminance(rhs))
return (lighter + 0.05) / (darker + 0.05)
}
private func luminance(_ color: NSColor) -> CGFloat {
func linear(_ value: CGFloat) -> CGFloat {
value <= 0.04045
? value / 12.92
: pow((value + 0.055) / 1.055, 2.4)
}
return 0.2126 * linear(color.redComponent)
+ 0.7152 * linear(color.greenComponent)
+ 0.0722 * linear(color.blueComponent)
}
}