agent-device 0.21.14 → 0.21.15

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.
Files changed (287) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.14.apk → agent-device-android-ime-helper-0.21.15.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.15.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.14.manifest.json → agent-device-android-ime-helper-0.21.15.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.15.apk +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.15.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.14.manifest.json → agent-device-android-snapshot-helper-0.21.15.manifest.json} +6 -6
  7. package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +11 -1
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +87 -2
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +4 -2
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerMainOwnedState.swift +27 -0
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.h +8 -2
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m +26 -1
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +40 -5
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandDispatch.swift +179 -91
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +5 -4
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+GestureExecution.swift +1 -0
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +36 -49
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+MainThreadWork.swift +11 -15
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +136 -29
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +4 -2
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +3 -0
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +6 -5
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollDragExecution.swift +4 -0
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +2 -0
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +8 -4
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotAcquisition.swift +56 -14
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +18 -8
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCaptureTarget.swift +9 -7
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotExecution.swift +5 -4
  31. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +2 -2
  32. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +4 -1
  33. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +5 -1
  34. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +78 -3
  35. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +32 -2
  36. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +7 -4
  37. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +23 -0
  38. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +17 -10
  39. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +8 -9
  40. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +2 -1
  41. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +12 -11
  42. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +7 -4
  43. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +15 -8
  44. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationProjection.swift +1 -0
  45. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +26 -9
  46. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFoldProjection.swift +1 -0
  47. package/dist/src/adapter.js +1 -1
  48. package/dist/src/adb-executor.js +1 -1
  49. package/dist/src/adb-failure.js +1 -1
  50. package/dist/src/adb-transport.js +1 -1
  51. package/dist/src/agent-browser-provider.js +1 -1
  52. package/dist/src/agent-browser-tool.js +1 -1
  53. package/dist/src/agent-device-client.js +2 -2
  54. package/dist/src/ai-sdk.js +1 -1
  55. package/dist/src/alert-runtime.js +1 -1
  56. package/dist/src/android-adb.d.ts +1 -8
  57. package/dist/src/android-input-ownership.js +1 -1
  58. package/dist/src/android-snapshot-quality.js +1 -0
  59. package/dist/src/android-system-chrome.js +1 -1
  60. package/dist/src/android.js +1 -1
  61. package/dist/src/app-catalog.js +1 -1
  62. package/dist/src/app-deployment-resolution.js +1 -1
  63. package/dist/src/app-inventory.js +1 -1
  64. package/dist/src/app-launch.js +1 -1
  65. package/dist/src/app-lifecycle.js +1 -1
  66. package/dist/src/app-lifecycle2.js +1 -1
  67. package/dist/src/app-log-files.js +1 -1
  68. package/dist/src/app-log-runtime2.js +1 -1
  69. package/dist/src/app-resolution.js +2 -2
  70. package/dist/src/apple-runner-platform.js +1 -1
  71. package/dist/src/application-lifecycle-interaction.js +1 -1
  72. package/dist/src/archive.js +1 -1
  73. package/dist/src/artifact-download.js +1 -1
  74. package/dist/src/auth-session.js +1 -1
  75. package/dist/src/auth.js +1 -1
  76. package/dist/src/backend-snapshot-options.js +1 -1
  77. package/dist/src/boot-diagnostics.js +1 -1
  78. package/dist/src/capture-disclosure.js +1 -1
  79. package/dist/src/capture.js +1 -1
  80. package/dist/src/cli-config.js +1 -1
  81. package/dist/src/cli.js +1 -1
  82. package/dist/src/click-button.js +1 -1
  83. package/dist/src/client-metro.js +1 -1
  84. package/dist/src/client-types.d.ts +102 -95
  85. package/dist/src/clipboard-shell-response.js +1 -1
  86. package/dist/src/command-schema.js +1 -1
  87. package/dist/src/command-tools.js +3 -3
  88. package/dist/src/connection-runtime.js +1 -1
  89. package/dist/src/connection.js +1 -1
  90. package/dist/src/contracts.d.ts +1 -1
  91. package/dist/src/contracts.js +1 -1
  92. package/dist/src/daemon-client-lifecycle.js +1 -1
  93. package/dist/src/daemon.js +1 -1
  94. package/dist/src/debug-symbols-facade.js +1 -1
  95. package/dist/src/device-claim-rule.js +2 -2
  96. package/dist/src/device-input-state.d.ts +15 -2
  97. package/dist/src/device-inventory-context.js +1 -1
  98. package/dist/src/device-rotation.js +1 -1
  99. package/dist/src/device-selection-resolver.js +1 -1
  100. package/dist/src/device-session.js +1 -1
  101. package/dist/src/device-shell.js +1 -1
  102. package/dist/src/device2.js +1 -1
  103. package/dist/src/devicectl.js +1 -1
  104. package/dist/src/diagnostics.js +1 -1
  105. package/dist/src/dispatch-resolve.js +1 -1
  106. package/dist/src/doctor2.js +1 -1
  107. package/dist/src/engine-eval-script.js +1 -1
  108. package/dist/src/error.js +1 -1
  109. package/dist/src/exec.js +1 -1
  110. package/dist/src/file.js +1 -1
  111. package/dist/src/find.js +1 -1
  112. package/dist/src/format.js +1 -1
  113. package/dist/src/gesture-plan-types.d.ts +1 -1
  114. package/dist/src/gesture-plan.js +1 -1
  115. package/dist/src/gesture-runtime.js +1 -1
  116. package/dist/src/harmonyos.js +1 -1
  117. package/dist/src/helper.js +2 -2
  118. package/dist/src/human-control-contract.js +1 -1
  119. package/dist/src/human-control.js +1 -1
  120. package/dist/src/index.d.ts +7 -3
  121. package/dist/src/index.js +1 -1
  122. package/dist/src/input-actions.js +1 -1
  123. package/dist/src/input-actions2.js +1 -1
  124. package/dist/src/install-artifact-facade.js +1 -1
  125. package/dist/src/install-source-network-transport.js +1 -1
  126. package/dist/src/install-source2.js +1 -1
  127. package/dist/src/interaction-positionals.js +1 -1
  128. package/dist/src/interaction-ref-policy.js +1 -1
  129. package/dist/src/interaction2.js +1 -1
  130. package/dist/src/interactor-operation-binding.js +1 -1
  131. package/dist/src/interactor.js +4 -4
  132. package/dist/src/interactor2.js +1 -1
  133. package/dist/src/interactors.js +1 -1
  134. package/dist/src/internal/bin.js +1 -1
  135. package/dist/src/internal/daemon.js +2 -2
  136. package/dist/src/internal/png-worker.js +1 -1
  137. package/dist/src/internal/run-script-http-child.js +1 -1
  138. package/dist/src/inventory.js +1 -1
  139. package/dist/src/inventory2.js +1 -1
  140. package/dist/src/inventory3.js +1 -1
  141. package/dist/src/inventory5.js +1 -1
  142. package/dist/src/ios-snapshot-acquisition.js +1 -1
  143. package/dist/src/ios-snapshot-engine.js +1 -0
  144. package/dist/src/ios-snapshot-runtime.js +1 -1
  145. package/dist/src/lease.js +1 -1
  146. package/dist/src/limrun-download-file.js +1 -1
  147. package/dist/src/limrun-runtime-dependencies.js +1 -1
  148. package/dist/src/limrun.d.ts +16 -3
  149. package/dist/src/linux.js +1 -1
  150. package/dist/src/location-coordinates.js +1 -1
  151. package/dist/src/managed-device-scope.js +1 -1
  152. package/dist/src/mechanics.js +17 -17
  153. package/dist/src/mobile-snapshot-semantics.js +1 -1
  154. package/dist/src/multitouch-support.js +1 -1
  155. package/dist/src/native-build-cache.js +1 -1
  156. package/dist/src/observation.js +1 -1
  157. package/dist/src/overlay.js +3 -0
  158. package/dist/src/parameterized-recorded-fill.js +1 -1
  159. package/dist/src/perf-facade.js +1 -1
  160. package/dist/src/perf-runtime-operation-builder.js +1 -1
  161. package/dist/src/perf-runtime-plan.js +1 -1
  162. package/dist/src/perf-target.js +1 -1
  163. package/dist/src/perf.js +1 -1
  164. package/dist/src/physical-device-control.js +1 -1
  165. package/dist/src/platform-runtime-managed-owner.js +1 -1
  166. package/dist/src/platform-runtime-open-target.js +1 -1
  167. package/dist/src/platform-runtime-operation-host.js +4 -4
  168. package/dist/src/platform-runtime-runtime-hints.js +1 -1
  169. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  170. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  171. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -1
  172. package/dist/src/platform-runtime.js +1 -1
  173. package/dist/src/platform-runtime2.js +1 -1
  174. package/dist/src/png-crop-bytes.js +1 -1
  175. package/dist/src/png-size.js +1 -1
  176. package/dist/src/png-worker-client.js +1 -1
  177. package/dist/src/png.js +1 -1
  178. package/dist/src/prepare-kind.js +1 -1
  179. package/dist/src/provider-session-ownership.js +1 -1
  180. package/dist/src/provider-webdriver.js +1 -1
  181. package/dist/src/proxy.js +1 -1
  182. package/dist/src/quality-warnings.js +1 -1
  183. package/dist/src/react-native.js +1 -1
  184. package/dist/src/record-runtime.js +1 -1
  185. package/dist/src/recording.js +1 -1
  186. package/dist/src/rect-center.js +1 -1
  187. package/dist/src/register-builtins.js +1 -1
  188. package/dist/src/registry.js +36 -36
  189. package/dist/src/remote-config2.js +1 -1
  190. package/dist/src/replay-divergence.js +1 -1
  191. package/dist/src/replay.js +1 -1
  192. package/dist/src/reporting.js +1 -1
  193. package/dist/src/request-admission.js +1 -1
  194. package/dist/src/request-cancel.js +1 -1
  195. package/dist/src/request-runtime-binding.js +1 -1
  196. package/dist/src/retry.js +1 -1
  197. package/dist/src/runner-cache-metadata.js +1 -1
  198. package/dist/src/runner-client.js +1 -1
  199. package/dist/src/runner-dev-tools-security.js +1 -1
  200. package/dist/src/runner-device-readiness.js +1 -1
  201. package/dist/src/runner-disposal.js +1 -1
  202. package/dist/src/runner-error-classification.js +2 -2
  203. package/dist/src/runner.js +1 -1
  204. package/dist/src/runtime-transport-hints.js +1 -1
  205. package/dist/src/runtime.js +1 -3
  206. package/dist/src/runtime10.js +2 -2
  207. package/dist/src/runtime2.js +4 -4
  208. package/dist/src/runtime3.js +1 -1
  209. package/dist/src/runtime4.js +5 -5
  210. package/dist/src/runtime5.js +1 -1
  211. package/dist/src/runtime6.js +1 -1
  212. package/dist/src/runtime7.js +1 -1
  213. package/dist/src/runtime8.js +1 -1
  214. package/dist/src/runtime9.js +1 -1
  215. package/dist/src/screen-recording-admission-ledger.js +1 -1
  216. package/dist/src/screen-recording-completion.js +1 -1
  217. package/dist/src/screen-recording-options.js +1 -1
  218. package/dist/src/screen-recording-resource-recovery.js +1 -1
  219. package/dist/src/screenshot-density.js +1 -1
  220. package/dist/src/screenshot-image.js +1 -1
  221. package/dist/src/screenshot-overlay-draw.js +1 -1
  222. package/dist/src/screenshot-runtime.js +1 -1
  223. package/dist/src/screenshot.js +1 -1
  224. package/dist/src/screenshot3.js +1 -1
  225. package/dist/src/script.js +1 -1
  226. package/dist/src/scroll-command.d.ts +13 -6
  227. package/dist/src/scroll-command.js +1 -1
  228. package/dist/src/scroll-gesture.js +1 -1
  229. package/dist/src/sdk-batch-runner.js +1 -1
  230. package/dist/src/sdk-batch.js +2 -2
  231. package/dist/src/sdk-contracts.d.ts +38 -10
  232. package/dist/src/sdk-contracts.js +2 -2
  233. package/dist/src/sdk-device.js +1 -1
  234. package/dist/src/sdk-finders.d.ts +1 -1
  235. package/dist/src/sdk-io.js +1 -1
  236. package/dist/src/sdk-remote-config.js +1 -1
  237. package/dist/src/sdk-selectors.d.ts +1 -1
  238. package/dist/src/selection.js +0 -0
  239. package/dist/src/selector-runtime.js +1 -1
  240. package/dist/src/server.js +1 -1
  241. package/dist/src/session-allocation.js +1 -1
  242. package/dist/src/session-routing.js +1 -1
  243. package/dist/src/session-runtime-admission.js +1 -1
  244. package/dist/src/session-snapshot.js +1 -1
  245. package/dist/src/session-store.js +2 -2
  246. package/dist/src/session.js +1 -1
  247. package/dist/src/session2.js +4 -4
  248. package/dist/src/settings.js +1 -1
  249. package/dist/src/settings2.js +1 -0
  250. package/dist/src/simulator.js +1 -1
  251. package/dist/src/snapshot-capture-annotations.js +1 -1
  252. package/dist/src/snapshot-desktop-projection.js +1 -1
  253. package/dist/src/snapshot-runtime.js +1 -1
  254. package/dist/src/snapshot-state.js +1 -1
  255. package/dist/src/snapshot2.js +1 -1
  256. package/dist/src/snapshot3.js +1 -1
  257. package/dist/src/snapshot4.js +1 -1
  258. package/dist/src/src.js +1 -1
  259. package/dist/src/src4.js +1 -1
  260. package/dist/src/src7.js +1 -1
  261. package/dist/src/src8.js +2 -2
  262. package/dist/src/string-enum.js +1 -1
  263. package/dist/src/surface-snapshot.js +1 -1
  264. package/dist/src/takeover.js +1 -1
  265. package/dist/src/text-entry-focus.js +1 -1
  266. package/dist/src/tool-provider.js +1 -1
  267. package/dist/src/tool-provider2.js +1 -1
  268. package/dist/src/touch-runtime.js +1 -1
  269. package/dist/src/transport.js +1 -1
  270. package/dist/src/tree.js +1 -1
  271. package/dist/src/tv-remote.js +1 -1
  272. package/dist/src/type-text-runtime.js +1 -1
  273. package/dist/src/verified-file.js +1 -1
  274. package/dist/src/video.js +1 -1
  275. package/dist/src/viewport.js +1 -1
  276. package/dist/src/web.js +1 -1
  277. package/dist/src/web2.js +1 -1
  278. package/dist/src/webdriver-source.js +1 -1
  279. package/package.json +1 -1
  280. package/server.json +2 -2
  281. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.14.apk.sha256 +0 -1
  282. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk +0 -0
  283. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk.sha256 +0 -1
  284. package/dist/src/absence-observation-errors.js +0 -1
  285. package/dist/src/runner-presentation.js +0 -1
  286. package/dist/src/semantic-index.js +0 -1
  287. package/dist/src/snapshot-scope.js +0 -1
@@ -35,6 +35,7 @@ extension RunnerTests {
35
35
  let label: String
36
36
  let identifier: String
37
37
  let valueText: String?
38
+ let placeholder: String?
38
39
  let focused: Bool
39
40
  let selected: Bool
40
41
  }
@@ -175,6 +176,7 @@ extension RunnerTests {
175
176
  )
176
177
  }
177
178
 
179
+ @MainActor
178
180
  func recursiveTreeSnapshotAcquisition(
179
181
  context: SnapshotTraversalContext,
180
182
  hint: CaptureHint
@@ -327,7 +329,7 @@ extension RunnerTests {
327
329
  private func boundedBlockingSystemAlertSnapshotBody(
328
330
  deadline: Date,
329
331
  penaltyTarget: SnapshotProbePenaltyTarget,
330
- probe: @escaping (Date) -> DataPayload?
332
+ probe: @escaping @MainActor (Date) -> DataPayload?
331
333
  ) -> DataPayload? {
332
334
  #if os(macOS)
333
335
  return nil
@@ -361,7 +363,7 @@ extension RunnerTests {
361
363
  )
362
364
  }
363
365
  ) {
364
- penaltyIdentity.captureFromMain(bundleId: self.currentBundleId)
366
+ penaltyIdentity.captureFromMain(bundleId: self.mainOwned.bundleId)
365
367
  return probe(probeDeadline)
366
368
  }
367
369
  } catch {
@@ -437,6 +439,7 @@ extension RunnerTests {
437
439
  )
438
440
  }
439
441
 
442
+ @MainActor
440
443
  func querySweepSnapshotAcquisition(
441
444
  app: XCUIApplication,
442
445
  hint: CaptureHint,
@@ -500,6 +503,7 @@ extension RunnerTests {
500
503
  label: candidate.label,
501
504
  identifier: candidate.identifier,
502
505
  value: candidate.value,
506
+ placeholder: candidate.placeholder,
503
507
  rect: candidate.rect,
504
508
  enabled: candidate.enabled,
505
509
  focused: candidate.focused,
@@ -527,7 +531,7 @@ extension RunnerTests {
527
531
  func snapshotAccessibilityUnavailable(failure: SnapshotCaptureFailure) -> DataPayload {
528
532
  NSLog("AGENT_DEVICE_RUNNER_SNAPSHOT_AX_UNAVAILABLE=%@", failure.message)
529
533
  applyMainOwnedSnapshotState("ax_unavailable_invalidation") {
530
- self.runnerAccessibilityHealth = .unavailable
534
+ self.mainOwned.accessibilityHealth = .unavailable
531
535
  self.invalidateCachedTarget(reason: Self.axSnapshotUnavailableReason)
532
536
  }
533
537
 
@@ -535,7 +539,7 @@ extension RunnerTests {
535
539
  return sparseTruncatedSnapshotPayload(
536
540
  message: recoveredSnapshotMessage(failure),
537
541
  snapshotQuality: SnapshotQuality(
538
- state: "sparse",
542
+ state: .sparse,
539
543
  backend: SnapshotBackendKind.recursiveTree.rawValue,
540
544
  reason: failure.message,
541
545
  reasonCode: "ax-rejected",
@@ -38,22 +38,22 @@ extension RunnerTests {
38
38
  captureDeadline: Date = .distantFuture,
39
39
  treeCaptureSliceBudgetOverride: TimeInterval? = nil
40
40
  ) throws -> SnapshotTraversalContext? {
41
+ let treeSliceBudget = treeCaptureSliceBudgetOverride ?? treeCaptureSliceBudget
42
+ let slice = min(treeSliceBudget, max(0.5, captureDeadline.timeIntervalSinceNow))
43
+ guard let rootSnapshot = try captureSnapshotRootBounded(app, sliceSeconds: slice) else {
44
+ return nil
45
+ }
46
+
41
47
 
42
48
 
43
49
 
44
50
  let viewport = try runMainThreadWork(
45
51
  "snapshot_viewport",
46
52
  timeout: min(1.0, max(0.1, captureDeadline.timeIntervalSinceNow)),
47
- timeoutError: snapshotMainThreadTimeoutError("preparing tree snapshot")
53
+ timeoutError: snapshotMainThreadTimeoutError("reading snapshot viewport")
48
54
  ) {
49
55
  self.safeSnapshotViewport(app: app, readingOrientation: true)
50
56
  }
51
- let treeSliceBudget = treeCaptureSliceBudgetOverride ?? treeCaptureSliceBudget
52
- let slice = min(treeSliceBudget, max(0.5, captureDeadline.timeIntervalSinceNow))
53
- guard let rootSnapshot = try captureSnapshotRootBounded(app, sliceSeconds: slice) else {
54
- return nil
55
- }
56
-
57
57
 
58
58
 
59
59
 
@@ -93,7 +93,7 @@ extension RunnerTests {
93
93
  }
94
94
  }
95
95
 
96
- private func treeCaptureTimeoutError(sliceSeconds: TimeInterval) -> () -> Error {
96
+ private func treeCaptureTimeoutError(sliceSeconds: TimeInterval) -> @Sendable () -> Error {
97
97
  {
98
98
  SnapshotCaptureFailure(
99
99
  code: Self.xCTestSnapshotTimeoutCode,
@@ -103,7 +103,7 @@ extension RunnerTests {
103
103
  }
104
104
  }
105
105
 
106
- func snapshotMainThreadTimeoutError(_ operation: String) -> () -> Error {
106
+ func snapshotMainThreadTimeoutError(_ operation: String) -> @Sendable () -> Error {
107
107
  {
108
108
  SnapshotCaptureFailure(
109
109
  code: Self.xCTestSnapshotTimeoutCode,
@@ -189,6 +189,7 @@ extension RunnerTests {
189
189
  label: label,
190
190
  identifier: identifier,
191
191
  valueText: valueText,
192
+ placeholder: placeholderText(snapshot.placeholderValue),
192
193
  focused: snapshotHasFocus(snapshot),
193
194
  selected: snapshotIsSelected(snapshot)
194
195
  )
@@ -209,6 +210,7 @@ extension RunnerTests {
209
210
  label: evaluation.label.isEmpty ? nil : evaluation.label,
210
211
  identifier: evaluation.identifier.isEmpty ? nil : evaluation.identifier,
211
212
  value: evaluation.valueText,
213
+ placeholder: evaluation.placeholder,
212
214
  rect: SnapshotRect(snapshot.frame),
213
215
  enabled: snapshot.isEnabled,
214
216
  focused: evaluation.focused ? true : nil,
@@ -235,6 +237,26 @@ extension RunnerTests {
235
237
  return text.isEmpty ? nil : text
236
238
  }
237
239
 
240
+
241
+
242
+
243
+ func placeholderText(_ placeholderValue: String?) -> String? {
244
+ let text = placeholderValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
245
+ return text.isEmpty ? nil : text
246
+ }
247
+
248
+
249
+
250
+
251
+ static let placeholderElementTypes: Set<XCUIElement.ElementType> = [
252
+ .textField, .secureTextField, .searchField, .textView,
253
+ ]
254
+
255
+ func elementPlaceholderText(_ element: XCUIElement, type: XCUIElement.ElementType) -> String? {
256
+ guard Self.placeholderElementTypes.contains(type) else { return nil }
257
+ return placeholderText(element.placeholderValue)
258
+ }
259
+
238
260
  private func snapshotAppFrame(app: XCUIApplication) -> CGRect {
239
261
  #if os(iOS)
240
262
  return onScreenWindowFrame(app: app)
@@ -341,6 +363,7 @@ extension RunnerTests {
341
363
  label: node.label,
342
364
  identifier: node.identifier,
343
365
  value: node.value,
366
+ placeholder: node.placeholder,
344
367
  rect: node.rect,
345
368
  enabled: node.enabled,
346
369
  focused: node.focused,
@@ -376,7 +399,8 @@ extension RunnerTests {
376
399
  let label = element.label.trimmingCharacters(in: .whitespacesAndNewlines)
377
400
  let identifier = element.identifier.trimmingCharacters(in: .whitespacesAndNewlines)
378
401
  let valueText = snapshotValueText(element)
379
- let hasContent = !label.isEmpty || !identifier.isEmpty || valueText != nil
402
+ let placeholder = elementPlaceholderText(element, type: elementType)
403
+ let hasContent = !label.isEmpty || !identifier.isEmpty || valueText != nil || placeholder != nil
380
404
  if !hasContent { return }
381
405
  if sameSemanticElement(
382
406
  containerSnapshot: containerSnapshot,
@@ -395,6 +419,7 @@ extension RunnerTests {
395
419
  label: label.isEmpty ? nil : label,
396
420
  identifier: identifier.isEmpty ? nil : identifier,
397
421
  value: valueText,
422
+ placeholder: placeholder,
398
423
  rect: SnapshotRect(frame),
399
424
  enabled: element.isEnabled,
400
425
  focused: elementHasFocus(element) ? true : nil,
@@ -417,13 +442,27 @@ extension RunnerTests {
417
442
  }
418
443
 
419
444
  private func snapshotHasFocus(_ snapshot: XCUIElementSnapshot) -> Bool {
420
- var focused = false
445
+ return focusBool(snapshot as! NSObject)
446
+ }
447
+
448
+
449
+
450
+
451
+
452
+
453
+
454
+ func focusBool(_ object: NSObject) -> Bool {
455
+ return kvcBool(object, forKey: "hasKeyboardFocus") || kvcBool(object, forKey: "hasFocus")
456
+ }
457
+
458
+ private func kvcBool(_ object: NSObject, forKey key: String) -> Bool {
459
+ var result = false
421
460
  _ = RunnerObjCExceptionCatcher.catchException({
422
- if let value = (snapshot as! NSObject).value(forKey: "hasFocus") as? Bool {
423
- focused = value
461
+ if let value = object.value(forKey: key) as? Bool {
462
+ result = value
424
463
  }
425
464
  })
426
- return focused
465
+ return result
427
466
  }
428
467
 
429
468
  private func snapshotIsSelected(_ snapshot: XCUIElementSnapshot) -> Bool {
@@ -461,6 +500,7 @@ extension RunnerTests {
461
500
  return containerLabel == label && containerIdentifier == identifier
462
501
  }
463
502
 
503
+ @MainActor
464
504
  func flatInteractiveElements(
465
505
  app: XCUIApplication,
466
506
  deadline: Date
@@ -519,6 +559,7 @@ extension RunnerTests {
519
559
  return (elements, .completed)
520
560
  }
521
561
 
562
+ @MainActor
522
563
  func snapshotElementsQuery(
523
564
  _ fetch: () -> [XCUIElement]
524
565
  ) -> (elements: [XCUIElement], axUnavailable: Bool) {
@@ -558,6 +599,7 @@ extension RunnerTests {
558
599
  label: label.isEmpty ? nil : label,
559
600
  identifier: identifier.isEmpty ? nil : identifier,
560
601
  value: valueText,
602
+ placeholder: elementPlaceholderText(element, type: elementType),
561
603
  rect: SnapshotRect(frame),
562
604
  enabled: enabled,
563
605
  focused: elementHasFocus(element) ? true : nil,
@@ -9,10 +9,20 @@ import AgentDeviceSnapshotPresentation
9
9
 
10
10
 
11
11
 
12
- struct SnapshotQuality: Codable {
13
12
 
14
13
 
15
- let state: String
14
+ enum SnapshotQualityState: String, Codable, CaseIterable {
15
+
16
+ case healthy
17
+
18
+ case recovered
19
+
20
+ case sparse
21
+ }
22
+
23
+
24
+ struct SnapshotQuality: Codable {
25
+ let state: SnapshotQualityState
16
26
 
17
27
  let backend: String
18
28
 
@@ -391,7 +401,7 @@ extension RunnerTests {
391
401
  return stampedSnapshotPayload(
392
402
  capture,
393
403
  backend: kind,
394
- state: recovered ? "recovered" : "healthy",
404
+ state: recovered ? .recovered : .healthy,
395
405
  reason: recovered || firstFailure?.code == "requested-backend" ? firstFailure : nil
396
406
  )
397
407
  }
@@ -416,11 +426,11 @@ extension RunnerTests {
416
426
  }
417
427
 
418
428
  let fallbackPayload =
419
- best.map { stampedSnapshotPayload($0.capture, backend: $0.kind, state: "sparse", reason: firstFailure) }
429
+ best.map { stampedSnapshotPayload($0.capture, backend: $0.kind, state: .sparse, reason: firstFailure) }
420
430
  ?? stampedSnapshotPayload(
421
431
  SnapshotBackendCapture(payload: sparseTruncatedSnapshotPayload(), effectiveDepth: nil),
422
432
  backend: effectivePlan.last ?? plan.last ?? .recursiveTree,
423
- state: "sparse",
433
+ state: .sparse,
424
434
  reason: firstFailure
425
435
  )
426
436
  return fallbackPayload
@@ -679,12 +689,12 @@ extension RunnerTests {
679
689
  func stampedSnapshotPayload(
680
690
  _ capture: SnapshotBackendCapture,
681
691
  backend: SnapshotBackendKind,
682
- state: String,
692
+ state: SnapshotQualityState,
683
693
  reason: (reason: String, code: String)?
684
694
  ) -> DataPayload {
685
695
  let health: RunnerAccessibilityHealth = reason?.code == "ax-rejected" ? .unavailable : .healthy
686
696
  applyMainOwnedSnapshotState("accessibility_health") {
687
- self.runnerAccessibilityHealth = health
697
+ self.mainOwned.accessibilityHealth = health
688
698
  }
689
699
  let payload = capture.payload
690
700
  let quality = SnapshotQuality(
@@ -704,7 +714,7 @@ extension RunnerTests {
704
714
 
705
715
 
706
716
 
707
- truncated: payload.truncated == true || state == "sparse" || capture.effectiveDepth != nil,
717
+ truncated: payload.truncated == true || state == .sparse || capture.effectiveDepth != nil,
708
718
  qualityPayload: capture.qualityPayload.flatMap { quality in
709
719
  guard let nodes = quality.nodes else { return nil }
710
720
  return SnapshotQualityPayload(nodes: nodes, truncated: quality.truncated == true)
@@ -8,7 +8,6 @@ import XCTest
8
8
 
9
9
 
10
10
 
11
-
12
11
  struct SnapshotCaptureTarget {
13
12
  let app: XCUIApplication
14
13
  let bundleId: String?
@@ -67,32 +66,35 @@ final class SnapshotProbePenaltyIdentity {
67
66
 
68
67
  extension RunnerTests {
69
68
 
69
+ @MainActor
70
70
  func takeSnapshotCaptureTarget(app: XCUIApplication) -> SnapshotCaptureTarget {
71
71
  SnapshotCaptureTarget(
72
72
  app: app,
73
- bundleId: currentBundleId,
74
- processIdentifier: currentAppProcessIdentifier
73
+ bundleId: mainOwned.bundleId,
74
+ processIdentifier: mainOwned.processIdentifier
75
75
  )
76
76
  }
77
77
 
78
78
 
79
79
 
80
80
 
81
- func applyMainOwnedSnapshotState(_ operation: String, _ write: @escaping () -> Void) {
81
+ func applyMainOwnedSnapshotState(_ operation: String, _ write: @escaping @MainActor () -> Void) {
82
82
  if Thread.isMainThread {
83
- write()
83
+ _ = runOnMainActor(write)
84
84
  return
85
85
  }
86
86
  guard !hasAbandonedMainThreadWork() else {
87
87
  NSLog("AGENT_DEVICE_RUNNER_SNAPSHOT_STATE_DEFERRED_XCTEST_OCCUPIED operation=%@", operation)
88
- DispatchQueue.main.async(execute: write)
88
+ DispatchQueue.main.async {
89
+ _ = runOnMainActor(write)
90
+ }
89
91
  return
90
92
  }
91
93
  do {
92
94
  try runMainThreadWork(
93
95
  operation,
94
96
  timeout: 1,
95
- timeoutError: mainThreadExecutionTimeoutError,
97
+ timeoutError: Self.mainThreadExecutionTimeoutError,
96
98
  write
97
99
  )
98
100
  } catch {
@@ -11,8 +11,8 @@ extension RunnerTests {
11
11
  private func executeSnapshotDispatchedOnce(command: Command) throws -> Response {
12
12
  let preparation: SnapshotCommandPreparation = try runMainThreadWork(
13
13
  "command_preparation",
14
- timeout: mainThreadExecutionTimeout,
15
- timeoutError: mainThreadExecutionTimeoutError
14
+ timeout: Self.mainThreadExecutionTimeout,
15
+ timeoutError: Self.mainThreadExecutionTimeoutError
16
16
  ) { () -> SnapshotCommandPreparation in
17
17
  switch try self.prepareActiveCommandContextSafely(command: command, routeToSpringboard: false) {
18
18
  case .response(let response):
@@ -36,6 +36,7 @@ extension RunnerTests {
36
36
  }
37
37
  }
38
38
 
39
+ @MainActor
39
40
  private func prepareActiveCommandContextSafely(
40
41
  command: Command,
41
42
  routeToSpringboard: Bool
@@ -125,9 +126,9 @@ extension RunnerTests {
125
126
  try runMainThreadWork(
126
127
  "post_snapshot_delay_mark",
127
128
  timeout: 1,
128
- timeoutError: mainThreadExecutionTimeoutError
129
+ timeoutError: Self.mainThreadExecutionTimeoutError
129
130
  ) {
130
- self.needsPostSnapshotInteractionDelay = true
131
+ self.mainOwned.needsPostSnapshotInteractionDelay = true
131
132
  }
132
133
  } catch {
133
134
  NSLog("AGENT_DEVICE_RUNNER_POST_SNAPSHOT_DELAY_MARK_FAILED=%@", String(describing: error))
@@ -24,11 +24,11 @@ enum SnapshotCapturePhase: Equatable {
24
24
  }
25
25
 
26
26
  struct SnapshotPhaseTimer {
27
- private let now: () -> Date
27
+ private let now: @Sendable () -> Date
28
28
  private var acquisitionSeconds: TimeInterval = 0
29
29
  private var presentationSeconds: TimeInterval = 0
30
30
 
31
- init(now: @escaping () -> Date = { Date() }) {
31
+ init(now: @escaping @Sendable () -> Date = { Date() }) {
32
32
  self.now = now
33
33
  }
34
34
 
@@ -125,6 +125,7 @@ enum SynthesizedGestureAttempt {
125
125
  }
126
126
 
127
127
  extension RunnerTests {
128
+ @MainActor
128
129
  func synthesizedSequenceCoordinateContext(
129
130
  steps: [SequenceStep],
130
131
  app: XCUIApplication
@@ -137,6 +138,7 @@ extension RunnerTests {
137
138
 
138
139
 
139
140
 
141
+ @MainActor
140
142
  func performSynthesizedGesture(
141
143
  _ app: XCUIApplication,
142
144
  kind: SynthesizedGesturePolicyKind,
@@ -149,7 +151,7 @@ extension RunnerTests {
149
151
  return .performed(timing: timing)
150
152
  }
151
153
  let fallbackAllowed = synthesizedGesturePolicy(kind).fallbackPolicy.allowsXCTestCoordinateFallback(
152
- accessibilityHealth: context?.accessibilityHealth ?? runnerAccessibilityHealth
154
+ accessibilityHealth: context?.accessibilityHealth ?? mainOwned.accessibilityHealth
153
155
  )
154
156
  logSynthesizedGesturePolicyDecision(
155
157
  kind: kind,
@@ -161,6 +163,7 @@ extension RunnerTests {
161
163
  : .refused(timing: timing, message: message, hint: hint)
162
164
  }
163
165
 
166
+ @MainActor
164
167
  func logSynthesizedGesturePolicyDecision(
165
168
  kind: SynthesizedGesturePolicyKind,
166
169
  context: SynthesizedCoordinateContext?,
@@ -2,6 +2,7 @@ import XCTest
2
2
  import AgentDeviceSnapshotPresentation
3
3
 
4
4
  extension RunnerTests {
5
+ @MainActor
5
6
  func synthesizedDragAt(
6
7
  app: XCUIApplication,
7
8
  x: Double,
@@ -89,6 +90,7 @@ extension RunnerTests {
89
90
  #endif
90
91
  }
91
92
 
93
+ @MainActor
92
94
  func synthesizedTapAt(
93
95
  app: XCUIApplication,
94
96
  x: Double,
@@ -276,6 +278,7 @@ extension RunnerTests {
276
278
  #endif
277
279
  }
278
280
 
281
+ @MainActor
279
282
  func axFreeSynthesizedDragPlan(
280
283
  app: XCUIApplication,
281
284
  x: Double,
@@ -364,12 +367,13 @@ extension RunnerTests {
364
367
  )
365
368
  }
366
369
 
370
+ @MainActor
367
371
  func synthesizedCoordinateContext(
368
372
  app: XCUIApplication,
369
373
  policy: SynthesizedGesturePolicy
370
374
  ) -> SynthesizedCoordinateContext? {
371
375
  #if os(iOS)
372
- let health = runnerAccessibilityHealth
376
+ let health = mainOwned.accessibilityHealth
373
377
  let resolved = resolveRunnerWindow(app: app)
374
378
  guard let window = resolved.window else {
375
379
  return nil
@@ -82,11 +82,11 @@ extension RunnerTests {
82
82
  text: String,
83
83
  delaySeconds: Double
84
84
  ) -> [SynthesizedReplacementStep] {
85
- let characters = Array(text)
86
- guard delaySeconds > 0, characters.count > 1 else {
85
+ guard synthesizedReplacementIsSpaced(characterCount: text.count, delaySeconds: delaySeconds)
86
+ else {
87
87
  return [SynthesizedReplacementStep(text: text, replacesExistingText: true)]
88
88
  }
89
- return characters.enumerated().map { index, character in
89
+ return Array(text).enumerated().map { index, character in
90
90
  SynthesizedReplacementStep(
91
91
  text: String(character),
92
92
  replacesExistingText: index == 0
@@ -94,11 +94,86 @@ extension RunnerTests {
94
94
  }
95
95
  }
96
96
 
97
+
98
+
99
+ static func synthesizedReplacementIsSpaced(characterCount: Int, delaySeconds: Double) -> Bool {
100
+ delaySeconds > 0 && characterCount > 1
101
+ }
102
+
103
+
104
+
105
+
106
+ enum SynthesizedDeliveryBudget {
107
+
108
+ static var characterInterval: TimeInterval {
109
+ 1.0 / Double(RunnerSynthesizedTextEntry.typingSpeedCharactersPerSecond())
110
+ }
111
+
112
+
113
+
114
+
115
+
116
+
117
+ static func projectedSeconds(
118
+ textLength: Int,
119
+ delaySeconds: TimeInterval,
120
+ typeWarmup: Bool = false
121
+ ) -> TimeInterval {
122
+ let spaced = synthesizedReplacementIsSpaced(characterCount: textLength, delaySeconds: delaySeconds)
123
+ let warmupSplit = typeWarmup && textLength > 1 && !spaced
124
+ let calls = spaced ? textLength : (warmupSplit ? 2 : 1)
125
+ return Double(textLength) * characterInterval
126
+ + Double(calls) * TextEntryTiming.synthesizeCallOverhead
127
+ + Double(calls - 1) * delaySeconds
128
+ + (warmupSplit ? TextEntryTiming.pollInterval : 0)
129
+ }
130
+
131
+ static func exceeds(
132
+ textLength: Int,
133
+ delaySeconds: TimeInterval,
134
+ typeWarmup: Bool = false
135
+ ) -> Bool {
136
+ projectedSeconds(textLength: textLength, delaySeconds: delaySeconds, typeWarmup: typeWarmup)
137
+ > TextEntryTiming.synthesizedDeliveryCeiling
138
+ }
139
+
140
+
141
+ static func maxTextLength(delaySeconds: TimeInterval) -> Int {
142
+ var length = 1
143
+ while !exceeds(textLength: length + 1, delaySeconds: delaySeconds) {
144
+ length += 1
145
+ }
146
+ return length
147
+ }
148
+ }
149
+
150
+ @MainActor
97
151
  func runSynthesizedReplacementRoute(
98
152
  _ request: SynthesizedReplacementRequest
99
153
  ) -> SynthesizedReplacementRouteOutcome {
100
154
  #if os(iOS)
101
155
  NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder-replacement")
156
+ if SynthesizedDeliveryBudget.exceeds(
157
+ textLength: request.text.count,
158
+ delaySeconds: request.delaySeconds
159
+ ) {
160
+ NSLog(
161
+ "AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder-replacement "
162
+ + "reason=delivery-budget-refused chars=%d budgetChars=%d",
163
+ request.text.count,
164
+ SynthesizedDeliveryBudget.maxTextLength(delaySeconds: request.delaySeconds)
165
+ )
166
+ return .completed(
167
+ TextEntryResult(
168
+ verified: nil,
169
+ repaired: false,
170
+ expectedText: request.text,
171
+ observedText: nil,
172
+ textEntryRoute: "synthesized-first-responder-replacement",
173
+ failure: .synthesisBudgetExceeded
174
+ )
175
+ )
176
+ }
102
177
  let steps = Self.synthesizedReplacementSteps(
103
178
  text: request.text,
104
179
  delaySeconds: request.delaySeconds
@@ -8,6 +8,7 @@ extension RunnerTests {
8
8
  case notFocused = "TEXT_INPUT_NOT_FOCUSED"
9
9
  case synthesisUnavailable = "TEXT_INPUT_SYNTHESIS_UNAVAILABLE"
10
10
  case commitNotObserved = "TEXT_INPUT_COMMIT_NOT_OBSERVED"
11
+ case synthesisBudgetExceeded = "TEXT_INPUT_SYNTHESIS_BUDGET_EXCEEDED"
11
12
 
12
13
  var message: String {
13
14
  switch self {
@@ -17,6 +18,8 @@ extension RunnerTests {
17
18
  return "Reliable text synthesis is unavailable while the software keyboard is hidden."
18
19
  case .commitNotObserved:
19
20
  return "The runner could not confirm the typed text reached the field."
21
+ case .synthesisBudgetExceeded:
22
+ return "The text is longer than one runner command can type at this pace."
20
23
  }
21
24
  }
22
25
 
@@ -27,7 +30,13 @@ extension RunnerTests {
27
30
  case .synthesisUnavailable:
28
31
  return "Show the software keyboard, then retry type."
29
32
  case .commitNotObserved:
30
- return "The field may hold none, part, or all of the text. Run snapshot -i and inspect the field: if it already matches, continue; otherwise retry fill with the full text quoted and --delay-ms 80. Do not use type, which appends to whatever committed."
33
+ return "The field may hold none, part, or all of the text. Run snapshot -i and inspect the field: if it already matches, continue; otherwise retry fill with the full text quoted and --delay-ms \(TextEntryTiming.recoveryDelayMilliseconds). Do not use type, which appends to whatever committed."
34
+ case .synthesisBudgetExceeded:
35
+ let recoveryDelay = TextEntryTiming.recoveryDelayMilliseconds
36
+ let recoveryBudget = SynthesizedDeliveryBudget.maxTextLength(
37
+ delaySeconds: Double(recoveryDelay) / 1000
38
+ )
39
+ return "Fill at most \(SynthesizedDeliveryBudget.maxTextLength(delaySeconds: 0)) characters at a time without --delay-ms and append the rest with separate type commands, keeping each command inside that budget. --delay-ms lowers the budget, because each character then gets its own synthesize call and each gap between characters pays the delay: \(recoveryBudget) characters at --delay-ms \(recoveryDelay). A longer timeout does not help: this route is chosen when the accessibility channel is already degraded, and the pace is what makes the text long."
31
40
  }
32
41
  }
33
42
  }
@@ -51,8 +60,29 @@ extension RunnerTests {
51
60
  static let synthesizedCommitStallTimeout: TimeInterval = 3.0
52
61
 
53
62
 
54
-
55
63
  static let synthesizedCommitCeiling: TimeInterval = 10.0
64
+
65
+
66
+ static let synthesizedReplacementFocusAllowance: TimeInterval = 2.0
67
+
68
+
69
+
70
+
71
+ static let synthesizedDeliveryCeiling: TimeInterval = RunnerTests.mainThreadExecutionTimeout
72
+ - synthesizedReplacementFocusAllowance
73
+ - synthesizedCommitCeiling
74
+
75
+
76
+
77
+
78
+
79
+ static let synthesizedAcknowledgeWindowSeconds: TimeInterval = 0.04
80
+
81
+
82
+
83
+ static let synthesizeCallOverhead: TimeInterval = 0.15
84
+
85
+ static let recoveryDelayMilliseconds = 80
56
86
  static let synthesizedCommitPollInterval: TimeInterval = 0.2
57
87
  }
58
88
 
@@ -6,6 +6,7 @@ import XCTest
6
6
 
7
7
 
8
8
  extension RunnerTests {
9
+ @MainActor
9
10
  func rememberTextEntryTap(_ element: XCUIElement?) {
10
11
  guard let element, isTextEntryElement(element) else {
11
12
  clearRememberedTextEntryTap()
@@ -13,8 +14,8 @@ extension RunnerTests {
13
14
  }
14
15
  textEntryTapWitness = TextEntryTapWitness(
15
16
  element: element,
16
- bundleId: currentBundleId,
17
- processIdentifier: currentAppProcessIdentifier
17
+ bundleId: mainOwned.bundleId,
18
+ processIdentifier: mainOwned.processIdentifier
18
19
  )
19
20
  }
20
21
 
@@ -22,6 +23,7 @@ extension RunnerTests {
22
23
  textEntryTapWitness = nil
23
24
  }
24
25
 
26
+ @MainActor
25
27
  private func rememberedTextEntryTarget() -> TextEntryTarget? {
26
28
  guard let witness = textEntryTapWitness else {
27
29
  return nil
@@ -30,8 +32,8 @@ extension RunnerTests {
30
32
 
31
33
  clearRememberedTextEntryTap()
32
34
  guard witness.matches(
33
- bundleId: currentBundleId,
34
- processIdentifier: currentAppProcessIdentifier
35
+ bundleId: mainOwned.bundleId,
36
+ processIdentifier: mainOwned.processIdentifier
35
37
  ) else {
36
38
  return nil
37
39
  }
@@ -80,6 +82,7 @@ extension RunnerTests {
80
82
  #endif
81
83
  }
82
84
 
85
+ @MainActor
83
86
  func focusTextInputForTextEntry(app: XCUIApplication, x: Double?, y: Double?) -> TextEntryTarget {
84
87
  guard let x, let y else {
85
88
  let softwareKeyboardVisible = isKeyboardVisible(app: app)