536 lines
21 KiB
Swift
536 lines
21 KiB
Swift
import Carbon.HIToolbox
|
|
import XCTest
|
|
import MacToolsPluginKit
|
|
@testable import MacTools
|
|
@testable import WindowSwitcherPlugin
|
|
|
|
@MainActor
|
|
private final class WindowSwitcherMemoryStorage: PluginStorage {
|
|
var values: [String: Any] = [:]
|
|
|
|
func object(forKey key: String) -> Any? {
|
|
values[key]
|
|
}
|
|
|
|
func data(forKey key: String) -> Data? {
|
|
values[key] as? Data
|
|
}
|
|
|
|
func string(forKey key: String) -> String? {
|
|
values[key] as? String
|
|
}
|
|
|
|
func stringArray(forKey key: String) -> [String]? {
|
|
values[key] as? [String]
|
|
}
|
|
|
|
func integer(forKey key: String) -> Int {
|
|
values[key] as? Int ?? 0
|
|
}
|
|
|
|
func bool(forKey key: String) -> Bool {
|
|
values[key] as? Bool ?? false
|
|
}
|
|
|
|
func set(_ value: Any?, forKey key: String) {
|
|
values[key] = value
|
|
}
|
|
|
|
func removeObject(forKey key: String) {
|
|
values.removeValue(forKey: key)
|
|
}
|
|
|
|
func migrateValueIfNeeded(fromLegacyKey legacyKey: String, to key: String) {
|
|
guard values[key] == nil, let value = values[legacyKey] else {
|
|
return
|
|
}
|
|
|
|
values[key] = value
|
|
values.removeValue(forKey: legacyKey)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
final class WindowSwitcherPluginTests: XCTestCase {
|
|
func testMetadataIdentifiesPlugin() {
|
|
let plugin = makePlugin()
|
|
|
|
XCTAssertEqual(plugin.metadata.id, "window-switcher")
|
|
XCTAssertEqual(plugin.metadata.title, "窗口切换")
|
|
XCTAssertNil(plugin.primaryPanel)
|
|
}
|
|
|
|
func testDefaultConfigurationIsEnabledKeyWindowMode() {
|
|
let store = WindowSwitcherStore(storage: WindowSwitcherMemoryStorage())
|
|
|
|
XCTAssertTrue(store.configuration.isEnabled)
|
|
XCTAssertEqual(store.configuration.mode, .keyWindow)
|
|
XCTAssertEqual(store.configuration.sortMode, .recentUse)
|
|
}
|
|
|
|
func testModePersists() {
|
|
let storage = WindowSwitcherMemoryStorage()
|
|
let store = WindowSwitcherStore(storage: storage)
|
|
|
|
store.setMode(.directCycle)
|
|
|
|
let loaded = WindowSwitcherStore(storage: storage)
|
|
XCTAssertEqual(loaded.configuration.mode, .directCycle)
|
|
}
|
|
|
|
func testSortModePersists() {
|
|
let storage = WindowSwitcherMemoryStorage()
|
|
let store = WindowSwitcherStore(storage: storage)
|
|
|
|
store.setSortMode(.fixed)
|
|
|
|
let loaded = WindowSwitcherStore(storage: storage)
|
|
XCTAssertEqual(loaded.configuration.sortMode, .fixed)
|
|
}
|
|
|
|
func testLegacyConfigurationDefaultsToEnabled() throws {
|
|
let storage = WindowSwitcherMemoryStorage()
|
|
let data = try XCTUnwrap(#"{"mode":"directCycle"}"#.data(using: .utf8))
|
|
storage.set(data, forKey: "configuration")
|
|
|
|
let store = WindowSwitcherStore(storage: storage)
|
|
|
|
XCTAssertTrue(store.configuration.isEnabled)
|
|
XCTAssertEqual(store.configuration.mode, .directCycle)
|
|
XCTAssertEqual(store.configuration.sortMode, .recentUse)
|
|
}
|
|
|
|
func testEnabledStatePersists() {
|
|
let storage = WindowSwitcherMemoryStorage()
|
|
let store = WindowSwitcherStore(storage: storage)
|
|
|
|
store.setEnabled(false)
|
|
|
|
let loaded = WindowSwitcherStore(storage: storage)
|
|
XCTAssertFalse(loaded.configuration.isEnabled)
|
|
}
|
|
|
|
func testObsoleteShortcutAssignmentsAreDiscarded() throws {
|
|
let storage = WindowSwitcherMemoryStorage()
|
|
storage.set(
|
|
try JSONEncoder().encode(["bundle:com.apple.Safari": "s"]),
|
|
forKey: "shortcut-assignments"
|
|
)
|
|
|
|
let store = WindowSwitcherStore(storage: storage)
|
|
|
|
XCTAssertEqual(store.shortcutBindings.manual, [:])
|
|
XCTAssertEqual(store.shortcutBindings.automatic, [:])
|
|
XCTAssertNil(storage.data(forKey: "shortcut-assignments"))
|
|
XCTAssertNil(storage.data(forKey: "shortcut-bindings"))
|
|
}
|
|
|
|
func testShortcutDefinitionDefaultsToCommandTab() {
|
|
let plugin = makePlugin()
|
|
let definition = plugin.shortcutDefinitions.first
|
|
|
|
XCTAssertEqual(definition?.id, "switcher")
|
|
XCTAssertEqual(definition?.defaultBinding?.keyCode, UInt16(kVK_Tab))
|
|
XCTAssertEqual(definition?.defaultBinding?.modifiers, .command)
|
|
XCTAssertEqual(definition?.isRequired, true)
|
|
}
|
|
|
|
func testShortcutDefinitionUsesEventTapScope() throws {
|
|
let plugin = makePlugin()
|
|
let definition = try XCTUnwrap(plugin.shortcutDefinitions.first)
|
|
|
|
switch definition.scope {
|
|
case .whilePluginActive:
|
|
break
|
|
case .global:
|
|
XCTFail("WindowSwitcher uses its event tap for shortcut handling.")
|
|
}
|
|
}
|
|
|
|
func testPermissionRequirementUsesAccessibility() {
|
|
let plugin = makePlugin(accessibilityTrusted: false)
|
|
|
|
XCTAssertEqual(plugin.permissionRequirements.map(\.id), ["accessibility"])
|
|
XCTAssertFalse(plugin.permissionState(for: "accessibility").isGranted)
|
|
}
|
|
|
|
func testOverlayMaskUsesSharedRoundedGeometry() {
|
|
let image = WindowSwitcherOverlayMetrics.roundedMaskImage()
|
|
let radius = WindowSwitcherOverlayMetrics.cornerRadius
|
|
|
|
XCTAssertEqual(image.size, NSSize(width: radius * 2 + 1, height: radius * 2 + 1))
|
|
XCTAssertEqual(image.capInsets.top, radius)
|
|
XCTAssertEqual(image.capInsets.left, radius)
|
|
XCTAssertEqual(image.capInsets.bottom, radius)
|
|
XCTAssertEqual(image.capInsets.right, radius)
|
|
XCTAssertEqual(image.resizingMode, .stretch)
|
|
}
|
|
|
|
func testOverlayIconGeometryUsesCompactHoverWithQuitButtonOutside() {
|
|
XCTAssertEqual(
|
|
WindowSwitcherOverlayMetrics.iconHighlightSize,
|
|
WindowSwitcherOverlayMetrics.iconSize
|
|
+ WindowSwitcherOverlayMetrics.iconHighlightPadding * 2
|
|
)
|
|
XCTAssertLessThan(
|
|
WindowSwitcherOverlayMetrics.iconHighlightSize,
|
|
WindowSwitcherOverlayMetrics.iconLayoutSize
|
|
)
|
|
let contentTopOffset = WindowSwitcherOverlayMetrics.shortcutHeight
|
|
+ WindowSwitcherOverlayMetrics.shortcutContentSpacing
|
|
let center = WindowSwitcherOverlayMetrics.quitButtonCenter(
|
|
tileWidth: WindowSwitcherOverlayMetrics.keyWindowTileWidth,
|
|
contentTopOffset: contentTopOffset
|
|
)
|
|
let highlightTop = contentTopOffset
|
|
+ (WindowSwitcherOverlayMetrics.iconLayoutSize
|
|
- WindowSwitcherOverlayMetrics.iconHighlightSize) / 2
|
|
let cornerCenter = CGPoint(
|
|
x: WindowSwitcherOverlayMetrics.keyWindowTileWidth / 2
|
|
+ WindowSwitcherOverlayMetrics.iconHighlightSize / 2
|
|
- WindowSwitcherOverlayMetrics.iconHighlightCornerRadius,
|
|
y: highlightTop + WindowSwitcherOverlayMetrics.iconHighlightCornerRadius
|
|
)
|
|
XCTAssertEqual(
|
|
hypot(center.x - cornerCenter.x, center.y - cornerCenter.y),
|
|
WindowSwitcherOverlayMetrics.iconHighlightCornerRadius
|
|
+ WindowSwitcherOverlayMetrics.quitButtonSize / 2
|
|
+ WindowSwitcherOverlayMetrics.quitButtonGap,
|
|
accuracy: 0.001
|
|
)
|
|
XCTAssertEqual(
|
|
WindowSwitcherOverlayMetrics.directTileHeight,
|
|
WindowSwitcherOverlayMetrics.directTopSpacing
|
|
+ WindowSwitcherOverlayMetrics.tileContentHeight
|
|
)
|
|
XCTAssertEqual(
|
|
WindowSwitcherOverlayMetrics.keyWindowTileHeight,
|
|
WindowSwitcherOverlayMetrics.shortcutHeight
|
|
+ WindowSwitcherOverlayMetrics.shortcutContentSpacing
|
|
+ WindowSwitcherOverlayMetrics.tileContentHeight
|
|
)
|
|
}
|
|
|
|
func testShortcutAssignmentUsesLettersThenDigitsThenCommandKeys() {
|
|
let entries = (0..<73).map { index in
|
|
makeEntry(index: index, appName: "\(index)")
|
|
}
|
|
|
|
let assigned = WindowSwitcherShortcutAssignment.assignShortcuts(to: entries)
|
|
|
|
XCTAssertEqual(assigned[0].shortcutToken, "f")
|
|
XCTAssertEqual(assigned[1].shortcutToken, "j")
|
|
XCTAssertEqual(assigned[25].shortcutToken, "y")
|
|
XCTAssertEqual(assigned[26].shortcutToken, "1")
|
|
XCTAssertEqual(assigned[35].shortcutToken, "0")
|
|
XCTAssertEqual(assigned[36].shortcutToken, "cmd+f")
|
|
XCTAssertEqual(assigned[71].shortcutToken, "cmd+0")
|
|
XCTAssertNil(assigned[72].shortcutToken)
|
|
}
|
|
|
|
func testShortcutAssignmentPrefersApplicationInitials() {
|
|
let entries = [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
makeEntry(index: 1, appName: "Finder", bundleIdentifier: "com.apple.finder"),
|
|
]
|
|
|
|
let assigned = WindowSwitcherShortcutAssignment.assignShortcuts(to: entries)
|
|
|
|
XCTAssertEqual(assigned[0].shortcutToken, "s")
|
|
XCTAssertEqual(assigned[1].shortcutToken, "f")
|
|
}
|
|
|
|
func testShortcutAssignmentKeepsStoredTokensAcrossOrderChanges() {
|
|
let entries = [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
makeEntry(index: 1, appName: "Slack", bundleIdentifier: "com.tinyspeck.slackmacgap"),
|
|
]
|
|
let first = WindowSwitcherShortcutAssignment.assignShortcuts(
|
|
to: entries,
|
|
bindingState: WindowSwitcherShortcutBindingState()
|
|
)
|
|
let reordered = [
|
|
makeEntry(index: 1, appName: "Slack", bundleIdentifier: "com.tinyspeck.slackmacgap"),
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
]
|
|
|
|
let second = WindowSwitcherShortcutAssignment.assignShortcuts(
|
|
to: reordered,
|
|
bindingState: first.bindingState
|
|
)
|
|
|
|
XCTAssertEqual(first.entries[0].shortcutToken, "s")
|
|
XCTAssertEqual(first.entries[1].shortcutToken, "f")
|
|
XCTAssertEqual(second.entries[0].shortcutToken, "f")
|
|
XCTAssertEqual(second.entries[1].shortcutToken, "s")
|
|
}
|
|
|
|
func testShortcutAssignmentReleasesAbsentApplicationTokens() {
|
|
let entries = [
|
|
makeEntry(index: 0, appName: "Sketch", bundleIdentifier: "com.bohemiancoding.sketch3"),
|
|
]
|
|
let stored = ["bundle:com.apple.Safari": "s"]
|
|
|
|
let result = WindowSwitcherShortcutAssignment.assignShortcuts(
|
|
to: entries,
|
|
bindingState: WindowSwitcherShortcutBindingState(automatic: stored)
|
|
)
|
|
|
|
XCTAssertEqual(result.entries[0].shortcutToken, "s")
|
|
XCTAssertNil(result.bindingState.automatic["bundle:com.apple.Safari"])
|
|
XCTAssertEqual(result.bindingState.automatic["bundle:com.bohemiancoding.sketch3"], "s")
|
|
}
|
|
|
|
func testShortcutAssignmentKeepsCommandKeyWhenListShrinks() {
|
|
let entries = [
|
|
makeEntry(index: 0, appName: "Numbers", bundleIdentifier: "com.apple.Numbers"),
|
|
]
|
|
let stored = ["bundle:com.apple.Numbers": "cmd+f"]
|
|
|
|
let result = WindowSwitcherShortcutAssignment.assignShortcuts(
|
|
to: entries,
|
|
bindingState: WindowSwitcherShortcutBindingState(automatic: stored)
|
|
)
|
|
|
|
XCTAssertEqual(result.entries[0].shortcutToken, "cmd+f")
|
|
XCTAssertEqual(result.bindingState.automatic["bundle:com.apple.Numbers"], "cmd+f")
|
|
}
|
|
|
|
func testManualShortcutTakesPriorityOverConflictingAutomaticShortcut() {
|
|
let entries = [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
makeEntry(index: 1, appName: "Finder", bundleIdentifier: "com.apple.finder"),
|
|
]
|
|
let state = WindowSwitcherShortcutBindingState(
|
|
manual: ["bundle:com.apple.Safari": "f"],
|
|
automatic: ["bundle:com.apple.finder": "f"]
|
|
)
|
|
|
|
let result = WindowSwitcherShortcutAssignment.assignShortcuts(
|
|
to: entries,
|
|
bindingState: state
|
|
)
|
|
|
|
XCTAssertEqual(result.entries[0].shortcutToken, "f")
|
|
XCTAssertEqual(result.entries[1].shortcutToken, "j")
|
|
XCTAssertEqual(result.bindingState.manual["bundle:com.apple.Safari"], "f")
|
|
XCTAssertEqual(result.bindingState.automatic["bundle:com.apple.finder"], "j")
|
|
}
|
|
|
|
func testManualShortcutRejectsConflictWithRunningEntry() {
|
|
let store = WindowSwitcherStore(storage: WindowSwitcherMemoryStorage())
|
|
let entries = store.assignShortcuts(to: [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
makeEntry(index: 1, appName: "Finder", bundleIdentifier: "com.apple.finder"),
|
|
])
|
|
let finderToken = entries[1].shortcutToken
|
|
let bindingsBeforeConflict = store.shortcutBindings
|
|
|
|
let result = store.setManualShortcut(finderToken, for: entries[0].id, in: entries)
|
|
|
|
guard case .conflict = result else {
|
|
return XCTFail("Expected an active shortcut conflict.")
|
|
}
|
|
XCTAssertEqual(store.shortcutBindings, bindingsBeforeConflict)
|
|
XCTAssertEqual(store.assignShortcuts(to: entries), entries)
|
|
}
|
|
|
|
func testManualShortcutPersistsAndSurvivesSortingChanges() {
|
|
let storage = WindowSwitcherMemoryStorage()
|
|
let store = WindowSwitcherStore(storage: storage)
|
|
let entries = store.assignShortcuts(to: [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
makeEntry(index: 1, appName: "Finder", bundleIdentifier: "com.apple.finder"),
|
|
])
|
|
|
|
let result = store.setManualShortcut("q", for: entries[0].id, in: entries)
|
|
guard case let .updated(updatedEntries) = result else {
|
|
return XCTFail("Expected a saved manual shortcut.")
|
|
}
|
|
|
|
XCTAssertEqual(updatedEntries[0].shortcutToken, "q")
|
|
let loaded = WindowSwitcherStore(storage: storage)
|
|
let reordered = loaded.assignShortcuts(to: [entries[1], entries[0]])
|
|
XCTAssertEqual(reordered[0].shortcutToken, entries[1].shortcutToken)
|
|
XCTAssertEqual(reordered[1].shortcutToken, "q")
|
|
XCTAssertEqual(loaded.shortcutBindings.manual["bundle:com.apple.Safari"], "q")
|
|
}
|
|
|
|
func testManualShortcutSupportsDigitsAndCommandKeys() {
|
|
let store = WindowSwitcherStore(storage: WindowSwitcherMemoryStorage())
|
|
let entries = store.assignShortcuts(to: [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
makeEntry(index: 1, appName: "Finder", bundleIdentifier: "com.apple.finder"),
|
|
])
|
|
|
|
let digitResult = store.setManualShortcut("7", for: entries[0].id, in: entries)
|
|
|
|
guard case let .updated(digitEntries) = digitResult else {
|
|
return XCTFail("Expected a digit manual shortcut.")
|
|
}
|
|
XCTAssertEqual(digitEntries[0].shortcutToken, "7")
|
|
|
|
let commandResult = store.setManualShortcut("cmd+q", for: entries[0].id, in: digitEntries)
|
|
guard case let .updated(commandEntries) = commandResult else {
|
|
return XCTFail("Expected a Command-key manual shortcut.")
|
|
}
|
|
XCTAssertEqual(commandEntries[0].shortcutToken, "cmd+q")
|
|
XCTAssertEqual(store.shortcutBindings.manual["bundle:com.apple.Safari"], "cmd+q")
|
|
}
|
|
|
|
func testManualShortcutRejectsMultiKeyAndUnsupportedInput() {
|
|
let store = WindowSwitcherStore(storage: WindowSwitcherMemoryStorage())
|
|
let entries = store.assignShortcuts(to: [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
])
|
|
|
|
for invalidToken in ["ff", "s1", "cmd+ff", "cmd+-", "⌘a", "窗口"] {
|
|
guard case .unavailable = store.setManualShortcut(
|
|
invalidToken,
|
|
for: entries[0].id,
|
|
in: entries
|
|
) else {
|
|
return XCTFail("Expected \(invalidToken) to be rejected.")
|
|
}
|
|
}
|
|
XCTAssertTrue(store.shortcutBindings.manual.isEmpty)
|
|
}
|
|
|
|
func testCommandAndUnmodifiedShortcutsDoNotConflict() throws {
|
|
let store = WindowSwitcherStore(storage: WindowSwitcherMemoryStorage())
|
|
let entries = store.assignShortcuts(to: [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
makeEntry(index: 1, appName: "Finder", bundleIdentifier: "com.apple.finder"),
|
|
])
|
|
let finderToken = try XCTUnwrap(entries[1].shortcutToken)
|
|
|
|
XCTAssertTrue(store.hasShortcutConflict(finderToken, for: entries[0].id, in: entries))
|
|
XCTAssertFalse(store.hasShortcutConflict("cmd+\(finderToken)", for: entries[0].id, in: entries))
|
|
}
|
|
|
|
func testSelectionShortcutNormalizesStorageAndDisplayValues() throws {
|
|
let letter = try XCTUnwrap(WindowSwitcherSelectionShortcut(storageValue: "CMD+Q"))
|
|
let digit = try XCTUnwrap(WindowSwitcherSelectionShortcut(storageValue: "7"))
|
|
|
|
XCTAssertEqual(letter.storageValue, "cmd+q")
|
|
XCTAssertEqual(letter.displayValue, "⌘Q")
|
|
XCTAssertEqual(digit.storageValue, "7")
|
|
XCTAssertEqual(digit.displayValue, "7")
|
|
XCTAssertNil(WindowSwitcherSelectionShortcut(storageValue: "qq"))
|
|
XCTAssertNil(WindowSwitcherSelectionShortcut(storageValue: "cmd+77"))
|
|
}
|
|
|
|
func testAssignmentRemovesUnsupportedStoredBindings() {
|
|
let entries = [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
makeEntry(index: 1, appName: "Finder", bundleIdentifier: "com.apple.finder"),
|
|
]
|
|
let state = WindowSwitcherShortcutBindingState(
|
|
manual: ["bundle:com.apple.Safari": "safari"],
|
|
automatic: ["bundle:com.apple.finder": "ff"]
|
|
)
|
|
|
|
let result = WindowSwitcherShortcutAssignment.assignShortcuts(
|
|
to: entries,
|
|
bindingState: state
|
|
)
|
|
|
|
XCTAssertTrue(result.bindingState.manual.isEmpty)
|
|
XCTAssertEqual(result.entries.map(\.shortcutToken), ["s", "f"])
|
|
XCTAssertEqual(result.bindingState.automatic["bundle:com.apple.Safari"], "s")
|
|
XCTAssertEqual(result.bindingState.automatic["bundle:com.apple.finder"], "f")
|
|
}
|
|
|
|
func testClearingManualShortcutRestoresAutomaticShortcut() {
|
|
let store = WindowSwitcherStore(storage: WindowSwitcherMemoryStorage())
|
|
let entries = store.assignShortcuts(to: [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
])
|
|
XCTAssertEqual(entries[0].shortcutToken, "s")
|
|
guard case let .updated(customized) = store.setManualShortcut(
|
|
"q",
|
|
for: entries[0].id,
|
|
in: entries
|
|
) else {
|
|
return XCTFail("Expected a saved manual shortcut.")
|
|
}
|
|
|
|
let result = store.setManualShortcut(nil, for: customized[0].id, in: customized)
|
|
|
|
guard case let .updated(restored) = result else {
|
|
return XCTFail("Expected the manual shortcut to be cleared.")
|
|
}
|
|
XCTAssertEqual(restored[0].shortcutToken, "s")
|
|
XCTAssertTrue(store.shortcutBindings.manual.isEmpty)
|
|
XCTAssertEqual(store.shortcutBindings.automatic["bundle:com.apple.Safari"], "s")
|
|
}
|
|
|
|
func testSavedManualShortcutStaysReservedWhenApplicationIsNotRunning() {
|
|
let entries = [
|
|
makeEntry(index: 0, appName: "Finder", bundleIdentifier: "com.apple.finder"),
|
|
]
|
|
let state = WindowSwitcherShortcutBindingState(
|
|
manual: ["bundle:com.apple.Safari": "f"],
|
|
automatic: ["bundle:com.apple.finder": "f"]
|
|
)
|
|
|
|
let result = WindowSwitcherShortcutAssignment.assignShortcuts(
|
|
to: entries,
|
|
bindingState: state
|
|
)
|
|
|
|
XCTAssertEqual(result.entries[0].shortcutToken, "j")
|
|
XCTAssertEqual(result.bindingState.manual["bundle:com.apple.Safari"], "f")
|
|
XCTAssertEqual(result.bindingState.automatic["bundle:com.apple.finder"], "j")
|
|
}
|
|
|
|
func testMultiWindowEntriesUseDistinctPersistentBindingIdentities() {
|
|
let store = WindowSwitcherStore(storage: WindowSwitcherMemoryStorage())
|
|
let entries = store.assignShortcuts(to: [
|
|
makeEntry(index: 0, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
makeEntry(index: 1, appName: "Safari", bundleIdentifier: "com.apple.Safari"),
|
|
])
|
|
|
|
let result = store.setManualShortcut("q", for: entries[1].id, in: entries)
|
|
|
|
guard case let .updated(updated) = result else {
|
|
return XCTFail("Expected a saved shortcut for the second window.")
|
|
}
|
|
XCTAssertEqual(updated[0].shortcutToken, "s")
|
|
XCTAssertEqual(updated[1].shortcutToken, "q")
|
|
XCTAssertEqual(
|
|
store.shortcutBindings.manual["bundle:com.apple.Safari#window:2"],
|
|
"q"
|
|
)
|
|
}
|
|
|
|
private func makePlugin(accessibilityTrusted: Bool = true) -> WindowSwitcherPlugin {
|
|
WindowSwitcherPlugin(
|
|
context: PluginRuntimeContext(
|
|
pluginID: "window-switcher",
|
|
storage: WindowSwitcherMemoryStorage()
|
|
),
|
|
accessibilityTrusted: { accessibilityTrusted },
|
|
requestAccessibilityTrust: { _ in accessibilityTrusted }
|
|
)
|
|
}
|
|
|
|
private func makeEntry(
|
|
index: Int,
|
|
appName: String,
|
|
bundleIdentifier: String? = nil
|
|
) -> WindowSwitcherAppEntry {
|
|
WindowSwitcherAppEntry(
|
|
id: "app-\(index)",
|
|
processIdentifier: pid_t(index + 100),
|
|
bundleIdentifier: bundleIdentifier,
|
|
appName: appName,
|
|
windowTitle: nil,
|
|
icon: nil,
|
|
windowElement: nil,
|
|
isMinimized: false,
|
|
shortcutToken: nil
|
|
)
|
|
}
|
|
}
|