agent-device 0.20.0 → 0.20.2

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 (128) hide show
  1. package/README.md +2 -3
  2. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.0.apk → agent-device-android-ime-helper-0.20.2.apk} +0 -0
  3. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.2.apk.sha256 +1 -0
  4. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.0.manifest.json → agent-device-android-ime-helper-0.20.2.manifest.json} +4 -4
  5. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.0.apk → agent-device-android-snapshot-helper-0.20.2.apk} +0 -0
  6. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.2.apk.sha256 +1 -0
  7. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.0.manifest.json → agent-device-android-snapshot-helper-0.20.2.manifest.json} +6 -6
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +2 -0
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +30 -16
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +0 -21
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +8 -3
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Orientation.swift +92 -0
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +4 -2
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +1 -1
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +2 -1
  17. package/dist/src/agent-device-client.js +2 -2
  18. package/dist/src/android-input-method-overlays.js +1 -1
  19. package/dist/src/android.js +1 -1
  20. package/dist/src/app-lifecycle.js +5 -5
  21. package/dist/src/app-log-request-scope.js +1 -1
  22. package/dist/src/app-resolution.js +1 -1
  23. package/dist/src/apple-multitouch-support.js +1 -1
  24. package/dist/src/apps.js +5 -5
  25. package/dist/src/boot-diagnostics.js +2 -2
  26. package/dist/src/cli-config.js +1 -1
  27. package/dist/src/cli-help.js +81 -24
  28. package/dist/src/cli-runner.js +1 -1
  29. package/dist/src/cli.js +14 -14
  30. package/dist/src/click-button.d.ts +5 -0
  31. package/dist/src/client-metro.js +1 -0
  32. package/dist/src/command-metadata.js +1 -1
  33. package/dist/src/command-schema.js +3 -3
  34. package/dist/src/compatibility-policy.js +1 -0
  35. package/dist/src/context.js +1 -1
  36. package/dist/src/contracts.d.ts +2 -2
  37. package/dist/src/debug-symbols.js +1 -1
  38. package/dist/src/device-input-state.js +1 -1
  39. package/dist/src/device.js +9 -0
  40. package/dist/src/devices2.js +1 -1
  41. package/dist/src/devices4.js +1 -0
  42. package/dist/src/find.js +1 -1
  43. package/dist/src/finders.js +1 -1
  44. package/dist/src/generic.js +1 -1
  45. package/dist/src/gesture-plan-types.js +1 -0
  46. package/dist/src/gesture-plan.js +1 -0
  47. package/dist/src/index.d.ts +1205 -1309
  48. package/dist/src/input-actions.js +4 -4
  49. package/dist/src/install-artifact.js +5 -1
  50. package/dist/src/install-artifact2.js +1 -0
  51. package/dist/src/interaction-snapshot.js +1 -1
  52. package/dist/src/interaction.d.ts +3 -6
  53. package/dist/src/interaction.js +1 -1
  54. package/dist/src/interactor.js +2 -2
  55. package/dist/src/interactor2.js +1 -0
  56. package/dist/src/internal/bin.js +1 -1
  57. package/dist/src/internal/daemon.js +14 -22
  58. package/dist/src/lease-context.js +1 -1
  59. package/dist/src/lease.js +1 -1
  60. package/dist/src/limrun.d.ts +361 -0
  61. package/dist/src/limrun.js +1 -0
  62. package/dist/src/linux.js +1 -1
  63. package/dist/src/logcat.js +1 -5
  64. package/dist/src/method-scope.js +1 -0
  65. package/dist/src/metro.d.ts +72 -3
  66. package/dist/src/metro.js +1 -1
  67. package/dist/src/metro2.js +1 -1
  68. package/dist/src/notifications.js +1 -1
  69. package/dist/src/physical-device-control.js +1 -0
  70. package/dist/src/plist.js +1 -0
  71. package/dist/src/react-native.js +1 -1
  72. package/dist/src/recording-export-quality.js +15 -0
  73. package/dist/src/register-builtins.js +1 -1
  74. package/dist/src/registry.js +24 -24
  75. package/dist/src/remote-config.d.ts +5 -2
  76. package/dist/src/remote-config.js +1 -1
  77. package/dist/src/remote-config2.js +1 -0
  78. package/dist/src/reporting.js +8 -8
  79. package/dist/src/runner-client.js +1 -1
  80. package/dist/src/runner-disposal.js +1 -2
  81. package/dist/src/runner-lease.js +1 -1
  82. package/dist/src/runner-provider.js +1 -1
  83. package/dist/src/runtime-transport.js +1 -1
  84. package/dist/src/runtime.js +3 -3
  85. package/dist/src/runtime2.js +1 -1
  86. package/dist/src/sdk-android-adb.d.ts +4 -24
  87. package/dist/src/sdk-android-adb.js +4 -4
  88. package/dist/src/sdk-batch-runner.d.ts +13 -3
  89. package/dist/src/sdk-batch-runner.js +3 -2
  90. package/dist/src/sdk-contracts.d.ts +31 -91
  91. package/dist/src/sdk-contracts.js +2 -2
  92. package/dist/src/sdk-device.js +1 -1
  93. package/dist/src/sdk-finders.d.ts +3 -2
  94. package/dist/src/sdk-finders.js +1 -1
  95. package/dist/src/sdk-remote-config.d.ts +83 -53
  96. package/dist/src/sdk-selectors.js +1 -1
  97. package/dist/src/selector-runtime.js +1 -1
  98. package/dist/src/selector-vocabulary.js +1 -1
  99. package/dist/src/selectors.d.ts +1 -1
  100. package/dist/src/selectors.js +1 -1
  101. package/dist/src/server.js +2 -2
  102. package/dist/src/session-target-evidence.js +1 -1
  103. package/dist/src/session.js +8 -8
  104. package/dist/src/simulator.js +1 -1
  105. package/dist/src/snapshot-diagnostics.js +4 -4
  106. package/dist/src/snapshot2.js +1 -1
  107. package/dist/src/tool-provider.js +3 -3
  108. package/dist/src/tool-provider2.js +1 -1
  109. package/dist/src/tool-provider3.js +1 -0
  110. package/dist/src/tv-remote.d.ts +213 -0
  111. package/dist/src/unsupported-interactor.js +1 -0
  112. package/dist/src/web.js +1 -1
  113. package/dist/src/xml.js +2 -0
  114. package/package.json +41 -19
  115. package/server.json +2 -2
  116. package/skills/agent-device/SKILL.md +5 -3
  117. package/smithery.yaml +1 -1
  118. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.0.apk.sha256 +0 -1
  119. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.0.apk.sha256 +0 -1
  120. package/dist/src/android-system-chrome.js +0 -1
  121. package/dist/src/client-metro-companion.js +0 -1
  122. package/dist/src/config.js +0 -1
  123. package/dist/src/daemon-shutdown-report.js +0 -1
  124. package/dist/src/project-runtime.js +0 -1
  125. package/dist/src/scoped-provider.js +0 -1
  126. package/dist/src/sdk-batch.js +0 -2
  127. package/dist/src/snapshot-quality.js +0 -1
  128. package/dist/src/tv-remote.js +0 -15
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  Let your coding agent verify its changes in the running app.
17
17
 
18
- `agent-device` lets coding agents inspect, control, and verify apps on iOS, Android, TV, web, macOS, and Linux. Agents can read token-efficient accessibility snapshots, find elements by ref or selector, run device actions, and save evidence for review.
18
+ `agent-device` lets coding agents inspect, control, and verify apps on iOS, Android, tvOS, Android TV, Amazon Vega OS TV through the Vega Virtual Device (VVD), web, macOS, and Linux. Agents can read token-efficient accessibility snapshots where supported, find elements by ref or selector, run device actions, and save evidence for review. Initial Vega OS support is VVD-only and covers discovery, app lifecycle, and complete TV-remote control; physical Fire TV, capture, and selector backends remain unsupported.
19
19
 
20
20
  Your coding agent or QA tool reads each result and chooses the next command. `agent-device` runs the command and saves evidence when asked.
21
21
 
@@ -28,7 +28,6 @@ Install the CLI and check setup. It requires Node.js 22.12 or newer; web automat
28
28
  ```bash
29
29
  npm install -g agent-device@latest
30
30
  agent-device doctor
31
- agent-device --version
32
31
  agent-device help workflow
33
32
  ```
34
33
 
@@ -108,7 +107,7 @@ See [Commands](https://oss.callstack.com/agent-device/docs/commands) for the com
108
107
 
109
108
  ## How it works
110
109
 
111
- `agent-device` keeps device state in sessions. It sends commands to XCTest on iOS and tvOS, ADB and the snapshot helper on Android, a local helper on macOS, and AT-SPI on Linux.
110
+ `agent-device` keeps device state in sessions. It sends commands to XCTest on iOS and tvOS, ADB and the snapshot helper on Android, Vega CLI/VDA on the Vega Virtual Device, a local helper on macOS, and AT-SPI on Linux.
112
111
 
113
112
  Node.js apps can use the typed client or public subpaths. `agent-device/android-adb` provides the Android ADB provider interface, helpers for logcat, the clipboard, the keyboard, and apps, and port reverse management.
114
113
 
@@ -0,0 +1 @@
1
+ 941f2997c4315e753fb2f5411562a2907b3a188370c3d52e762692843be1b50f agent-device-android-ime-helper-0.20.2.apk
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "android-ime-helper",
3
- "version": "0.20.0",
4
- "assetName": "agent-device-android-ime-helper-0.20.0.apk",
5
- "sha256": "315907205b4d573cbfe4dfa4bcb7bafd360d9f6d93427f255574d7f02ea6656e",
3
+ "version": "0.20.2",
4
+ "assetName": "agent-device-android-ime-helper-0.20.2.apk",
5
+ "sha256": "941f2997c4315e753fb2f5411562a2907b3a188370c3d52e762692843be1b50f",
6
6
  "packageName": "com.callstack.agentdevice.imehelper",
7
- "versionCode": 20000,
7
+ "versionCode": 20002,
8
8
  "serviceComponent": "com.callstack.agentdevice.imehelper/.TestInputMethodService",
9
9
  "broadcastProtocol": "android-ime-helper-v1"
10
10
  }
@@ -0,0 +1 @@
1
+ 003859ea5dc5d1ed8ddc5daf786eacb790ff471bbb0c3b0e1239712f73f37684 agent-device-android-snapshot-helper-0.20.2.apk
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "android-snapshot-helper",
3
- "version": "0.20.0",
4
- "releaseTag": "v0.20.0",
5
- "assetName": "agent-device-android-snapshot-helper-0.20.0.apk",
3
+ "version": "0.20.2",
4
+ "releaseTag": "v0.20.2",
5
+ "assetName": "agent-device-android-snapshot-helper-0.20.2.apk",
6
6
  "apkUrl": null,
7
- "sha256": "d635fe2e577ece9d5924719f907232693231644d69ba08b7d21d7bea04baa652",
8
- "checksumName": "agent-device-android-snapshot-helper-0.20.0.apk.sha256",
7
+ "sha256": "003859ea5dc5d1ed8ddc5daf786eacb790ff471bbb0c3b0e1239712f73f37684",
8
+ "checksumName": "agent-device-android-snapshot-helper-0.20.2.apk.sha256",
9
9
  "packageName": "com.callstack.agentdevice.snapshothelper",
10
- "versionCode": 20000,
10
+ "versionCode": 20002,
11
11
  "instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
12
12
  "minSdk": 23,
13
13
  "targetSdk": 36,
@@ -220,10 +220,12 @@ extension RunnerTests {
220
220
  "yes",
221
221
  "continue",
222
222
  "done",
223
+ "open",
223
224
  "open settings"
224
225
  ].contains(normalized) || normalized.hasPrefix("confirm")
225
226
  }
226
227
 
228
+
227
229
  private func isDismissButton(_ label: String) -> Bool {
228
230
  [
229
231
  "cancel",
@@ -755,6 +755,28 @@ extension RunnerTests {
755
755
  }
756
756
  case .uptime:
757
757
  return executeUptime()
758
+ case .activate:
759
+ guard
760
+ let bundleId = command.appBundleId?.trimmingCharacters(in: .whitespacesAndNewlines),
761
+ !bundleId.isEmpty
762
+ else {
763
+ return Response(ok: false, error: ErrorPayload(message: "activate requires appBundleId"))
764
+ }
765
+ // prepareActiveCommandContext already activated this bundle. Keep this case as the
766
+ // explicit acknowledgement after that preflight, not as a second activation.
767
+ return Response(ok: true, data: DataPayload(message: "app activated"))
768
+ case .terminate:
769
+ guard
770
+ let bundleId = command.appBundleId?.trimmingCharacters(in: .whitespacesAndNewlines),
771
+ !bundleId.isEmpty
772
+ else {
773
+ return Response(ok: false, error: ErrorPayload(message: "terminate requires appBundleId"))
774
+ }
775
+ XCUIApplication(bundleIdentifier: bundleId).terminate()
776
+ if currentBundleId == bundleId {
777
+ invalidateCachedTarget(reason: "target_terminated")
778
+ }
779
+ return Response(ok: true, data: DataPayload(message: "app terminated"))
758
780
  default:
759
781
  break
760
782
  }
@@ -844,7 +866,7 @@ extension RunnerTests {
844
866
  ) throws -> Response {
845
867
  var activeApp = activeApp
846
868
  switch command.command {
847
- case .status, .targetReset, .shutdown, .recordStart, .recordStop, .uptime:
869
+ case .status, .activate, .terminate, .targetReset, .shutdown, .recordStart, .recordStop, .uptime:
848
870
  return Response(
849
871
  ok: false,
850
872
  error: ErrorPayload(
@@ -1231,6 +1253,12 @@ extension RunnerTests {
1231
1253
  guard let pngData = runnerPngData(for: screenshot.image) else {
1232
1254
  return Response(ok: false, error: ErrorPayload(message: "Failed to encode screenshot as PNG"))
1233
1255
  }
1256
+ if command.inlineScreenshot == true {
1257
+ return Response(
1258
+ ok: true,
1259
+ data: DataPayload(imageBase64: pngData.base64EncodedString())
1260
+ )
1261
+ }
1234
1262
  let fileName = "screenshot-\(Int(Date().timeIntervalSince1970 * 1000)).png"
1235
1263
  let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(fileName)
1236
1264
  do {
@@ -1259,21 +1287,7 @@ extension RunnerTests {
1259
1287
  pressHomeButton()
1260
1288
  return Response(ok: true, data: DataPayload(message: "home"))
1261
1289
  case .rotate:
1262
- guard let orientation = command.orientation?.trimmingCharacters(in: .whitespacesAndNewlines),
1263
- !orientation.isEmpty
1264
- else {
1265
- return Response(ok: false, error: ErrorPayload(message: "rotate requires orientation"))
1266
- }
1267
- if rotateDevice(to: orientation) {
1268
- return Response(
1269
- ok: true,
1270
- data: DataPayload(message: "rotate", orientation: orientation)
1271
- )
1272
- }
1273
- return Response(
1274
- ok: false,
1275
- error: ErrorPayload(message: "unsupported rotate orientation: \(orientation)")
1276
- )
1290
+ return executeRotateCommand(command)
1277
1291
  case .appSwitcher:
1278
1292
  performAppSwitcherGesture(app: activeApp)
1279
1293
  return Response(ok: true, data: DataPayload(message: "appSwitcher"))
@@ -147,7 +147,7 @@ final class RunnerCommandJournal {
147
147
  .remotePress, .type, .swipe, .scroll, .desktopScroll, .findText, .querySelector, .readText, .back,
148
148
  .backInApp, .backSystem, .home, .rotate, .appSwitcher, .keyboardDismiss, .keyboardReturn,
149
149
  .alert, .sequence, .gesture, .gestureViewport, .recordStart, .recordStop,
150
- .status, .uptime, .targetReset, .shutdown:
150
+ .status, .uptime, .activate, .terminate, .targetReset, .shutdown:
151
151
  return true
152
152
  }
153
153
  }
@@ -118,27 +118,6 @@ extension RunnerTests {
118
118
  #endif
119
119
  }
120
120
 
121
- func rotateDevice(to orientationName: String) -> Bool {
122
- #if os(macOS) || os(tvOS) || os(visionOS)
123
- return false
124
- #else
125
- switch orientationName {
126
- case "portrait":
127
- XCUIDevice.shared.orientation = .portrait
128
- case "portrait-upside-down":
129
- XCUIDevice.shared.orientation = .portraitUpsideDown
130
- case "landscape-left":
131
- XCUIDevice.shared.orientation = .landscapeLeft
132
- case "landscape-right":
133
- XCUIDevice.shared.orientation = .landscapeRight
134
- default:
135
- return false
136
- }
137
- sleepFor(0.2)
138
- return true
139
- #endif
140
- }
141
-
142
121
  func findElement(app: XCUIApplication, text: String) -> XCUIElement? {
143
122
  let predicate = NSPredicate(format: "label CONTAINS[c] %@ OR identifier CONTAINS[c] %@ OR value CONTAINS[c] %@", text, text, text)
144
123
  let element = app.descendants(matching: .any).matching(predicate).firstMatch
@@ -31,6 +31,8 @@ enum CommandType: String, Codable {
31
31
  case recordStop
32
32
  case status
33
33
  case uptime
34
+ case activate
35
+ case terminate
34
36
  case targetReset
35
37
  case shutdown
36
38
  }
@@ -90,7 +92,7 @@ extension CommandType {
90
92
  return CommandTraits(isInteraction: false, readOnly: .conditional, isLifecycle: false)
91
93
 
92
94
  // Runner-lifecycle commands: skip the app-activation preflight.
93
- case .recordStop, .uptime, .targetReset, .shutdown:
95
+ case .recordStop, .uptime, .terminate, .targetReset, .shutdown:
94
96
  return CommandTraits(isInteraction: false, readOnly: .never, isLifecycle: true)
95
97
 
96
98
  case .status:
@@ -101,7 +103,7 @@ extension CommandType {
101
103
  // guard interacts with bespoke macOS activation, so classifying it needs a macOS smoke
102
104
  // check first (tracked as a follow-up). Also preserved: querySelector is NOT read-only;
103
105
  // recordStart is NOT a lifecycle command; home/alert remain non-interaction by design.
104
- case .mouseClick, .querySelector, .home, .recordStart:
106
+ case .mouseClick, .querySelector, .home, .recordStart, .activate:
105
107
  return CommandTraits(isInteraction: false, readOnly: .never, isLifecycle: false)
106
108
  }
107
109
  }
@@ -118,7 +120,6 @@ struct Command: Codable {
118
120
  let allowNonHittableCoordinateFallback: Bool?
119
121
  let delayMs: Int?
120
122
  let textEntryMode: String?
121
- let clearFirst: Bool?
122
123
  let action: String?
123
124
  let x: Double?
124
125
  let y: Double?
@@ -141,6 +142,7 @@ struct Command: Codable {
141
142
  let scope: String?
142
143
  let raw: Bool?
143
144
  let fullscreen: Bool?
145
+ let inlineScreenshot: Bool?
144
146
  let synthesized: Bool?
145
147
  let steps: [SequenceStep]?
146
148
  }
@@ -228,6 +230,7 @@ extension Response {
228
230
 
229
231
  struct DataPayload: Codable {
230
232
  let message: String?
233
+ let imageBase64: String?
231
234
  let text: String?
232
235
  let found: Bool?
233
236
  let items: [String]?
@@ -267,6 +270,7 @@ struct DataPayload: Codable {
267
270
 
268
271
  init(
269
272
  message: String? = nil,
273
+ imageBase64: String? = nil,
270
274
  text: String? = nil,
271
275
  found: Bool? = nil,
272
276
  items: [String]? = nil,
@@ -305,6 +309,7 @@ struct DataPayload: Codable {
305
309
  sequenceResults: [SequenceStepResult]? = nil
306
310
  ) {
307
311
  self.message = message
312
+ self.imageBase64 = imageBase64
308
313
  self.text = text
309
314
  self.found = found
310
315
  self.items = items
@@ -0,0 +1,92 @@
1
+ import XCTest
2
+
3
+ private enum OrientationObservationTiming {
4
+ static let pollInterval: TimeInterval = 0.05
5
+ static let timeout: TimeInterval = 2
6
+ }
7
+
8
+ extension RunnerTests {
9
+ func executeRotateCommand(_ command: Command) -> Response {
10
+ guard let orientation = command.orientation?.trimmingCharacters(in: .whitespacesAndNewlines),
11
+ !orientation.isEmpty
12
+ else {
13
+ return Response(ok: false, error: ErrorPayload(message: "rotate requires orientation"))
14
+ }
15
+ let supportedOrientations = [
16
+ "portrait",
17
+ "portrait-upside-down",
18
+ "landscape-left",
19
+ "landscape-right",
20
+ ]
21
+ guard supportedOrientations.contains(orientation) else {
22
+ return Response(
23
+ ok: false,
24
+ error: ErrorPayload(message: "unsupported rotate orientation: \(orientation)")
25
+ )
26
+ }
27
+ guard let observedOrientation = rotateDevice(to: orientation) else {
28
+ return Response(
29
+ ok: false,
30
+ error: ErrorPayload(message: "unable to observe device orientation after rotate")
31
+ )
32
+ }
33
+ guard observedOrientation == orientation else {
34
+ return Response(
35
+ ok: false,
36
+ error: ErrorPayload(
37
+ message: "device orientation is \(observedOrientation), expected \(orientation)"
38
+ )
39
+ )
40
+ }
41
+ return Response(
42
+ ok: true,
43
+ data: DataPayload(message: "rotate", orientation: observedOrientation)
44
+ )
45
+ }
46
+
47
+ private func rotateDevice(to orientationName: String) -> String? {
48
+ #if os(macOS) || os(tvOS) || os(visionOS)
49
+ return nil
50
+ #else
51
+ switch orientationName {
52
+ case "portrait":
53
+ XCUIDevice.shared.orientation = .portrait
54
+ case "portrait-upside-down":
55
+ XCUIDevice.shared.orientation = .portraitUpsideDown
56
+ case "landscape-left":
57
+ XCUIDevice.shared.orientation = .landscapeLeft
58
+ case "landscape-right":
59
+ XCUIDevice.shared.orientation = .landscapeRight
60
+ default:
61
+ return nil
62
+ }
63
+ let deadline = Date().addingTimeInterval(OrientationObservationTiming.timeout)
64
+ repeat {
65
+ if observedDeviceOrientation() == orientationName {
66
+ return orientationName
67
+ }
68
+ sleepFor(OrientationObservationTiming.pollInterval)
69
+ } while Date() < deadline
70
+ return observedDeviceOrientation()
71
+ #endif
72
+ }
73
+
74
+ private func observedDeviceOrientation() -> String? {
75
+ #if os(macOS) || os(tvOS) || os(visionOS)
76
+ return nil
77
+ #else
78
+ switch XCUIDevice.shared.orientation {
79
+ case .portrait:
80
+ return "portrait"
81
+ case .portraitUpsideDown:
82
+ return "portrait-upside-down"
83
+ case .landscapeLeft:
84
+ return "landscape-left"
85
+ case .landscapeRight:
86
+ return "landscape-right"
87
+ default:
88
+ return nil
89
+ }
90
+ #endif
91
+ }
92
+ }
@@ -57,12 +57,16 @@ extension RunnerTests {
57
57
  case .collectionView: return "CollectionView"
58
58
  case .table: return "Table"
59
59
  case .scrollView: return "ScrollView"
60
+ case .toolbar: return "Toolbar"
60
61
  case .searchField: return "SearchField"
61
62
  case .segmentedControl: return "SegmentedControl"
62
63
  case .stepper: return "Stepper"
63
64
  case .picker: return "Picker"
65
+ case .activityIndicator: return "ActivityIndicator"
66
+ case .progressIndicator: return "ProgressIndicator"
64
67
  case .checkBox: return "CheckBox"
65
68
  case .menuItem: return "MenuItem"
69
+ case .webView: return "WebView"
66
70
  case .other: return "Other"
67
71
  default:
68
72
  switch type.rawValue {
@@ -70,8 +74,6 @@ extension RunnerTests {
70
74
  return "Keyboard"
71
75
  case 20:
72
76
  return "Key"
73
- case 24:
74
- return "SearchField"
75
77
  default:
76
78
  return "Element(\(type.rawValue))"
77
79
  }
@@ -240,7 +240,7 @@ extension RunnerTests {
240
240
  case "replace":
241
241
  return .replacement
242
242
  default:
243
- return command.clearFirst == true ? .replacement : .none
243
+ return .none
244
244
  }
245
245
  }
246
246
 
@@ -121,7 +121,8 @@ final class RunnerTests: XCTestCase {
121
121
  .tabBar,
122
122
  .textField,
123
123
  .secureTextField,
124
- .textView
124
+ .textView,
125
+ .webView
125
126
  ]
126
127
  // Keep blocker actions narrow to avoid false positives from generic hittable containers.
127
128
  let actionableTypes: Set<XCUIElement.ElementType> = [