94 lines
4.2 KiB
Swift
94 lines
4.2 KiB
Swift
// SPDX-FileCopyrightText: 2026 Kaede Akino
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import AppKit
|
|
import TermindPresentation
|
|
import Testing
|
|
|
|
@testable import Termind
|
|
|
|
@Suite @MainActor
|
|
struct TerminalContextMenuTests {
|
|
@Test func uploadActionAppearsOnlyWhenBoundAndUsesLiveAvailability() throws {
|
|
let view = TermindTerminalView(frame: .zero)
|
|
let title = String(localized: AppStrings.Terminal.uploadFile)
|
|
#expect(view.selectionContextMenu().item(withTitle: title) == nil)
|
|
var available = false
|
|
var requests = 0
|
|
view.uploadFiles = { requests += 1 }
|
|
view.canUploadFiles = { available }
|
|
#expect(view.selectionContextMenu().item(withTitle: title)?.isEnabled == false)
|
|
available = true
|
|
let item = try #require(view.selectionContextMenu().item(withTitle: title))
|
|
#expect(item.isEnabled)
|
|
_ = view.perform(item.action, with: item)
|
|
#expect(requests == 1)
|
|
available = false
|
|
_ = view.perform(item.action, with: item)
|
|
#expect(requests == 1)
|
|
}
|
|
|
|
@Test func oldPaneTeardownCannotRemoveOrDisableTheReplacementUploadMenu() throws {
|
|
let owner = PersistentTerminalViewOwner(write: { _, _ in }, resize: { _ in })
|
|
let mount = TerminalMountView(frame: .zero)
|
|
owner.mount(in: mount, state: owner.state, isVisible: false, requestsFocus: false)
|
|
defer { owner.unmount(from: mount) }
|
|
let view = try #require(mount.subviews.first as? TermindTerminalView)
|
|
let oldBinding = owner.bindUpload(
|
|
action: { Issue.record("Retired pane received upload") }, isAvailable: { true })
|
|
var requests = 0
|
|
let currentBinding = owner.bindUpload(action: { requests += 1 }, isAvailable: { true })
|
|
|
|
owner.updateUploadAvailability(for: oldBinding, isAvailable: { false })
|
|
#expect(!owner.unbindUpload(oldBinding))
|
|
let title = String(localized: AppStrings.Terminal.uploadFile)
|
|
let item = try #require(view.selectionContextMenu().item(withTitle: title))
|
|
#expect(item.isEnabled)
|
|
_ = view.perform(item.action, with: item)
|
|
#expect(requests == 1)
|
|
|
|
#expect(owner.unbindUpload(currentBinding))
|
|
#expect(view.selectionContextMenu().item(withTitle: title) == nil)
|
|
}
|
|
|
|
@Test func defaultMenuValidatesPasteAndOptionBypassesAutomaticPaste() throws {
|
|
let pasteboard = NSPasteboard.general
|
|
let saved = (pasteboard.pasteboardItems ?? []).map { item in
|
|
Dictionary(
|
|
uniqueKeysWithValues: item.types.compactMap { type in
|
|
item.data(forType: type).map { (type, $0) }
|
|
})
|
|
}
|
|
defer {
|
|
pasteboard.clearContents()
|
|
pasteboard.writeObjects(
|
|
saved.map { values in
|
|
let item = NSPasteboardItem()
|
|
for (type, data) in values { item.setData(data, forType: type) }
|
|
return item
|
|
})
|
|
}
|
|
let view = TermindTerminalView(frame: .zero)
|
|
let event = try #require(
|
|
NSEvent.mouseEvent(
|
|
with: .rightMouseDown, location: .zero, modifierFlags: [], timestamp: 1,
|
|
windowNumber: 0, context: nil, eventNumber: 0, clickCount: 1, pressure: 1))
|
|
pasteboard.clearContents()
|
|
let emptyMenu = try #require(view.menu(for: event))
|
|
let pasteTitle = String(localized: AppStrings.Common.paste)
|
|
#expect(emptyMenu.item(withTitle: pasteTitle)?.isEnabled == false)
|
|
#expect(emptyMenu.items.contains { $0.action == #selector(view.selectAll(_:)) && $0.isEnabled })
|
|
#expect(!emptyMenu.items.contains { $0.action == #selector(view.copy(_:)) })
|
|
|
|
pasteboard.setString("echo hello", forType: .string)
|
|
#expect(view.menu(for: event)?.item(withTitle: pasteTitle)?.isEnabled == true)
|
|
view.pasteOnRightClick = true
|
|
#expect(view.menu(for: event) == nil)
|
|
let optionEvent = try #require(
|
|
NSEvent.mouseEvent(
|
|
with: .rightMouseDown, location: .zero, modifierFlags: .option, timestamp: 1,
|
|
windowNumber: 0, context: nil, eventNumber: 0, clickCount: 1, pressure: 1))
|
|
#expect(view.menu(for: optionEvent)?.item(withTitle: pasteTitle)?.isEnabled == true)
|
|
}
|
|
}
|