138 lines
7.6 KiB
Swift
138 lines
7.6 KiB
Swift
import AppKit
|
|
import ConjureKit
|
|
import Testing
|
|
@testable import Conjure
|
|
|
|
@Test @MainActor
|
|
func existingWindowsAndNotesFollowPaletteAppearanceWithoutResettingState() throws {
|
|
_ = NSApplication.shared
|
|
let originalPalette = ThemePalette.loaded
|
|
let originalAppearance = NSApp.appearance
|
|
defer {
|
|
NSApp.appearance = originalAppearance
|
|
ThemePalette.loaded = originalPalette
|
|
NotificationCenter.default.post(name: ThemePalette.didChangeNotification, object: nil)
|
|
}
|
|
let suite = "Conjure.LightAppearanceViews.\(UUID().uuidString)"
|
|
let defaults = try #require(UserDefaults(suiteName: suite))
|
|
let directory = FileManager.default.temporaryDirectory.appendingPathComponent(suite)
|
|
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
|
defer {
|
|
defaults.removePersistentDomain(forName: suite)
|
|
try? FileManager.default.removeItem(at: directory)
|
|
}
|
|
try Data("theme = \"tint\"".utf8).write(to: directory.appendingPathComponent("config.toml"))
|
|
func writePalette(light: Bool) throws {
|
|
try Data("""
|
|
{"variant":"\(light ? "light" : "dark")","colors":{
|
|
"background":"\(light ? "#ffffff" : "#201714")",
|
|
"text":"\(light ? "#112233" : "#ffe3b5")",
|
|
"secondaryText":"\(light ? "#667788" : "#d9c7a6")"
|
|
}}
|
|
""".utf8).write(to: directory.appendingPathComponent("theme.json"))
|
|
}
|
|
func descendants(_ view: NSView) -> [NSView] { [view] + view.subviews.flatMap(descendants) }
|
|
func appearanceName(_ appearance: NSAppearance) -> NSAppearance.Name? {
|
|
appearance.bestMatch(from: [.aqua, .darkAqua])
|
|
}
|
|
try writePalette(light: false)
|
|
ThemePalette.reload(from: directory)
|
|
NSApp.appearance = NSAppearance(named: .aqua)
|
|
let launcher = LauncherWindowController(
|
|
history: LaunchHistory(defaults: defaults, now: { Date(timeIntervalSinceReferenceDate: 12_000) }),
|
|
defaults: defaults, notesDirectory: directory.appendingPathComponent("notes"))
|
|
let window = try #require(launcher.window)
|
|
let content = try #require(window.contentView)
|
|
defer { window.orderOut(nil) }
|
|
#expect(appearanceName(window.effectiveAppearance) == .darkAqua)
|
|
let search = try #require(descendants(content).compactMap { $0 as? NSSearchField }.first)
|
|
search.stringValue = "notes"
|
|
launcher.controlTextDidChange(Notification(name: NSControl.textDidChangeNotification, object: search))
|
|
_ = launcher.control(search, textView: NSTextView(), doCommandBy: #selector(NSResponder.insertNewline(_:)))
|
|
let editor = try #require(descendants(content).compactMap { $0 as? NotesTextView }.first)
|
|
let document = NSMutableAttributedString(string: "let answer = 42\nplain")
|
|
let code = NSRange(location: 0, length: 16)
|
|
document.addAttribute(NoteAttributes.block, value: "code", range: code)
|
|
document.addAttribute(NoteAttributes.language, value: "swift", range: code)
|
|
editor.setDocumentContents(document)
|
|
editor.beginInsertionSession()
|
|
editor.insertText("!", replacementRange: NSRange(location: document.length, length: 0))
|
|
editor.endInsertionSession()
|
|
let selection = NSRange(location: 1, length: 2)
|
|
editor.setSelectedRange(selection)
|
|
let text = editor.string
|
|
let undoGrouping = editor.undoManager?.groupingLevel
|
|
let frame = window.frame
|
|
let style = window.styleMask
|
|
let settings = SettingsWindowController(shortcuts: GlobalShortcuts(defaults: defaults) { _ in })
|
|
let settingsWindow = try #require(settings.window)
|
|
defer { settingsWindow.close() }
|
|
let settingsContent = try #require(settingsWindow.contentView)
|
|
let destination = try #require(descendants(settingsContent).compactMap { $0 as? NSPopUpButton }.first)
|
|
destination.selectItem(at: 2)
|
|
let credential = try #require(descendants(settingsContent).compactMap { $0 as? NSSecureTextField }.first)
|
|
credential.stringValue = "unsaved-value"
|
|
let anchor = try #require(descendants(content).compactMap { $0 as? NSButton }.first {
|
|
$0.title == "Actions" && $0.target is NotesController
|
|
})
|
|
let notes = try #require(anchor.target as? NotesController)
|
|
let popup = ApplicationActionsController()
|
|
popup.showLanguages([("en", "English"), ("ja", "Japanese")], selected: "en", source: true, relativeTo: anchor)
|
|
defer { popup.close(animated: false) }
|
|
let popupSearch = try #require(descendants(popup.view).compactMap { $0 as? NSSearchField }.first)
|
|
popupSearch.stringValue = "Eng"
|
|
popup.controlTextDidChange(Notification(name: NSControl.textDidChangeNotification, object: popupSearch))
|
|
let table = try #require(descendants(popup.view).compactMap { $0 as? NSTableView }.first)
|
|
let rows = table.numberOfRows
|
|
let selectedRows = table.selectedRowIndexes
|
|
let material = try #require(popup.view.subviews.compactMap { $0 as? NSVisualEffectView }.first)
|
|
#expect(appearanceName(editor.effectiveAppearance) == .darkAqua)
|
|
#expect(appearanceName(settingsWindow.effectiveAppearance) == .darkAqua)
|
|
#expect(appearanceName(material.effectiveAppearance) == .darkAqua)
|
|
|
|
// Deliberately oppose the light palette: app/system inheritance is not the contract.
|
|
NSApp.appearance = NSAppearance(named: .darkAqua)
|
|
for light in [true, false] {
|
|
try writePalette(light: light)
|
|
#expect(ThemePalette.reload(from: directory))
|
|
let expected: NSAppearance.Name = light ? .aqua : .darkAqua
|
|
for appearance in [window.effectiveAppearance, content.effectiveAppearance, editor.effectiveAppearance,
|
|
settingsWindow.effectiveAppearance, settingsContent.effectiveAppearance,
|
|
popup.view.effectiveAppearance, material.effectiveAppearance] {
|
|
#expect(appearanceName(appearance) == expected)
|
|
}
|
|
#expect(launcher.window === window)
|
|
#expect(window.frame == frame)
|
|
#expect(window.styleMask == style)
|
|
#expect(editor.string == text)
|
|
#expect(editor.selectedRange() == selection)
|
|
#expect(editor.undoManager?.groupingLevel == undoGrouping)
|
|
#expect(editor.undoManager?.canUndo == true)
|
|
#expect(editor.textStorage?.attribute(NoteAttributes.block, at: 0, effectiveRange: nil) as? String == "code")
|
|
#expect(editor.textStorage?.attribute(NoteAttributes.language, at: 0, effectiveRange: nil) as? String == "swift")
|
|
let keyword = try #require((editor.textStorage?.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? NSColor)?.usingColorSpace(.sRGB))
|
|
let rgb: (CGFloat, CGFloat, CGFloat) = light ? (166, 38, 164) : (198, 120, 221)
|
|
#expect(abs(keyword.redComponent * 255 - rgb.0) < 1)
|
|
#expect(abs(keyword.greenComponent * 255 - rgb.1) < 1)
|
|
#expect(abs(keyword.blueComponent * 255 - rgb.2) < 1)
|
|
#expect(destination.indexOfSelectedItem == 2)
|
|
#expect(credential.stringValue == "unsaved-value")
|
|
#expect(popup.isShown)
|
|
#expect(popupSearch.stringValue == "Eng")
|
|
#expect(table.numberOfRows == rows)
|
|
#expect(table.selectedRowIndexes == selectedRows)
|
|
#expect(material.layer?.borderColor == ThemePalette.secondaryText.withAlphaComponent(0.22).cgColor)
|
|
#expect(material.layer?.borderColor?.alpha == 0.22)
|
|
}
|
|
popup.close(animated: false)
|
|
editor.undoManager?.undo()
|
|
#expect(editor.string == document.string)
|
|
try writePalette(light: true)
|
|
#expect(ThemePalette.reload(from: directory))
|
|
notes.goBack()
|
|
#expect(window.styleMask == [.borderless])
|
|
#expect(appearanceName(window.effectiveAppearance) == .aqua)
|
|
#expect(appearanceName(content.effectiveAppearance) == .aqua)
|
|
#expect(search.stringValue == "notes")
|
|
}
|