Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
101 lines
3.2 KiB
Swift
101 lines
3.2 KiB
Swift
import AppKit
|
|
import SwiftUI
|
|
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)
|
|
}
|
|
}
|