agent-device 0.18.3 → 0.19.1

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 (219) hide show
  1. package/README.md +5 -1
  2. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.18.3.apk → agent-device-android-multitouch-helper-0.19.1.apk} +0 -0
  3. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.1.apk.sha256 +1 -0
  4. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.18.3.manifest.json → agent-device-android-multitouch-helper-0.19.1.manifest.json} +4 -4
  5. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.18.3.apk → agent-device-android-snapshot-helper-0.19.1.apk} +0 -0
  6. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.1.apk.sha256 +1 -0
  7. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.18.3.manifest.json → agent-device-android-snapshot-helper-0.19.1.manifest.json} +6 -6
  8. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +28 -0
  9. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +33 -127
  10. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +630 -177
  11. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +155 -0
  12. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +67 -0
  13. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +173 -122
  14. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +172 -0
  15. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +64 -0
  16. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +76 -182
  17. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +92 -51
  18. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +213 -137
  19. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +142 -0
  20. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +208 -46
  21. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +82 -11
  22. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +74 -0
  23. package/dist/src/agent-browser-tool.js +1 -0
  24. package/dist/src/android-adb.d.ts +63 -234
  25. package/dist/src/android-adb.js +1 -1
  26. package/dist/src/android-input-ownership.js +6 -0
  27. package/dist/src/android.js +1 -5
  28. package/dist/src/app-lifecycle.js +7 -0
  29. package/dist/src/apple-runner-options.js +1 -0
  30. package/dist/src/apps.js +5 -11
  31. package/dist/src/artifacts.d.ts +4 -3
  32. package/dist/src/artifacts.js +1 -1
  33. package/dist/src/audio-probe.js +1 -1
  34. package/dist/src/batch.d.ts +2 -177
  35. package/dist/src/batch.js +1 -1
  36. package/dist/src/cli-help.js +178 -60
  37. package/dist/src/cli-output.js +1 -1
  38. package/dist/src/cli-runner.js +2 -0
  39. package/dist/src/cli.js +12 -41
  40. package/dist/src/client-metro-companion.js +1 -0
  41. package/dist/src/client.js +3 -0
  42. package/dist/src/command-metadata.js +1 -0
  43. package/dist/src/command-schema.js +22 -0
  44. package/dist/src/command-surface.js +1 -1
  45. package/dist/src/contracts.d.ts +2 -213
  46. package/dist/src/contracts.js +1 -1
  47. package/dist/src/debug-symbols.js +5 -3
  48. package/dist/src/device-input-state.js +2 -0
  49. package/dist/src/devices.js +1 -1
  50. package/dist/src/devices2.js +1 -0
  51. package/dist/src/devices3.js +1 -0
  52. package/dist/src/find.js +1 -1
  53. package/dist/src/finders.d.ts +2 -81
  54. package/dist/src/finders.js +1 -1
  55. package/dist/src/generic.js +1 -1
  56. package/dist/src/handler-utils.js +1 -1
  57. package/dist/src/helper.js +2 -0
  58. package/dist/src/host-process.js +2 -0
  59. package/dist/src/index.d.ts +1444 -1786
  60. package/dist/src/index.js +1 -1
  61. package/dist/src/input-actions.js +3 -1
  62. package/dist/src/input-actions2.js +1 -0
  63. package/dist/src/install-source.d.ts +13 -14
  64. package/dist/src/install-source.js +1 -1
  65. package/dist/src/install-source2.js +1 -0
  66. package/dist/src/interaction-snapshot.js +1 -0
  67. package/dist/src/interaction.js +1 -1
  68. package/dist/src/interactor.js +2 -1
  69. package/dist/src/internal/bin.d.ts +1 -1
  70. package/dist/src/internal/bin.js +2 -5
  71. package/dist/src/internal/companion-tunnel.d.ts +1 -1
  72. package/dist/src/internal/companion-tunnel.js +1 -1
  73. package/dist/src/internal/daemon.d.ts +1 -1
  74. package/dist/src/internal/daemon.js +18 -1
  75. package/dist/src/internal/png-worker.d.ts +29 -26
  76. package/dist/src/internal/png-worker.js +1 -1
  77. package/dist/src/internal/update-check-entry.d.ts +1 -1
  78. package/dist/src/internal/update-check-entry.js +1 -1
  79. package/dist/src/io.d.ts +2 -85
  80. package/dist/src/io.js +1 -1
  81. package/dist/src/lease.js +1 -1
  82. package/dist/src/linux.js +1 -1
  83. package/dist/src/manifest.js +1 -0
  84. package/dist/src/metro.d.ts +69 -113
  85. package/dist/src/metro.js +1 -1
  86. package/dist/src/metro2.js +1 -0
  87. package/dist/src/notifications.js +1 -1
  88. package/dist/src/output-path.js +1 -0
  89. package/dist/src/path-resolution.js +1 -0
  90. package/dist/src/png-worker-contract.js +1 -0
  91. package/dist/src/points.js +4 -0
  92. package/dist/src/project-runtime.js +1 -0
  93. package/dist/src/provider.js +1 -1
  94. package/dist/src/providers.js +1 -0
  95. package/dist/src/react-native.js +1 -1
  96. package/dist/src/record-trace-recording.js +2 -5
  97. package/dist/src/record-trace.js +1 -1
  98. package/dist/src/recording-provider.js +1 -1
  99. package/dist/src/registry.js +38 -0
  100. package/dist/src/remote-config.d.ts +2 -83
  101. package/dist/src/reporting.js +9 -0
  102. package/dist/src/rolldown-runtime.js +1 -0
  103. package/dist/src/runner-client.js +1 -0
  104. package/dist/src/runner-provider.js +1 -0
  105. package/dist/src/runner-sequence.js +1 -1
  106. package/dist/src/runtime-transport.js +1 -0
  107. package/dist/src/runtime.js +3 -0
  108. package/dist/src/scoped-provider.js +1 -0
  109. package/dist/src/sdk-android-adb.d.ts +144 -0
  110. package/dist/src/sdk-android-adb.js +4 -0
  111. package/dist/src/sdk-app-inventory.d.ts +4 -0
  112. package/dist/src/sdk-app-inventory.js +1 -0
  113. package/dist/src/sdk-batch-runner.d.ts +39 -0
  114. package/dist/src/sdk-batch-runner.js +2 -0
  115. package/dist/src/sdk-batch.js +2 -0
  116. package/dist/src/sdk-contracts.d.ts +386 -0
  117. package/dist/src/sdk-contracts.js +2 -0
  118. package/dist/src/sdk-finders.d.ts +60 -0
  119. package/dist/src/sdk-finders.js +1 -0
  120. package/dist/src/sdk-io.d.ts +78 -0
  121. package/dist/src/sdk-io.js +1 -0
  122. package/dist/src/sdk-remote-config.d.ts +139 -0
  123. package/dist/src/sdk-remote-config.js +1 -0
  124. package/dist/src/sdk-selectors.js +1 -0
  125. package/dist/src/selector-runtime.js +1 -1
  126. package/dist/src/selectors.d.ts +38 -137
  127. package/dist/src/selectors.js +1 -1
  128. package/dist/src/selectors2.js +1 -0
  129. package/dist/src/server.js +3 -2
  130. package/dist/src/session.js +10 -11
  131. package/dist/src/setting-state.js +1 -0
  132. package/dist/src/simctl.js +1 -0
  133. package/dist/src/simulator.js +1 -2
  134. package/dist/src/snapshot-diagnostics.js +3 -0
  135. package/dist/src/snapshot-helper.js +5 -0
  136. package/dist/src/snapshot-quality.js +1 -0
  137. package/dist/src/snapshot.js +1 -2
  138. package/dist/src/snapshot2.js +2 -0
  139. package/dist/src/{5560.js → timeouts.js} +1 -1
  140. package/dist/src/tool-provider.js +3 -0
  141. package/dist/src/tool-provider2.js +1 -0
  142. package/dist/src/ttl-memo.js +1 -0
  143. package/dist/src/tv-remote.js +14 -0
  144. package/dist/src/update-check.js +1 -0
  145. package/dist/src/url.js +1 -0
  146. package/dist/src/version.js +1 -0
  147. package/dist/src/web.js +1 -1
  148. package/dist/src/xml.js +2 -0
  149. package/package.json +13 -19
  150. package/server.json +2 -2
  151. package/skills/agent-device/SKILL.md +8 -7
  152. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.18.3.apk.sha256 +0 -1
  153. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.18.3.apk.sha256 +0 -1
  154. package/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +0 -443
  155. package/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +0 -169
  156. package/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +0 -220
  157. package/dist/src/1404.js +0 -1
  158. package/dist/src/1986.js +0 -1
  159. package/dist/src/2748.js +0 -1
  160. package/dist/src/2948.js +0 -30
  161. package/dist/src/3006.js +0 -1
  162. package/dist/src/314.js +0 -1
  163. package/dist/src/3340.js +0 -8
  164. package/dist/src/4418.js +0 -1
  165. package/dist/src/4428.js +0 -3
  166. package/dist/src/485.js +0 -1
  167. package/dist/src/4917.js +0 -1
  168. package/dist/src/495.js +0 -5
  169. package/dist/src/5448.js +0 -3
  170. package/dist/src/5484.js +0 -1
  171. package/dist/src/5643.js +0 -1
  172. package/dist/src/587.js +0 -1
  173. package/dist/src/5913.js +0 -4
  174. package/dist/src/6849.js +0 -1
  175. package/dist/src/6918.js +0 -1
  176. package/dist/src/6967.js +0 -1
  177. package/dist/src/760.js +0 -1
  178. package/dist/src/7871.js +0 -1
  179. package/dist/src/8184.js +0 -1
  180. package/dist/src/8225.js +0 -1
  181. package/dist/src/8391.js +0 -1
  182. package/dist/src/8404.js +0 -2
  183. package/dist/src/8414.js +0 -2
  184. package/dist/src/8875.js +0 -4
  185. package/dist/src/893.js +0 -1
  186. package/dist/src/8936.js +0 -1
  187. package/dist/src/9414.js +0 -1
  188. package/dist/src/9581.js +0 -22
  189. package/dist/src/9612.js +0 -1
  190. package/dist/src/9624.js +0 -1
  191. package/dist/src/9722.js +0 -39
  192. package/dist/src/devices~1.js +0 -1
  193. package/dist/src/devices~2.js +0 -1
  194. package/dist/src/input-actions~1.js +0 -1
  195. package/dist/src/rslib-runtime.js +0 -1
  196. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +0 -0
  197. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +0 -0
  198. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
  199. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcshareddata/xcschemes/AgentDeviceRunner.xcscheme +0 -0
  200. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +0 -0
  201. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests.entitlements +0 -0
  202. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +0 -0
  203. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +0 -0
  204. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.h +0 -0
  205. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.m +0 -0
  206. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +0 -0
  207. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +0 -0
  208. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +0 -0
  209. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +0 -0
  210. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +0 -0
  211. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +0 -0
  212. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +0 -0
  213. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +0 -0
  214. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +0 -0
  215. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +0 -0
  216. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan +0 -0
  217. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +0 -0
  218. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/RecordingScripts/recording-resize.swift +0 -0
  219. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/RecordingScripts/recording-trim.swift +0 -0
package/README.md CHANGED
@@ -52,11 +52,15 @@ Install the CLI:
52
52
 
53
53
  ```bash
54
54
  npm install -g agent-device@latest
55
+ agent-device doctor
55
56
  agent-device --version
56
57
  agent-device help workflow
57
58
  ```
58
59
 
59
- The installed CLI help is the source of truth for agents. Start with `agent-device help workflow`, then follow the topic-specific help when a task needs dogfooding, debugging, replay, or React Native profiling.
60
+ Run `agent-device doctor` yourself after installation to check local setup before
61
+ handing the CLI to an agent. The installed CLI help is the source of truth for
62
+ agents. Start with `agent-device help workflow`, then follow the topic-specific
63
+ help when a task needs dogfooding, debugging, replay, or React Native profiling.
60
64
 
61
65
  Prerequisites depend on the target platform: Node.js 22+, Xcode for iOS/tvOS/macOS targets, Android SDK + ADB for Android, and macOS Accessibility permission for desktop automation. Web automation requires Node 24+. See [Installation](https://oss.callstack.com/agent-device/docs/installation) for platform setup.
62
66
 
@@ -0,0 +1 @@
1
+ 047938649fa5a1be4959384c3a6b64443cf4135567fb8f72cfb1107375b0eec7 agent-device-android-multitouch-helper-0.19.1.apk
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "android-multitouch-helper",
3
- "version": "0.18.3",
4
- "assetName": "agent-device-android-multitouch-helper-0.18.3.apk",
5
- "sha256": "ace47651ae6742d57cae188bf9fc23a24fccc4a19973a051f948f84dbd2ac8f7",
3
+ "version": "0.19.1",
4
+ "assetName": "agent-device-android-multitouch-helper-0.19.1.apk",
5
+ "sha256": "047938649fa5a1be4959384c3a6b64443cf4135567fb8f72cfb1107375b0eec7",
6
6
  "packageName": "com.callstack.agentdevice.multitouchhelper",
7
- "versionCode": 18003,
7
+ "versionCode": 19001,
8
8
  "instrumentationRunner": "com.callstack.agentdevice.multitouchhelper/.MultiTouchInstrumentation",
9
9
  "statusProtocol": "android-multitouch-helper-v1"
10
10
  }
@@ -0,0 +1 @@
1
+ e6e5119b4b2ef7d67b8e50acc745f0ffec78951be2094a9198cb491c5e078b22 agent-device-android-snapshot-helper-0.19.1.apk
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "android-snapshot-helper",
3
- "version": "0.18.3",
4
- "releaseTag": "v0.18.3",
5
- "assetName": "agent-device-android-snapshot-helper-0.18.3.apk",
3
+ "version": "0.19.1",
4
+ "releaseTag": "v0.19.1",
5
+ "assetName": "agent-device-android-snapshot-helper-0.19.1.apk",
6
6
  "apkUrl": null,
7
- "sha256": "89d37238a2ff1daeecff94ac48434047d2d9e9e591fb2d97b2901cb937c20ffa",
8
- "checksumName": "agent-device-android-snapshot-helper-0.18.3.apk.sha256",
7
+ "sha256": "e6e5119b4b2ef7d67b8e50acc745f0ffec78951be2094a9198cb491c5e078b22",
8
+ "checksumName": "agent-device-android-snapshot-helper-0.19.1.apk.sha256",
9
9
  "packageName": "com.callstack.agentdevice.snapshothelper",
10
- "versionCode": 18003,
10
+ "versionCode": 19001,
11
11
  "instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
12
12
  "minSdk": 23,
13
13
  "targetSdk": 36,
@@ -0,0 +1,28 @@
1
+ import XCTest
2
+
3
+ // The tap-point rule the runner shares with the TS runtime (ADR 0011 Layer 2).
4
+ //
5
+ // RULE: the tap point is the element frame's CENTER; a tap is allowed iff that
6
+ // center lies inside the window frame, edges inclusive. An empty or invalid
7
+ // window frame fails open (allowed): resolving the best available frame is the
8
+ // impure caller's job (onScreenWindowFrame / app.frame), and the policy must
9
+ // not turn a missing frame into a refusal.
10
+ //
11
+ // This is pure geometry on purpose — no XCUIElement — so the exact decision
12
+ // can be proven against the golden fixture table shared with the TS twin:
13
+ // table: contracts/fixtures/tap-point-policy.json
14
+ // TS twin: src/snapshot/mobile-snapshot-semantics.ts#isTapPointInsideViewport
15
+ // TS test: src/snapshot/__tests__/tap-point-policy-parity.test.ts
16
+ // Drift on either side turns CI red without needing a simulator.
17
+ enum TapPointPolicy {
18
+ static func isAllowed(elementFrame: CGRect, windowFrame: CGRect) -> Bool {
19
+ if windowFrame.isNull || windowFrame.isEmpty || windowFrame.isInfinite {
20
+ return true
21
+ }
22
+ let centerX = elementFrame.midX
23
+ let centerY = elementFrame.midY
24
+ return centerX >= windowFrame.minX && centerX <= windowFrame.maxX
25
+ && centerY >= windowFrame.minY && centerY <= windowFrame.maxY
26
+ }
27
+ }
28
+
@@ -12,7 +12,8 @@ extension RunnerTests {
12
12
 
13
13
  func privateAXSnapshotCapture(
14
14
  app: XCUIApplication,
15
- options: SnapshotOptions
15
+ options: SnapshotOptions,
16
+ deadline: Date = .distantFuture
16
17
  ) -> SnapshotBackendCapture? {
17
18
  #if os(iOS) && targetEnvironment(simulator)
18
19
  let requestedDepth = options.depth ?? 64
@@ -24,6 +25,13 @@ extension RunnerTests {
24
25
  var effectiveDepth = requestedDepth
25
26
  var lastError = "unknown private AX snapshot failure"
26
27
  for depth in attemptDepths {
28
+ // The first rung always runs (the plan gated entry on its own budget); later rungs
29
+ // stop when the capture-plan deadline is spent so ladder retries can never stack
30
+ // past the runner's main-thread watchdog (#1105).
31
+ if depth != attemptDepths.first, Date() >= deadline {
32
+ NSLog("AGENT_DEVICE_RUNNER_PRIVATE_AX_SNAPSHOT_BUDGET_EXHAUSTED depth=%ld", depth)
33
+ break
34
+ }
27
35
  response = RunnerAXSnapshotBridge.snapshotTree(
28
36
  for: app,
29
37
  maxDepth: depth,
@@ -49,14 +57,8 @@ extension RunnerTests {
49
57
  return nil
50
58
  }
51
59
 
52
- // If the app frame is unavailable, the private root's own frame is the reliable screen
53
- // rect here. Avoid public window queries: stale transient windows can record XCTest
54
- // failures after the runner already returned a successful command response.
55
- var viewport = safeSnapshotViewport(app: app)
56
60
  let rootFrame = privateAXRect(root["frame"])
57
- if viewport.isInfinite || viewport.isNull || viewport.isEmpty, !rootFrame.isEmpty {
58
- viewport = rootFrame
59
- }
61
+ let viewport = privateAXSnapshotViewport(app: app, rootFrame: rootFrame)
60
62
  var nodes: [SnapshotNode] = []
61
63
  appendPrivateAXNode(
62
64
  root,
@@ -87,6 +89,29 @@ extension RunnerTests {
87
89
  #endif
88
90
  }
89
91
 
92
+ private func privateAXSnapshotViewport(app: XCUIApplication, rootFrame: CGRect) -> CGRect {
93
+ let fallback = rootFrame.isEmpty ? CGRect.infinite : rootFrame
94
+ guard !hasAbandonedTreeCapture() else {
95
+ return fallback
96
+ }
97
+ do {
98
+ let viewport = try runMainThreadWork(
99
+ command: nil,
100
+ timeout: 1,
101
+ timeoutError: snapshotMainThreadTimeoutError("reading private AX viewport")
102
+ ) {
103
+ self.safeSnapshotViewport(app: app)
104
+ }
105
+ if viewport.isInfinite || viewport.isNull || viewport.isEmpty {
106
+ return fallback
107
+ }
108
+ return viewport
109
+ } catch {
110
+ NSLog("AGENT_DEVICE_RUNNER_PRIVATE_AX_VIEWPORT_FALLBACK=%@", String(describing: error))
111
+ return fallback
112
+ }
113
+ }
114
+
90
115
  private func appendPrivateAXNode(
91
116
  _ rawNode: [String: Any],
92
117
  to nodes: inout [SnapshotNode],
@@ -208,122 +233,3 @@ extension RunnerTests {
208
233
  }
209
234
  }
210
235
 
211
- #if AGENT_DEVICE_RUNNER_UNIT_TESTS
212
- // MARK: - In-bundle unit tests
213
-
214
- extension RunnerTests {
215
- func testPrivateAXScopeSelectsSubtreeNotMatchingLabels() {
216
- let tree: [String: Any] = [
217
- "type": 1, "label": "App",
218
- "children": [
219
- [
220
- "type": 9, "identifier": "homeScreen",
221
- "children": [
222
- ["type": 48, "label": "Post body without the scope text", "children": []]
223
- ],
224
- ],
225
- ["type": 9, "label": "unrelated sibling", "children": []],
226
- ],
227
- ]
228
- var nodes: [SnapshotNode] = []
229
- appendPrivateAXNode(
230
- tree,
231
- to: &nodes,
232
- options: SnapshotOptions(interactiveOnly: false, depth: nil, scope: "homeScreen", raw: false),
233
- viewport: .infinite,
234
- depth: 0,
235
- parentIndex: nil,
236
- insideMatchedScope: false
237
- )
238
-
239
- let labels = nodes.compactMap { $0.label ?? $0.identifier }
240
- XCTAssertTrue(labels.contains("homeScreen"))
241
- // Descendants of the matched scope are included even when they do not contain the text.
242
- XCTAssertTrue(labels.contains("Post body without the scope text"))
243
- XCTAssertFalse(labels.contains("unrelated sibling"))
244
- }
245
-
246
- func testPrivateAXInteractiveFiltersLoginLikeHiddenDrawer() {
247
- let tree: [String: Any] = [
248
- "type": Int(XCUIElement.ElementType.application.rawValue),
249
- "label": "Blue Sky",
250
- "frame": ["x": 0, "y": 0, "width": 390, "height": 844],
251
- "children": [
252
- [
253
- "type": Int(XCUIElement.ElementType.scrollView.rawValue),
254
- "frame": ["x": 0, "y": 0, "width": 390, "height": 844],
255
- "children": [
256
- [
257
- "type": Int(XCUIElement.ElementType.image.rawValue),
258
- "label": "Callstack",
259
- "frame": ["x": 145, "y": 104, "width": 100, "height": 100],
260
- "children": [],
261
- ],
262
- [
263
- "type": Int(XCUIElement.ElementType.staticText.rawValue),
264
- "label": "Welcome back",
265
- "frame": ["x": 32, "y": 260, "width": 326, "height": 32],
266
- "children": [],
267
- ],
268
- [
269
- "type": Int(XCUIElement.ElementType.textField.rawValue),
270
- "label": "Email",
271
- "identifier": "login.email",
272
- "frame": ["x": 32, "y": 348, "width": 326, "height": 48],
273
- "children": [],
274
- ],
275
- [
276
- "type": Int(XCUIElement.ElementType.secureTextField.rawValue),
277
- "label": "Password",
278
- "identifier": "login.password",
279
- "frame": ["x": 32, "y": 412, "width": 326, "height": 48],
280
- "children": [],
281
- ],
282
- [
283
- "type": Int(XCUIElement.ElementType.button.rawValue),
284
- "label": "Sign in",
285
- "identifier": "login.submit",
286
- "frame": ["x": 32, "y": 492, "width": 326, "height": 52],
287
- "children": [],
288
- ],
289
- [
290
- "type": Int(XCUIElement.ElementType.link.rawValue),
291
- "label": "Forgot password?",
292
- "frame": ["x": 128, "y": 568, "width": 134, "height": 32],
293
- "children": [],
294
- ],
295
- [
296
- "type": Int(XCUIElement.ElementType.button.rawValue),
297
- "label": "Admin settings",
298
- "frame": ["x": -260, "y": 184, "width": 220, "height": 44],
299
- "children": [],
300
- ],
301
- [
302
- "type": Int(XCUIElement.ElementType.other.rawValue),
303
- "frame": ["x": 16, "y": 184, "width": 220, "height": 44],
304
- "children": [],
305
- ],
306
- ],
307
- ]
308
- ],
309
- ]
310
- var nodes: [SnapshotNode] = []
311
- appendPrivateAXNode(
312
- tree,
313
- to: &nodes,
314
- options: SnapshotOptions(interactiveOnly: true, depth: nil, scope: nil, raw: false),
315
- viewport: CGRect(x: 0, y: 0, width: 390, height: 844),
316
- depth: 0,
317
- parentIndex: nil,
318
- insideMatchedScope: false
319
- )
320
-
321
- let labels = nodes.compactMap { $0.label }
322
- XCTAssertEqual(
323
- labels,
324
- ["Blue Sky", "Callstack", "Welcome back", "Email", "Password", "Sign in", "Forgot password?"]
325
- )
326
- XCTAssertFalse(labels.contains("Admin settings"))
327
- }
328
- }
329
- #endif