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
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "ZMR Android workflow demo",
3
+ "appId": "com.example.mobiletest",
4
+ "steps": [
5
+ { "action": "stop" },
6
+ { "action": "launch" },
7
+ {
8
+ "action": "waitVisible",
9
+ "selector": { "text": "ZMR Android Demo" },
10
+ "timeoutMs": 30000
11
+ },
12
+ {
13
+ "action": "tap",
14
+ "selector": { "resourceId": "com.example.mobiletest:id/continue_button" }
15
+ },
16
+ {
17
+ "action": "waitVisible",
18
+ "selector": { "text": "Profile" },
19
+ "timeoutMs": 10000
20
+ },
21
+ {
22
+ "action": "typeText",
23
+ "selector": { "resourceId": "com.example.mobiletest:id/profile_name_input" },
24
+ "text": "Riley"
25
+ },
26
+ {
27
+ "action": "typeText",
28
+ "selector": { "resourceId": "com.example.mobiletest:id/profile_email_input" },
29
+ "text": "riley@example.test"
30
+ },
31
+ { "action": "hideKeyboard" },
32
+ {
33
+ "action": "tap",
34
+ "selector": { "resourceId": "com.example.mobiletest:id/save_profile_button" }
35
+ },
36
+ {
37
+ "action": "waitVisible",
38
+ "selector": { "text": "Catalog" },
39
+ "timeoutMs": 10000
40
+ },
41
+ {
42
+ "action": "scrollUntilVisible",
43
+ "selector": { "resourceId": "com.example.mobiletest:id/catalog_item_north_ridge_pack" },
44
+ "direction": "down",
45
+ "timeoutMs": 10000
46
+ },
47
+ {
48
+ "action": "tap",
49
+ "selector": { "resourceId": "com.example.mobiletest:id/catalog_item_north_ridge_pack" }
50
+ },
51
+ {
52
+ "action": "waitVisible",
53
+ "selector": { "text": "North Ridge Pack" },
54
+ "timeoutMs": 10000
55
+ },
56
+ {
57
+ "action": "tap",
58
+ "selector": { "resourceId": "com.example.mobiletest:id/detail_save_button" }
59
+ },
60
+ {
61
+ "action": "waitVisible",
62
+ "selector": { "text": "Saved North Ridge Pack" },
63
+ "timeoutMs": 10000
64
+ },
65
+ {
66
+ "action": "tap",
67
+ "selector": { "resourceId": "com.example.mobiletest:id/review_button" }
68
+ },
69
+ {
70
+ "action": "assertVisible",
71
+ "selector": {
72
+ "resourceId": "com.example.mobiletest:id/workflow_status",
73
+ "text": "Workflow complete"
74
+ },
75
+ "timeoutMs": 10000
76
+ },
77
+ { "action": "snapshot" }
78
+ ]
79
+ }
@@ -2,25 +2,36 @@
2
2
  "name": "iOS Expo dev-client open-link smoke",
3
3
  "appId": "com.example.mobiletest",
4
4
  "steps": [
5
- { "action": "stop" },
5
+ {
6
+ "action": "stop"
7
+ },
6
8
  {
7
9
  "action": "openLink",
8
10
  "url": "exp+mobiletest://expo-development-client/?url=http%3A%2F%2F127.0.0.1%3A8081"
9
11
  },
10
12
  {
11
- "action": "waitAny",
12
- "selectors": [
13
- { "textContains": "Downloading" },
14
- { "textContains": "Connected to:" },
15
- { "textContains": "Reload" },
16
- { "textContains": "Continue" },
17
- { "textContains": "Sign in" },
18
- { "textContains": "Home" },
19
- { "textContains": "Unable to load" }
20
- ],
13
+ "action": "waitNotVisible",
14
+ "selector": {
15
+ "textContains": "evelopment servers"
16
+ },
21
17
  "timeoutMs": 120000
22
18
  },
23
- { "action": "assertHealthy" },
24
- { "action": "snapshot" }
19
+ {
20
+ "action": "assertNoneVisible",
21
+ "selectors": [
22
+ {
23
+ "textContains": "Unable to load"
24
+ },
25
+ {
26
+ "textContains": "There was a problem loading"
27
+ }
28
+ ]
29
+ },
30
+ {
31
+ "action": "assertHealthy"
32
+ },
33
+ {
34
+ "action": "snapshot"
35
+ }
25
36
  ]
26
37
  }
@@ -2,23 +2,48 @@
2
2
  "name": "iOS Expo dev-client route snapshot",
3
3
  "appId": "com.example.mobiletest",
4
4
  "steps": [
5
- { "action": "stop" },
5
+ {
6
+ "action": "stop"
7
+ },
6
8
  {
7
9
  "action": "openLink",
8
10
  "url": "exampleapp:///settings?mode=manage&source=zmr"
9
11
  },
12
+ {
13
+ "action": "waitNotVisible",
14
+ "selector": {
15
+ "textContains": "evelopment servers"
16
+ },
17
+ "timeoutMs": 60000
18
+ },
10
19
  {
11
20
  "action": "waitAny",
12
21
  "selectors": [
13
- { "textContains": "Settings" },
14
- { "textContains": "Manage" },
15
- { "textContains": "Home" },
16
- { "textContains": "Sign in" },
17
- { "textContains": "Unable to load" }
22
+ {
23
+ "textContains": "Settings"
24
+ },
25
+ {
26
+ "textContains": "Manage"
27
+ }
18
28
  ],
19
29
  "timeoutMs": 60000
20
30
  },
21
- { "action": "assertHealthy" },
22
- { "action": "snapshot" }
31
+ {
32
+ "action": "assertNoneVisible",
33
+ "selectors": [
34
+ {
35
+ "textContains": "Unable to load"
36
+ },
37
+ {
38
+ "textContains": "There was a problem loading"
39
+ }
40
+ ]
41
+ },
42
+ {
43
+ "action": "assertHealthy"
44
+ },
45
+ {
46
+ "action": "snapshot"
47
+ }
23
48
  ]
24
49
  }
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "ZMR iOS shim workflow demo",
3
+ "appId": "com.example.mobiletest",
4
+ "steps": [
5
+ { "action": "stop" },
6
+ { "action": "launch" },
7
+ {
8
+ "action": "waitVisible",
9
+ "selector": { "text": "ZMR iOS Demo" },
10
+ "timeoutMs": 30000
11
+ },
12
+ {
13
+ "action": "tap",
14
+ "selector": { "resourceId": "continue_button" }
15
+ },
16
+ {
17
+ "action": "waitVisible",
18
+ "selector": { "text": "Profile" },
19
+ "timeoutMs": 10000
20
+ },
21
+ {
22
+ "action": "typeText",
23
+ "selector": { "resourceId": "profile_name_input" },
24
+ "text": "Riley"
25
+ },
26
+ {
27
+ "action": "typeText",
28
+ "selector": { "resourceId": "profile_email_input" },
29
+ "text": "riley@example.test"
30
+ },
31
+ { "action": "hideKeyboard" },
32
+ {
33
+ "action": "tap",
34
+ "selector": { "resourceId": "save_profile_button" }
35
+ },
36
+ {
37
+ "action": "waitVisible",
38
+ "selector": { "text": "Catalog" },
39
+ "timeoutMs": 10000
40
+ },
41
+ {
42
+ "action": "scrollUntilVisible",
43
+ "selector": { "resourceId": "catalog_item_north_ridge_pack" },
44
+ "direction": "down",
45
+ "timeoutMs": 10000
46
+ },
47
+ {
48
+ "action": "tap",
49
+ "selector": { "resourceId": "catalog_item_north_ridge_pack" }
50
+ },
51
+ {
52
+ "action": "waitVisible",
53
+ "selector": { "text": "North Ridge Pack" },
54
+ "timeoutMs": 10000
55
+ },
56
+ {
57
+ "action": "tap",
58
+ "selector": { "resourceId": "detail_save_button" }
59
+ },
60
+ {
61
+ "action": "waitVisible",
62
+ "selector": { "text": "Saved North Ridge Pack" },
63
+ "timeoutMs": 10000
64
+ },
65
+ {
66
+ "action": "tap",
67
+ "selector": { "resourceId": "review_button" }
68
+ },
69
+ {
70
+ "action": "assertVisible",
71
+ "selector": {
72
+ "resourceId": "workflow_status",
73
+ "text": "Workflow complete"
74
+ },
75
+ "timeoutMs": 10000
76
+ },
77
+ { "action": "snapshot" }
78
+ ]
79
+ }
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "ZMR React Native Expo workflow demo",
3
+ "appId": "com.example.mobiletest",
4
+ "steps": [
5
+ { "action": "openLink", "url": "zenoexpodemo://benchmark" },
6
+ {
7
+ "action": "waitVisible",
8
+ "selector": { "text": "Zeno Expo Demo" },
9
+ "timeoutMs": 30000
10
+ },
11
+ {
12
+ "action": "tap",
13
+ "selector": { "contentDesc": "continue_button" }
14
+ },
15
+ {
16
+ "action": "waitVisible",
17
+ "selector": { "text": "Profile" },
18
+ "timeoutMs": 10000
19
+ },
20
+ {
21
+ "action": "typeText",
22
+ "selector": { "contentDesc": "profile_name_input" },
23
+ "text": "Riley"
24
+ },
25
+ {
26
+ "action": "typeText",
27
+ "selector": { "contentDesc": "profile_email_input" },
28
+ "text": "riley@example.test"
29
+ },
30
+ { "action": "hideKeyboard" },
31
+ {
32
+ "action": "tap",
33
+ "selector": { "contentDesc": "save_profile_button" }
34
+ },
35
+ {
36
+ "action": "waitVisible",
37
+ "selector": { "text": "Catalog" },
38
+ "timeoutMs": 10000
39
+ },
40
+ {
41
+ "action": "scrollUntilVisible",
42
+ "selector": { "contentDesc": "catalog_item_north_ridge_pack" },
43
+ "direction": "down",
44
+ "timeoutMs": 10000
45
+ },
46
+ {
47
+ "action": "tap",
48
+ "selector": { "contentDesc": "catalog_item_north_ridge_pack" }
49
+ },
50
+ {
51
+ "action": "waitVisible",
52
+ "selector": { "text": "North Ridge Pack" },
53
+ "timeoutMs": 10000
54
+ },
55
+ {
56
+ "action": "tap",
57
+ "selector": { "contentDesc": "detail_save_button" }
58
+ },
59
+ {
60
+ "action": "waitVisible",
61
+ "selector": { "text": "Saved North Ridge Pack" },
62
+ "timeoutMs": 10000
63
+ },
64
+ {
65
+ "action": "tap",
66
+ "selector": { "contentDesc": "review_button" }
67
+ },
68
+ {
69
+ "action": "assertVisible",
70
+ "selector": { "text": "Workflow complete" },
71
+ "timeoutMs": 10000
72
+ },
73
+ { "action": "snapshot" }
74
+ ]
75
+ }
package/npm/scenarios.mjs CHANGED
@@ -64,6 +64,14 @@ export function scenarioFiles(appId, { android = true, ios = true, expoDevClient
64
64
  }
65
65
 
66
66
  export function devClientScenario(name, appId, scheme, metroUrl) {
67
+ // The Expo dev-launcher chrome shares tokens with common app copy ("Home",
68
+ // "Continue", "Sign in"), and bundle-loading overlays ("Downloading") can
69
+ // flash faster than a snapshot poll, so neither can prove the app's JS
70
+ // bundle loaded. Instead wait for the launcher's persistent marker to be
71
+ // GONE: "evelopment servers" matches both "Development servers" and "No
72
+ // development servers found" (matching is case-sensitive), passes
73
+ // immediately when the deep link navigates without showing the launcher,
74
+ // and times out — failing the run — when the launcher is stuck on screen.
67
75
  return {
68
76
  name,
69
77
  appId,
@@ -74,17 +82,16 @@ export function devClientScenario(name, appId, scheme, metroUrl) {
74
82
  url: `exp+${scheme}://expo-development-client/?url=${encodeURIComponent(metroUrl)}`,
75
83
  },
76
84
  {
77
- action: "waitAny",
85
+ action: "waitNotVisible",
86
+ selector: { textContains: "evelopment servers" },
87
+ timeoutMs: 120000,
88
+ },
89
+ {
90
+ action: "assertNoneVisible",
78
91
  selectors: [
79
- { textContains: "Downloading" },
80
- { textContains: "Connected to:" },
81
- { textContains: "Reload" },
82
- { textContains: "Continue" },
83
- { textContains: "Sign in" },
84
- { textContains: "Home" },
85
92
  { textContains: "Unable to load" },
93
+ { textContains: "There was a problem loading" },
86
94
  ],
87
- timeoutMs: 120000,
88
95
  },
89
96
  { action: "assertHealthy" },
90
97
  { action: "snapshot" },
package/npm/wizard.mjs CHANGED
@@ -23,7 +23,7 @@ if (!options.json) {
23
23
  console.log("================");
24
24
  }
25
25
 
26
- if (!options.yes && !options.json) {
26
+ if (!options.yes && !options.json && input.isTTY) {
27
27
  await promptForMissingOptions(options);
28
28
  }
29
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeno-mobile-runner",
3
- "version": "0.1.8",
3
+ "version": "0.2.1",
4
4
  "description": "Agent-native mobile app test runner for React Native, Expo, Flutter, and native Android/iOS.",
5
5
  "main": "npm/index.mjs",
6
6
  "repository": {
@@ -16,6 +16,7 @@
16
16
  "zmr-init": "npm/init-app.mjs",
17
17
  "zmr-wizard": "npm/wizard.mjs",
18
18
  "zmr-benchmark": "scripts/benchmark.sh",
19
+ "zmr-benchmark-lab": "scripts/benchmark-lab.py",
19
20
  "zmr-benchmark-command": "scripts/benchmark-command.sh",
20
21
  "zmr-compare-benchmarks": "scripts/compare-benchmarks.py",
21
22
  "zmr-device-matrix": "scripts/device-matrix.sh",
@@ -26,6 +27,7 @@
26
27
  "zmr-install-ios-shim": "scripts/install-ios-shim.sh",
27
28
  "zmr-create-android-demo-app": "scripts/create-android-demo-app.sh",
28
29
  "zmr-create-ios-demo-app": "scripts/create-ios-demo-app.sh",
30
+ "zmr-create-react-native-expo-demo-app": "scripts/create-react-native-expo-demo-app.sh",
29
31
  "zmr-demo-android": "scripts/demo-android-real.sh",
30
32
  "zmr-demo-ios": "scripts/demo-ios-real.sh"
31
33
  },
@@ -63,9 +65,12 @@
63
65
  "shims/",
64
66
  "viewer/",
65
67
  "docs/",
68
+ "!docs/assets/",
66
69
  "scripts/",
67
70
  "!scripts/build-npm-package.sh",
68
71
  "!scripts/build-release.sh",
72
+ "!scripts/capture-screenshots.sh",
73
+ "!scripts/capture-viewer-shots.mjs",
69
74
  "!scripts/ci-gate.sh",
70
75
  "!scripts/coverage.sh",
71
76
  "!scripts/generate-homebrew-formula.mjs",
Binary file
Binary file
Binary file
Binary file