@deeeed/metamask-harness 0.33.3 → 0.34.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 (45) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/adapters/manifest.json +0 -8
  3. package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +32 -5
  4. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +91 -24
  5. package/adapters/mobile/launch-metro.cjs +6 -4
  6. package/adapters/mobile/open-device.sh +164 -15
  7. package/adapters/mobile/start-metro.sh +165 -93
  8. package/adapters/mobile/stop-metro.sh +1 -0
  9. package/dist/adapters/mobile/prepare.js +14 -0
  10. package/dist/adapters/mobile/video-recorder.js +284 -0
  11. package/dist/adapters.js +225 -35
  12. package/dist/command-contract.js +1 -0
  13. package/dist/commands/device-target.js +17 -6
  14. package/dist/commands/launch/index.js +36 -5
  15. package/dist/commands/launch/mobile.js +10 -3
  16. package/dist/commands/run-engine.js +34 -2
  17. package/dist/devices.js +9 -2
  18. package/dist/heal-bounds.js +10 -0
  19. package/dist/live-adapter-contract.js +7 -0
  20. package/dist/mm-harness-cli.js +4 -2
  21. package/dist/recipe-security.js +1 -0
  22. package/dist/recording-target.js +23 -5
  23. package/dist/runner.js +107 -6
  24. package/dist/runtime-context.js +33 -1
  25. package/docs/RECIPES.md +41 -0
  26. package/library/actions/mobile/app/network-control.mjs +135 -0
  27. package/library/actions/mobile/app/network.mjs +4 -0
  28. package/library/actions/mobile/perps/capture_performance.mjs +4 -0
  29. package/library/actions/mobile/perps/performance-capture.mjs +465 -0
  30. package/library/actions/mobile/perps/perps.mjs +16 -0
  31. package/library/actions/mobile/platform/bridge.mjs +121 -3
  32. package/library/actions/mobile/wallet/ensure_unlocked.mjs +56 -12
  33. package/library/actions/mobile/wallet/select_account.mjs +21 -17
  34. package/library/manifests/mobile.action-manifest.json +148 -1
  35. package/library/recipes/mobile/perps/performance.homepage.android-background-reconnect.recipe.json +159 -0
  36. package/library/recipes/mobile/perps/performance.homepage.android-background-short.recipe.json +157 -0
  37. package/library/recipes/mobile/perps/performance.homepage.android-cold-disk-cache.recipe.json +165 -0
  38. package/library/recipes/mobile/perps/performance.homepage.android-cold-no-cache.recipe.json +139 -0
  39. package/library/recipes/mobile/perps/performance.homepage.android-network-recovery.recipe.json +149 -0
  40. package/library/recipes/mobile/perps/performance.homepage.ios-background-reconnect.recipe.json +110 -0
  41. package/library/recipes/mobile/perps/performance.homepage.ios-background-short.recipe.json +108 -0
  42. package/library/recipes/mobile/perps/performance.homepage.ios-cold-disk-cache.recipe.json +122 -0
  43. package/library/recipes/mobile/perps/performance.homepage.ios-cold-no-cache.recipe.json +95 -0
  44. package/package.json +1 -1
  45. package/adapters/mobile/metro-config.cjs +0 -93
@@ -0,0 +1,122 @@
1
+ {
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
+ "title": "iOS Homepage Perps cold disk-cache proof",
4
+ "description": "Warms the Perps disk cache without creating exchange state, restarts the assigned iOS app, and validates disk hydration plus fresh trending content at the visible Homepage endpoint.",
5
+ "workflow": {
6
+ "entry": "ensure-wallet",
7
+ "nodes": {
8
+ "ensure-wallet": {
9
+ "action": "metamask.wallet.ensure_unlocked",
10
+ "intent": "Use the existing simulator fixture",
11
+ "next": "open-wallet-home-before"
12
+ },
13
+ "open-wallet-home-before": {
14
+ "action": "ui.navigate",
15
+ "page": "home",
16
+ "intent": "Open Wallet Home so current Perps streams are cached",
17
+ "timeout_ms": 30000,
18
+ "next": "wait-perps-before"
19
+ },
20
+ "wait-perps-before": {
21
+ "action": "ui.wait_for",
22
+ "test_id": "homepage-section-title-perps",
23
+ "expected": "present",
24
+ "intent": "Require Perps content before restart",
25
+ "timeout_ms": 30000,
26
+ "next": "show-perps-before"
27
+ },
28
+ "show-perps-before": {
29
+ "action": "ui.scroll",
30
+ "test_id": "homepage-section-title-perps",
31
+ "scroll_into_view": true,
32
+ "animated": true,
33
+ "settle": true,
34
+ "intent": "Make current Perps data visible and cacheable",
35
+ "timeout_ms": 30000,
36
+ "next": "warm-disk-cache"
37
+ },
38
+ "warm-disk-cache": {
39
+ "action": "wait",
40
+ "duration_ms": 2500,
41
+ "intent": "Allow persisted stream cache writes to settle",
42
+ "next": "start-performance-capture"
43
+ },
44
+ "start-performance-capture": {
45
+ "action": "metamask.perps.capture_performance",
46
+ "phase": "start",
47
+ "intent": "Start evidence before cold process restart",
48
+ "next": "restart-app"
49
+ },
50
+ "restart-app": {
51
+ "action": "app.lifecycle",
52
+ "command": "restart",
53
+ "settle_ms": 15000,
54
+ "runtime_ready_timeout_ms": 60000,
55
+ "intent": "Cold-restart while preserving persisted app data",
56
+ "next": "unlock-after-restart"
57
+ },
58
+ "unlock-after-restart": {
59
+ "action": "metamask.wallet.ensure_unlocked",
60
+ "intent": "Restore the same fixture after cold launch",
61
+ "next": "open-wallet-home-after"
62
+ },
63
+ "open-wallet-home-after": {
64
+ "action": "ui.navigate",
65
+ "page": "home",
66
+ "intent": "Return to Wallet Home after disk hydration",
67
+ "timeout_ms": 30000,
68
+ "next": "wait-perps-after"
69
+ },
70
+ "wait-perps-after": {
71
+ "action": "ui.wait_for",
72
+ "test_id": "homepage-section-title-perps",
73
+ "expected": "present",
74
+ "intent": "Require the Perps section after cold launch",
75
+ "timeout_ms": 30000,
76
+ "next": "show-perps-after"
77
+ },
78
+ "show-perps-after": {
79
+ "action": "ui.scroll",
80
+ "test_id": "homepage-section-title-perps",
81
+ "scroll_into_view": true,
82
+ "animated": true,
83
+ "settle": true,
84
+ "intent": "Bring disk-hydrated/fresh content into the viewport",
85
+ "timeout_ms": 30000,
86
+ "next": "capture-after"
87
+ },
88
+ "capture-after": {
89
+ "action": "ui.screenshot",
90
+ "path": "screenshots/ios-homepage-perps-cold-disk-cache.png",
91
+ "intent": "Preserve visible cold disk-cache content",
92
+ "next": "settle-frame"
93
+ },
94
+ "settle-frame": {
95
+ "action": "wait",
96
+ "duration_ms": 2500,
97
+ "intent": "Allow the visible next-frame checkpoint to finish",
98
+ "next": "finish-performance-capture"
99
+ },
100
+ "finish-performance-capture": {
101
+ "action": "metamask.perps.capture_performance",
102
+ "phase": "end",
103
+ "required_stages": [
104
+ "disk_cache_hydrated",
105
+ "viewport_demand",
106
+ "socket_received",
107
+ "react_commit",
108
+ "next_frame_checkpoint"
109
+ ],
110
+ "required_lifecycles": ["cold_disk_cache"],
111
+ "required_sources": ["disk_cache", "fresh_socket"],
112
+ "required_fresh_content_variants": ["trending"],
113
+ "intent": "Require cold disk hydration and fresh visible content",
114
+ "next": "done"
115
+ },
116
+ "done": {
117
+ "action": "end",
118
+ "status": "pass"
119
+ }
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,95 @@
1
+ {
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
+ "title": "iOS Homepage Perps cold start without cache proof",
4
+ "description": "Clears only Perps performance caches, restarts the assigned iOS app without replacing wallet state, and validates fresh trending content at the visible Homepage endpoint.",
5
+ "workflow": {
6
+ "entry": "ensure-wallet",
7
+ "nodes": {
8
+ "ensure-wallet": {
9
+ "action": "metamask.wallet.ensure_unlocked",
10
+ "intent": "Preserve and unlock the existing simulator fixture",
11
+ "next": "start-performance-capture"
12
+ },
13
+ "start-performance-capture": {
14
+ "action": "metamask.perps.capture_performance",
15
+ "phase": "start",
16
+ "intent": "Start evidence before cache removal and process restart",
17
+ "next": "clear-perps-caches"
18
+ },
19
+ "clear-perps-caches": {
20
+ "action": "metamask.perps.clear_performance_caches",
21
+ "intent": "Remove only Perps persisted market and user snapshots",
22
+ "next": "restart-app"
23
+ },
24
+ "restart-app": {
25
+ "action": "app.lifecycle",
26
+ "command": "restart",
27
+ "settle_ms": 15000,
28
+ "runtime_ready_timeout_ms": 60000,
29
+ "intent": "Cold-restart the assigned iOS app without deleting wallet state",
30
+ "next": "unlock-after-restart"
31
+ },
32
+ "unlock-after-restart": {
33
+ "action": "metamask.wallet.ensure_unlocked",
34
+ "intent": "Restore the same fixture after the cold process launch",
35
+ "next": "open-wallet-home"
36
+ },
37
+ "open-wallet-home": {
38
+ "action": "ui.navigate",
39
+ "page": "home",
40
+ "intent": "Open Wallet Home after the no-cache cold launch",
41
+ "timeout_ms": 30000,
42
+ "next": "wait-perps-section"
43
+ },
44
+ "wait-perps-section": {
45
+ "action": "ui.wait_for",
46
+ "test_id": "homepage-section-title-perps",
47
+ "expected": "present",
48
+ "intent": "Require the Perps section after cold launch",
49
+ "timeout_ms": 30000,
50
+ "next": "show-perps-section"
51
+ },
52
+ "show-perps-section": {
53
+ "action": "ui.scroll",
54
+ "test_id": "homepage-section-title-perps",
55
+ "scroll_into_view": true,
56
+ "animated": true,
57
+ "settle": true,
58
+ "intent": "Bring fresh Perps content into the viewport",
59
+ "timeout_ms": 30000,
60
+ "next": "capture-after"
61
+ },
62
+ "capture-after": {
63
+ "action": "ui.screenshot",
64
+ "path": "screenshots/ios-homepage-perps-cold-no-cache.png",
65
+ "intent": "Preserve visible cold-no-cache content",
66
+ "next": "settle-frame"
67
+ },
68
+ "settle-frame": {
69
+ "action": "wait",
70
+ "duration_ms": 2500,
71
+ "intent": "Allow the visible next-frame checkpoint to finish",
72
+ "next": "finish-performance-capture"
73
+ },
74
+ "finish-performance-capture": {
75
+ "action": "metamask.perps.capture_performance",
76
+ "phase": "end",
77
+ "required_stages": [
78
+ "viewport_demand",
79
+ "socket_received",
80
+ "react_commit",
81
+ "next_frame_checkpoint"
82
+ ],
83
+ "required_lifecycles": ["cold_no_cache"],
84
+ "required_sources": ["fresh_socket"],
85
+ "required_fresh_content_variants": ["trending"],
86
+ "intent": "Require cold-no-cache lifecycle and fresh visible trending content",
87
+ "next": "done"
88
+ },
89
+ "done": {
90
+ "action": "end",
91
+ "status": "pass"
92
+ }
93
+ }
94
+ }
95
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.33.3",
3
+ "version": "0.34.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"
@@ -1,93 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs');
4
- const { createRequire } = require('node:module');
5
- const path = require('node:path');
6
- const { pathToFileURL } = require('node:url');
7
-
8
- function escapeRegExp(value) {
9
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
10
- }
11
-
12
- function appendBlockList(blockList, pattern) {
13
- if (Array.isArray(blockList)) return [...blockList, pattern];
14
- return blockList instanceof RegExp ? [blockList, pattern] : [pattern];
15
- }
16
-
17
- function blockListFlags(blockList) {
18
- const first = Array.isArray(blockList) ? blockList[0] : blockList;
19
- return first instanceof RegExp ? first.flags : '';
20
- }
21
-
22
- function resolveExpoConfigLoader(projectRoot) {
23
- const requireFromProject = createRequire(path.join(projectRoot, 'package.json'));
24
- let requireUtilsPath;
25
- try {
26
- requireUtilsPath = requireFromProject.resolve('@expo/require-utils');
27
- } catch (error) {
28
- if (error?.code !== 'MODULE_NOT_FOUND') throw error;
29
- return null;
30
- }
31
- const { loadModuleSync } = requireFromProject(requireUtilsPath);
32
- return typeof loadModuleSync === 'function' ? loadModuleSync : null;
33
- }
34
-
35
- async function loadProductConfig(configPath, baseConfig, projectRoot) {
36
- let loaded;
37
- const expoConfigLoader = resolveExpoConfigLoader(projectRoot);
38
- if (expoConfigLoader) {
39
- loaded = expoConfigLoader(configPath);
40
- } else {
41
- try {
42
- loaded = require(configPath);
43
- } catch (error) {
44
- if (error?.code !== 'ERR_REQUIRE_ESM') throw error;
45
- loaded = await import(pathToFileURL(configPath).href);
46
- }
47
- }
48
-
49
- const moduleValue = await loaded;
50
- const exported =
51
- moduleValue?.__esModule ||
52
- moduleValue?.[Symbol.toStringTag] === 'Module'
53
- ? moduleValue.default
54
- : moduleValue;
55
- const config = await exported;
56
- return typeof config === 'function' ? config(baseConfig) : config;
57
- }
58
-
59
- module.exports = async function harnessMetroConfig(baseConfig) {
60
- const projectRoot = path.resolve(
61
- process.env.MM_HARNESS_METRO_PROJECT_ROOT || process.cwd(),
62
- );
63
- const productConfigPath = path.resolve(
64
- process.env.MM_HARNESS_METRO_BASE_CONFIG ||
65
- path.join(projectRoot, 'metro.config.js'),
66
- );
67
- let productConfig = baseConfig;
68
- if (fs.existsSync(productConfigPath)) {
69
- productConfig = await loadProductConfig(
70
- productConfigPath,
71
- baseConfig,
72
- projectRoot,
73
- );
74
- }
75
-
76
- const tempRoot = path.join(projectRoot, 'temp');
77
- const existingBlockList =
78
- productConfig.resolver?.blockList ?? baseConfig.resolver?.blockList;
79
- const tempPattern = new RegExp(
80
- `^${escapeRegExp(tempRoot)}(?:${escapeRegExp(path.sep)}|$)`,
81
- blockListFlags(existingBlockList),
82
- );
83
- return {
84
- ...productConfig,
85
- resolver: {
86
- ...productConfig.resolver,
87
- blockList: appendBlockList(
88
- existingBlockList,
89
- tempPattern,
90
- ),
91
- },
92
- };
93
- };