WIP: add numbered quick paste
This commit is contained in:
@@ -11,6 +11,7 @@ The project is intentionally dependency-light: Swift Package Manager, AppKit, Ca
|
|||||||
- Global shortcuts:
|
- Global shortcuts:
|
||||||
- `Command + Option + V` toggles the clipboard panel
|
- `Command + Option + V` toggles the clipboard panel
|
||||||
- `Command + ,` opens settings
|
- `Command + ,` opens settings
|
||||||
|
- `Command + 1` through `Command + 9` paste the numbered visible card; add `Shift` to paste that card as plain text
|
||||||
- Clipboard history for text, URLs with local preview thumbnails when available, images, audio, RTF/HTML rich text, PDFs, and file references
|
- Clipboard history for text, URLs with local preview thumbnails when available, images, audio, RTF/HTML rich text, PDFs, and file references
|
||||||
- SQLite persistence with bounded history, pinned-item retention, and encrypted app-managed payloads
|
- SQLite persistence with bounded history, pinned-item retention, and encrypted app-managed payloads
|
||||||
- Search with independent token matching, structured filters such as `app:Safari`, `type:image`, `date:2026-06-30`, and optional local OCR for copied images
|
- Search with independent token matching, structured filters such as `app:Safari`, `type:image`, `date:2026-06-30`, and optional local OCR for copied images
|
||||||
|
|||||||
@@ -50,8 +50,9 @@ Use this checklist before a release or after changes to panel, pasteboard, setti
|
|||||||
4. Select an audio item and paste into an app that accepts sound pasteboard data.
|
4. Select an audio item and paste into an app that accepts sound pasteboard data.
|
||||||
5. Select a PDF item and paste into Preview, Finder, or an app that accepts PDF pasteboard data.
|
5. Select a PDF item and paste into Preview, Finder, or an app that accepts PDF pasteboard data.
|
||||||
6. Select a rich text item and paste into TextEdit rich text mode or Mail. Confirm basic formatting is preserved and plain-text paste still works in a text-only field.
|
6. Select a rich text item and paste into TextEdit rich text mode or Mail. Confirm basic formatting is preserved and plain-text paste still works in a text-only field.
|
||||||
7. Without Accessibility permission, confirm paste actions copy and show the permission fallback status.
|
7. Press `Command + 1` through `Command + 9` on visible numbered cards and confirm the matching card is pasted or copied; add `Shift` and confirm URL/rich items paste as plain text only.
|
||||||
8. With Accessibility permission granted, confirm paste returns focus to the previous app and inserts the selected item.
|
8. Without Accessibility permission, confirm paste actions copy and show the permission fallback status.
|
||||||
|
9. With Accessibility permission granted, confirm paste returns focus to the previous app and inserts the selected item.
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,17 @@ final class ClipboardPanelController: NSObject, NSWindowDelegate, QLPreviewPanel
|
|||||||
private var isAnimating = false
|
private var isAnimating = false
|
||||||
private var quickLookURL: URL?
|
private var quickLookURL: URL?
|
||||||
private var screenParametersObserver: NSObjectProtocol?
|
private var screenParametersObserver: NSObjectProtocol?
|
||||||
|
private static let quickPasteKeyCodes: [UInt16: Int] = [
|
||||||
|
18: 0,
|
||||||
|
19: 1,
|
||||||
|
20: 2,
|
||||||
|
21: 3,
|
||||||
|
23: 4,
|
||||||
|
22: 5,
|
||||||
|
26: 6,
|
||||||
|
28: 7,
|
||||||
|
25: 8
|
||||||
|
]
|
||||||
private static let collectionShortcuts: [UInt16: ClipboardSortMode] = [
|
private static let collectionShortcuts: [UInt16: ClipboardSortMode] = [
|
||||||
18: .mostRecent,
|
18: .mostRecent,
|
||||||
19: .mostUsed,
|
19: .mostUsed,
|
||||||
@@ -326,6 +337,16 @@ final class ClipboardPanelController: NSObject, NSWindowDelegate, QLPreviewPanel
|
|||||||
removeKeyMonitor()
|
removeKeyMonitor()
|
||||||
keyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
|
keyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
|
||||||
guard let self else { return event }
|
guard let self else { return event }
|
||||||
|
if self.shouldHandlePanelKeyEvent(event, allowSearchFieldEditing: true),
|
||||||
|
let index = Self.quickPasteIndex(forKeyCode: event.keyCode, modifiers: event.modifierFlags) {
|
||||||
|
self.viewModel.pasteItem(at: index)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if self.shouldHandlePanelKeyEvent(event, allowSearchFieldEditing: true),
|
||||||
|
let index = Self.quickPastePlainTextIndex(forKeyCode: event.keyCode, modifiers: event.modifierFlags) {
|
||||||
|
self.viewModel.pasteItemPlainText(at: index)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if self.shouldHandlePanelKeyEvent(event, allowSearchFieldEditing: true),
|
if self.shouldHandlePanelKeyEvent(event, allowSearchFieldEditing: true),
|
||||||
let mode = Self.collectionShortcutMode(forKeyCode: event.keyCode, modifiers: event.modifierFlags) {
|
let mode = Self.collectionShortcutMode(forKeyCode: event.keyCode, modifiers: event.modifierFlags) {
|
||||||
self.viewModel.sortMode = mode
|
self.viewModel.sortMode = mode
|
||||||
@@ -424,9 +445,21 @@ final class ClipboardPanelController: NSObject, NSWindowDelegate, QLPreviewPanel
|
|||||||
|| NSApp.window(withWindowNumber: event.windowNumber) === panel
|
|| NSApp.window(withWindowNumber: event.windowNumber) === panel
|
||||||
}
|
}
|
||||||
|
|
||||||
static func collectionShortcutMode(forKeyCode keyCode: UInt16, modifiers: NSEvent.ModifierFlags) -> ClipboardSortMode? {
|
static func quickPasteIndex(forKeyCode keyCode: UInt16, modifiers: NSEvent.ModifierFlags) -> Int? {
|
||||||
let relevantModifiers = modifiers.intersection(.deviceIndependentFlagsMask)
|
let relevantModifiers = modifiers.intersection(.deviceIndependentFlagsMask)
|
||||||
guard relevantModifiers == .command else { return nil }
|
guard relevantModifiers == .command else { return nil }
|
||||||
|
return quickPasteKeyCodes[keyCode]
|
||||||
|
}
|
||||||
|
|
||||||
|
static func quickPastePlainTextIndex(forKeyCode keyCode: UInt16, modifiers: NSEvent.ModifierFlags) -> Int? {
|
||||||
|
let relevantModifiers = modifiers.intersection(.deviceIndependentFlagsMask)
|
||||||
|
guard relevantModifiers == [.command, .shift] else { return nil }
|
||||||
|
return quickPasteKeyCodes[keyCode]
|
||||||
|
}
|
||||||
|
|
||||||
|
static func collectionShortcutMode(forKeyCode keyCode: UInt16, modifiers: NSEvent.ModifierFlags) -> ClipboardSortMode? {
|
||||||
|
let relevantModifiers = modifiers.intersection(.deviceIndependentFlagsMask)
|
||||||
|
guard relevantModifiers == [.command, .option] else { return nil }
|
||||||
return collectionShortcuts[keyCode]
|
return collectionShortcuts[keyCode]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -990,6 +990,10 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
|||||||
cardViews.map(\.debugHeaderBadgeSymbol)
|
cardViews.map(\.debugHeaderBadgeSymbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var debugQuickPasteBadgeTexts: [String] {
|
||||||
|
cardViews.compactMap(\.debugQuickPasteBadgeText)
|
||||||
|
}
|
||||||
|
|
||||||
var debugSelectedCardFrameInDocument: NSRect {
|
var debugSelectedCardFrameInDocument: NSRect {
|
||||||
guard viewModel.selectedIndex >= 0, viewModel.selectedIndex < cardViews.count else {
|
guard viewModel.selectedIndex >= 0, viewModel.selectedIndex < cardViews.count else {
|
||||||
return .zero
|
return .zero
|
||||||
@@ -1399,6 +1403,7 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
|
|||||||
private var actionRailButtons: [NSButton] = []
|
private var actionRailButtons: [NSButton] = []
|
||||||
private weak var headerBadgeView: NSView?
|
private weak var headerBadgeView: NSView?
|
||||||
private weak var headerPinView: NSView?
|
private weak var headerPinView: NSView?
|
||||||
|
private weak var quickPasteBadgeLabel: NSTextField?
|
||||||
private var isSelected = false
|
private var isSelected = false
|
||||||
private var isHovered = false
|
private var isHovered = false
|
||||||
private var mouseDownLocation: NSPoint?
|
private var mouseDownLocation: NSPoint?
|
||||||
@@ -1564,6 +1569,10 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
|
|||||||
var debugHeaderBadgeIsHidden: Bool {
|
var debugHeaderBadgeIsHidden: Bool {
|
||||||
headerBadgeView?.isHidden ?? false
|
headerBadgeView?.isHidden ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var debugQuickPasteBadgeText: String? {
|
||||||
|
quickPasteBadgeLabel?.stringValue
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private func contextMenu() -> NSMenu {
|
private func contextMenu() -> NSMenu {
|
||||||
@@ -1959,11 +1968,16 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
|
|||||||
titleAndSource.spacing = 2
|
titleAndSource.spacing = 2
|
||||||
titleAndSource.translatesAutoresizingMaskIntoConstraints = false
|
titleAndSource.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
let labelStack = NSStackView(views: [titleAndSource])
|
var labelViews: [NSView] = []
|
||||||
|
if let quickPasteBadge = quickPasteBadge() {
|
||||||
|
labelViews.append(quickPasteBadge)
|
||||||
|
}
|
||||||
|
labelViews.append(titleAndSource)
|
||||||
|
let labelStack = NSStackView(views: labelViews)
|
||||||
labelStack.orientation = .horizontal
|
labelStack.orientation = .horizontal
|
||||||
labelStack.alignment = .centerY
|
labelStack.alignment = .centerY
|
||||||
labelStack.distribution = .fill
|
labelStack.distribution = .fill
|
||||||
labelStack.spacing = 1
|
labelStack.spacing = labelViews.count > 1 ? 9 : 1
|
||||||
labelStack.translatesAutoresizingMaskIntoConstraints = false
|
labelStack.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
let badge = iconBadge(for: item)
|
let badge = iconBadge(for: item)
|
||||||
@@ -2011,6 +2025,27 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
|
|||||||
return header
|
return header
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func quickPasteBadge() -> NSTextField? {
|
||||||
|
guard index < 9 else { return nil }
|
||||||
|
let label = NSTextField(labelWithString: "\(index + 1)")
|
||||||
|
label.font = .monospacedDigitSystemFont(ofSize: 11, weight: .bold)
|
||||||
|
label.textColor = NSColor.white.withAlphaComponent(0.92)
|
||||||
|
label.alignment = .center
|
||||||
|
label.lineBreakMode = .byClipping
|
||||||
|
label.wantsLayer = true
|
||||||
|
label.layer?.cornerRadius = 9
|
||||||
|
label.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.18).cgColor
|
||||||
|
label.layer?.borderWidth = 0.5
|
||||||
|
label.layer?.borderColor = NSColor.white.withAlphaComponent(0.24).cgColor
|
||||||
|
label.toolTip = "Press Command-\(index + 1) to paste"
|
||||||
|
label.setAccessibilityLabel("Quick paste \(index + 1)")
|
||||||
|
label.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
label.widthAnchor.constraint(equalToConstant: 19).isActive = true
|
||||||
|
label.heightAnchor.constraint(equalToConstant: 19).isActive = true
|
||||||
|
quickPasteBadgeLabel = label
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
private func bodyView(for item: ClipboardItem, thumbnail: NSImage?) -> NSView {
|
private func bodyView(for item: ClipboardItem, thumbnail: NSImage?) -> NSView {
|
||||||
let body = NSView()
|
let body = NSView()
|
||||||
body.wantsLayer = true
|
body.wantsLayer = true
|
||||||
|
|||||||
@@ -218,6 +218,18 @@ final class ClipboardPanelViewModel {
|
|||||||
settings.setPasteStatus(message: result.message)
|
settings.setPasteStatus(message: result.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pasteItem(at index: Int) {
|
||||||
|
guard index >= 0 && index < visibleItems.count else { return }
|
||||||
|
selectItem(at: index)
|
||||||
|
pasteSelected()
|
||||||
|
}
|
||||||
|
|
||||||
|
func pasteItemPlainText(at index: Int) {
|
||||||
|
guard index >= 0 && index < visibleItems.count else { return }
|
||||||
|
selectItem(at: index)
|
||||||
|
pasteSelectedPlainText()
|
||||||
|
}
|
||||||
|
|
||||||
func copySelected() {
|
func copySelected() {
|
||||||
guard let item = selectedItem else { return }
|
guard let item = selectedItem else { return }
|
||||||
let result = pasteService.copy(item)
|
let result = pasteService.copy(item)
|
||||||
|
|||||||
@@ -128,20 +128,39 @@ final class ClipboardPanelControllerTests: XCTestCase {
|
|||||||
XCTAssertEqual(inset, 18)
|
XCTAssertEqual(inset, 18)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCommandNumberShortcutsMapToCollections() {
|
func testCommandNumberShortcutsMapToQuickPasteSlots() {
|
||||||
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 18, modifiers: .command), .mostRecent)
|
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 18, modifiers: .command), 0)
|
||||||
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 19, modifiers: .command), .mostUsed)
|
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 19, modifiers: .command), 1)
|
||||||
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 20, modifiers: .command), .text)
|
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 20, modifiers: .command), 2)
|
||||||
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 21, modifiers: .command), .links)
|
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 21, modifiers: .command), 3)
|
||||||
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 23, modifiers: .command), .images)
|
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 23, modifiers: .command), 4)
|
||||||
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 22, modifiers: .command), .files)
|
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 22, modifiers: .command), 5)
|
||||||
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 26, modifiers: .command), .pinned)
|
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 26, modifiers: .command), 6)
|
||||||
XCTAssertEqual(ClipboardPanelController.collectionShortcutMode(forKeyCode: 28, modifiers: .command), .audio)
|
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 28, modifiers: .command), 7)
|
||||||
|
XCTAssertEqual(ClipboardPanelController.quickPasteIndex(forKeyCode: 25, modifiers: .command), 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCollectionShortcutsRequireCommandOnlySoSearchTypingIsUntouched() {
|
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]))
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCollectionShortcutsRequireCommandOptionSoQuickPasteKeepsCommandNumbers() {
|
||||||
XCTAssertNil(ClipboardPanelController.collectionShortcutMode(forKeyCode: 18, modifiers: []))
|
XCTAssertNil(ClipboardPanelController.collectionShortcutMode(forKeyCode: 18, modifiers: []))
|
||||||
XCTAssertNil(ClipboardPanelController.collectionShortcutMode(forKeyCode: 18, modifiers: [.command, .shift]))
|
XCTAssertNil(ClipboardPanelController.collectionShortcutMode(forKeyCode: 18, modifiers: .command))
|
||||||
XCTAssertNil(ClipboardPanelController.collectionShortcutMode(forKeyCode: 29, modifiers: .command))
|
XCTAssertNil(ClipboardPanelController.collectionShortcutMode(forKeyCode: 29, modifiers: .command))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -395,6 +395,52 @@ final class ClipboardPanelViewModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(store.items.first?.useCount, 1)
|
XCTAssertEqual(store.items.first?.useCount, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testQuickPasteItemByVisibleIndexWritesThatCard() {
|
||||||
|
let settings = makeSettings()
|
||||||
|
let cacheService = makeCacheService()
|
||||||
|
let store = makeStore(settings: settings, cacheService: cacheService)
|
||||||
|
store.upsert(makeTextItem("first visible quick paste", createdAt: Date(timeIntervalSince1970: 200)))
|
||||||
|
store.upsert(makeTextItem("second visible quick paste", createdAt: Date(timeIntervalSince1970: 100)))
|
||||||
|
store.flushPersistenceForTesting()
|
||||||
|
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
|
||||||
|
waitForVisibleItems(in: viewModel, count: 2)
|
||||||
|
NSPasteboard.general.clearContents()
|
||||||
|
|
||||||
|
viewModel.pasteItem(at: 1)
|
||||||
|
|
||||||
|
XCTAssertEqual(NSPasteboard.general.string(forType: .string), "second visible quick paste")
|
||||||
|
XCTAssertEqual(viewModel.statusMessage, "Copied")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testQuickPastePlainTextByVisibleIndexOmitsRichPasteboardTypes() {
|
||||||
|
let settings = makeSettings()
|
||||||
|
let cacheService = makeCacheService()
|
||||||
|
let store = makeStore(settings: settings, cacheService: cacheService)
|
||||||
|
let item = ClipboardItem(
|
||||||
|
id: UUID(),
|
||||||
|
kind: .url,
|
||||||
|
displayText: "Example",
|
||||||
|
payload: "https://example.com/quick",
|
||||||
|
payloadHash: hash("https://example.com/quick"),
|
||||||
|
createdAt: Date(timeIntervalSince1970: 200),
|
||||||
|
lastUsedAt: Date(timeIntervalSince1970: 200),
|
||||||
|
useCount: 0,
|
||||||
|
sourceApp: "Safari",
|
||||||
|
imagePath: nil,
|
||||||
|
thumbnailPath: nil
|
||||||
|
)
|
||||||
|
store.upsert(item)
|
||||||
|
store.flushPersistenceForTesting()
|
||||||
|
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
|
||||||
|
waitForVisibleItems(in: viewModel, count: 1)
|
||||||
|
NSPasteboard.general.clearContents()
|
||||||
|
|
||||||
|
viewModel.pasteItemPlainText(at: 0)
|
||||||
|
|
||||||
|
XCTAssertEqual(NSPasteboard.general.string(forType: .string), "https://example.com/quick")
|
||||||
|
XCTAssertNil(NSPasteboard.general.string(forType: .URL))
|
||||||
|
}
|
||||||
|
|
||||||
func testStackPastesQueuedItemsInOrderAndConsumesThem() {
|
func testStackPastesQueuedItemsInOrderAndConsumesThem() {
|
||||||
let settings = makeSettings()
|
let settings = makeSettings()
|
||||||
let cacheService = makeCacheService()
|
let cacheService = makeCacheService()
|
||||||
|
|||||||
@@ -59,6 +59,18 @@ final class ClipboardPanelViewTests: XCTestCase {
|
|||||||
XCTAssertEqual(fixture.view.debugCardPreviewStyles, ["text-preview"])
|
XCTAssertEqual(fixture.view.debugCardPreviewStyles, ["text-preview"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testCardsShowQuickPasteNumberBadgesForFirstNineItems() {
|
||||||
|
let fixture = makePanelFixture()
|
||||||
|
for index in 0..<10 {
|
||||||
|
fixture.store.upsert(makeTextItem("Quick paste badge \(index)", store: fixture.store))
|
||||||
|
drainMainQueue()
|
||||||
|
}
|
||||||
|
fixture.window.contentView?.layoutSubtreeIfNeeded()
|
||||||
|
|
||||||
|
XCTAssertEqual(fixture.view.debugVisibleCardCount, 10)
|
||||||
|
XCTAssertEqual(fixture.view.debugQuickPasteBadgeTexts, ["1", "2", "3", "4", "5", "6", "7", "8", "9"])
|
||||||
|
}
|
||||||
|
|
||||||
func testFooterShowsCaptureStatusInsteadOfShortcutInstructions() {
|
func testFooterShowsCaptureStatusInsteadOfShortcutInstructions() {
|
||||||
let fixture = makePanelFixture()
|
let fixture = makePanelFixture()
|
||||||
fixture.store.upsert(makeTextItem("Footer status item", store: fixture.store))
|
fixture.store.upsert(makeTextItem("Footer status item", store: fixture.store))
|
||||||
|
|||||||
Reference in New Issue
Block a user