|
| 1 | +// |
| 2 | +// KittyKeyboardAppKitReproductionTests.swift |
| 3 | +// |
| 4 | +// AppKit integration coverage for issue #624. Enable this suite with: |
| 5 | +// RUN_APPKIT_TESTS=1 swift test --filter KittyKeyboardAppKitReproductionTests |
| 6 | +// |
| 7 | + |
| 8 | +#if os(macOS) |
| 9 | +import AppKit |
| 10 | +import Foundation |
| 11 | +import Testing |
| 12 | +@testable import SwiftTerm |
| 13 | + |
| 14 | +private func appKitTestsEnabled() -> Bool { |
| 15 | + ProcessInfo.processInfo.environment["RUN_APPKIT_TESTS"] == "1" |
| 16 | +} |
| 17 | + |
| 18 | +@Suite(.enabled(if: appKitTestsEnabled())) |
| 19 | +@MainActor |
| 20 | +final class KittyKeyboardAppKitReproductionTests { |
| 21 | + private final class CapturingDelegate: TerminalViewDelegate { |
| 22 | + var sent: [UInt8] = [] |
| 23 | + |
| 24 | + func send(source: TerminalView, data: ArraySlice<UInt8>) { |
| 25 | + sent.append(contentsOf: data) |
| 26 | + } |
| 27 | + |
| 28 | + func sizeChanged(source: TerminalView, newCols: Int, newRows: Int) {} |
| 29 | + func setTerminalTitle(source: TerminalView, title: String) {} |
| 30 | + func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {} |
| 31 | + func scrolled(source: TerminalView, position: Double) {} |
| 32 | + func requestOpenLink(source: TerminalView, link: String, params: [String: String]) {} |
| 33 | + func bell(source: TerminalView) {} |
| 34 | + func clipboardCopy(source: TerminalView, content: Data) {} |
| 35 | + func clipboardRead(source: TerminalView) -> Data? { nil } |
| 36 | + func iTermContent(source: TerminalView, content: ArraySlice<UInt8>) {} |
| 37 | + func rangeChanged(source: TerminalView, startY: Int, endY: Int) {} |
| 38 | + } |
| 39 | + |
| 40 | + private func press(flags: Int, |
| 41 | + characters: String, |
| 42 | + charactersIgnoringModifiers: String, |
| 43 | + keyCode: UInt16) -> [UInt8] { |
| 44 | + _ = NSApplication.shared |
| 45 | + |
| 46 | + let view = TerminalView(frame: CGRect(x: 0, y: 0, width: 400, height: 200)) |
| 47 | + let capture = CapturingDelegate() |
| 48 | + view.terminalDelegate = capture |
| 49 | + view.feed(text: "\u{1b}[>\(flags)u") |
| 50 | + |
| 51 | + let window = NSWindow(contentRect: view.frame, |
| 52 | + styleMask: [.titled], |
| 53 | + backing: .buffered, |
| 54 | + defer: false) |
| 55 | + window.contentView?.addSubview(view) |
| 56 | + window.makeFirstResponder(view) |
| 57 | + capture.sent.removeAll() |
| 58 | + |
| 59 | + let event = NSEvent.keyEvent( |
| 60 | + with: .keyDown, |
| 61 | + location: .zero, |
| 62 | + modifierFlags: [.shift], |
| 63 | + timestamp: ProcessInfo.processInfo.systemUptime, |
| 64 | + windowNumber: window.windowNumber, |
| 65 | + context: nil, |
| 66 | + characters: characters, |
| 67 | + charactersIgnoringModifiers: charactersIgnoringModifiers, |
| 68 | + isARepeat: false, |
| 69 | + keyCode: keyCode |
| 70 | + )! |
| 71 | + view.keyDown(with: event) |
| 72 | + return capture.sent |
| 73 | + } |
| 74 | + |
| 75 | + @Test func reportAlternatesDoesNotConvertTextProducingKeysToCSIU() { |
| 76 | + for flags in [5, 7] { // disambiguate + reportAlternates, with optional reportEvents |
| 77 | + #expect(press(flags: flags, |
| 78 | + characters: "E", |
| 79 | + charactersIgnoringModifiers: "e", |
| 80 | + keyCode: 14) == Array("E".utf8)) |
| 81 | + #expect(press(flags: flags, |
| 82 | + characters: "é", |
| 83 | + charactersIgnoringModifiers: "è", |
| 84 | + keyCode: 33) == Array("é".utf8)) |
| 85 | + } |
| 86 | + } |
| 87 | +} |
| 88 | +#endif |
0 commit comments