agent-device 0.21.13 → 0.21.14

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 (244) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.13.apk → agent-device-android-ime-helper-0.21.14.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.14.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.13.manifest.json → agent-device-android-ime-helper-0.21.14.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.13.manifest.json → agent-device-android-snapshot-helper-0.21.14.manifest.json} +6 -6
  7. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +23 -0
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +12 -19
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +19 -15
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AlertObservation.swift +2 -3
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +16 -20
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+GestureExecution.swift +8 -13
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +16 -19
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollDragExecution.swift +8 -24
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +20 -42
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +7 -14
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotAcquisition.swift +17 -17
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +2 -3
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +48 -19
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +0 -1
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +0 -7
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +7 -14
  23. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotCoordinateSpace.swift +16 -19
  24. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +21 -9
  25. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +59 -10
  26. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationInvariant.swift +12 -10
  27. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +13 -8
  28. package/dist/src/absence-observation-errors.js +1 -1
  29. package/dist/src/adapter.js +1 -1
  30. package/dist/src/adb-executor.js +1 -1
  31. package/dist/src/adb-failure.js +1 -1
  32. package/dist/src/adb-transport.js +1 -1
  33. package/dist/src/agent-browser-provider.js +1 -1
  34. package/dist/src/agent-browser-tool.js +1 -1
  35. package/dist/src/agent-device-client.js +2 -2
  36. package/dist/src/ai-sdk.js +1 -1
  37. package/dist/src/alert-runtime.js +1 -1
  38. package/dist/src/app-catalog.js +1 -1
  39. package/dist/src/app-deployment-resolution.js +1 -1
  40. package/dist/src/app-inventory.js +1 -1
  41. package/dist/src/app-launch.js +1 -1
  42. package/dist/src/app-lifecycle.js +1 -1
  43. package/dist/src/app-lifecycle2.js +1 -1
  44. package/dist/src/app-log-files.js +1 -1
  45. package/dist/src/app-log-runtime2.js +1 -1
  46. package/dist/src/app-resolution.js +2 -2
  47. package/dist/src/apple-runner-platform.js +1 -1
  48. package/dist/src/application-lifecycle-interaction.js +1 -1
  49. package/dist/src/archive.js +1 -1
  50. package/dist/src/artifact-download.js +1 -1
  51. package/dist/src/auth-session.js +1 -1
  52. package/dist/src/auth.js +1 -1
  53. package/dist/src/backend-snapshot-options.js +1 -1
  54. package/dist/src/boot-diagnostics.js +1 -1
  55. package/dist/src/capture-disclosure.js +1 -1
  56. package/dist/src/capture.js +1 -1
  57. package/dist/src/cli-config.js +1 -1
  58. package/dist/src/cli-help.js +2 -2
  59. package/dist/src/cli.js +4 -4
  60. package/dist/src/click-button.js +1 -1
  61. package/dist/src/client-metro.js +1 -1
  62. package/dist/src/clipboard-shell-response.js +1 -1
  63. package/dist/src/command-schema.js +1 -1
  64. package/dist/src/command-tools.js +2 -2
  65. package/dist/src/connection-runtime.js +1 -1
  66. package/dist/src/connection.js +1 -1
  67. package/dist/src/contracts.js +1 -1
  68. package/dist/src/daemon-client-lifecycle.js +1 -1
  69. package/dist/src/daemon.js +1 -1
  70. package/dist/src/debug-symbols-facade.js +1 -1
  71. package/dist/src/device-claim-rule.js +2 -2
  72. package/dist/src/device-inventory-context.js +1 -1
  73. package/dist/src/device-rotation.js +1 -1
  74. package/dist/src/device-selection-resolver.js +1 -1
  75. package/dist/src/device-session.js +1 -1
  76. package/dist/src/device-shell.js +1 -1
  77. package/dist/src/device2.js +1 -1
  78. package/dist/src/devicectl.js +1 -1
  79. package/dist/src/diagnostics.js +1 -1
  80. package/dist/src/dispatch-resolve.js +1 -1
  81. package/dist/src/doctor2.js +1 -1
  82. package/dist/src/engine-eval-script.js +1 -1
  83. package/dist/src/error.js +1 -1
  84. package/dist/src/exec.js +1 -1
  85. package/dist/src/file.js +1 -1
  86. package/dist/src/find.js +1 -1
  87. package/dist/src/format.js +1 -1
  88. package/dist/src/gesture-plan.js +1 -1
  89. package/dist/src/gesture-runtime.js +1 -1
  90. package/dist/src/harmonyos.js +1 -1
  91. package/dist/src/helper.js +1 -1
  92. package/dist/src/human-control-contract.js +1 -1
  93. package/dist/src/human-control.js +1 -1
  94. package/dist/src/index.js +1 -1
  95. package/dist/src/input-actions.js +1 -1
  96. package/dist/src/input-actions2.js +1 -1
  97. package/dist/src/install-artifact-facade.js +1 -1
  98. package/dist/src/install-source-network-transport.js +1 -1
  99. package/dist/src/install-source2.js +1 -1
  100. package/dist/src/interaction-positionals.js +1 -1
  101. package/dist/src/interaction-ref-policy.js +1 -1
  102. package/dist/src/interaction2.js +1 -1
  103. package/dist/src/interactor-operation-binding.js +1 -1
  104. package/dist/src/interactor.js +1 -1
  105. package/dist/src/interactor2.js +1 -1
  106. package/dist/src/interactors.js +1 -1
  107. package/dist/src/internal/bin.js +1 -1
  108. package/dist/src/internal/daemon.js +2 -2
  109. package/dist/src/internal/png-worker.js +1 -1
  110. package/dist/src/internal/run-script-http-child.js +1 -1
  111. package/dist/src/inventory.js +1 -1
  112. package/dist/src/inventory2.js +1 -1
  113. package/dist/src/inventory3.js +1 -1
  114. package/dist/src/inventory5.js +1 -1
  115. package/dist/src/ios-snapshot-runtime.js +1 -1
  116. package/dist/src/lease.js +1 -1
  117. package/dist/src/limrun-download-file.js +1 -1
  118. package/dist/src/limrun-runtime-dependencies.js +1 -1
  119. package/dist/src/linux.js +1 -1
  120. package/dist/src/location-coordinates.js +1 -1
  121. package/dist/src/managed-device-scope.js +1 -1
  122. package/dist/src/mechanics.js +5 -5
  123. package/dist/src/multitouch-support.js +1 -1
  124. package/dist/src/native-build-cache.js +1 -0
  125. package/dist/src/observation.js +1 -1
  126. package/dist/src/parameterized-recorded-fill.js +1 -1
  127. package/dist/src/perf-facade.js +1 -1
  128. package/dist/src/perf-runtime-operation-builder.js +1 -1
  129. package/dist/src/perf-runtime-plan.js +1 -1
  130. package/dist/src/perf-target.js +2 -2
  131. package/dist/src/perf.js +1 -1
  132. package/dist/src/physical-device-control.js +1 -1
  133. package/dist/src/platform-runtime-host.js +1 -1
  134. package/dist/src/platform-runtime-managed-owner.js +1 -1
  135. package/dist/src/platform-runtime-open-target.js +1 -1
  136. package/dist/src/platform-runtime-operation-host.js +2 -2
  137. package/dist/src/platform-runtime-runtime-hints.js +1 -1
  138. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  139. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  140. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -1
  141. package/dist/src/platform-runtime.js +1 -1
  142. package/dist/src/platform-runtime2.js +1 -1
  143. package/dist/src/png-crop-bytes.js +1 -1
  144. package/dist/src/png-size.js +1 -1
  145. package/dist/src/png-worker-client.js +1 -1
  146. package/dist/src/png.js +1 -1
  147. package/dist/src/prepare-kind.js +1 -1
  148. package/dist/src/provider-session-ownership.js +1 -1
  149. package/dist/src/provider-webdriver.js +1 -1
  150. package/dist/src/proxy.js +1 -1
  151. package/dist/src/quality-warnings.js +1 -1
  152. package/dist/src/react-native.js +1 -1
  153. package/dist/src/record-runtime.js +1 -1
  154. package/dist/src/recording.js +1 -1
  155. package/dist/src/register-builtins.js +1 -1
  156. package/dist/src/registry.js +13 -13
  157. package/dist/src/remote-config2.js +1 -1
  158. package/dist/src/replay-divergence.js +1 -1
  159. package/dist/src/replay.js +1 -1
  160. package/dist/src/reporting.js +1 -1
  161. package/dist/src/request-admission.js +1 -1
  162. package/dist/src/request-cancel.js +1 -1
  163. package/dist/src/request-runtime-binding.js +1 -1
  164. package/dist/src/retry.js +1 -1
  165. package/dist/src/runner-cache-metadata.js +1 -1
  166. package/dist/src/runner-client.js +1 -1
  167. package/dist/src/runner-dev-tools-security.js +1 -1
  168. package/dist/src/runner-device-readiness.js +1 -1
  169. package/dist/src/runner-disposal.js +1 -1
  170. package/dist/src/runner-error-classification.js +2 -2
  171. package/dist/src/runner.js +1 -1
  172. package/dist/src/runtime-transport-hints.js +1 -1
  173. package/dist/src/runtime.js +3 -3
  174. package/dist/src/runtime10.js +2 -2
  175. package/dist/src/runtime2.js +1 -1
  176. package/dist/src/runtime3.js +1 -1
  177. package/dist/src/runtime4.js +5 -5
  178. package/dist/src/runtime5.js +1 -1
  179. package/dist/src/runtime6.js +1 -1
  180. package/dist/src/runtime7.js +1 -1
  181. package/dist/src/runtime8.js +1 -1
  182. package/dist/src/runtime9.js +1 -1
  183. package/dist/src/screen-recording-admission-ledger.js +1 -1
  184. package/dist/src/screen-recording-completion.js +1 -1
  185. package/dist/src/screen-recording-options.js +1 -1
  186. package/dist/src/screen-recording-resource-recovery.js +1 -1
  187. package/dist/src/screenshot-density.js +1 -1
  188. package/dist/src/screenshot-image.js +1 -1
  189. package/dist/src/screenshot-runtime.js +1 -1
  190. package/dist/src/screenshot.js +1 -1
  191. package/dist/src/screenshot3.js +1 -1
  192. package/dist/src/script.js +1 -1
  193. package/dist/src/scroll-command.js +1 -1
  194. package/dist/src/scroll-gesture.js +1 -1
  195. package/dist/src/sdk-batch-runner.js +2 -2
  196. package/dist/src/sdk-batch.js +2 -2
  197. package/dist/src/sdk-contracts.d.ts +21 -2
  198. package/dist/src/sdk-contracts.js +1 -1
  199. package/dist/src/sdk-device.js +1 -1
  200. package/dist/src/sdk-io.js +1 -1
  201. package/dist/src/sdk-remote-config.js +1 -1
  202. package/dist/src/selector-runtime.js +1 -1
  203. package/dist/src/server.js +1 -1
  204. package/dist/src/session-allocation.js +1 -1
  205. package/dist/src/session-routing.js +1 -1
  206. package/dist/src/session-runtime-admission.js +1 -1
  207. package/dist/src/session-snapshot.js +1 -1
  208. package/dist/src/session-store.js +2 -2
  209. package/dist/src/session2.js +3 -3
  210. package/dist/src/settings.js +1 -1
  211. package/dist/src/simctl.js +1 -1
  212. package/dist/src/simulator-state.js +1 -1
  213. package/dist/src/simulator.js +1 -1
  214. package/dist/src/snapshot-runtime-binding.js +1 -1
  215. package/dist/src/snapshot-runtime.js +1 -1
  216. package/dist/src/snapshot-state.js +1 -1
  217. package/dist/src/snapshot2.js +1 -1
  218. package/dist/src/snapshot3.js +1 -1
  219. package/dist/src/snapshot4.js +1 -1
  220. package/dist/src/src.js +1 -1
  221. package/dist/src/src7.js +1 -1
  222. package/dist/src/src8.js +2 -2
  223. package/dist/src/string-enum.js +1 -1
  224. package/dist/src/takeover.js +1 -1
  225. package/dist/src/text-entry-focus.js +1 -1
  226. package/dist/src/tool-provider-facade.js +1 -1
  227. package/dist/src/tool-provider.js +2 -2
  228. package/dist/src/tool-provider2.js +1 -1
  229. package/dist/src/touch-runtime.js +1 -1
  230. package/dist/src/transport.js +1 -1
  231. package/dist/src/tv-remote.js +1 -1
  232. package/dist/src/type-text-runtime.js +1 -1
  233. package/dist/src/verified-file.js +1 -1
  234. package/dist/src/video.js +1 -1
  235. package/dist/src/viewport.js +1 -1
  236. package/dist/src/web.js +1 -1
  237. package/dist/src/web2.js +1 -1
  238. package/dist/src/webdriver-source.js +1 -1
  239. package/package.json +1 -1
  240. package/server.json +2 -2
  241. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.13.apk.sha256 +0 -1
  242. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.13.apk +0 -0
  243. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.13.apk.sha256 +0 -1
  244. package/dist/src/snapshot-process.js +0 -1
@@ -0,0 +1 @@
1
+ 8a21cd8be9cc7792c51cda50949e2e64c455b9b37e680079182d3d09fabba6d4 agent-device-android-ime-helper-0.21.14.apk
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "android-ime-helper",
3
- "version": "0.21.13",
4
- "assetName": "agent-device-android-ime-helper-0.21.13.apk",
5
- "sha256": "8388dc7a3c3f18aca7322cc50bc4f3ff769d426733746a764f45b56d50a0f267",
3
+ "version": "0.21.14",
4
+ "assetName": "agent-device-android-ime-helper-0.21.14.apk",
5
+ "sha256": "8a21cd8be9cc7792c51cda50949e2e64c455b9b37e680079182d3d09fabba6d4",
6
6
  "packageName": "com.callstack.agentdevice.imehelper",
7
- "versionCode": 21013,
7
+ "versionCode": 21014,
8
8
  "serviceComponent": "com.callstack.agentdevice.imehelper/.TestInputMethodService",
9
9
  "broadcastProtocol": "android-ime-helper-v1"
10
10
  }
@@ -0,0 +1 @@
1
+ 2309cff0de9a4f9229d3818e328ab90e192c73f414373415c4882d4de20d6723 agent-device-android-snapshot-helper-0.21.14.apk
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "android-snapshot-helper",
3
- "version": "0.21.13",
4
- "releaseTag": "v0.21.13",
5
- "assetName": "agent-device-android-snapshot-helper-0.21.13.apk",
3
+ "version": "0.21.14",
4
+ "releaseTag": "v0.21.14",
5
+ "assetName": "agent-device-android-snapshot-helper-0.21.14.apk",
6
6
  "apkUrl": null,
7
- "sha256": "e07b556384093a04be99af068b6493e0b0a66e7e7cf09264da4fe60f75f2d164",
8
- "checksumName": "agent-device-android-snapshot-helper-0.21.13.apk.sha256",
7
+ "sha256": "2309cff0de9a4f9229d3818e328ab90e192c73f414373415c4882d4de20d6723",
8
+ "checksumName": "agent-device-android-snapshot-helper-0.21.14.apk.sha256",
9
9
  "packageName": "com.callstack.agentdevice.snapshothelper",
10
- "versionCode": 21013,
10
+ "versionCode": 21014,
11
11
  "instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
12
12
  "minSdk": 23,
13
13
  "targetSdk": 36,
@@ -295,6 +295,24 @@ static NSTimeInterval AgentDeviceAlertActivationBusyWindow(void) {
295
295
  ]];
296
296
  }
297
297
 
298
+ if ([NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-crowded-screen"]) {
299
+ for (NSUInteger row = 0; row < 500; row++) {
300
+ UILabel *rowLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 60 + (row % 30) * 24, 400, 16)];
301
+ rowLabel.text = [NSString stringWithFormat:@"Crowded row %lu", (unsigned long)row];
302
+ rowLabel.accessibilityIdentifier = [NSString stringWithFormat:@"agent-device-crowded-row-%lu", (unsigned long)row];
303
+ [self.view addSubview:rowLabel];
304
+ }
305
+ }
306
+
307
+ BOOL markedButton = [NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-dismiss-popup"];
308
+ if (markedButton || [NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-dismiss-popup-window"]) {
309
+ UIButton *dismissRegion = [UIButton buttonWithType:UIButtonTypeSystem];
310
+ dismissRegion.accessibilityIdentifier = markedButton ? @" Dismiss Popup " : @"agent-device-close-popover";
311
+ [dismissRegion setTitle:@"Close popover" forState:UIControlStateNormal];
312
+ dismissRegion.frame = CGRectMake(40, 40, 200, 44);
313
+ [self.view addSubview:dismissRegion];
314
+ }
315
+
298
316
  if ([NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-selector-read-regression"]) {
299
317
  NSString *const duplicateIdentifier = @"agent-device-selector-read-duplicate";
300
318
 
@@ -338,6 +356,11 @@ static NSTimeInterval AgentDeviceAlertActivationBusyWindow(void) {
338
356
 
339
357
  self.window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];
340
358
  self.window.rootViewController = [[AgentDeviceRunnerViewController alloc] init];
359
+ #if TARGET_OS_IOS
360
+ if ([NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-dismiss-popup-window"]) {
361
+ self.window.accessibilityIdentifier = @"Dismiss popup";
362
+ }
363
+ #endif
341
364
  [self.window makeKeyAndVisible];
342
365
  }
343
366
 
@@ -217,12 +217,11 @@ extension RunnerTests {
217
217
  }
218
218
 
219
219
  let rootFrame = privateAXRect(root["frame"])
220
- let geometry = privateAXSnapshotGeometry(
220
+ let viewport = privateAXSnapshotViewport(
221
221
  app: app,
222
222
  bundleId: target.bundleId,
223
223
  rootFrame: rootFrame
224
224
  )
225
- let viewport = geometry.viewport
226
225
  let nodes = privateAXAcquisition(
227
226
  rawRoot: root,
228
227
  hint: hint
@@ -259,8 +258,7 @@ extension RunnerTests {
259
258
  customActions: Self.privateAXCustomActionCoverage(
260
259
  response[RunnerAXSnapshotCustomActionsKey]
261
260
  ),
262
- viewport: viewport,
263
- interfaceOrientation: geometry.interfaceOrientation
261
+ viewport: viewport
264
262
  )
265
263
  #else
266
264
  return nil
@@ -277,35 +275,30 @@ extension RunnerTests {
277
275
 
278
276
 
279
277
 
280
-
281
-
282
- private func privateAXSnapshotGeometry(
278
+ private func privateAXSnapshotViewport(
283
279
  app: XCUIApplication,
284
280
  bundleId: String?,
285
281
  rootFrame: CGRect
286
- ) -> (viewport: CGRect, interfaceOrientation: Int) {
287
- let fallback = rootFrame.isEmpty ? CGRect.infinite : rootFrame
282
+ ) -> SnapshotViewport {
283
+ let fallback = SnapshotViewport.derived(box: rootFrame)
288
284
  guard shouldReadPrivateAXViewportViaXCTest(bundleId: bundleId) else {
289
- return (fallback, RunnerInterfaceOrientation.unknown)
285
+ return fallback
290
286
  }
291
287
  do {
292
- let anchor = try runMainThreadWork(
288
+ let reported = try runMainThreadWork(
293
289
  "private_ax_viewport",
294
290
  timeout: 1,
295
291
  timeoutError: snapshotMainThreadTimeoutError("reading private AX viewport")
296
292
  ) {
297
- (
298
- viewport: self.safeSnapshotViewport(app: app),
299
- interfaceOrientation: self.capturedInterfaceOrientation(app: app)
300
- )
293
+ self.safeSnapshotViewport(app: app, readingOrientation: true)
301
294
  }
302
- if anchor.viewport.isInfinite || anchor.viewport.isNull || anchor.viewport.isEmpty {
303
- return (fallback, RunnerInterfaceOrientation.unknown)
295
+ if case .missing = reported {
296
+ return fallback
304
297
  }
305
- return (anchor.viewport, anchor.interfaceOrientation)
298
+ return reported
306
299
  } catch {
307
300
  NSLog("AGENT_DEVICE_RUNNER_PRIVATE_AX_VIEWPORT_FALLBACK=%@", String(describing: error))
308
- return (fallback, RunnerInterfaceOrientation.unknown)
301
+ return fallback
309
302
  }
310
303
  }
311
304
 
@@ -161,22 +161,26 @@ extension RunnerTests {
161
161
  elements.first { isVisibleElement($0) }
162
162
  }
163
163
 
164
+
165
+
166
+
167
+
164
168
  private func firstDismissPopupWindow(in app: XCUIApplication) -> XCUIElement? {
165
- safeElementsQuery {
166
- app.windows.allElementsBoundByIndex
167
- }.first { window in
168
- if !isVisibleElement(window) { return false }
169
- if isDismissPopupMarker(window.label) || isDismissPopupMarker(window.identifier) {
170
- return true
171
- }
172
- return safeElementsQuery {
173
- window.descendants(matching: .any).allElementsBoundByIndex
174
- }.contains { descendant in
175
- isDismissPopupMarker(descendant.label) || isDismissPopupMarker(descendant.identifier)
176
- }
177
- }
169
+ firstExistingElement(in: safeElementsQuery {
170
+ app.windows.containing(Self.dismissPopupMarker).allElementsBoundByIndex
171
+ })
178
172
  }
179
173
 
174
+
175
+
176
+ private static let dismissPopupMarkerPattern = #"\s*dismiss popup\s*"#
177
+ private static let dismissPopupMarker = NSPredicate(
178
+ format: "label MATCHES[c] %@ OR identifier MATCHES[c] %@",
179
+ dismissPopupMarkerPattern,
180
+ dismissPopupMarkerPattern
181
+ )
182
+ private static let dismissPopupMarkerText = NSPredicate(format: "SELF MATCHES[c] %@", dismissPopupMarkerPattern)
183
+
180
184
  private func chooseAlertButton(_ buttons: [XCUIElement], action: String) -> XCUIElement? {
181
185
  if action == "accept" {
182
186
  if let accept = buttons.first(where: { isAcceptButton($0.label) }) {
@@ -285,7 +289,7 @@ extension RunnerTests {
285
289
  return hittable
286
290
  }
287
291
 
288
- private func isDismissPopupMarker(_ label: String) -> Bool {
289
- label.trimmingCharacters(in: .whitespacesAndNewlines).caseInsensitiveCompare("dismiss popup") == .orderedSame
292
+ func isDismissPopupMarker(_ text: String) -> Bool {
293
+ Self.dismissPopupMarkerText.evaluate(with: text)
290
294
  }
291
295
  }
@@ -68,9 +68,8 @@ extension RunnerTests {
68
68
  }
69
69
 
70
70
  private func containsDismissPopupMarker(_ snapshot: XCUIElementSnapshot) -> Bool {
71
- [snapshot.label, snapshot.identifier].contains {
72
- $0.trimmingCharacters(in: .whitespacesAndNewlines).caseInsensitiveCompare("dismiss popup") == .orderedSame
73
- } || snapshot.children.contains { containsDismissPopupMarker($0) }
71
+ [snapshot.label, snapshot.identifier].contains { isDismissPopupMarker($0) } ||
72
+ snapshot.children.contains { containsDismissPopupMarker($0) }
74
73
  }
75
74
 
76
75
  private func alertPresentation(_ snapshot: XCUIElementSnapshot) -> RunnerAlertPresentation {
@@ -78,20 +78,15 @@ extension RunnerTests {
78
78
  app: activeApp,
79
79
  policy: synthesizedGesturePolicy(policyKind)
80
80
  )
81
- let (timing, outcome) = performGesture(activeApp, idleTimeout: false) {
81
+ switch performSynthesizedGesture(activeApp, kind: policyKind, context: context, synthesize: {
82
82
  synthesizedTapAt(
83
83
  app: activeApp,
84
84
  x: touchPoint.x,
85
85
  y: touchPoint.y,
86
86
  context: context
87
87
  )
88
- }
89
- if case .performed = outcome {
90
- logSynthesizedGesturePolicyDecision(
91
- kind: policyKind,
92
- context: context,
93
- fallbackAttempted: false
94
- )
88
+ }) {
89
+ case .performed(let timing):
95
90
  if isTextEntry {
96
91
  waitForTextEntryReadinessAfterTap(app: activeApp, element: element)
97
92
  }
@@ -107,13 +102,12 @@ extension RunnerTests {
107
102
  ? match.usedNonHittableFallback
108
103
  : nil
109
104
  )
105
+ case .xctestFallback(let message, let hint):
106
+ fallback = GestureFallback(strategy: "xctest-coordinate-tap", message: message, hint: hint)
107
+ case .refused(_, let message, let hint):
108
+ clearRememberedTextEntryTap()
109
+ return unsupportedResponse(message: message, hint: hint)
110
110
  }
111
- logSynthesizedGesturePolicyDecision(
112
- kind: policyKind,
113
- context: context,
114
- fallbackAttempted: true
115
- )
116
- fallback = gestureFallback(strategy: "xctest-coordinate-tap", from: outcome)
117
111
  }
118
112
  let (timing, outcome) = performGesture(activeApp) {
119
113
  if expectedPoint != nil || match.usedNonHittableFallback {
@@ -172,16 +166,18 @@ extension RunnerTests {
172
166
  app: activeApp,
173
167
  policy: synthesizedGesturePolicy(policyKind)
174
168
  )
175
- let (timing, outcome) = performGesture(activeApp, idleTimeout: false) {
169
+ switch performSynthesizedGesture(activeApp, kind: policyKind, context: context, synthesize: {
176
170
  synthesizedTapAt(app: activeApp, x: x, y: y, context: context)
177
- }
178
- if case .performed = outcome {
179
- logSynthesizedGesturePolicyDecision(kind: policyKind, context: context, fallbackAttempted: false)
171
+ }) {
172
+ case .performed(let timing):
180
173
  rememberTextEntryTap(textInput)
181
174
  return gestureResponse(message: "tapped", timing: timing)
175
+ case .xctestFallback(let message, let hint):
176
+ fallback = GestureFallback(strategy: "xctest-coordinate-tap", message: message, hint: hint)
177
+ case .refused(_, let message, let hint):
178
+ clearRememberedTextEntryTap()
179
+ return unsupportedResponse(message: message, hint: hint)
182
180
  }
183
- logSynthesizedGesturePolicyDecision(kind: policyKind, context: context, fallbackAttempted: true)
184
- fallback = gestureFallback(strategy: "xctest-coordinate-tap", from: outcome)
185
181
  }
186
182
  let touchFrame = resolvedTouchVisualizationFrame(app: activeApp, x: x, y: y)
187
183
  let (timing, outcome) = performGesture(activeApp) { tapAt(app: activeApp, x: x, y: y) }
@@ -25,13 +25,17 @@ extension RunnerTests {
25
25
  case .performed:
26
26
  return nil
27
27
  case .unsupported(let message, let hint):
28
- return Response(
29
- ok: false,
30
- error: ErrorPayload(code: "UNSUPPORTED_OPERATION", message: message, hint: hint)
31
- )
28
+ return unsupportedResponse(message: message, hint: hint)
32
29
  }
33
30
  }
34
31
 
32
+ func unsupportedResponse(message: String, hint: String?) -> Response {
33
+ Response(
34
+ ok: false,
35
+ error: ErrorPayload(code: "UNSUPPORTED_OPERATION", message: message, hint: hint)
36
+ )
37
+ }
38
+
35
39
 
36
40
  enum GestureFrame {
37
41
  case none
@@ -45,15 +49,6 @@ extension RunnerTests {
45
49
  let hint: String?
46
50
  }
47
51
 
48
- func gestureFallback(strategy: String, from outcome: RunnerInteractionOutcome) -> GestureFallback? {
49
- switch outcome {
50
- case .performed:
51
- return nil
52
- case .unsupported(let message, let hint):
53
- return GestureFallback(strategy: strategy, message: message, hint: hint)
54
- }
55
- }
56
-
57
52
 
58
53
 
59
54
 
@@ -1,3 +1,4 @@
1
+ import AgentDeviceSnapshotPresentation
1
2
  import XCTest
2
3
 
3
4
  extension RunnerTests {
@@ -120,16 +121,9 @@ extension RunnerTests {
120
121
  return navigationBackKeywords.firstIndex { text.contains($0) }
121
122
  }
122
123
 
123
-
124
- static func isUsableNavigationFrame(_ frame: CGRect) -> Bool {
125
- guard frame.width.isFinite, frame.height.isFinite, frame.width > 0, frame.height > 0 else {
126
- return false
127
- }
128
- return !frame.isInfinite
129
- }
130
-
131
124
  static func isTopNavigationControlFrame(_ candidate: CGRect, in window: CGRect) -> Bool {
132
- guard isUsableNavigationFrame(candidate), isUsableNavigationFrame(window) else {
125
+ guard SnapshotGeometry.isPositiveFinite(candidate), SnapshotGeometry.isPositiveFinite(window)
126
+ else {
133
127
  return false
134
128
  }
135
129
 
@@ -138,7 +132,7 @@ extension RunnerTests {
138
132
  }
139
133
 
140
134
  static func topLeadingNavigationFallbackPoint(in frame: CGRect) -> CGPoint? {
141
- guard isUsableNavigationFrame(frame) else {
135
+ guard SnapshotGeometry.isPositiveFinite(frame) else {
142
136
  return nil
143
137
  }
144
138
 
@@ -159,17 +153,20 @@ extension RunnerTests {
159
153
  app: app,
160
154
  policy: synthesizedGesturePolicy(.coordinateTap)
161
155
  )?.withReferenceFrame(frame)
162
- let synthesized = performGesture(app, idleTimeout: false) {
156
+ switch performSynthesizedGesture(app, kind: .coordinateTap, context: context, synthesize: {
163
157
  synthesizedTapAt(app: app, x: point.x, y: point.y, context: context)
164
- }
165
- if case .performed = synthesized.outcome {
166
- return verifyNavigationFallbackOutcome(app: app, before: before)
167
- }
168
- let fallback = performGesture(app) {
169
- tapAt(app: app, x: point.x, y: point.y)
170
- }
171
- if case .performed = fallback.outcome {
158
+ }) {
159
+ case .performed:
172
160
  return verifyNavigationFallbackOutcome(app: app, before: before)
161
+ case .refused:
162
+ return .unavailable
163
+ case .xctestFallback:
164
+ let fallback = performGesture(app) {
165
+ tapAt(app: app, x: point.x, y: point.y)
166
+ }
167
+ if case .performed = fallback.outcome {
168
+ return verifyNavigationFallbackOutcome(app: app, before: before)
169
+ }
173
170
  }
174
171
  #endif
175
172
  return .unavailable
@@ -149,19 +149,6 @@ extension RunnerTests {
149
149
  context: context
150
150
  )
151
151
  else {
152
- if context?.allowsXCTestCoordinateFallback == true {
153
- logSynthesizedGesturePolicyDecision(kind: policyKind, context: context, fallbackAttempted: true)
154
- return executeCoordinateDragFallback(
155
- activeApp: activeApp,
156
- x: x,
157
- y: y,
158
- x2: x2,
159
- y2: y2,
160
- durationMs: durationMs,
161
- message: message,
162
- fallback: nil
163
- )
164
- }
165
152
  logSynthesizedGesturePolicyDecision(kind: policyKind, context: context, fallbackAttempted: false)
166
153
  return Response(
167
154
  ok: false,
@@ -179,7 +166,7 @@ extension RunnerTests {
179
166
  y2: plan.points.y2,
180
167
  referenceFrame: plan.referenceFrame
181
168
  )
182
- let (timing, outcome) = performGesture(activeApp, idleTimeout: false) {
169
+ switch performSynthesizedGesture(activeApp, kind: policyKind, context: plan.context, synthesize: {
183
170
  synthesizedDragAt(
184
171
  app: activeApp,
185
172
  x: plan.points.x,
@@ -190,13 +177,10 @@ extension RunnerTests {
190
177
  profile: profile,
191
178
  context: plan.context
192
179
  )
193
- }
194
- if case .performed = outcome {
195
- logSynthesizedGesturePolicyDecision(kind: policyKind, context: plan.context, fallbackAttempted: false)
180
+ }) {
181
+ case .performed(let timing):
196
182
  return gestureResponse(message: message, timing: timing, frame: .drag(dragFrame))
197
- }
198
- if plan.context.allowsXCTestCoordinateFallback {
199
- logSynthesizedGesturePolicyDecision(kind: policyKind, context: plan.context, fallbackAttempted: true)
183
+ case .xctestFallback(let fallbackMessage, let hint):
200
184
  return executeCoordinateDragFallback(
201
185
  activeApp: activeApp,
202
186
  x: plan.points.x,
@@ -205,11 +189,11 @@ extension RunnerTests {
205
189
  y2: plan.points.y2,
206
190
  durationMs: durationMs,
207
191
  message: message,
208
- fallback: gestureFallback(strategy: "xctest-coordinate-drag", from: outcome)
192
+ fallback: GestureFallback(strategy: "xctest-coordinate-drag", message: fallbackMessage, hint: hint)
209
193
  )
194
+ case .refused(_, let refusalMessage, let hint):
195
+ return unsupportedResponse(message: refusalMessage, hint: hint)
210
196
  }
211
- logSynthesizedGesturePolicyDecision(kind: policyKind, context: plan.context, fallbackAttempted: false)
212
- return unsupportedResponse(for: outcome)
213
197
  #else
214
198
  return nil
215
199
  #endif
@@ -223,7 +207,7 @@ extension RunnerTests {
223
207
  y2: Double,
224
208
  durationMs: Double,
225
209
  message: String,
226
- fallback: GestureFallback?
210
+ fallback: GestureFallback
227
211
  ) -> Response {
228
212
  let dragPoints = keyboardAvoidingDragPoints(app: activeApp, x: x, y: y, x2: x2, y2: y2)
229
213
  let dragFrame = resolvedDragVisualizationFrame(
@@ -143,51 +143,21 @@ extension RunnerTests {
143
143
  ) -> SequenceStepOutcome {
144
144
  let x = step.x ?? 0
145
145
  let y = step.y ?? 0
146
-
147
-
148
-
149
- if step.kind == "tap", step.synthesized == true {
150
- let policyKind = SynthesizedGesturePolicyKind.synthesizedDrag
151
- #if os(iOS)
152
- guard let synthesizedContext else {
153
- let nowMs = ProcessInfo.processInfo.systemUptime * 1000
154
- logSynthesizedGesturePolicyDecision(kind: policyKind, context: nil, fallbackAttempted: false)
155
- return SequenceStepOutcome(
156
- outcome: .unsupported(
157
- message: "synthesized coordinate tap could not resolve a finite coordinate frame",
158
- hint: "Retry after the app is foregrounded, or use a plain screenshot to choose coordinates."
159
- ),
160
- gestureStartUptimeMs: nowMs,
161
- gestureEndUptimeMs: nowMs
162
- )
163
- }
164
- #endif
165
- let (timing, outcome) = performGesture(activeApp, idleTimeout: false) {
146
+ if let policyKind = synthesizedPolicyKind(forSequenceStep: step) {
147
+ switch performSynthesizedGesture(activeApp, kind: policyKind, context: synthesizedContext, synthesize: {
166
148
  synthesizedTapAt(app: activeApp, x: x, y: y, context: synthesizedContext)
167
- }
168
- if case .performed = outcome {
169
- logSynthesizedGesturePolicyDecision(kind: policyKind, context: synthesizedContext, fallbackAttempted: false)
170
- if let pauseMs = step.pauseMs, pauseMs > 0 {
171
- sleepFor(min(max(pauseMs, 0), 10000) / 1000.0)
172
- }
173
- return SequenceStepOutcome(
174
- outcome: outcome,
175
- gestureStartUptimeMs: timing.gestureStartUptimeMs,
176
- gestureEndUptimeMs: timing.gestureEndUptimeMs
149
+ }) {
150
+ case .performed(let timing):
151
+ return finishedSequenceStep(step, timing: timing, outcome: .performed)
152
+ case .refused(let timing, let message, let hint):
153
+ return finishedSequenceStep(
154
+ step,
155
+ timing: timing,
156
+ outcome: .unsupported(message: message, hint: hint)
177
157
  )
158
+ case .xctestFallback:
159
+ break
178
160
  }
179
- #if os(iOS)
180
- guard synthesizedContext.allowsXCTestCoordinateFallback else {
181
- logSynthesizedGesturePolicyDecision(kind: policyKind, context: synthesizedContext, fallbackAttempted: false)
182
- return SequenceStepOutcome(
183
- outcome: outcome,
184
- gestureStartUptimeMs: timing.gestureStartUptimeMs,
185
- gestureEndUptimeMs: timing.gestureEndUptimeMs
186
- )
187
- }
188
- logSynthesizedGesturePolicyDecision(kind: policyKind, context: synthesizedContext, fallbackAttempted: true)
189
- #endif
190
-
191
161
  }
192
162
  let (timing, outcome) = performGesture(activeApp) {
193
163
  switch step.kind {
@@ -201,6 +171,14 @@ extension RunnerTests {
201
171
  return tapAt(app: activeApp, x: x, y: y)
202
172
  }
203
173
  }
174
+ return finishedSequenceStep(step, timing: timing, outcome: outcome)
175
+ }
176
+
177
+ private func finishedSequenceStep(
178
+ _ step: SequenceStep,
179
+ timing: (gestureStartUptimeMs: Double, gestureEndUptimeMs: Double),
180
+ outcome: RunnerInteractionOutcome
181
+ ) -> SequenceStepOutcome {
204
182
 
205
183
 
206
184
 
@@ -21,9 +21,8 @@ extension RunnerTests {
21
21
  struct SnapshotTraversalContext {
22
22
  let queryRoot: XCUIElement
23
23
  let rootSnapshot: XCUIElementSnapshot
24
- let viewport: CGRect
25
24
 
26
- let interfaceOrientation: Int
25
+ let viewport: SnapshotViewport
27
26
 
28
27
 
29
28
 
@@ -278,8 +277,7 @@ extension RunnerTests {
278
277
  nodes: nodes,
279
278
  truncated: false,
280
279
  effectiveDepth: nil,
281
- viewport: context.viewport,
282
- interfaceOrientation: context.interfaceOrientation
280
+ viewport: context.viewport
283
281
  )
284
282
  }
285
283
 
@@ -435,8 +433,7 @@ extension RunnerTests {
435
433
  nodes: nodes,
436
434
  truncated: false,
437
435
  effectiveDepth: nil,
438
- viewport: context.viewport,
439
- interfaceOrientation: context.interfaceOrientation
436
+ viewport: context.viewport
440
437
  )
441
438
  }
442
439
 
@@ -455,14 +452,13 @@ extension RunnerTests {
455
452
  nodes: nodes,
456
453
  truncated: false,
457
454
  effectiveDepth: nil,
458
- viewport: .infinite,
459
- interfaceOrientation: RunnerInterfaceOrientation.unknown
455
+ viewport: .missing(reason: .notProvided)
460
456
  ),
461
457
  .completed
462
458
  )
463
459
  }
464
460
 
465
- let viewport = safeSnapshotViewport(app: app)
461
+ let viewport = safeSnapshotViewport(app: app, readingOrientation: false)
466
462
  var seen = Set<String>()
467
463
  var candidates: [RawAXNode] = []
468
464
  let flatElements = flatInteractiveElements(app: app, deadline: deadline)
@@ -494,9 +490,7 @@ extension RunnerTests {
494
490
 
495
491
 
496
492
 
497
- let rootRect = viewport.isInfinite || viewport.isNull || viewport.isEmpty
498
- ? interactiveRootFrame(for: candidates)
499
- : viewport
493
+ let rootRect = viewport.rect ?? interactiveRootFrame(for: candidates)
500
494
  nodes[0] = interactiveRootNode(rect: rootRect)
501
495
  for candidate in candidates {
502
496
  nodes.append(
@@ -524,8 +518,7 @@ extension RunnerTests {
524
518
  nodes: nodes,
525
519
  truncated: outcome == .deadlineExhausted,
526
520
  effectiveDepth: nil,
527
- viewport: viewport,
528
- interfaceOrientation: RunnerInterfaceOrientation.unknown
521
+ viewport: viewport
529
522
  ),
530
523
  outcome
531
524
  )