agent-device 0.20.5 → 0.20.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.5.apk → agent-device-android-ime-helper-0.20.6.apk} +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.6.apk.sha256 +1 -0
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.5.manifest.json → agent-device-android-ime-helper-0.20.6.manifest.json} +4 -4
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.5.apk → agent-device-android-snapshot-helper-0.20.6.apk} +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.6.apk.sha256 +1 -0
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.5.manifest.json → agent-device-android-snapshot-helper-0.20.6.manifest.json} +6 -6
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +28 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +1 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +41 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +298 -92
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +126 -187
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.h +32 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m +212 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +118 -8
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +134 -27
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +23 -5
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +10 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+LifecycleCacheTests.swift +0 -67
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +11 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +2 -22
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +48 -11
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTraversalIdentityTests.swift +0 -20
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +177 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +105 -448
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryPolicyTests.swift +4 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +587 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +18 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.h +72 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.m +89 -0
- package/dist/src/agent-device-client.js +2 -2
- package/dist/src/android.js +1 -1
- package/dist/src/app-log-request-scope.js +1 -1
- package/dist/src/app-resolution.js +1 -1
- package/dist/src/apps.js +5 -5
- package/dist/src/cli-config.js +1 -1
- package/dist/src/cli-help.js +34 -24
- package/dist/src/cli-runner.js +1 -1
- package/dist/src/cli.js +15 -15
- package/dist/src/command-metadata.js +1 -1
- package/dist/src/command-schema.js +3 -3
- package/dist/src/context.js +1 -1
- package/dist/src/debug-symbols.js +1 -1
- package/dist/src/find.js +1 -1
- package/dist/src/finders.js +1 -1
- package/dist/src/generic-settle.js +1 -0
- package/dist/src/generic.js +1 -1
- package/dist/src/ime-lifecycle.js +3 -3
- package/dist/src/index.d.ts +27 -7
- package/dist/src/input-actions.js +4 -4
- package/dist/src/interaction-runtime.js +1 -0
- package/dist/src/interaction-snapshot.js +1 -1
- package/dist/src/interaction-touch-reference-frame.js +1 -0
- package/dist/src/interaction.js +1 -1
- package/dist/src/interactor.js +2 -2
- package/dist/src/interactor2.js +1 -1
- package/dist/src/internal/bin.js +2 -2
- package/dist/src/internal/daemon.js +15 -15
- package/dist/src/lease-context.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/limrun.d.ts +23 -2
- package/dist/src/linux.js +1 -1
- package/dist/src/process-exit.js +1 -0
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-trace.js +1 -1
- package/dist/src/register-builtins.js +1 -1
- package/dist/src/registry.js +25 -24
- package/dist/src/remote-config.js +1 -1
- package/dist/src/remote-config2.js +1 -1
- package/dist/src/reporting.js +5 -5
- package/dist/src/runner-client.js +1 -1
- package/dist/src/runner-disposal.js +1 -1
- package/dist/src/runner-lease.js +1 -1
- package/dist/src/runtime.js +3 -3
- package/dist/src/screenshot-geometry.js +1 -1
- package/dist/src/screenshot-result.js +4 -4
- package/dist/src/sdk-android-adb.d.ts +20 -2
- package/dist/src/sdk-android-adb.js +1 -1
- package/dist/src/sdk-batch-runner.js +3 -3
- package/dist/src/sdk-contracts.d.ts +17 -2
- package/dist/src/sdk-finders.d.ts +4 -20
- package/dist/src/sdk-finders.js +1 -1
- package/dist/src/{interaction.d.ts → sdk-selectors.d.ts} +98 -3
- package/dist/src/sdk-selectors.js +1 -1
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selector-vocabulary.js +1 -1
- package/dist/src/selectors.d.ts +1 -46
- package/dist/src/selectors.js +1 -1
- package/dist/src/server.js +3 -3
- package/dist/src/session-target-evidence.js +1 -1
- package/dist/src/session.js +9 -9
- package/dist/src/snapshot2.js +1 -1
- package/dist/src/text-surface.js +1 -0
- package/dist/src/viewport-dimension.js +13 -12
- package/dist/src/web.js +1 -1
- package/package.json +41 -35
- package/server.json +3 -3
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.5.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.5.apk.sha256 +0 -1
- package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-resize.swift +0 -225
|
@@ -144,6 +144,41 @@ extension RunnerTests {
|
|
|
144
144
|
return pending
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
/// The pre-seeded first-failure a penalized plan stamps into its verdict. The deferred case
|
|
148
|
+
/// uses the dedicated 'deferred' code: the breaker pre-selected the backend, nothing new
|
|
149
|
+
/// degraded on this capture, and the daemon keys warning suppression and the settle budget
|
|
150
|
+
/// reset off exactly that distinction. The bounded probe keeps 'budget': its short XCTest
|
|
151
|
+
/// slice genuinely constrains what this capture could read.
|
|
152
|
+
static func xcTestChannelStateFirstFailure(
|
|
153
|
+
_ state: SnapshotXCTestChannelPlanState
|
|
154
|
+
) -> (reason: String, code: String)? {
|
|
155
|
+
switch state {
|
|
156
|
+
case .normal:
|
|
157
|
+
return nil
|
|
158
|
+
case .deferredToIndependentBackend:
|
|
159
|
+
return (
|
|
160
|
+
"XCTest-backed snapshot tiers were deferred after recent slow accessibility work on this screen",
|
|
161
|
+
"deferred"
|
|
162
|
+
)
|
|
163
|
+
case .boundedXCTestProbe:
|
|
164
|
+
return (
|
|
165
|
+
"XCTest-backed snapshot tiers are running with a short recovery probe after recent slow accessibility work on this screen",
|
|
166
|
+
"budget"
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// Pure gate: a capture is planned as penalized when the channel penalty is
|
|
172
|
+
/// active OR the daemon pinned the private-AX backend (same-backend evidence
|
|
173
|
+
/// probe) — both mean "do not enter XCTest tree work first, and stamp the
|
|
174
|
+
/// pre-selection as 'deferred' rather than a degradation".
|
|
175
|
+
static func snapshotXCTestChannelTreatedAsPenalized(
|
|
176
|
+
penalized: Bool,
|
|
177
|
+
preferredBackend: String?
|
|
178
|
+
) -> Bool {
|
|
179
|
+
penalized || preferredBackend == SnapshotBackendKind.privateAX.rawValue
|
|
180
|
+
}
|
|
181
|
+
|
|
147
182
|
/// Pure plan-reorder rule: a penalized XCTest accessibility channel uses independent backends
|
|
148
183
|
/// when the platform has one, otherwise it keeps XCTest work on a short probe. The raw
|
|
149
184
|
/// diagnostic plan keeps tree-first errors, and unknown plans are left untouched.
|
|
@@ -198,9 +233,15 @@ extension RunnerTests {
|
|
|
198
233
|
// Reorder is iOS-only because hostile screens can make XCTest tree/query work grind while
|
|
199
234
|
// the app remains visually responsive. Simulators can avoid that channel through private AX;
|
|
200
235
|
// physical devices have no independent semantic backend yet, so they use a bounded probe.
|
|
236
|
+
// A daemon-preferred private-AX capture (same-backend evidence probe) takes the exact
|
|
237
|
+
// penalized route: privateAX-first plan, 'deferred' verdict — the backend was pre-selected
|
|
238
|
+
// deliberately, so no degradation warning should render for it.
|
|
201
239
|
var xCTestChannelPenalized = false
|
|
202
240
|
#if os(iOS)
|
|
203
|
-
xCTestChannelPenalized =
|
|
241
|
+
xCTestChannelPenalized = Self.snapshotXCTestChannelTreatedAsPenalized(
|
|
242
|
+
penalized: isSnapshotXCTestChannelPenalized(bundleId: currentBundleId),
|
|
243
|
+
preferredBackend: options.preferredBackend
|
|
244
|
+
)
|
|
204
245
|
#endif
|
|
205
246
|
let effective = Self.effectiveSnapshotCapturePlan(
|
|
206
247
|
plan,
|
|
@@ -208,20 +249,13 @@ extension RunnerTests {
|
|
|
208
249
|
availableBackends: Set(SnapshotBackendKind.allCases.filter(\.isAvailableOnCurrentPlatform))
|
|
209
250
|
)
|
|
210
251
|
let effectivePlan = effective.plan
|
|
252
|
+
firstFailure = Self.xcTestChannelStateFirstFailure(effective.xCTestChannelState)
|
|
211
253
|
switch effective.xCTestChannelState {
|
|
212
254
|
case .normal:
|
|
213
255
|
break
|
|
214
256
|
case .deferredToIndependentBackend:
|
|
215
|
-
firstFailure = (
|
|
216
|
-
"XCTest-backed snapshot tiers were deferred after recent slow accessibility work on this screen",
|
|
217
|
-
"budget"
|
|
218
|
-
)
|
|
219
257
|
NSLog("AGENT_DEVICE_RUNNER_SNAPSHOT_XCTEST_CHANNEL_DEFERRED bundle=%@", currentBundleId ?? "")
|
|
220
258
|
case .boundedXCTestProbe:
|
|
221
|
-
firstFailure = (
|
|
222
|
-
"XCTest-backed snapshot tiers are running with a short recovery probe after recent slow accessibility work on this screen",
|
|
223
|
-
"budget"
|
|
224
|
-
)
|
|
225
259
|
NSLog("AGENT_DEVICE_RUNNER_SNAPSHOT_XCTEST_CHANNEL_PROBE_BOUNDED bundle=%@", currentBundleId ?? "")
|
|
226
260
|
}
|
|
227
261
|
|
|
@@ -519,7 +553,7 @@ extension RunnerTests {
|
|
|
519
553
|
guard quality.state != "healthy" || quality.collapsedLeafIndexes != nil else { return nil }
|
|
520
554
|
var parts: [String] = []
|
|
521
555
|
if quality.state == "recovered" {
|
|
522
|
-
let meaning = quality.reasonCode == "budget"
|
|
556
|
+
let meaning = quality.reasonCode == "budget" || quality.reasonCode == "deferred"
|
|
523
557
|
? " The primary capture ran out of its time budget (busy app or simulator); the recovered tree is authoritative for this screen."
|
|
524
558
|
: " This usually means the app publishes an unhealthy accessibility tree — fixing the app's accessibility is the real cure. Treat screenshot as visual truth when this warning appears."
|
|
525
559
|
parts.append(
|
|
@@ -536,8 +570,11 @@ extension RunnerTests {
|
|
|
536
570
|
)
|
|
537
571
|
}
|
|
538
572
|
if let depth = quality.effectiveDepth {
|
|
573
|
+
// No --depth remedy here: an explicit --depth capture disables the
|
|
574
|
+
// frontier extension, so following it would return strictly less than
|
|
575
|
+
// this capture did. A plain re-run retries with a fresh extension budget.
|
|
539
576
|
parts.append(
|
|
540
|
-
"The accessibility server rejected deeper requests;
|
|
577
|
+
"The accessibility server rejected deeper requests; content below depth \(depth) may be missing — re-run snapshot to retry deeper content."
|
|
541
578
|
)
|
|
542
579
|
}
|
|
543
580
|
return parts.isEmpty ? nil : parts.joined(separator: " ")
|
|
@@ -1,24 +1,4 @@
|
|
|
1
1
|
import XCTest
|
|
2
2
|
|
|
3
3
|
extension RunnerTests {
|
|
4
|
-
func testSnapshotTraversalIdentityPreservesSameOriginNodesWithDifferentBounds() {
|
|
5
|
-
let wrapper = Self.snapshotTraversalIdentity(
|
|
6
|
-
elementType: .other,
|
|
7
|
-
label: "Article",
|
|
8
|
-
identifier: "",
|
|
9
|
-
frame: CGRect(x: 0, y: 97, width: 393, height: 48)
|
|
10
|
-
)
|
|
11
|
-
let leaf = Self.snapshotTraversalIdentity(
|
|
12
|
-
elementType: .other,
|
|
13
|
-
label: "Article",
|
|
14
|
-
identifier: "",
|
|
15
|
-
frame: CGRect(x: 0, y: 97, width: 131, height: 48)
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
#if os(iOS)
|
|
19
|
-
XCTAssertNotEqual(wrapper, leaf)
|
|
20
|
-
#else
|
|
21
|
-
XCTAssertEqual(wrapper, leaf)
|
|
22
|
-
#endif
|
|
23
|
-
}
|
|
24
4
|
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
|
|
3
|
+
// AX-independent text synthesis: private-XCTest boundary, pacing plan, and route policy.
|
|
4
|
+
// The verified XCUIElement path remains in TextEntry/TextTyping.
|
|
5
|
+
extension RunnerTests {
|
|
6
|
+
enum SynthesizedReplacementRouteOutcome {
|
|
7
|
+
case notApplicable
|
|
8
|
+
case completed(TextEntryResult)
|
|
9
|
+
case fallback(TextEntryTarget)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
struct SynthesizedReplacementRequest {
|
|
13
|
+
let app: XCUIApplication
|
|
14
|
+
let target: TextEntryTarget
|
|
15
|
+
let text: String
|
|
16
|
+
let delaySeconds: Double
|
|
17
|
+
let synthesizer: any TextEntrySynthesizing
|
|
18
|
+
let commandId: String?
|
|
19
|
+
let startedAt: Date
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
enum SynthesizedTextEntryAction: Equatable {
|
|
23
|
+
case continueTyping
|
|
24
|
+
case fallback
|
|
25
|
+
case raise(String?)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protocol TextEntrySynthesizing {
|
|
29
|
+
func enterText(
|
|
30
|
+
app: XCUIApplication,
|
|
31
|
+
text: String,
|
|
32
|
+
replacingExistingText: Bool
|
|
33
|
+
) -> SynthesizedTextEntryAction
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
struct PrivateXCTestTextEntrySynthesizer: TextEntrySynthesizing {
|
|
37
|
+
func enterText(
|
|
38
|
+
app: XCUIApplication,
|
|
39
|
+
text: String,
|
|
40
|
+
replacingExistingText: Bool
|
|
41
|
+
) -> SynthesizedTextEntryAction {
|
|
42
|
+
#if os(iOS)
|
|
43
|
+
let result = replacingExistingText
|
|
44
|
+
? RunnerSynthesizedTextEntry.replaceText(withApplication: app, text: text)
|
|
45
|
+
: RunnerSynthesizedTextEntry.synthesizeText(withApplication: app, text: text)
|
|
46
|
+
return Self.action(status: result.status, message: result.message)
|
|
47
|
+
#else
|
|
48
|
+
return .fallback
|
|
49
|
+
#endif
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#if os(iOS)
|
|
53
|
+
static func action(
|
|
54
|
+
status: RunnerSynthesizedTextEntryStatus,
|
|
55
|
+
message: String?
|
|
56
|
+
) -> SynthesizedTextEntryAction {
|
|
57
|
+
switch status {
|
|
58
|
+
case .succeeded:
|
|
59
|
+
return .continueTyping
|
|
60
|
+
case .unavailable:
|
|
61
|
+
return .fallback
|
|
62
|
+
case .failed:
|
|
63
|
+
return .raise(message)
|
|
64
|
+
@unknown default:
|
|
65
|
+
return .raise(message)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
#endif
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
struct SynthesizedReplacementStep: Equatable {
|
|
72
|
+
let text: String
|
|
73
|
+
let replacesExistingText: Bool
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static func synthesizedReplacementSteps(
|
|
77
|
+
text: String,
|
|
78
|
+
delaySeconds: Double
|
|
79
|
+
) -> [SynthesizedReplacementStep] {
|
|
80
|
+
let characters = Array(text)
|
|
81
|
+
guard delaySeconds > 0, characters.count > 1 else {
|
|
82
|
+
return [SynthesizedReplacementStep(text: text, replacesExistingText: true)]
|
|
83
|
+
}
|
|
84
|
+
return characters.enumerated().map { index, character in
|
|
85
|
+
SynthesizedReplacementStep(
|
|
86
|
+
text: String(character),
|
|
87
|
+
replacesExistingText: index == 0
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
func runSynthesizedReplacementRoute(
|
|
93
|
+
_ request: SynthesizedReplacementRequest
|
|
94
|
+
) -> SynthesizedReplacementRouteOutcome {
|
|
95
|
+
#if os(iOS)
|
|
96
|
+
NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder-replacement")
|
|
97
|
+
let steps = Self.synthesizedReplacementSteps(
|
|
98
|
+
text: request.text,
|
|
99
|
+
delaySeconds: request.delaySeconds
|
|
100
|
+
)
|
|
101
|
+
for (index, step) in steps.enumerated() {
|
|
102
|
+
switch request.synthesizer.enterText(
|
|
103
|
+
app: request.app,
|
|
104
|
+
text: step.text,
|
|
105
|
+
replacingExistingText: step.replacesExistingText
|
|
106
|
+
) {
|
|
107
|
+
case .fallback:
|
|
108
|
+
NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=verified-fallback reason=synthesis-unavailable")
|
|
109
|
+
guard let point = request.target.refreshPoint else { return .notApplicable }
|
|
110
|
+
return .fallback(
|
|
111
|
+
focusTextInputForTextEntry(app: request.app, x: point.x, y: point.y)
|
|
112
|
+
)
|
|
113
|
+
case .raise(let message):
|
|
114
|
+
NSException(
|
|
115
|
+
name: NSExceptionName.internalInconsistencyException,
|
|
116
|
+
reason: message ?? "private XCTest text synthesis failed"
|
|
117
|
+
).raise()
|
|
118
|
+
case .continueTyping:
|
|
119
|
+
break
|
|
120
|
+
}
|
|
121
|
+
if index + 1 < steps.count {
|
|
122
|
+
sleepFor(request.delaySeconds)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
logTextEntryPhase(
|
|
126
|
+
commandId: request.commandId,
|
|
127
|
+
phase: "total",
|
|
128
|
+
startedAt: request.startedAt,
|
|
129
|
+
chars: request.text.count,
|
|
130
|
+
mode: .replacement
|
|
131
|
+
)
|
|
132
|
+
return .completed(
|
|
133
|
+
TextEntryResult(
|
|
134
|
+
verified: nil,
|
|
135
|
+
repaired: false,
|
|
136
|
+
expectedText: request.text,
|
|
137
|
+
observedText: nil,
|
|
138
|
+
textEntryRoute: "synthesized-first-responder-replacement"
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
#else
|
|
142
|
+
return .notApplicable
|
|
143
|
+
#endif
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static func shouldUseSynthesizedFirstResponderReplacement(
|
|
147
|
+
hasResolvedElement: Bool,
|
|
148
|
+
hasRefreshPoint: Bool,
|
|
149
|
+
xCTestChannelPenalized: Bool
|
|
150
|
+
) -> Bool {
|
|
151
|
+
!hasResolvedElement && hasRefreshPoint && xCTestChannelPenalized
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static func shouldUseSynthesizedFirstResponderType(
|
|
155
|
+
repairMode: TextTypingRepairMode,
|
|
156
|
+
fromTapWitness: Bool,
|
|
157
|
+
softwareKeyboardVisible: Bool
|
|
158
|
+
) -> Bool {
|
|
159
|
+
repairMode == .none && fromTapWitness && !softwareKeyboardVisible
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static func shouldUseResolvedCoordinateTextEntryRoute(
|
|
163
|
+
repairMode: TextTypingRepairMode,
|
|
164
|
+
hasX: Bool,
|
|
165
|
+
hasY: Bool,
|
|
166
|
+
xCTestChannelPenalized: Bool
|
|
167
|
+
) -> Bool {
|
|
168
|
+
repairMode == .replacement && hasX && hasY && xCTestChannelPenalized
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static func shouldFallbackFromSynthesizedTextEntryFocus(
|
|
172
|
+
_ outcome: RunnerInteractionOutcome
|
|
173
|
+
) -> Bool {
|
|
174
|
+
if case .unsupported = outcome { return true }
|
|
175
|
+
return false
|
|
176
|
+
}
|
|
177
|
+
}
|