import AppKit import ConjureKit import Testing @testable import Conjure @Test @MainActor func emojiPickerNavigatesCopiesAndRemembersRecentSelections() throws { _ = NSApplication.shared let suite = "ConjureEmoji.\(UUID().uuidString)" let defaults = UserDefaults(suiteName: suite)! let pasteboard = NSPasteboard.withUniqueName() defer { defaults.removePersistentDomain(forName: suite) pasteboard.releaseGlobally() } var query = "face" var closed = false let picker = EmojiPickerController(defaults: defaults, pasteboard: pasteboard) picker.input = { query } picker.closed = { closed = true } _ = picker.view let matches = EmojiCatalog.search(query) #expect(picker.handle(#selector(NSResponder.moveRight(_:)))) #expect(picker.handle(#selector(NSResponder.insertNewline(_:)))) #expect(pasteboard.string(forType: .string) == matches[1].value) #expect(closed) #expect(defaults.stringArray(forKey: "recentEmoji") == [matches[1].value]) closed = false query = "no-such-emoji" picker.inputChanged() picker.copySelection() #expect(!closed) #expect(pasteboard.string(forType: .string) == matches[1].value) query = "👩‍💻" picker.inputChanged() picker.copySelection() #expect(pasteboard.string(forType: .string) == "👩‍💻") let reopened = EmojiPickerController(defaults: defaults, pasteboard: pasteboard) _ = reopened.view reopened.copySelection() #expect(pasteboard.string(forType: .string) == "👩‍💻") #expect(defaults.stringArray(forKey: "recentEmoji") == ["👩‍💻", matches[1].value]) } @Test @MainActor func emojiCommandRestoresLauncherSearchOnEscape() throws { _ = NSApplication.shared let suite = "ConjureEmojiCommand.\(UUID().uuidString)" let defaults = UserDefaults(suiteName: suite)! defer { defaults.removePersistentDomain(forName: suite) } let history = LaunchHistory(defaults: defaults, now: { Date(timeIntervalSinceReferenceDate: 12_000) }) let launcher = LauncherWindowController(history: history) let content = try #require(launcher.window?.contentView) func descendants(_ view: NSView) -> [NSView] { [view] + view.subviews.flatMap(descendants) } let search = try #require(descendants(content).compactMap { $0 as? NSSearchField }.first) search.stringValue = "emoji" launcher.controlTextDidChange(Notification(name: NSControl.textDidChangeNotification, object: search)) _ = launcher.control(search, textView: NSTextView(), doCommandBy: #selector(NSResponder.insertNewline(_:))) let grid = try #require(descendants(content).compactMap { $0 as? NSCollectionView }.first) search.stringValue = ":woman_technologist:" launcher.controlTextDidChange(Notification(name: NSControl.textDidChangeNotification, object: search)) #expect(grid.numberOfItems(inSection: 0) == 1) #expect((grid.dataSource?.collectionView(grid, itemForRepresentedObjectAt: IndexPath(item: 0, section: 0)).view as? NSButton)?.title == "👩‍💻") _ = launcher.control(search, textView: NSTextView(), doCommandBy: #selector(NSResponder.cancelOperation(_:))) #expect(!descendants(content).contains { $0 === grid }) #expect(search.stringValue == "emoji") let table = try #require(descendants(content).compactMap { $0 as? NSTableView }.first) #expect(table.numberOfRows == 1) }