fix: unify navigation context and feedback across tools

This commit is contained in:
Valdemar Lorenzen
2026-10-15 12:13:45 +02:00
parent 999cd73891
commit 6fd3931f3a
6 changed files with 238 additions and 44 deletions
@@ -0,0 +1,89 @@
import AppKit
import ConjureKit
import Testing
@testable import Conjure
@MainActor
private func descendants(_ view: NSView) -> [NSView] {
[view] + view.subviews.flatMap { descendants($0) }
}
@Test @MainActor
func uninstallFilteringPreservesDiscoveryStateAndCheckedFiles() throws {
_ = NSApplication.shared
let controller = UninstallReviewController(
application: Application(url: URL(fileURLWithPath: "/Applications/Example.app"), name: "Example"),
uninstaller: ApplicationUninstaller())
let views = descendants(controller.view)
let filter = try #require(views.compactMap { $0 as? NSSearchField }.first)
let status = try #require(views.compactMap { $0 as? NSTextField }.first {
$0.accessibilityLabel() == "Uninstall selection and progress"
})
let table = try #require(views.compactMap { $0 as? NSTableView }.first)
let remove = try #require(views.compactMap { $0 as? NSButton }.first { $0.keyEquivalent == "\r" })
func filterBy(_ query: String) {
filter.stringValue = query
controller.controlTextDidChange(Notification(name: NSControl.textDidChangeNotification, object: filter))
}
let loading = status.stringValue
filterBy("cache")
#expect(status.stringValue == loading)
#expect(!remove.isEnabled)
struct ScanFailure: LocalizedError {
var errorDescription: String? { "Access denied by the filesystem" }
}
controller.applyDiscoveryResult(.failure(ScanFailure()))
let failure = status.stringValue
#expect(failure.contains(ScanFailure().localizedDescription))
#expect(status.textColor == .systemRed)
filterBy("different")
#expect(status.stringValue == failure)
#expect(!remove.isEnabled)
let candidate = UninstallCandidate(url: URL(fileURLWithPath: "/tmp/example-cache"), size: 1024,
isSelectable: true, defaultSelected: true, reason: nil)
controller.applyDiscoveryResult(.success([candidate]))
filterBy("")
let counts = status.stringValue
#expect(status.textColor == SunsetPalette.secondaryText)
#expect(remove.isEnabled)
#expect(table.numberOfRows == 1)
filterBy("no-match")
#expect(table.numberOfRows == 0)
#expect(remove.isEnabled)
filterBy("")
#expect(status.stringValue == counts)
#expect(table.numberOfRows == 1)
_ = controller.control(filter, textView: NSTextView(), doCommandBy: #selector(NSResponder.insertNewline(_:)))
#expect(!remove.isEnabled)
}
@Test @MainActor
func shortcutModifierGuidanceDoesNotAppearAsAnError() throws {
_ = NSApplication.shared
let suite = "Conjure.ToolFeedbackTests.\(UUID().uuidString)"
let defaults = try #require(UserDefaults(suiteName: suite))
defer { defaults.removePersistentDomain(forName: suite) }
let controller = SettingsWindowController(shortcut: GlobalShortcut(defaults: defaults, action: {}))
let window = try #require(controller.window)
let views = descendants(try #require(window.contentView))
let recorder = try #require(views.compactMap { $0 as? NSButton }.first {
$0.accessibilityLabel() == "Record global shortcut"
})
let error = try #require(views.compactMap { $0 as? NSTextField }.first {
$0.accessibilityLabel() == "Shortcut feedback"
})
let status = try #require(views.compactMap { $0 as? NSTextField }.first {
$0.accessibilityLabel() == "Global shortcut registration status"
})
recorder.performClick(nil)
defer { window.close() }
#expect(window.firstResponder === recorder)
let modifier = try #require(NSEvent.keyEvent(with: .flagsChanged, location: .zero,
modifierFlags: .command, timestamp: 1, windowNumber: window.windowNumber,
context: nil, characters: "", charactersIgnoringModifiers: "", isARepeat: false, keyCode: 55))
recorder.flagsChanged(with: modifier)
#expect(error.stringValue.isEmpty)
#expect(status.textColor == SunsetPalette.secondaryText)
}