Skip to content

Commit 96de9b9

Browse files
committed
fix: share deadline-aware alert tap synthesis
1 parent 99916af commit 96de9b9

4 files changed

Lines changed: 39 additions & 44 deletions

File tree

‎apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift‎

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import XCTest
2-
import AgentDeviceSnapshotPresentation
32

43
extension RunnerTests {
54
enum RunnerAlertSource {
@@ -148,52 +147,22 @@ extension RunnerTests {
148147
)
149148
return
150149
}
151-
guard let context = synthesizedCoordinateContext(
152-
app: alert.ownerApp,
153-
policy: synthesizedGesturePolicy(.coordinateTap)
154-
) else {
155-
outcome = .unsupported(
156-
message: "alert activation could not resolve its application window",
157-
hint: "Inspect the current alert before deciding whether to act again."
158-
)
159-
return
160-
}
161-
let orientation = Int(RunnerSynthesizedGesture.interfaceOrientation(forApplication: alert.ownerApp))
162-
let point = CoordinateSpaceRotation.native(
163-
point: CGPoint(x: frame.midX, y: frame.midY),
164-
in: context.referenceFrame,
165-
interfaceOrientation: orientation
166-
)
167-
var message: NSString?
168-
let status = RunnerSynthesizedGesture.synthesizeTap(
169-
withApplication: alert.ownerApp,
170-
resolvedWindow: context.resolvedWindow,
171-
x: Double(point.x),
172-
y: Double(point.y),
173-
deadline: deadline,
174-
errorMessage: &message
175-
)
176-
switch status {
177-
case .succeeded:
150+
switch synthesizedTapAt(app: alert.ownerApp, x: frame.midX, y: frame.midY, deadline: deadline) {
151+
case .performed?:
178152
typealias WaitForQuiescence = @convention(c) (NSObject, Selector, Bool) -> Void
179153
let waitForQuiescence = unsafeBitCast(
180154
alert.ownerApp.method(for: postEventWaitSelector),
181155
to: WaitForQuiescence.self
182156
)
183157
waitForQuiescence(alert.ownerApp, postEventWaitSelector, false)
184158
outcome = .performed
185-
case .deadlineExceeded:
186-
outcome = nil
187-
case .failed:
188-
outcome = .unsupported(
189-
message: message as String? ?? "private XCTest event synthesis failed",
190-
hint: "Inspect the current alert before deciding whether to act again."
191-
)
192-
@unknown default:
159+
case .unsupported(let message, _)?:
193160
outcome = .unsupported(
194-
message: "private XCTest event synthesis returned an unknown status",
161+
message: message,
195162
hint: "Inspect the current alert before deciding whether to act again."
196163
)
164+
case nil:
165+
break
197166
}
198167
#else
199168
outcome = activateElement(

‎apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,14 @@ extension RunnerTests {
143143
_ app: XCUIApplication,
144144
kind: SynthesizedGesturePolicyKind,
145145
context: SynthesizedCoordinateContext?,
146-
synthesize: () -> RunnerInteractionOutcome
146+
synthesize: () -> RunnerInteractionOutcome?
147147
) -> SynthesizedGestureAttempt {
148-
let (timing, outcome) = performGesture(app, idleTimeout: false, synthesize)
148+
var outcome: RunnerInteractionOutcome?
149+
let timing = measureGesture { outcome = synthesize() }
150+
guard let outcome else {
151+
logSynthesizedGesturePolicyDecision(kind: kind, context: context, fallbackAttempted: false)
152+
return .refused(timing: timing, message: "synthesized gesture deadline expired before dispatch", hint: nil)
153+
}
149154
guard case .unsupported(let message, let hint) = outcome else {
150155
logSynthesizedGesturePolicyDecision(kind: kind, context: context, fallbackAttempted: false)
151156
return .performed(timing: timing)

‎apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,9 @@ extension RunnerTests {
9595
app: XCUIApplication,
9696
x: Double,
9797
y: Double,
98-
context: SynthesizedCoordinateContext? = nil
99-
) -> RunnerInteractionOutcome {
98+
context: SynthesizedCoordinateContext? = nil,
99+
deadline: Date? = nil
100+
) -> RunnerInteractionOutcome? {
100101
#if os(iOS)
101102
guard x.isFinite, y.isFinite else {
102103
return .unsupported(
@@ -132,16 +133,25 @@ extension RunnerTests {
132133
resolvedWindow: context.resolvedWindow,
133134
x: Double(point.x),
134135
y: Double(point.y),
135-
deadline: nil,
136+
deadline: deadline,
136137
errorMessage: &message
137138
)
138-
if status != .succeeded {
139+
switch status {
140+
case .succeeded:
141+
return .performed
142+
case .deadlineExceeded:
143+
return nil
144+
case .failed:
139145
return .unsupported(
140146
message: message as String? ?? "private XCTest event synthesis failed",
141147
hint: "Falling back to XCTest coordinate tap may be slower and can still need a healthy accessibility tree."
142148
)
149+
@unknown default:
150+
return .unsupported(
151+
message: "private XCTest event synthesis returned an unknown status",
152+
hint: "Inspect the current app before deciding whether to act again."
153+
)
143154
}
144-
return .performed
145155
#elseif os(tvOS)
146156
return .unsupported(
147157
message: "coordinate tap is not supported on tvOS; move focus with swipe or scroll, then select the focused element",

‎apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/UnitTests/RunnerTests+SynthesizedGesturePolicyTests.swift‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,17 @@ extension RunnerTests {
106106
XCTAssertNil(synthesizedPolicyKind(forSequenceStep: sequenceStep("longPress", synthesized: true)))
107107
}
108108

109+
@MainActor
110+
func testCoordinateTapThatWasNotDispatchedDoesNotFallBackToXCTest() {
111+
for health: RunnerAccessibilityHealth in [.unknown, .healthy, .unavailable] {
112+
mainOwned.accessibilityHealth = health
113+
for context in [nil, synthesizedGestureTestContext(accessibilityHealth: health)] {
114+
let attempt = performSynthesizedGesture(app, kind: .coordinateTap, context: context) { nil }
115+
XCTAssertEqual(synthesizedGestureRoute(attempt), "refused", "axHealth=\(health.rawValue)")
116+
}
117+
}
118+
}
119+
109120
@MainActor
110121
func testFailedCoordinateTapSynthesisFallsBackToXCTestAtEveryAccessibilityHealth() {
111122
for health: RunnerAccessibilityHealth in [.unknown, .healthy, .unavailable] {

0 commit comments

Comments
 (0)