320dc777c9
Add save-to-Notes export and direct map handoff while aligning multi-touch typing, period shortcuts, return actions, and navigation visuals with system behavior.
151 lines
5.2 KiB
Swift
151 lines
5.2 KiB
Swift
// TypingTouchTrackerTests.swift
|
|
// OSGKeyboard · Ext unit tests
|
|
//
|
|
// Multi-finger overlap: press order commits, Shift hold + type, slide reselect.
|
|
|
|
import XCTest
|
|
@testable import OSGKeyboardShared
|
|
|
|
final class TypingTouchTrackerTests: XCTestCase {
|
|
private final class Token {}
|
|
|
|
private func letter(_ id: String) -> TypingKeyHitTarget {
|
|
TypingKeyHitTarget(
|
|
id: id,
|
|
label: id,
|
|
visualFrame: .zero,
|
|
behavior: .commitOnRelease
|
|
)
|
|
}
|
|
|
|
private func shiftKey() -> TypingKeyHitTarget {
|
|
TypingKeyHitTarget(
|
|
id: "shift",
|
|
label: "⇧",
|
|
visualFrame: .zero,
|
|
behavior: .shiftHold
|
|
)
|
|
}
|
|
|
|
private func deleteKey() -> TypingKeyHitTarget {
|
|
TypingKeyHitTarget(
|
|
id: "delete",
|
|
label: "⌫",
|
|
visualFrame: .zero,
|
|
behavior: .deleteRepeat
|
|
)
|
|
}
|
|
|
|
func testSecondFingerDownCommitsFirstInPressOrder() {
|
|
let tracker = TypingTouchTracker()
|
|
let a = Token()
|
|
let b = Token()
|
|
let keyA = letter("a")
|
|
let keyB = letter("b")
|
|
|
|
let downA = tracker.began(id: ObjectIdentifier(a), key: keyA)
|
|
XCTAssertTrue(downA.commits.isEmpty)
|
|
XCTAssertEqual(tracker.highlightedKeyIDs, ["a"])
|
|
|
|
let downB = tracker.began(id: ObjectIdentifier(b), key: keyB)
|
|
XCTAssertEqual(downB.commits.map(\.id), ["a"])
|
|
XCTAssertEqual(tracker.highlightedKeyIDs, ["b"])
|
|
|
|
let upA = tracker.ended(id: ObjectIdentifier(a), key: keyA)
|
|
XCTAssertTrue(upA.commits.isEmpty)
|
|
|
|
let upB = tracker.ended(id: ObjectIdentifier(b), key: keyB)
|
|
XCTAssertEqual(upB.commits.map(\.id), ["b"])
|
|
XCTAssertTrue(tracker.highlightedKeyIDs.isEmpty)
|
|
}
|
|
|
|
func testShiftHoldWithOtherFingerDoesNotCommitShift() {
|
|
let tracker = TypingTouchTracker()
|
|
let shiftFinger = Token()
|
|
let letterFinger = Token()
|
|
let shift = shiftKey()
|
|
let keyA = letter("a")
|
|
|
|
let downShift = tracker.began(id: ObjectIdentifier(shiftFinger), key: shift)
|
|
XCTAssertTrue(downShift.beginShift)
|
|
XCTAssertTrue(downShift.commits.isEmpty)
|
|
|
|
let downA = tracker.began(id: ObjectIdentifier(letterFinger), key: keyA)
|
|
XCTAssertTrue(downA.commits.isEmpty)
|
|
XCTAssertFalse(downA.beginShift)
|
|
|
|
let upA = tracker.ended(id: ObjectIdentifier(letterFinger), key: keyA)
|
|
XCTAssertEqual(upA.commits.map(\.id), ["a"])
|
|
XCTAssertFalse(upA.endShift)
|
|
|
|
let upShift = tracker.ended(id: ObjectIdentifier(shiftFinger), key: shift)
|
|
XCTAssertTrue(upShift.endShift)
|
|
XCTAssertTrue(upShift.commits.isEmpty)
|
|
}
|
|
|
|
func testSlideReselectsWithoutCommittingPreviousKey() {
|
|
let tracker = TypingTouchTracker()
|
|
let finger = Token()
|
|
let keyA = letter("a")
|
|
let keyS = letter("s")
|
|
|
|
_ = tracker.began(id: ObjectIdentifier(finger), key: keyA)
|
|
let moved = tracker.moved(id: ObjectIdentifier(finger), key: keyS)
|
|
XCTAssertTrue(moved.commits.isEmpty)
|
|
XCTAssertEqual(moved.playFeedback?.id, "s")
|
|
XCTAssertEqual(tracker.highlightedKeyIDs, ["s"])
|
|
|
|
let ended = tracker.ended(id: ObjectIdentifier(finger), key: keyS)
|
|
XCTAssertEqual(ended.commits.map(\.id), ["s"])
|
|
}
|
|
|
|
func testLiftOutsideCancelsPendingKey() {
|
|
let tracker = TypingTouchTracker()
|
|
let finger = Token()
|
|
_ = tracker.began(id: ObjectIdentifier(finger), key: letter("a"))
|
|
let ended = tracker.ended(id: ObjectIdentifier(finger), key: nil)
|
|
XCTAssertTrue(ended.commits.isEmpty)
|
|
}
|
|
|
|
func testCancelDoesNotCommit() {
|
|
let tracker = TypingTouchTracker()
|
|
let finger = Token()
|
|
_ = tracker.began(id: ObjectIdentifier(finger), key: letter("a"))
|
|
let cancelled = tracker.cancelled(id: ObjectIdentifier(finger))
|
|
XCTAssertTrue(cancelled.commits.isEmpty)
|
|
XCTAssertTrue(tracker.highlightedKeyIDs.isEmpty)
|
|
}
|
|
|
|
func testLetterWhileDeletingStopsRepeat() {
|
|
let tracker = TypingTouchTracker()
|
|
let delFinger = Token()
|
|
let letterFinger = Token()
|
|
|
|
let downDelete = tracker.began(id: ObjectIdentifier(delFinger), key: deleteKey())
|
|
XCTAssertTrue(downDelete.deleteFire)
|
|
XCTAssertTrue(downDelete.startDeleteRepeat)
|
|
|
|
let downLetter = tracker.began(id: ObjectIdentifier(letterFinger), key: letter("a"))
|
|
XCTAssertTrue(downLetter.stopDeleteRepeat)
|
|
XCTAssertFalse(downLetter.deleteFire)
|
|
XCTAssertTrue(downLetter.commits.isEmpty)
|
|
}
|
|
|
|
func testSecondFingerOnSameKeyIsIgnored() {
|
|
let tracker = TypingTouchTracker()
|
|
let first = Token()
|
|
let second = Token()
|
|
let keyA = letter("a")
|
|
|
|
_ = tracker.began(id: ObjectIdentifier(first), key: keyA)
|
|
let duplicate = tracker.began(id: ObjectIdentifier(second), key: keyA)
|
|
XCTAssertTrue(duplicate.commits.isEmpty)
|
|
XCTAssertNil(duplicate.playFeedback)
|
|
|
|
let upFirst = tracker.ended(id: ObjectIdentifier(first), key: keyA)
|
|
XCTAssertEqual(upFirst.commits.map(\.id), ["a"])
|
|
let upSecond = tracker.ended(id: ObjectIdentifier(second), key: keyA)
|
|
XCTAssertTrue(upSecond.commits.isEmpty)
|
|
}
|
|
}
|