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.suspend() 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 }) }