Files
OSGKeyboard/OSGKeyboardTests/RecordButtonGesturePolicyTests.swift
T
Rocky 5d7fcdf24e feat(keyboard): harden polish/clipboard guards and ship 1.6.6 (build 59)
Keep marketing version at 1.6.6 and bump build to 59. Strengthen never-answer
polish safeguards, clipboard reply-intent continuity, voice undo UX, device
UITests harness, eval fixtures, and What's New assets.
2026-08-09 12:45:33 +08:00

123 lines
4.3 KiB
Swift

// RecordButtonGesturePolicyTests.swift
// OSGKeyboard · Tests
import XCTest
@testable import OSGKeyboardShared
final class RecordButtonGesturePolicyTests: XCTestCase {
// MARK: - Hold
func testHoldOnIdleStartsClipboardCommandAndOwnsThePress() {
let action = RecordButtonGesturePolicy.holdAction(
phase: .idleReady,
isEnabled: true,
supportsClipboardLongPress: true
)
XCTAssertEqual(action, .beginClipboardCommand)
XCTAssertTrue(RecordButtonGesturePolicy.consumesPress(action))
}
func testHoldWithoutClipboardMaterialLeavesThePressToTheTap() {
let action = RecordButtonGesturePolicy.holdAction(
phase: .idleReady,
isEnabled: true,
supportsClipboardLongPress: false
)
XCTAssertEqual(action, .none)
XCTAssertFalse(RecordButtonGesturePolicy.consumesPress(action))
// Release still starts plain dictation, so the hold is not a dead key.
XCTAssertEqual(
RecordButtonGesturePolicy.tapAction(phase: .idleReady, isEnabled: true),
.toggle
)
}
func testHoldWhileRecordingStops() {
let action = RecordButtonGesturePolicy.holdAction(
phase: .recording,
isEnabled: true,
supportsClipboardLongPress: true
)
XCTAssertEqual(action, .toggle)
XCTAssertTrue(RecordButtonGesturePolicy.consumesPress(action))
}
/// The press that opened a clipboard round is still down when the phase
/// reaches `.preparing`; a hold there must not end the round it started.
func testHoldWhilePreparingNeverActsOnItsOwn() {
let action = RecordButtonGesturePolicy.holdAction(
phase: .preparing,
isEnabled: true,
supportsClipboardLongPress: true
)
XCTAssertEqual(action, .none)
XCTAssertFalse(RecordButtonGesturePolicy.consumesPress(action))
}
func testHoldWhileProcessingIsInert() {
XCTAssertEqual(
RecordButtonGesturePolicy.holdAction(
phase: .processing,
isEnabled: true,
supportsClipboardLongPress: true
),
.none
)
}
// MARK: - Tap
func testTapCancelsWhilePreparingAndStopsWhileRecording() {
XCTAssertEqual(RecordButtonGesturePolicy.tapAction(phase: .preparing, isEnabled: true), .toggle)
XCTAssertEqual(RecordButtonGesturePolicy.tapAction(phase: .recording, isEnabled: true), .toggle)
}
func testTapWhileProcessingIsInert() {
XCTAssertEqual(RecordButtonGesturePolicy.tapAction(phase: .processing, isEnabled: true), .none)
}
/// The unavailable mic must stay tappable so the user can reach the reason.
func testTapOnUnavailableMicStillReportsThrough() {
XCTAssertEqual(
RecordButtonGesturePolicy.tapAction(phase: .idleUnavailable, isEnabled: false),
.toggle
)
}
func testDisabledMicSwallowsTapsInLivePhases() {
XCTAssertEqual(RecordButtonGesturePolicy.tapAction(phase: .recording, isEnabled: false), .none)
XCTAssertEqual(RecordButtonGesturePolicy.tapAction(phase: .preparing, isEnabled: false), .none)
}
// MARK: - One press, one action
/// Full clipboard round: hold arms, phase advances under the finger, and the
/// release must stay swallowed no matter which phase it lands in.
func testSinglePressProducesExactlyOneActionAcrossPhaseFlips() {
let hold = RecordButtonGesturePolicy.holdAction(
phase: .idleReady,
isEnabled: true,
supportsClipboardLongPress: true
)
XCTAssertEqual(hold, .beginClipboardCommand)
var armed = RecordButtonGesturePolicy.consumesPress(hold)
XCTAssertTrue(armed)
for landingPhase in [RecordButton.Phase.preparing, .recording] {
// Release handling: an armed press is consumed, never replayed.
var delivered: RecordButtonGestureAction = .none
if armed {
armed = false
} else {
delivered = RecordButtonGesturePolicy.tapAction(
phase: landingPhase,
isEnabled: true
)
}
XCTAssertEqual(delivered, .none, "release in \(landingPhase) must not act")
armed = true
}
}
}