Slim down app and test suite

This commit is contained in:
Akshay Kolli
2026-07-09 22:30:43 -04:00
parent 52f712eb73
commit 54fac96dec
28 changed files with 1046 additions and 8950 deletions

View File

@@ -4,21 +4,6 @@ import XCTest
@testable import ClipBored
final class ClipboardPanelControllerTests: XCTestCase {
private var tempURLs: [URL] = []
private var defaultsSuites: [String] = []
override func tearDown() {
for url in tempURLs {
try? FileManager.default.removeItem(at: url)
}
tempURLs.removeAll()
for suite in defaultsSuites {
UserDefaults(suiteName: suite)?.removePersistentDomain(forName: suite)
}
defaultsSuites.removeAll()
super.tearDown()
}
func testPanelFrameUsesRightSideShelfByDefault() {
let screenFrame = CGRect(x: -1200, y: -200, width: 1200, height: 800)
let frames = ClipboardPanelController.panelFrames(forScreenFrame: screenFrame, visibleFrame: screenFrame)
@@ -31,7 +16,7 @@ final class ClipboardPanelControllerTests: XCTestCase {
XCTAssertEqual(frames.hidden.minY, frames.shown.minY)
}
func testOpenScreenSelectionUsesStatusClickScreenWhenProvided() {
func testOpenScreenSelectionPrefersExplicitThenPointerScreen() {
XCTAssertEqual(
ClipboardPanelController.selectedOpenScreen(
explicit: "status-item-screen",
@@ -41,9 +26,6 @@ final class ClipboardPanelControllerTests: XCTestCase {
),
"status-item-screen"
)
}
func testOpenScreenSelectionFallsBackToPointerForGlobalShortcut() {
XCTAssertEqual(
ClipboardPanelController.selectedOpenScreen(
explicit: Optional<String>.none,
@@ -141,31 +123,6 @@ final class ClipboardPanelControllerTests: XCTestCase {
XCTAssertEqual(frames.hidden.minX, screenFrame.maxX + 1)
}
func testPanelFramePlanningIsDeterministicAcrossRepeatedToggles() {
let screenFrame = CGRect(x: -1512, y: -120, width: 1512, height: 982)
let visibleFrame = CGRect(x: -1512, y: -24, width: 1512, height: 861)
let first = ClipboardPanelController.panelFrames(forScreenFrame: screenFrame, visibleFrame: visibleFrame)
for _ in 0..<50 {
let frames = ClipboardPanelController.panelFrames(forScreenFrame: screenFrame, visibleFrame: visibleFrame)
XCTAssertEqual(frames.shown, first.shown)
XCTAssertEqual(frames.hidden, first.hidden)
XCTAssertEqual(frames.hidden.minX, frames.shown.maxX + 1)
}
}
func testPanelAnimationProfileStaysShortForSixtyFpsFeel() {
let profile = ClipboardPanelController.animationProfile
XCTAssertEqual(profile.showDuration, 0.22)
XCTAssertEqual(profile.hideDuration, 0.16)
XCTAssertEqual(profile.reflowDuration, 0.18)
XCTAssertLessThanOrEqual(profile.showDuration * 60, 14)
XCTAssertLessThanOrEqual(profile.hideDuration * 60, 10)
XCTAssertLessThanOrEqual(profile.reflowDuration * 60, 11)
XCTAssertEqual(profile.easing, .easeInEaseOut)
}
func testPanelCollectionBehaviorStaysLocalToActiveSpaceAndSupportsFullscreen() {
let behavior = ClipboardPanelController.panelCollectionBehavior
@@ -175,69 +132,6 @@ final class ClipboardPanelControllerTests: XCTestCase {
XCTAssertFalse(behavior.contains(.canJoinAllSpaces))
}
func testShowingPanelClearsAndCollapsesStaleSearchState() throws {
let screen = try XCTUnwrap(NSScreen.screens.first)
let (controller, _) = makeController(preferredScreen: screen)
controller.debugSetSearchFieldText("stale query")
drainMainQueue()
XCTAssertEqual(controller.debugSearchFieldText, "stale query")
XCTAssertEqual(controller.debugSearchFieldWidth, 238, accuracy: 1)
XCTAssertEqual(controller.debugSearchFieldPlaceholderText, "Search clips")
XCTAssertTrue(controller.debugSearchFieldIsVisible)
XCTAssertTrue(controller.debugSearchIconButtonIsVisible)
controller.show(preferredScreen: screen)
defer { controller.hide(immediate: true) }
drainMainQueue()
XCTAssertEqual(controller.debugSearchFieldText, "")
XCTAssertEqual(controller.debugSearchFieldWidth, 30, accuracy: 1)
XCTAssertEqual(controller.debugSearchFieldPlaceholderText, "")
XCTAssertFalse(controller.debugSearchFieldIsVisible)
XCTAssertTrue(controller.debugSearchIconButtonIsVisible)
XCTAssertFalse(controller.debugIsSearchFieldEditing)
}
func testReflowPlanKeepsOpenPanelOnRightSideWhenBottomDockIsVisible() {
let screenFrame = CGRect(x: 0, y: 0, width: 1512, height: 982)
let visibleFrame = CGRect(x: 0, y: 112, width: 1512, height: 845)
let plan = ClipboardPanelController.reflowPlan(forScreenFrame: screenFrame, visibleFrame: visibleFrame)
XCTAssertEqual(plan.frame.maxX, visibleFrame.maxX)
XCTAssertEqual(plan.frame.minY, screenFrame.minY)
XCTAssertEqual(plan.frame.maxY, visibleFrame.maxY)
XCTAssertEqual(plan.frame.width, 336)
XCTAssertEqual(plan.bottomSafeInset, 20)
}
func testReflowPlanTouchesScreenBottomWhenBottomDockIsAutoHidden() {
let screenFrame = CGRect(x: 0, y: 0, width: 1512, height: 982)
let visibleFrame = CGRect(x: 0, y: 4, width: 1512, height: 953)
let plan = ClipboardPanelController.reflowPlan(forScreenFrame: screenFrame, visibleFrame: visibleFrame)
XCTAssertEqual(plan.frame.maxX, visibleFrame.maxX)
XCTAssertEqual(plan.frame.minY, screenFrame.minY)
XCTAssertEqual(plan.frame.maxY, visibleFrame.maxY)
XCTAssertEqual(plan.frame.width, 336)
XCTAssertEqual(plan.bottomSafeInset, 18)
}
func testReflowPlanTracksSideDockVisibleFrameWithoutBottomInsetInflation() {
let screenFrame = CGRect(x: 0, y: 0, width: 1512, height: 982)
let visibleFrame = CGRect(x: 86, y: 0, width: 1426, height: 957)
let plan = ClipboardPanelController.reflowPlan(forScreenFrame: screenFrame, visibleFrame: visibleFrame)
XCTAssertEqual(plan.frame.maxX, visibleFrame.maxX)
XCTAssertEqual(plan.frame.minX, visibleFrame.maxX - 336)
XCTAssertEqual(plan.frame.minY, screenFrame.minY)
XCTAssertEqual(plan.frame.height, 957)
XCTAssertEqual(plan.bottomSafeInset, 18)
}
func testPanelFrameUsesConfiguredRightSideShelf() {
let screenFrame = CGRect(x: 0, y: 0, width: 1512, height: 982)
let visibleFrame = CGRect(x: 0, y: 96, width: 1512, height: 861)
@@ -289,22 +183,20 @@ final class ClipboardPanelControllerTests: XCTestCase {
XCTAssertEqual(frames.shown.width, 336)
}
func testContentBottomInsetReservesBottomDockSpace() {
func testContentBottomInsetHandlesBottomAndSideDock() {
let screenFrame = CGRect(x: 0, y: 0, width: 1512, height: 982)
let visibleFrame = CGRect(x: 0, y: 96, width: 1512, height: 861)
let inset = ClipboardPanelController.contentBottomInset(forScreenFrame: screenFrame, visibleFrame: visibleFrame)
XCTAssertEqual(inset, 20)
}
func testContentBottomInsetUsesMinimumWhenDockIsNotAtBottom() {
let screenFrame = CGRect(x: 0, y: 0, width: 1512, height: 982)
let visibleFrame = CGRect(x: 80, y: 0, width: 1432, height: 957)
let inset = ClipboardPanelController.contentBottomInset(forScreenFrame: screenFrame, visibleFrame: visibleFrame)
XCTAssertEqual(inset, 18)
XCTAssertEqual(
ClipboardPanelController.contentBottomInset(
forScreenFrame: screenFrame,
visibleFrame: CGRect(x: 80, y: 0, width: 1432, height: 957)
),
18
)
}
func testPanelSharingTypeHidesWindowFromScreenCaptureWhenEnabled() {
@@ -326,41 +218,38 @@ final class ClipboardPanelControllerTests: XCTestCase {
}
func testCommandNumberShortcutsMapToQuickPasteSlots() {
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 18, modifiers: .command), 0)
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 19, modifiers: .command), 1)
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 20, modifiers: .command), 2)
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 21, modifiers: .command), 3)
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 23, modifiers: .command), 4)
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 22, modifiers: .command), 5)
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 26, modifiers: .command), 6)
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 28, modifiers: .command), 7)
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 25, modifiers: .command), 8)
assertShortcutMappings([
(18, .command, 0), (19, .command, 1), (20, .command, 2),
(21, .command, 3), (23, .command, 4), (22, .command, 5),
(26, .command, 6), (28, .command, 7), (25, .command, 8)
], using: ClipboardPanelController.quickPasteIndex)
}
func testShiftCommandNumberShortcutsMapToPlainTextQuickPasteSlots() {
XCTAssertEqual(ClipboardPanelController.quickPastePlainTextIndex(forKeyCode: 18, modifiers: [.command, .shift]), 0)
XCTAssertEqual(ClipboardPanelController.quickPastePlainTextIndex(forKeyCode: 25, modifiers: [.command, .shift]), 8)
XCTAssertNil(ClipboardPanelController.quickPastePlainTextIndex(forKeyCode: 18, modifiers: .command))
XCTAssertNil(ClipboardPanelController.quickPastePlainTextIndex(forKeyCode: 18, modifiers: [.command, .option, .shift]))
assertShortcutMappings([
(18, [.command, .shift], 0),
(25, [.command, .shift], 8),
(18, .command, nil),
(18, [.command, .option, .shift], nil)
], using: ClipboardPanelController.quickPastePlainTextIndex)
}
func testCommandOptionNumberShortcutsMapToCollections() {
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 18, modifiers: [.command, .option]), .mostRecent)
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 19, modifiers: [.command, .option]), .mostUsed)
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 20, modifiers: [.command, .option]), .text)
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 21, modifiers: [.command, .option]), .links)
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 23, modifiers: [.command, .option]), .images)
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 22, modifiers: [.command, .option]), .files)
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 26, modifiers: [.command, .option]), .pinned)
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 28, modifiers: [.command, .option]), .audio)
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 25, modifiers: [.command, .option]), .colors)
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 29, modifiers: [.command, .option]), .code)
}
func testCollectionShortcutsRequireCommandOptionSoQuickPasteKeepsCommandNumbers() {
XCTAssertNil(ClipboardPanelController.collectionShortcutMode(forKeyCode: 18, modifiers: []))
XCTAssertNil(ClipboardPanelController.collectionShortcutMode(forKeyCode: 18, modifiers: .command))
XCTAssertNil(ClipboardPanelController.collectionShortcutMode(forKeyCode: 29, modifiers: .command))
assertShortcutMappings([
(18, [.command, .option], .mostRecent),
(19, [.command, .option], .mostUsed),
(20, [.command, .option], .text),
(21, [.command, .option], .links),
(23, [.command, .option], .images),
(22, [.command, .option], .files),
(26, [.command, .option], .pinned),
(28, [.command, .option], .audio),
(25, [.command, .option], .colors),
(29, [.command, .option], .code),
(18, [], nil),
(18, .command, nil),
(29, .command, nil)
], using: ClipboardPanelController.collectionShortcutMode)
}
func testSearchFieldSpacePreviewShortcutRequiresEmptySearchAndNoModifiers() {
@@ -372,60 +261,35 @@ final class ClipboardPanelControllerTests: XCTestCase {
}
func testNavigationShortcutsMapToShelfMovement() {
XCTAssertEqual(ClipboardPanelController.navigationShortcutAction(forKeyCode: 115, modifiers: []), .first)
XCTAssertEqual(ClipboardPanelController.navigationShortcutAction(forKeyCode: 119, modifiers: []), .last)
XCTAssertEqual(ClipboardPanelController.navigationShortcutAction(forKeyCode: 124, modifiers: []), .next)
XCTAssertEqual(ClipboardPanelController.navigationShortcutAction(forKeyCode: 121, modifiers: []), .pageNext)
XCTAssertEqual(ClipboardPanelController.navigationShortcutAction(forKeyCode: 116, modifiers: []), .pagePrevious)
XCTAssertEqual(ClipboardPanelController.navigationShortcutAction(forKeyCode: 123, modifiers: []), .previous)
XCTAssertEqual(ClipboardPanelController.navigationShortcutAction(forKeyCode: 126, modifiers: .command), .first)
XCTAssertEqual(ClipboardPanelController.navigationShortcutAction(forKeyCode: 125, modifiers: .command), .last)
}
func testNavigationShortcutsRejectUnsupportedModifiers() {
XCTAssertNil(ClipboardPanelController.navigationShortcutAction(forKeyCode: 124, modifiers: .command))
XCTAssertNil(ClipboardPanelController.navigationShortcutAction(forKeyCode: 126, modifiers: []))
XCTAssertNil(ClipboardPanelController.navigationShortcutAction(forKeyCode: 125, modifiers: []))
XCTAssertNil(ClipboardPanelController.navigationShortcutAction(forKeyCode: 121, modifiers: .shift))
XCTAssertNil(ClipboardPanelController.navigationShortcutAction(forKeyCode: 35, modifiers: []))
assertShortcutMappings([
(115, [], .first), (119, [], .last), (124, [], .next),
(121, [], .pageNext), (116, [], .pagePrevious), (123, [], .previous),
(126, .command, .first), (125, .command, .last),
(124, .command, nil), (126, [], nil), (125, [], nil),
(121, .shift, nil), (35, [], nil)
], using: ClipboardPanelController.navigationShortcutAction)
}
func testSelectionShortcutsMapToRangeAndSelectAllActions() {
XCTAssertEqual(ClipboardPanelController.selectionShortcutAction(forKeyCode: 0, modifiers: .command), .selectAll)
XCTAssertEqual(ClipboardPanelController.selectionShortcutAction(forKeyCode: 115, modifiers: .shift), .extendFirst)
XCTAssertEqual(ClipboardPanelController.selectionShortcutAction(forKeyCode: 119, modifiers: .shift), .extendLast)
XCTAssertEqual(ClipboardPanelController.selectionShortcutAction(forKeyCode: 124, modifiers: .shift), .extendNext)
XCTAssertEqual(ClipboardPanelController.selectionShortcutAction(forKeyCode: 121, modifiers: .shift), .extendPageNext)
XCTAssertEqual(ClipboardPanelController.selectionShortcutAction(forKeyCode: 116, modifiers: .shift), .extendPagePrevious)
XCTAssertEqual(ClipboardPanelController.selectionShortcutAction(forKeyCode: 123, modifiers: .shift), .extendPrevious)
}
func testSelectionShortcutsRequireExactModifierSets() {
XCTAssertNil(ClipboardPanelController.selectionShortcutAction(forKeyCode: 0, modifiers: []))
XCTAssertNil(ClipboardPanelController.selectionShortcutAction(forKeyCode: 0, modifiers: [.command, .shift]))
XCTAssertNil(ClipboardPanelController.selectionShortcutAction(forKeyCode: 124, modifiers: []))
XCTAssertNil(ClipboardPanelController.selectionShortcutAction(forKeyCode: 124, modifiers: [.command, .shift]))
assertShortcutMappings([
(0, .command, .selectAll),
(115, .shift, .extendFirst), (119, .shift, .extendLast),
(124, .shift, .extendNext), (121, .shift, .extendPageNext),
(116, .shift, .extendPagePrevious), (123, .shift, .extendPrevious),
(0, [], nil), (0, [.command, .shift], nil),
(124, [], nil), (124, [.command, .shift], nil)
], using: ClipboardPanelController.selectionShortcutAction)
}
func testCommandActionShortcutsMapToSelectedClipActions() {
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 8, modifiers: .command), .copy)
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 14, modifiers: .command), .edit)
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 3, modifiers: .command), .focusSearch)
XCTAssertNil(ClipboardPanelController.commandShortcutAction(forKeyCode: 45, modifiers: .command))
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 5, modifiers: .command), .showInClipboard)
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 16, modifiers: .command), .preview)
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 31, modifiers: .command), .open)
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 15, modifiers: .command), .rename)
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 17, modifiers: .command), .toggleCapturePause)
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 6, modifiers: .command), .undoDelete)
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 123, modifiers: .command), .previousCollection)
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 124, modifiers: .command), .nextCollection)
}
func testCommandActionShortcutsRequireCommandOnlySoSearchTypingIsUntouched() {
XCTAssertNil(ClipboardPanelController.commandShortcutAction(forKeyCode: 8, modifiers: []))
XCTAssertNil(ClipboardPanelController.commandShortcutAction(forKeyCode: 8, modifiers: [.command, .shift]))
XCTAssertNil(ClipboardPanelController.commandShortcutAction(forKeyCode: 9, modifiers: .command))
assertShortcutMappings([
(8, .command, .copy), (14, .command, .edit), (3, .command, .focusSearch),
(45, .command, nil), (5, .command, .showInClipboard),
(16, .command, .preview), (31, .command, .open), (15, .command, .rename),
(17, .command, .toggleCapturePause), (6, .command, .undoDelete),
(123, .command, .previousCollection), (124, .command, .nextCollection),
(8, [], nil), (8, [.command, .shift], nil), (9, .command, nil)
], using: ClipboardPanelController.commandShortcutAction)
}
func testSettingsShortcutMatchesOnlyItsExactLocalBinding() {
@@ -455,67 +319,32 @@ final class ClipboardPanelControllerTests: XCTestCase {
}
func testModifiedShortcutsMapToPanelActions() {
XCTAssertEqual(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 36, modifiers: .shift), .pastePlainText)
XCTAssertEqual(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 1, modifiers: [.command, .shift]), .toggleStack)
XCTAssertEqual(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 8, modifiers: [.command, .shift]), .toggleStackCapture)
XCTAssertEqual(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 9, modifiers: [.command, .shift]), .pastePlainText)
XCTAssertEqual(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 45, modifiers: [.command, .shift]), .newCollection)
XCTAssertEqual(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 36, modifiers: [.command, .shift]), .pasteStackNext)
assertShortcutMappings([
(36, .shift, .pastePlainText),
(1, [.command, .shift], .toggleStack),
(8, [.command, .shift], .toggleStackCapture),
(9, [.command, .shift], .pastePlainText),
(45, [.command, .shift], .newCollection),
(36, [.command, .shift], .pasteStackNext),
(36, [], nil), (8, .shift, nil), (8, .command, nil),
(8, [.command, .option, .shift], nil), (31, [.command, .shift], nil)
], using: ClipboardPanelController.modifiedShortcutAction)
}
func testModifiedShortcutsRequireCommandShiftOnly() {
XCTAssertNil(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 36, modifiers: []))
XCTAssertNil(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 8, modifiers: .shift))
XCTAssertNil(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 8, modifiers: .command))
XCTAssertNil(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 8, modifiers: [.command, .option, .shift]))
XCTAssertNil(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 31, modifiers: [.command, .shift]))
}
private func makeController(preferredScreen: NSScreen) -> (ClipboardPanelController, ClipboardStore) {
let settings = makeSettings()
let encryptionService = ClipboardEncryptionService(keyProvider: { nil })
let cacheService = ClipboardCacheService(
baseURL: makeTempDirectory(),
encryptionService: encryptionService
)
let store = ClipboardStore(
settings: settings,
cacheService: cacheService,
baseURL: makeTempDirectory(),
encryptionService: encryptionService
)
let controller = ClipboardPanelController(
store: store,
settings: settings,
cacheService: cacheService,
preferredScreen: { preferredScreen }
)
return (controller, store)
}
private func makeSettings() -> SettingsModel {
let suite = "com.clipbored.controllertest.\(UUID().uuidString)"
defaultsSuites.append(suite)
let defaults = UserDefaults(suiteName: suite)!
let settings = SettingsModel(defaults: defaults)
settings.maxHistoryItems = 10
settings.historyRetention = .forever
settings.pruneDuplicates = false
return settings
}
private func makeTempDirectory() -> URL {
let directory = FileManager.default.temporaryDirectory
.appendingPathComponent("clipbored-controllertest")
.appendingPathComponent(UUID().uuidString)
try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
tempURLs.append(directory)
return directory
}
private func drainMainQueue() {
for _ in 0..<24 {
RunLoop.main.run(until: Date().addingTimeInterval(0.01))
private func assertShortcutMappings<Value: Equatable>(
_ cases: [(keyCode: UInt16, modifiers: NSEvent.ModifierFlags, expected: Value?)],
using mapping: (UInt16, NSEvent.ModifierFlags) -> Value?,
file: StaticString = #filePath,
line: UInt = #line
) {
for testCase in cases {
XCTAssertEqual(
mapping(testCase.keyCode, testCase.modifiers),
testCase.expected,
"keyCode \(testCase.keyCode), modifiers \(testCase.modifiers.rawValue)",
file: file,
line: line
)
}
}
}

View File

@@ -39,294 +39,6 @@ final class ClipboardPanelViewModelTests: XCTestCase {
XCTAssertEqual(pinnedOnly.map(\.payload), ["four", "one"])
}
func testSelectingCollectionWithEmptySearchUsesIndexedVisibleItems() {
let settings = makeSettings()
settings.maxHistoryItems = 260
let cacheService = makeCacheService()
let store = makeStore(settings: settings, cacheService: cacheService)
for index in 0..<220 {
store.upsert(makeTextItem("outside indexed category \(index)", createdAt: Date(timeIntervalSince1970: Double(index))))
}
for index in 0..<30 {
var item = makeTextItem("client indexed category \(index)", createdAt: Date(timeIntervalSince1970: Double(1_000 + index)))
item.collectionName = "Client Work"
store.upsert(item)
}
store.flushPersistenceForTesting()
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: 250)
viewModel.debugResetVisibleItemsPerformanceCounters()
viewModel.selectCollection(named: "Client Work")
XCTAssertEqual(viewModel.visibleItems.count, 30)
XCTAssertEqual(viewModel.debugVisibleItemsFullScanCount, 0)
XCTAssertEqual(viewModel.debugVisibleItemsIndexedLookupCount, 1)
XCTAssertEqual(viewModel.visibleItems.first?.payload, "client indexed category 29")
}
func testRepeatedThirtyItemCollectionSelectionsStayOnIndexedFastPath() {
let collectionCount = 60
let itemsPerCollection = 30
let settings = makeSettings()
settings.maxHistoryItems = collectionCount * itemsPerCollection
let cacheService = makeCacheService()
let store = makeStore(settings: settings, cacheService: cacheService)
let collectionNames = (0..<collectionCount).map { "Client Work \($0)" }
for (collectionIndex, collectionName) in collectionNames.enumerated() {
for itemIndex in 0..<itemsPerCollection {
var item = makeTextItem(
"\(collectionName) clip \(itemIndex)",
createdAt: Date(timeIntervalSince1970: Double((collectionIndex * itemsPerCollection) + itemIndex))
)
item.collectionName = collectionName
store.upsert(item)
}
}
store.flushPersistenceForTesting()
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: collectionCount * itemsPerCollection)
viewModel.debugResetVisibleItemsPerformanceCounters()
let start = CFAbsoluteTimeGetCurrent()
for collectionName in collectionNames {
viewModel.selectCollection(named: collectionName)
XCTAssertEqual(viewModel.visibleItems.count, itemsPerCollection)
}
let elapsed = CFAbsoluteTimeGetCurrent() - start
XCTAssertEqual(viewModel.debugVisibleItemsFullScanCount, 0)
XCTAssertEqual(viewModel.debugVisibleItemsIndexedLookupCount, collectionCount)
XCTAssertLessThan(elapsed, 0.20)
}
func testCommandSelectedCategoriesUseIndexedUnionFastPath() {
let settings = makeSettings()
let cacheService = makeCacheService()
let store = makeStore(settings: settings, cacheService: cacheService)
let text = makeTextItem("category text", createdAt: Date(timeIntervalSince1970: 100))
let link = ClipboardItem(
id: UUID(),
kind: .url,
displayText: "category link",
payload: "https://example.com/category",
payloadHash: store.hashString("https://example.com/category"),
createdAt: Date(timeIntervalSince1970: 200),
lastUsedAt: Date(timeIntervalSince1970: 200),
useCount: 0,
sourceApp: nil,
imagePath: nil,
thumbnailPath: nil
)
store.upsert(text)
store.upsert(link)
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: 2)
viewModel.debugResetVisibleItemsPerformanceCounters()
viewModel.selectSortMode(.text, extending: true)
viewModel.selectSortMode(.links, extending: true)
XCTAssertEqual(viewModel.visibleItems.map(\.payload), ["https://example.com/category", "category text"])
XCTAssertEqual(viewModel.debugVisibleItemsFullScanCount, 0)
XCTAssertEqual(viewModel.debugVisibleItemsIndexedLookupCount, 2)
XCTAssertTrue(viewModel.isSortModeCategorySelected(.text))
XCTAssertTrue(viewModel.isSortModeCategorySelected(.links))
}
func testCommandSelectedCustomCollectionCombinesWithCategoryFilter() {
let settings = makeSettings()
let cacheService = makeCacheService()
let store = makeStore(settings: settings, cacheService: cacheService)
var client = makeTextItem("client note", createdAt: Date(timeIntervalSince1970: 100))
client.collectionName = "Client Work"
let link = ClipboardItem(
id: UUID(),
kind: .url,
displayText: "category link",
payload: "https://example.com/category",
payloadHash: store.hashString("https://example.com/category"),
createdAt: Date(timeIntervalSince1970: 200),
lastUsedAt: Date(timeIntervalSince1970: 200),
useCount: 0,
sourceApp: nil,
imagePath: nil,
thumbnailPath: nil
)
store.upsert(client)
store.upsert(link)
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: 2)
viewModel.debugResetVisibleItemsPerformanceCounters()
viewModel.selectCollection(named: "Client Work")
viewModel.selectSortMode(.links, extending: true)
XCTAssertEqual(viewModel.visibleItems.map(\.payload), ["https://example.com/category", "client note"])
XCTAssertEqual(viewModel.debugVisibleItemsFullScanCount, 0)
XCTAssertEqual(viewModel.debugVisibleItemsIndexedLookupCount, 2)
XCTAssertTrue(viewModel.isCollectionCategorySelected(named: "Client Work"))
XCTAssertTrue(viewModel.isSortModeCategorySelected(.links))
}
func testEmptySearchCollectionCountSummaryUsesIndexedCounts() {
let settings = makeSettings()
settings.maxHistoryItems = 260
let cacheService = makeCacheService()
let store = makeStore(settings: settings, cacheService: cacheService)
for index in 0..<220 {
store.upsert(makeTextItem("outside indexed count \(index)", createdAt: Date(timeIntervalSince1970: Double(index))))
}
for index in 0..<30 {
var item = makeTextItem("client indexed count \(index)", createdAt: Date(timeIntervalSince1970: Double(1_000 + index)))
item.collectionName = "Client Work"
store.upsert(item)
}
store.flushPersistenceForTesting()
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: 250)
viewModel.debugResetVisibleItemsPerformanceCounters()
let counts = viewModel.collectionCountSummary()
XCTAssertEqual(counts.count(for: .mostRecent), 250)
XCTAssertEqual(counts.count(for: .text), 250)
XCTAssertEqual(counts.count(named: "Client Work"), 30)
XCTAssertEqual(viewModel.debugCollectionCountFullScanCount, 0)
XCTAssertEqual(viewModel.debugCollectionCountIndexedLookupCount, 1)
}
func testSearchMatchesAreEvaluatedOnceAcrossVisibleItemsCountsAndCategoryChanges() {
let settings = makeSettings()
settings.maxHistoryItems = 120
let cacheService = makeCacheService()
let store = makeStore(settings: settings, cacheService: cacheService)
for index in 0..<100 {
var item = makeTextItem(
index.isMultiple(of: 5) ? "shared search needle \(index)" : "unmatched history \(index)",
createdAt: Date(timeIntervalSince1970: Double(index))
)
if index.isMultiple(of: 2) {
item.collectionName = "Client Work"
}
store.upsert(item)
}
store.flushPersistenceForTesting()
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: 100)
viewModel.debugResetVisibleItemsPerformanceCounters()
viewModel.searchText = "needle"
XCTAssertEqual(viewModel.visibleItems.count, 20)
XCTAssertEqual(viewModel.debugSearchItemEvaluationCount, 100)
XCTAssertEqual(viewModel.debugSearchDocumentBuildCount, 100)
let counts = viewModel.collectionCountSummary()
XCTAssertEqual(counts.count(for: .text), 20)
XCTAssertEqual(counts.count(named: "Client Work"), 10)
XCTAssertEqual(viewModel.debugSearchItemEvaluationCount, 100)
XCTAssertEqual(viewModel.debugSearchMatchCacheHitCount, 1)
viewModel.selectSortMode(.text, extending: true)
viewModel.selectSortMode(.links, extending: true)
XCTAssertEqual(viewModel.visibleItems.count, 20)
XCTAssertEqual(viewModel.debugSearchItemEvaluationCount, 100)
XCTAssertEqual(viewModel.debugSearchMatchCacheHitCount, 3)
viewModel.searchText = "shared"
XCTAssertEqual(viewModel.visibleItems.count, 20)
XCTAssertEqual(viewModel.debugSearchItemEvaluationCount, 200)
XCTAssertEqual(viewModel.debugSearchDocumentBuildCount, 100)
XCTAssertEqual(viewModel.debugSearchDocumentCacheHitCount, 100)
}
func testEquivalentDiacriticSearchReusesVisibleAndCollectionCountCaches() {
let settings = makeSettings()
let cacheService = makeCacheService()
let store = makeStore(settings: settings, cacheService: cacheService)
store.upsert(makeTextItem("Résumé draft", createdAt: Date(timeIntervalSince1970: 100)))
store.upsert(makeTextItem("Meeting note", createdAt: Date(timeIntervalSince1970: 200)))
store.flushPersistenceForTesting()
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: 2)
viewModel.debugResetVisibleItemsPerformanceCounters()
viewModel.searchText = "resume"
XCTAssertEqual(viewModel.visibleItems.map(\.payload), ["Résumé draft"])
_ = viewModel.collectionCountSummary()
XCTAssertEqual(viewModel.debugSearchItemEvaluationCount, 2)
XCTAssertEqual(viewModel.debugCollectionCountFullScanCount, 1)
viewModel.searchText = " RÉSUMÉ "
_ = viewModel.collectionCountSummary()
XCTAssertEqual(viewModel.visibleItems.map(\.payload), ["Résumé draft"])
XCTAssertEqual(viewModel.debugSearchItemEvaluationCount, 2)
XCTAssertEqual(viewModel.debugCollectionCountFullScanCount, 1)
}
func testCategoryFilterSelectionIsBuiltOncePerMutationAndReusedForChipStateQueries() {
let settings = makeSettings()
let cacheService = makeCacheService()
let store = makeStore(settings: settings, cacheService: cacheService)
store.upsert(makeTextItem("category state", createdAt: Date(timeIntervalSince1970: 100)))
store.flushPersistenceForTesting()
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: 1)
viewModel.debugResetVisibleItemsPerformanceCounters()
viewModel.selectSortMode(.text, extending: true)
XCTAssertEqual(viewModel.debugCategoryFilterSelectionBuildCount, 1)
for _ in 0..<20 {
for mode in ClipboardSortMode.allCases {
_ = viewModel.isSortModeCategorySelected(mode)
}
_ = viewModel.isCollectionCategorySelected(named: "Client Work")
_ = viewModel.canShowVisibleItemsInClipboard
}
XCTAssertEqual(viewModel.debugCategoryFilterSelectionBuildCount, 1)
}
func testRepeatedRecomputesDoNotRescanUnchangedStackMembership() {
let settings = makeSettings()
let cacheService = makeCacheService()
let store = makeStore(settings: settings, cacheService: cacheService)
store.upsert(makeTextItem("stacked needle", createdAt: Date(timeIntervalSince1970: 100)))
store.upsert(makeTextItem("outside note", createdAt: Date(timeIntervalSince1970: 200)))
store.flushPersistenceForTesting()
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: 2)
viewModel.selectItem(at: 1)
viewModel.toggleSelectedStackMembership()
viewModel.debugResetVisibleItemsPerformanceCounters()
viewModel.searchText = "needle"
viewModel.clearSearch()
viewModel.selectSortMode(.text)
viewModel.selectSortMode(.mostRecent)
XCTAssertEqual(viewModel.stackCount, 1)
XCTAssertEqual(viewModel.debugStackPruneScanCount, 0)
}
func testRepeatedHoverSelectionDoesNotNotifyAnUnchangedSelection() {
let settings = makeSettings()
@@ -405,139 +117,64 @@ final class ClipboardPanelViewModelTests: XCTestCase {
XCTAssertEqual(completionMainThreadValues, [true, true])
}
func testComputeVisibleItemsFiltersColorClipsAndStructuredType() {
func testContentKindsSupportCategoryStructuredAndTextSearch() {
let settings = makeSettings()
let store = makeStore(settings: settings)
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: makeCacheService())
let color = ClipboardItem(
id: UUID(),
let color = makeItem(
kind: .color,
displayText: "#0A84FF",
payload: "#0A84FF",
payloadHash: hash("#0A84FF"),
createdAt: Date(timeIntervalSince1970: 100),
lastUsedAt: Date(timeIntervalSince1970: 100),
useCount: 0,
sourceApp: "Design Tool",
imagePath: nil,
thumbnailPath: nil
timestamp: 100,
sourceApp: "Design Tool"
)
let text = ClipboardItem(
id: UUID(),
kind: .text,
displayText: "Color note",
payload: "Color note",
payloadHash: hash("Color note"),
createdAt: Date(timeIntervalSince1970: 200),
lastUsedAt: Date(timeIntervalSince1970: 200),
useCount: 0,
sourceApp: "Notes",
imagePath: nil,
thumbnailPath: nil
)
XCTAssertEqual(
viewModel.computeVisibleItems(from: [text, color], query: "", sortMode: .colors).map(\.payload),
["#0A84FF"]
)
XCTAssertEqual(
viewModel.computeVisibleItems(from: [text, color], query: "type:swatch", sortMode: .mostRecent).map(\.payload),
["#0A84FF"]
)
XCTAssertEqual(
viewModel.computeVisibleItems(from: [text, color], query: "hex 0a84ff", sortMode: .mostRecent).map(\.payload),
["#0A84FF"]
)
}
func testComputeVisibleItemsFiltersCodeSnippetsAndKeepsThemInTextView() {
let settings = makeSettings()
let store = makeStore(settings: settings)
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: makeCacheService())
let code = ClipboardItem(
id: UUID(),
let code = makeItem(
kind: .code,
displayText: "Swift Snippet",
payload: "func greet(name: String) -> String {\n return \"Hi \\(name)\"\n}",
payloadHash: hash("swift-snippet"),
createdAt: Date(timeIntervalSince1970: 200),
lastUsedAt: Date(timeIntervalSince1970: 200),
useCount: 0,
sourceApp: "Xcode",
imagePath: nil,
thumbnailPath: nil
timestamp: 300,
sourceApp: "Xcode"
)
let text = ClipboardItem(
id: UUID(),
kind: .text,
displayText: "Meeting note",
payload: "Meeting note",
payloadHash: hash("Meeting note"),
createdAt: Date(timeIntervalSince1970: 100),
lastUsedAt: Date(timeIntervalSince1970: 100),
useCount: 0,
sourceApp: "Notes",
imagePath: nil,
thumbnailPath: nil
)
XCTAssertEqual(
viewModel.computeVisibleItems(from: [code, text], query: "", sortMode: .code).map(\.kind),
[.code]
)
XCTAssertEqual(
viewModel.computeVisibleItems(from: [code, text], query: "", sortMode: .text).map(\.kind),
[.code, .text]
)
XCTAssertEqual(
viewModel.computeVisibleItems(from: [code, text], query: "type:snippet greet", sortMode: .mostRecent).map(\.kind),
[.code]
)
}
func testComputeVisibleItemsFiltersVideoClipsAndStructuredType() {
let settings = makeSettings()
let store = makeStore(settings: settings)
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: makeCacheService())
let video = ClipboardItem(
id: UUID(),
let video = makeItem(
kind: .video,
displayText: "Video (12 KB)",
payload: "/tmp/clip.mp4",
payloadHash: hash("clip-video"),
createdAt: Date(timeIntervalSince1970: 200),
lastUsedAt: Date(timeIntervalSince1970: 200),
useCount: 0,
sourceApp: "QuickTime Player",
imagePath: nil,
thumbnailPath: nil
timestamp: 200,
sourceApp: "QuickTime Player"
)
let image = ClipboardItem(
id: UUID(),
let text = makeItem(
kind: .text,
displayText: "Meeting note",
payload: "Meeting note",
timestamp: 100,
sourceApp: "Notes"
)
let image = makeItem(
kind: .image,
displayText: "Image",
payload: "/tmp/image.png",
payloadHash: hash("image"),
createdAt: Date(timeIntervalSince1970: 100),
lastUsedAt: Date(timeIntervalSince1970: 100),
useCount: 0,
sourceApp: "Preview",
imagePath: nil,
thumbnailPath: nil
)
XCTAssertEqual(
viewModel.computeVisibleItems(from: [video, image], query: "", sortMode: .videos).map(\.kind),
[.video]
)
XCTAssertEqual(
viewModel.computeVisibleItems(from: [video, image], query: "type:movie", sortMode: .mostRecent).map(\.kind),
[.video]
)
XCTAssertEqual(
viewModel.computeVisibleItems(from: [video, image], query: "mp4", sortMode: .mostRecent).map(\.kind),
[.video]
timestamp: 100,
sourceApp: "Preview"
)
let items = [color, code, video, text, image]
let cases: [(ClipboardSortMode, String, [ClipboardItemKind])] = [
(.colors, "", [.color]),
(.mostRecent, "type:swatch", [.color]),
(.mostRecent, "hex 0a84ff", [.color]),
(.code, "", [.code]),
(.text, "", [.code, .text]),
(.mostRecent, "type:snippet greet", [.code]),
(.videos, "", [.video]),
(.mostRecent, "type:movie", [.video]),
(.mostRecent, "mp4", [.video])
]
for (sortMode, query, expectedKinds) in cases {
XCTAssertEqual(
viewModel.computeVisibleItems(from: items, query: query, sortMode: sortMode).map(\.kind),
expectedKinds,
"sort=\(sortMode), query=\(query)"
)
}
}
func testSearchMatchesIndependentTokensCaseInsensitively() {
@@ -2547,7 +2184,7 @@ final class ClipboardPanelViewModelTests: XCTestCase {
viewModel.searchText = "second"
XCTAssertEqual(viewModel.visibleItems.map(\.payload), ["second queue note"])
viewModel.clearSearch()
viewModel.searchText = ""
viewModel.sortMode = .text
XCTAssertFalse(viewModel.isStackFilterSelected)
XCTAssertEqual(viewModel.visibleItems.map(\.payload), ["outside note", "second queue note", "first queue note"])
@@ -2566,7 +2203,7 @@ final class ClipboardPanelViewModelTests: XCTestCase {
viewModel.searchText = "draft"
XCTAssertEqual(viewModel.visibleItems.map(\.id), [item.id])
viewModel.clearSearch()
viewModel.searchText = ""
XCTAssertEqual(viewModel.editableTextForSelected(), "draft meeting note")
viewModel.updateSelectedText(to: "final launch note")
@@ -2634,8 +2271,8 @@ final class ClipboardPanelViewModelTests: XCTestCase {
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: 2)
XCTAssertNil(viewModel.editableTextForItem(at: 0))
viewModel.selectItem(at: 0)
XCTAssertNil(viewModel.editableTextForSelected())
viewModel.updateSelectedText(to: "should not apply")
XCTAssertEqual(store.items.first?.payload, file.payload)
@@ -2963,18 +2600,32 @@ final class ClipboardPanelViewModelTests: XCTestCase {
}
private func makeTextItem(_ text: String, createdAt: Date) -> ClipboardItem {
makeItem(kind: .text, displayText: text, payload: text, timestamp: createdAt.timeIntervalSince1970)
}
private func makeItem(
kind: ClipboardItemKind,
displayText: String,
payload: String,
timestamp: TimeInterval,
lastUsedTimestamp: TimeInterval? = nil,
useCount: Int = 0,
sourceApp: String? = nil,
isPinned: Bool = false
) -> ClipboardItem {
ClipboardItem(
id: UUID(),
kind: .text,
displayText: text,
payload: text,
payloadHash: hash(text),
createdAt: createdAt,
lastUsedAt: createdAt,
useCount: 0,
sourceApp: nil,
kind: kind,
displayText: displayText,
payload: payload,
payloadHash: hash(payload),
createdAt: Date(timeIntervalSince1970: timestamp),
lastUsedAt: Date(timeIntervalSince1970: lastUsedTimestamp ?? timestamp),
useCount: useCount,
sourceApp: sourceApp,
imagePath: nil,
thumbnailPath: nil
thumbnailPath: nil,
isPinned: isPinned
)
}
@@ -3031,88 +2682,56 @@ final class ClipboardPanelViewModelTests: XCTestCase {
private func makeSampleItems() -> [ClipboardItem] {
[
ClipboardItem(
id: UUID(),
makeItem(
kind: .text,
displayText: "Project notes",
payload: "one",
payloadHash: hash("one"),
createdAt: Date(timeIntervalSince1970: 1000),
lastUsedAt: Date(timeIntervalSince1970: 1000),
timestamp: 1000,
useCount: 2,
sourceApp: nil,
imagePath: nil,
thumbnailPath: nil,
isPinned: true
),
ClipboardItem(
id: UUID(),
makeItem(
kind: .richText,
displayText: "Two",
payload: "two",
payloadHash: hash("two"),
createdAt: Date(timeIntervalSince1970: 1100),
lastUsedAt: Date(timeIntervalSince1970: 1080),
timestamp: 1100,
lastUsedTimestamp: 1080,
useCount: 4,
sourceApp: "Mail",
imagePath: nil,
thumbnailPath: nil,
isPinned: false
sourceApp: "Mail"
),
ClipboardItem(
id: UUID(),
makeItem(
kind: .url,
displayText: "Apple",
payload: "https://apple.com",
payloadHash: hash("https://apple.com"),
createdAt: Date(timeIntervalSince1970: 1030),
lastUsedAt: Date(timeIntervalSince1970: 1050),
timestamp: 1030,
lastUsedTimestamp: 1050,
useCount: 1,
sourceApp: "Safari",
imagePath: nil,
thumbnailPath: nil,
isPinned: false
sourceApp: "Safari"
),
ClipboardItem(
id: UUID(),
makeItem(
kind: .file,
displayText: "report.pdf",
payload: "/tmp/report.pdf",
payloadHash: hash("/tmp/report.pdf"),
createdAt: Date(timeIntervalSince1970: 1060),
lastUsedAt: Date(timeIntervalSince1970: 1070),
timestamp: 1060,
lastUsedTimestamp: 1070,
useCount: 3,
sourceApp: "Finder",
imagePath: nil,
thumbnailPath: nil,
isPinned: false
sourceApp: "Finder"
),
ClipboardItem(
id: UUID(),
makeItem(
kind: .audio,
displayText: "Voice memo",
payload: "/tmp/voice.sound",
payloadHash: hash("/tmp/voice.sound"),
createdAt: Date(timeIntervalSince1970: 1040),
lastUsedAt: Date(timeIntervalSince1970: 1060),
timestamp: 1040,
lastUsedTimestamp: 1060,
useCount: 2,
sourceApp: "Voice Memos",
imagePath: nil,
thumbnailPath: nil,
isPinned: false
sourceApp: "Voice Memos"
),
ClipboardItem(
id: UUID(),
makeItem(
kind: .text,
displayText: "Four",
payload: "four",
payloadHash: hash("four"),
createdAt: Date(timeIntervalSince1970: 1200),
lastUsedAt: Date(timeIntervalSince1970: 1200),
useCount: 0,
timestamp: 1200,
sourceApp: "Notes",
imagePath: nil,
thumbnailPath: nil,
isPinned: true
)
]

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +0,0 @@
import XCTest
@testable import ClipBored
final class DiagnosticsServiceTests: XCTestCase {
func testCountersCanBeIncrementedAndReset() {
let diagnostics = DiagnosticsService.shared
diagnostics.reset()
diagnostics.incrementMonitorTick()
diagnostics.incrementPasteboardChange()
diagnostics.incrementExtractionAttempt()
diagnostics.incrementDatabaseMutation()
diagnostics.incrementCachePurge()
// The snapshot read synchronizes with the serial diagnostics queue.
let snapshot = diagnostics.currentSnapshot()
XCTAssertEqual(snapshot.monitorTicks, 1)
XCTAssertEqual(snapshot.pasteboardChanges, 1)
XCTAssertEqual(snapshot.extractionAttempts, 1)
XCTAssertEqual(snapshot.databaseMutations, 1)
XCTAssertEqual(snapshot.cachePurges, 1)
diagnostics.reset()
XCTAssertEqual(diagnostics.currentSnapshot(), .init(monitorTicks: 0, pasteboardChanges: 0, extractionAttempts: 0, databaseMutations: 0, cachePurges: 0))
}
}

View File

@@ -1,81 +0,0 @@
import XCTest
@testable import ClipBored
final class LinkPreviewWindowControllerTests: XCTestCase {
func testReusedPreviewIgnoresStaleObservedTitleUntilNewNavigationStarts() throws {
let controller = LinkPreviewWindowController()
let firstURL = try XCTUnwrap(URL(string: "https://example.com/old"))
let secondURL = try XCTUnwrap(URL(string: "https://example.com/new"))
controller.debugPrepareForPreview(LinkPreviewRequest(url: firstURL, title: "Old request title"))
controller.debugAllowObservedPageTitles()
controller.debugApplyObservedPageTitle("Old loaded title")
XCTAssertEqual(controller.debugTitleText, "Old loaded title")
controller.debugPrepareForPreview(LinkPreviewRequest(url: secondURL, title: "New request title"))
controller.debugApplyObservedPageTitle("Old loaded title")
XCTAssertEqual(controller.debugTitleText, "New request title")
XCTAssertEqual(controller.debugAddressText, "https://example.com/new")
XCTAssertEqual(controller.debugStatusText, "Loading")
controller.debugAllowObservedPageTitles()
controller.debugApplyObservedPageTitle("New loaded title")
XCTAssertEqual(controller.debugTitleText, "New loaded title")
}
func testCancelledNavigationDoesNotShowFalseLoadFailure() throws {
let controller = LinkPreviewWindowController()
let url = try XCTUnwrap(URL(string: "https://example.com/new"))
controller.debugPrepareForPreview(LinkPreviewRequest(url: url, title: "New request title"))
controller.debugApplyNavigationFailure(URLError(.cancelled))
XCTAssertEqual(controller.debugStatusText, "Loading")
controller.debugApplyNavigationFailure(URLError(.timedOut))
XCTAssertEqual(controller.debugStatusText, "Could not load")
}
func testToolbarTooltipsTrackFullVisibleText() throws {
let controller = LinkPreviewWindowController()
let url = try XCTUnwrap(URL(string: "https://example.com/articles/a-very-long-release-note-title?ref=clipbored"))
controller.debugPrepareForPreview(LinkPreviewRequest(url: url, title: "Release note with a long title"))
XCTAssertEqual(controller.debugTitleTooltip, "Release note with a long title")
XCTAssertEqual(controller.debugAddressTooltip, url.absoluteString)
XCTAssertEqual(controller.debugStatusTooltip, "Loading")
controller.debugAllowObservedPageTitles()
controller.debugApplyObservedPageTitle("Loaded page title that may truncate in the toolbar")
XCTAssertEqual(controller.debugTitleTooltip, "Loaded page title that may truncate in the toolbar")
controller.debugApplyNavigationFailure(URLError(.timedOut))
XCTAssertEqual(controller.debugStatusTooltip, "Could not load")
}
func testOpenInBrowserUsesDisplayedPageURLAndResetsForReusedPreview() throws {
var openedURLs: [URL] = []
let controller = LinkPreviewWindowController { openedURLs.append($0) }
let firstURL = try XCTUnwrap(URL(string: "https://example.com/old"))
let navigatedURL = try XCTUnwrap(URL(string: "https://example.com/old/details"))
let secondURL = try XCTUnwrap(URL(string: "https://example.com/new"))
controller.debugPrepareForPreview(LinkPreviewRequest(url: firstURL, title: "Old request title"))
controller.debugSetDisplayedPageURL(navigatedURL)
controller.debugOpenInBrowser()
XCTAssertEqual(openedURLs, [navigatedURL])
controller.debugPrepareForPreview(LinkPreviewRequest(url: secondURL, title: "New request title"))
controller.debugOpenInBrowser()
XCTAssertEqual(openedURLs, [navigatedURL, secondURL])
}
}

View File

@@ -51,25 +51,4 @@ final class OnboardingWindowControllerTests: XCTestCase {
XCTAssertTrue(presentation.showDockIcon)
}
func testDisplayedEntryPointSelectionIsNormalizedOnRefresh() {
let settings = makeSettings()
settings.showMenuBarIcon = false
settings.showDockIcon = false
let controller = OnboardingWindowController(
settings: settings,
onOpenAccessibility: {},
onFinish: {}
)
XCTAssertTrue(controller.debugShowMenuBarIconIsEnabled)
XCTAssertFalse(controller.debugShowDockIconIsEnabled)
}
private func makeSettings() -> SettingsModel {
let suiteName = "com.clipbored.onboarding.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
defaults.removePersistentDomain(forName: suiteName)
return SettingsModel(defaults: defaults)
}
}

View File

@@ -43,6 +43,7 @@ final class SensitiveContentDetectorTests: XCTestCase {
func testDetectsCreditCardWithLuhnCheck() {
XCTAssertEqual(SensitiveContentDetector.detect("4242424242424242"), .creditCard)
XCTAssertEqual(SensitiveContentDetector.detect("Card: 4242 4242 4242 4242"), .creditCard)
XCTAssertNil(SensitiveContentDetector.detect("4242424242424241"))
}

View File

@@ -0,0 +1,117 @@
import AppKit
import XCTest
@testable import ClipBored
final class SettingsPresentationTests: XCTestCase {
func testPasteAndDataStatusColors() {
let pasteCases: [(String, NSColor)] = [
("", .secondaryLabelColor),
("Pasted", .systemGreen),
("Copied. Grant Accessibility access to paste automatically.", .systemOrange),
("Could not write item to clipboard.", .systemRed)
]
for (message, color) in pasteCases {
XCTAssertEqual(SettingsWindowController.pasteStatusPresentation(storedStatus: message).textColor, color)
}
let dataCases: [(String, NSColor)] = [
("", .secondaryLabelColor),
("Exported 3 clips.", .systemGreen),
("Imported 3 clips. Skipped 1 clip.", .systemOrange),
("The archive couldn't be opened.", .systemRed)
]
for (message, color) in dataCases {
XCTAssertEqual(SettingsWindowController.dataStatusPresentation(storedStatus: message).textColor, color)
}
}
func testCaptureStatusColors() {
let cases: [(String, NSColor)] = [
("", .secondaryLabelColor),
("Captured text from Safari.", .systemGreen),
("Skipped: Audio items are ignored.", .systemOrange),
("At least one content type must stay enabled.", .systemOrange),
("Error: Clipboard read failed.", .systemRed)
]
for (message, color) in cases {
XCTAssertEqual(SettingsWindowController.captureStatusPresentation(storedStatus: message).textColor, color)
}
}
func testShortcutPermissionAndLifecycleStatusColors() {
XCTAssertEqual(
SettingsWindowController.shortcutStatusPresentation(storedStatus: "").textColor,
.systemGreen
)
XCTAssertEqual(
SettingsWindowController.shortcutStatusPresentation(storedStatus: "Unsupported shortcut").textColor,
.systemRed
)
XCTAssertEqual(
SettingsWindowController.accessibilityPermissionStatusPresentation(
storedStatus: "",
isTrusted: true
).textColor,
.systemGreen
)
XCTAssertEqual(
SettingsWindowController.accessibilityPermissionStatusPresentation(
storedStatus: "Permission not granted",
isTrusted: true
).textColor,
.systemOrange
)
XCTAssertEqual(
SettingsWindowController.launchAtLoginStatusPresentation(storedStatus: "Service unavailable").textColor,
.systemRed
)
}
func testCloudSyncStatusPrecedence() {
let ready = ClipboardCloudSyncStatus(
isAvailable: true,
archiveURL: nil,
lastModifiedAt: nil,
message: "iCloud is ready."
)
let unavailable = ClipboardCloudSyncStatus(
isAvailable: false,
archiveURL: nil,
lastModifiedAt: nil,
message: "iCloud is unavailable."
)
XCTAssertEqual(
SettingsWindowController.cloudSyncStatusPresentation(
storedStatus: "",
isSyncEnabled: false,
cloudStatus: ready
).message,
"iCloud Sync is off."
)
XCTAssertEqual(
SettingsWindowController.cloudSyncStatusPresentation(
storedStatus: "",
isSyncEnabled: true,
cloudStatus: unavailable
).textColor,
.systemOrange
)
XCTAssertEqual(
SettingsWindowController.cloudSyncStatusPresentation(
storedStatus: "Synced 3 clips.",
isSyncEnabled: true,
cloudStatus: ready
).textColor,
.systemGreen
)
XCTAssertEqual(
SettingsWindowController.cloudSyncStatusPresentation(
storedStatus: "iCloud Sync failed.",
isSyncEnabled: true,
cloudStatus: ready
).textColor,
.systemRed
)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -36,13 +36,6 @@ final class ShortcutManagerTests: XCTestCase {
manager.stop()
}
func testGlobalRegistrationExcludesLocalSettingsShortcut() {
let bindings = ShortcutManager.globalShortcutBindings(openShortcut: AppConfiguration.defaultOpenShortcut)
XCTAssertEqual(bindings, [AppConfiguration.defaultOpenShortcut, ShortcutManager.stackCaptureShortcut])
XCTAssertFalse(bindings.contains(AppConfiguration.defaultSettingsShortcut))
}
func testRejectsConfiguredShortcutConflictWithFixedStackCaptureShortcut() {
let manager = makeManager(openShortcut: ShortcutManager.stackCaptureShortcut)