zeno-mobile-runner 0.1.8 → 0.2.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 (66) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/FEATURES.md +1 -1
  3. package/README.md +175 -238
  4. package/clients/kotlin/README.md +1 -1
  5. package/clients/kotlin/build.gradle.kts +1 -1
  6. package/clients/python/pyproject.toml +1 -1
  7. package/clients/rust/Cargo.lock +1 -1
  8. package/clients/rust/Cargo.toml +1 -1
  9. package/clients/typescript/package.json +1 -1
  10. package/docs/agent-discovery.md +10 -0
  11. package/docs/ai-agents.md +18 -0
  12. package/docs/benchmarking.md +39 -0
  13. package/docs/benchmarks/2026-06-09-android-workflow.md +73 -0
  14. package/docs/benchmarks/2026-06-09-android-workflow.results.jsonl +20 -0
  15. package/docs/benchmarks/2026-06-09-framework-baseline-status.md +32 -0
  16. package/docs/benchmarks/2026-06-09-ios-appium-comparison.md +115 -0
  17. package/docs/benchmarks/2026-06-09-ios-appium-comparison.results.jsonl +40 -0
  18. package/docs/benchmarks/2026-06-09-ios-demo.md +90 -0
  19. package/docs/benchmarks/2026-06-09-ios-demo.results.jsonl +20 -0
  20. package/docs/benchmarks/2026-06-09-ios-maestro-comparison.md +128 -0
  21. package/docs/benchmarks/2026-06-09-ios-maestro-comparison.results.jsonl +40 -0
  22. package/docs/benchmarks/2026-06-09-ios-workflow-comparison.md +143 -0
  23. package/docs/benchmarks/2026-06-09-ios-workflow-comparison.results.jsonl +40 -0
  24. package/docs/benchmarks/2026-06-09-ios-xctest-floor.md +106 -0
  25. package/docs/benchmarks/2026-06-09-ios-xctest-floor.results.jsonl +40 -0
  26. package/docs/benchmarks/README.md +36 -0
  27. package/docs/benchmarks/benchmark-lab-v1.json +155 -0
  28. package/docs/benchmarks/benchmark-lab-v1.md +95 -0
  29. package/docs/clients.md +16 -0
  30. package/docs/demo.md +36 -1
  31. package/docs/frameworks.md +10 -0
  32. package/docs/npm.md +44 -2
  33. package/docs/protocol-fixtures/core-session.responses.jsonl +1 -1
  34. package/docs/protocol.md +10 -10
  35. package/docs/scenario-authoring.md +15 -0
  36. package/docs/trace-privacy.md +9 -0
  37. package/docs/troubleshooting.md +6 -0
  38. package/examples/android-workflow.json +79 -0
  39. package/examples/ios-dev-client-open-link.json +24 -13
  40. package/examples/ios-dev-client-route-snapshot.json +33 -8
  41. package/examples/ios-shim-workflow.json +79 -0
  42. package/examples/react-native-expo-workflow.json +75 -0
  43. package/npm/scenarios.mjs +15 -8
  44. package/npm/wizard.mjs +1 -1
  45. package/package.json +6 -1
  46. package/prebuilds/darwin-arm64/zmr +0 -0
  47. package/prebuilds/darwin-x64/zmr +0 -0
  48. package/prebuilds/linux-arm64/zmr +0 -0
  49. package/prebuilds/linux-x64/zmr +0 -0
  50. package/scripts/benchmark-lab.py +253 -0
  51. package/scripts/create-android-demo-app.sh +324 -29
  52. package/scripts/create-ios-demo-app.sh +174 -7
  53. package/scripts/create-react-native-expo-demo-app.sh +727 -0
  54. package/scripts/demo.sh +3 -0
  55. package/scripts/install-ios-shim.sh +2 -2
  56. package/shims/ios/ZMRShim.swift +10 -0
  57. package/shims/ios/ZMRShimUITestCase.swift +49 -1
  58. package/shims/ios/protocol.md +1 -0
  59. package/src/cli_import.zig +31 -15
  60. package/src/cli_trace.zig +38 -16
  61. package/src/cli_validate.zig +12 -6
  62. package/src/ios.zig +44 -11
  63. package/src/ios_shim.zig +36 -2
  64. package/src/main.zig +6 -0
  65. package/src/version.zig +1 -1
  66. package/viewer/app.js +23 -3
@@ -37,6 +37,7 @@ After generation:
37
37
  xcodebuild -project ios/ZMRDemo.xcodeproj -scheme ZMRDemo -destination 'generic/platform=iOS Simulator' -derivedDataPath DerivedData build
38
38
  xcrun simctl install booted DerivedData/Build/Products/Debug-iphonesimulator/ZMRDemo.app
39
39
  zmr run .zmr/ios-shim-smoke.json --platform ios --device booted --app-id com.example.mobiletest --ios-shim ./.zmr/ios-shim --trace-dir traces/zmr-ios-demo
40
+ zmr run .zmr/ios-shim-workflow.json --platform ios --device booted --app-id com.example.mobiletest --ios-shim ./.zmr/ios-shim --trace-dir traces/zmr-ios-workflow
40
41
  USAGE
41
42
  }
42
43
 
@@ -110,12 +111,75 @@ EOF
110
111
  cat > "$SOURCE_DIR/ContentView.swift" <<'EOF'
111
112
  import SwiftUI
112
113
 
114
+ private enum DemoScreen {
115
+ case welcome
116
+ case profile
117
+ case catalog
118
+ case detail
119
+ case review
120
+ }
121
+
122
+ private struct CatalogItem: Identifiable {
123
+ let id: String
124
+ let title: String
125
+ let subtitle: String
126
+ }
127
+
113
128
  struct ContentView: View {
114
129
  @State private var input = ""
115
130
  @State private var status = "Ready"
131
+ @State private var screen: DemoScreen = .welcome
132
+ @State private var profileName = ""
133
+ @State private var profileEmail = ""
134
+ @State private var selectedItem = CatalogItem(id: "north_ridge_pack", title: "North Ridge Pack", subtitle: "Weatherproof day pack")
116
135
  @FocusState private var inputFocused: Bool
117
136
 
137
+ private let catalogItems = [
138
+ CatalogItem(id: "trail_lamp", title: "Trail Lamp", subtitle: "Compact campsite light"),
139
+ CatalogItem(id: "river_bottle", title: "River Bottle", subtitle: "Insulated hydration bottle"),
140
+ CatalogItem(id: "north_ridge_pack", title: "North Ridge Pack", subtitle: "Weatherproof day pack"),
141
+ CatalogItem(id: "summit_shell", title: "Summit Shell", subtitle: "Lightweight rain layer"),
142
+ CatalogItem(id: "basecamp_roll", title: "Basecamp Roll", subtitle: "Modular storage roll"),
143
+ CatalogItem(id: "maple_organizer", title: "Maple Organizer", subtitle: "Cable and tool pouch"),
144
+ CatalogItem(id: "canyon_sling", title: "Canyon Sling", subtitle: "Cross-body field bag"),
145
+ CatalogItem(id: "harbor_tote", title: "Harbor Tote", subtitle: "Daily carry tote"),
146
+ CatalogItem(id: "studio_stand", title: "Studio Stand", subtitle: "Fold-flat work stand")
147
+ ]
148
+
118
149
  var body: some View {
150
+ VStack(spacing: 16) {
151
+ switch screen {
152
+ case .welcome:
153
+ welcomeView
154
+ case .profile:
155
+ profileView
156
+ case .catalog:
157
+ catalogView
158
+ case .detail:
159
+ detailView
160
+ case .review:
161
+ reviewView
162
+ }
163
+
164
+ VStack(spacing: 4) {
165
+ Text(status)
166
+ .font(.footnote)
167
+ .foregroundStyle(.secondary)
168
+ .accessibilityIdentifier("demo_status")
169
+
170
+ Text(status)
171
+ .font(.footnote)
172
+ .foregroundStyle(.secondary)
173
+ .accessibilityIdentifier("workflow_status")
174
+ }
175
+ }
176
+ .padding()
177
+ .onOpenURL { _ in
178
+ status = "Deep link opened"
179
+ }
180
+ }
181
+
182
+ private var welcomeView: some View {
119
183
  VStack(spacing: 20) {
120
184
  Text("ZMR iOS Demo")
121
185
  .font(.title)
@@ -123,24 +187,126 @@ struct ContentView: View {
123
187
 
124
188
  Button("Continue") {
125
189
  status = "Continue tapped"
126
- inputFocused = true
190
+ screen = .profile
127
191
  }
128
192
  .buttonStyle(.borderedProminent)
129
193
  .accessibilityIdentifier("continue_button")
194
+ }
195
+ }
196
+
197
+ private var profileView: some View {
198
+ VStack(spacing: 14) {
199
+ Text("Profile")
200
+ .font(.title2)
201
+ .accessibilityIdentifier("profile_title")
130
202
 
131
203
  TextField("Type here", text: $input)
132
204
  .textFieldStyle(.roundedBorder)
133
205
  .focused($inputFocused)
134
206
  .accessibilityIdentifier("demo_input")
135
- .padding(.horizontal, 32)
136
207
 
137
- Text(status)
138
- .accessibilityIdentifier("demo_status")
208
+ TextField("Name", text: $profileName)
209
+ .textFieldStyle(.roundedBorder)
210
+ .textInputAutocapitalization(.never)
211
+ .autocorrectionDisabled()
212
+ .accessibilityIdentifier("profile_name_input")
213
+
214
+ TextField("Email", text: $profileEmail)
215
+ .textFieldStyle(.roundedBorder)
216
+ .keyboardType(.emailAddress)
217
+ .textInputAutocapitalization(.never)
218
+ .autocorrectionDisabled()
219
+ .accessibilityIdentifier("profile_email_input")
220
+
221
+ Button("Save profile") {
222
+ status = "Profile saved"
223
+ screen = .catalog
224
+ }
225
+ .buttonStyle(.borderedProminent)
226
+ .accessibilityIdentifier("save_profile_button")
139
227
  }
140
- .padding()
141
- .onOpenURL { _ in
142
- status = "Deep link opened"
228
+ }
229
+
230
+ private var catalogView: some View {
231
+ VStack(spacing: 14) {
232
+ Text("Catalog")
233
+ .font(.title2)
234
+ .accessibilityIdentifier("catalog_title")
235
+
236
+ ScrollView {
237
+ LazyVStack(spacing: 10) {
238
+ ForEach(catalogItems) { item in
239
+ Button {
240
+ selectedItem = item
241
+ status = "Selected \(item.title)"
242
+ screen = .detail
243
+ } label: {
244
+ VStack(alignment: .leading, spacing: 3) {
245
+ Text(item.title)
246
+ .font(.headline)
247
+ Text(item.subtitle)
248
+ .font(.subheadline)
249
+ .foregroundStyle(.secondary)
250
+ }
251
+ .frame(maxWidth: .infinity, alignment: .leading)
252
+ .padding()
253
+ }
254
+ .buttonStyle(.bordered)
255
+ .accessibilityIdentifier(catalogAccessibilityIdentifier(for: item))
256
+ }
257
+ }
258
+ }
259
+ .frame(maxHeight: 340)
260
+ .accessibilityIdentifier("catalog_list")
261
+ }
262
+ }
263
+
264
+ private var detailView: some View {
265
+ VStack(spacing: 16) {
266
+ Text(selectedItem.title)
267
+ .font(.title2)
268
+ .accessibilityIdentifier("detail_title")
269
+
270
+ Text(selectedItem.subtitle)
271
+ .foregroundStyle(.secondary)
272
+ .accessibilityIdentifier("detail_subtitle")
273
+
274
+ Button("Save item") {
275
+ status = "Saved \(selectedItem.title)"
276
+ }
277
+ .buttonStyle(.borderedProminent)
278
+ .accessibilityIdentifier("detail_save_button")
279
+
280
+ Button("Review order") {
281
+ status = "Workflow complete"
282
+ screen = .review
283
+ }
284
+ .buttonStyle(.bordered)
285
+ .accessibilityIdentifier("review_button")
286
+ }
287
+ }
288
+
289
+ private var reviewView: some View {
290
+ VStack(spacing: 16) {
291
+ Text("Review")
292
+ .font(.title2)
293
+ .accessibilityIdentifier("review_title")
294
+
295
+ Text("Workflow complete")
296
+ .font(.headline)
297
+ .accessibilityIdentifier("review_complete")
298
+
299
+ Text(selectedItem.title)
300
+ .foregroundStyle(.secondary)
301
+ .accessibilityIdentifier("review_item")
302
+ }
303
+ }
304
+
305
+ private func catalogAccessibilityIdentifier(for item: CatalogItem) -> String {
306
+ if item.id == "north_ridge_pack" {
307
+ return "catalog_item_north_ridge_pack"
143
308
  }
309
+ return "catalog_item_\(item.id)"
144
310
  }
145
311
  }
146
312
  EOF
@@ -253,6 +419,7 @@ RUBY
253
419
 
254
420
  cp "$ROOT/examples/ios-smoke.json" "$OUT/.zmr/ios-smoke.json"
255
421
  cp "$ROOT/examples/ios-shim-smoke.json" "$OUT/.zmr/ios-shim-smoke.json"
422
+ cp "$ROOT/examples/ios-shim-workflow.json" "$OUT/.zmr/ios-shim-workflow.json"
256
423
 
257
424
  echo "created iOS demo app at $OUT"
258
425
  echo "project: $PROJECT_PATH"