import AppKit import SwiftUI import Testing @testable import CodexBar @MainActor struct SettingsWindowAppearanceTests { @Test func `settings sidebar uses a fixed noncollapsible width`() { #expect(SettingsPane.sidebarWidth == 260) #expect(SettingsPane.windowMinWidth > SettingsPane.sidebarWidth) #expect(SettingsPane.detailMaxWidth > SettingsPane.windowMinWidth - SettingsPane.sidebarWidth) } @Test func `settings window sizing repairs collapsed saved frames`() { let window = NSWindow( contentRect: NSRect(x: 120, y: 160, width: 180, height: 140), styleMask: [.titled], backing: .buffered, defer: false) let originalMaxY = window.frame.maxY SettingsWindowSizing.enforceMinimumSize(window) #expect(window.minSize.width == SettingsPane.windowMinWidth) #expect(window.minSize.height >= SettingsPane.windowMinHeight) #expect(window.frame.width >= window.minSize.width) #expect(window.frame.height >= window.minSize.height) #expect(abs(window.frame.maxY - originalMaxY) < 1) } @Test func `settings window sizing leaves valid frames alone`() { let window = NSWindow( contentRect: NSRect(x: 120, y: 160, width: SettingsPane.windowWidth, height: SettingsPane.windowHeight), styleMask: [.titled], backing: .buffered, defer: false) let originalFrame = window.frame SettingsWindowSizing.enforceMinimumSize(window) #expect(window.frame == originalFrame) #expect(window.minSize.width == SettingsPane.windowMinWidth) #expect(window.minSize.height >= SettingsPane.windowMinHeight) } @Test func `settings window sizing does not mutate content split views`() { let window = NSWindow( contentRect: NSRect(x: 120, y: 160, width: 180, height: 140), styleMask: [.titled], backing: .buffered, defer: false) let splitView = NSSplitView( frame: NSRect(x: 0, y: 0, width: SettingsPane.windowWidth, height: SettingsPane.windowHeight)) splitView.isVertical = true let sidebar = NSView(frame: NSRect(x: 0, y: 0, width: 0, height: SettingsPane.windowHeight)) let detail = NSView( frame: NSRect(x: 0, y: 0, width: SettingsPane.windowWidth, height: SettingsPane.windowHeight)) splitView.addSubview(sidebar) splitView.addSubview(detail) window.contentView = splitView SettingsWindowSizing.enforceMinimumSize(window) #expect(window.frame.width >= window.minSize.width) #expect(sidebar.frame.width == 0) } @Test func `bridge pulses exact effective appearance then restores inheritance`() { let application = NSApplication.shared let effectiveAppearance = application.effectiveAppearance let staleSource = NSView() staleSource.appearance = NSAppearance(named: .aqua) let resetCapture = ResetCapture() let bridge = SettingsWindowAppearanceView { resetCapture.actions.append($0) } let window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 400, height: 300), styleMask: [.titled], backing: .buffered, defer: false) window.appearance = NSAppearance(named: .aqua) window.appearanceSource = staleSource window.contentView = bridge let pulseMatchesEffectiveAppearance = window.appearance === effectiveAppearance let sourceIsApplication = (window.appearanceSource as AnyObject?) === application #expect(pulseMatchesEffectiveAppearance) #expect(sourceIsApplication) #expect(resetCapture.actions.count == 1) resetCapture.actions[0]() #expect(window.appearance == nil) #expect(window.viewsNeedDisplay) } @Test func `bridge updates window title without pulsing appearance on pane changes`() { let resetCapture = ResetCapture() let bridge = SettingsWindowAppearanceView { resetCapture.actions.append($0) } let window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 400, height: 300), styleMask: [.titled], backing: .buffered, defer: false) window.contentView = bridge resetCapture.actions.removeAll() bridge.refreshWindowAppearance(for: .light, windowTitle: "Display") #expect(resetCapture.actions.count == 1) bridge.refreshWindowAppearance(for: .light, windowTitle: "General") #expect(window.title == "General") #expect(resetCapture.actions.count == 1) } @Test func `settings window style remains resizable`() { let bridge = SettingsWindowAppearanceView() let window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 400, height: 300), styleMask: [.titled], backing: .buffered, defer: false) window.contentView = bridge #expect(window.styleMask.contains(.resizable)) } @Test func `settings window extends content behind the titlebar for the edge-to-edge sidebar`() { let bridge = SettingsWindowAppearanceView() let window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 400, height: 300), styleMask: [.titled], backing: .buffered, defer: false) window.contentView = bridge #expect(window.styleMask.contains(.fullSizeContentView)) #expect(window.titlebarAppearsTransparent) } @Test func `repeated theme updates cannot leave an explicit appearance`() { let resetCapture = ResetCapture() let bridge = SettingsWindowAppearanceView { resetCapture.actions.append($0) } let window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 400, height: 300), styleMask: [.titled], backing: .buffered, defer: false) window.contentView = bridge bridge.refreshWindowAppearance(for: .light) bridge.refreshWindowAppearance(for: .light) bridge.refreshWindowAppearance(for: .dark) #expect(resetCapture.actions.count == 3) for action in resetCapture.actions { action() } let sourceIsApplication = (window.appearanceSource as AnyObject?) === NSApplication.shared #expect(window.appearance == nil) #expect(sourceIsApplication) } } @MainActor private final class ResetCapture { var actions: [SettingsWindowAppearance.ResetAction] = [] }