Files
conjure/Tests/ConjureKitTests/GlobalShortcutsTests.swift
T
2026-10-16 12:06:41 +02:00

122 lines
6.3 KiB
Swift

import AppKit
@testable import ConjureKit
import Testing
@Test @MainActor
func globalShortcutKeepsLegacyStorageAndScopesCustomStorage() throws {
let suite = "GlobalShortcutsTests.\(UUID().uuidString)"
let defaults = try #require(UserDefaults(suiteName: suite))
defer { defaults.removePersistentDomain(forName: suite) }
let commandShiftSpace = try #require(KeyboardShortcut(keyCode: 49, modifiers: [.command, .shift]))
defaults.set(KeyboardShortcut.commandSpace.storedValue, forKey: "globalActivationShortcut")
defaults.set(commandShiftSpace.storedValue, forKey: "customShortcut")
let legacy = GlobalShortcut(defaults: defaults) {}
let custom = GlobalShortcut(defaults: defaults, storageKey: "customShortcut") {}
#expect(legacy.shortcut == .commandSpace)
#expect(custom.shortcut == commandShiftSpace)
try custom.setShortcut(nil)
#expect(defaults.object(forKey: "customShortcut") == nil)
#expect(KeyboardShortcut(storedValue: defaults.object(forKey: "globalActivationShortcut")) == .commandSpace)
#expect(GlobalShortcut(defaults: defaults, action: {}).shortcut == .commandSpace)
}
@Test @MainActor
func duplicateGlobalBindingIsRejectedWhileSuspendedWithoutChangingSavedBinding() throws {
let suite = "GlobalShortcutsTests.\(UUID().uuidString)"
let defaults = try #require(UserDefaults(suiteName: suite))
defer { defaults.removePersistentDomain(forName: suite) }
let candidate = try #require(KeyboardShortcut(keyCode: 49, modifiers: [.command, .shift]))
let previous = try #require(KeyboardShortcut(keyCode: 49, modifiers: [.control, .option]))
let emojiIndex = try #require(GlobalShortcuts.destinations.firstIndex(of: .command(.emoji)))
defaults.set(candidate.storedValue, forKey: "globalActivationShortcut")
defaults.set(previous.storedValue, forKey: "globalCommandShortcut.emoji")
let shortcuts = GlobalShortcuts(defaults: defaults) { _ in }
shortcuts.beginRecording()
do {
try shortcuts.setShortcut(candidate, at: emojiIndex)
Issue.record("A duplicate shortcut was accepted")
} catch {
#expect(error.localizedDescription.contains("Applications"))
#expect(error.localizedDescription.contains("already assigned"))
}
let commandReturn = try #require(KeyboardShortcut(keyCode: 36, modifiers: .command))
do {
try shortcuts.setShortcut(commandReturn, at: emojiIndex)
Issue.record("A Conjure action shortcut was accepted")
} catch {
#expect(error.localizedDescription.contains("already used by Conjure"))
#expect(error.localizedDescription.contains("confirm uninstall"))
}
#expect(shortcuts.bindings[emojiIndex].shortcut == previous)
#expect(KeyboardShortcut(storedValue: defaults.object(forKey: "globalCommandShortcut.emoji")) == previous)
#expect(shortcuts.bindings.allSatisfy { !$0.isRegistered })
try shortcuts.validate(candidate, at: 0)
try shortcuts.setShortcut(nil, at: emojiIndex)
let restored = GlobalShortcuts(defaults: defaults) { _ in }
#expect(restored.bindings[0].shortcut == candidate)
#expect(restored.bindings[emojiIndex].shortcut == nil)
}
@Test @MainActor
func localShortcutConflictsMatchMenusActionsAndPanelNavigation() throws {
let commandKey = try #require(KeyboardShortcut(keyCode: 0, modifiers: .command))
for key in [",", "h", "q", "w", "x", "c", "v", "a", "m", "k", "Return", "1", "9"] {
#expect(GlobalShortcuts.localAction(for: commandKey, keyName: key) != nil)
}
#expect(GlobalShortcuts.localAction(for: commandKey, keyName: "0") == nil)
let hideOthers = try #require(KeyboardShortcut(keyCode: 0, modifiers: [.command, .option]))
#expect(GlobalShortcuts.localAction(for: hideOthers, keyName: "H") == "Hide Others")
let controlKey = try #require(KeyboardShortcut(keyCode: 0, modifiers: .control))
for key in ["h", "j", "k", "l"] {
#expect(GlobalShortcuts.localAction(for: controlKey, keyName: key) != nil)
}
let commandShiftSpace = try #require(KeyboardShortcut(keyCode: 49, modifiers: [.command, .shift]))
#expect(GlobalShortcuts.localAction(for: commandShiftSpace, keyName: "Space") == nil)
let commandShiftK = try #require(KeyboardShortcut(keyCode: 40, modifiers: [.command, .shift]))
#expect(GlobalShortcuts.localAction(for: commandShiftK, keyName: "K") == nil)
}
@Test @MainActor
func startupReportsEveryInvalidSavedBindingWithoutDiscardingThem() throws {
let suite = "GlobalShortcutsTests.\(UUID().uuidString)"
let defaults = try #require(UserDefaults(suiteName: suite))
defer { defaults.removePersistentDomain(forName: suite) }
let commandReturn = try #require(KeyboardShortcut(keyCode: 36, modifiers: .command))
defaults.set("invalid", forKey: "globalActivationShortcut")
defaults.set(commandReturn.storedValue, forKey: "globalCommandShortcut.translate")
defaults.set(["keyCode": -1, "modifiers": 0], forKey: "globalCommandShortcut.emoji")
let shortcuts = GlobalShortcuts(defaults: defaults) { _ in }
let errors = shortcuts.start()
#expect(errors.count == 3)
#expect(errors[0].hasPrefix("Applications:"))
#expect(errors[1].hasPrefix("Conjure Translate:"))
#expect(errors[1].contains("already used by Conjure"))
#expect(errors[2].hasPrefix("Conjure Emoji:"))
#expect(defaults.string(forKey: "globalActivationShortcut") == "invalid")
#expect(KeyboardShortcut(storedValue: defaults.object(forKey: "globalCommandShortcut.translate")) == commandReturn)
#expect(defaults.object(forKey: "globalCommandShortcut.emoji") != nil)
#expect(shortcuts.bindings.allSatisfy { !$0.isRegistered })
}
@Test @MainActor
func startupRejectsEveryDuplicateEvenBeforeNativeRegistration() throws {
let suite = "GlobalShortcutsTests.\(UUID().uuidString)"
let defaults = try #require(UserDefaults(suiteName: suite))
defer { defaults.removePersistentDomain(forName: suite) }
let candidate = try #require(KeyboardShortcut(keyCode: 49, modifiers: [.command, .shift]))
defaults.set(candidate.storedValue, forKey: "globalActivationShortcut")
defaults.set(candidate.storedValue, forKey: "globalCommandShortcut.emoji")
let shortcuts = GlobalShortcuts(defaults: defaults) { _ in }
let errors = shortcuts.start()
#expect(errors.count == 2)
#expect(errors[0].contains("Conjure Emoji"))
#expect(errors[1].contains("Applications"))
#expect(shortcuts.bindings.allSatisfy { !$0.isRegistered })
}