diff --git a/macos/GhosttyUITests/AppKitExtensions.swift b/macos/GhosttyUITests/AppKitExtensions.swift new file mode 100644 index 000000000..6bb0601a8 --- /dev/null +++ b/macos/GhosttyUITests/AppKitExtensions.swift @@ -0,0 +1,34 @@ +// +// AppKitExtensions.swift +// Ghostty +// +// Created by luca on 27.10.2025. +// + +import AppKit + +extension NSColor { + var isLightColor: Bool { + return self.luminance > 0.5 + } + + var luminance: Double { + var r: CGFloat = 0 + var g: CGFloat = 0 + var b: CGFloat = 0 + var a: CGFloat = 0 + + guard let rgb = self.usingColorSpace(.sRGB) else { return 0 } + rgb.getRed(&r, green: &g, blue: &b, alpha: &a) + return (0.299 * r) + (0.587 * g) + (0.114 * b) + } +} + +extension NSImage { + func colorAt(x: Int, y: Int) -> NSColor? { + guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else { + return nil + } + return NSBitmapImageRep(cgImage: cgImage).colorAt(x: x, y: y) + } +} diff --git a/macos/GhosttyUITests/GhosttyThemeTests.swift b/macos/GhosttyUITests/GhosttyThemeTests.swift new file mode 100644 index 000000000..fce4b061b --- /dev/null +++ b/macos/GhosttyUITests/GhosttyThemeTests.swift @@ -0,0 +1,46 @@ +// +// GhosttyThemeTests.swift +// Ghostty +// +// Created by luca on 27.10.2025. +// + +import XCTest +import AppKit + +final class GhosttyThemeTests: GhosttyCustomConfigCase { + + /// https://github.com/ghostty-org/ghostty/issues/8282 + func testIssue8282() throws { + try updateConfig("theme=light:3024 Day,dark:3024 Night\ntitle=GhosttyThemeTests") + XCUIDevice.shared.appearance = .dark + + let app = try ghosttyApplication() + app.launch() + let windowTitle = app.windows.firstMatch.title + let titleView = app.windows.firstMatch.staticTexts.element(matching: NSPredicate(format: "value == '\(windowTitle)'")) + + let image = titleView.screenshot().image + guard let imageColor = image.colorAt(x: 0, y: 0) else { + return + } + XCTAssertLessThanOrEqual(imageColor.luminance, 0.5, "Expected dark appearance for this test") + // create a split + app.groups["Terminal pane"].typeKey("d", modifierFlags: .command) + // reload config + app.typeKey(",", modifierFlags: [.command, .shift]) + // create a new window + app.typeKey("n", modifierFlags: [.command]) + + for i in 0..