@deeeed/metamask-harness 0.36.0 → 0.38.0

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 (140) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/adapters/extension/artifact-runtime-state.cjs +138 -0
  3. package/adapters/extension/inject.mjs +2 -0
  4. package/adapters/extension/launch-browser.cjs +24 -0
  5. package/adapters/extension/live.sh +82 -12
  6. package/adapters/extension/readiness.mjs +77 -36
  7. package/adapters/extension/snapshot-dist.sh +88 -3
  8. package/adapters/extension/verify.sh +6 -2
  9. package/adapters/manifest.json +17 -1
  10. package/adapters/shared/log-tui.mjs +1 -1
  11. package/dist/adapters/extension/artifact-integrity.js +38 -0
  12. package/dist/adapters/extension/extension-id.js +23 -4
  13. package/dist/adapters/extension/release-artifact.js +386 -0
  14. package/dist/adapters/extension/runtime-decision.js +164 -20
  15. package/dist/adapters/extension/runtime.js +169 -3
  16. package/dist/adapters/mobile/provision.js +38 -3
  17. package/dist/adapters/mobile/release-artifact-state.js +124 -0
  18. package/dist/adapters/mobile/release-artifact.js +295 -0
  19. package/dist/adapters.js +78 -6
  20. package/dist/app-lifecycle.js +4 -3
  21. package/dist/command-contract.js +16 -0
  22. package/dist/commands/call.js +6 -2
  23. package/dist/commands/launch/extension.js +74 -9
  24. package/dist/commands/launch/index.js +3 -0
  25. package/dist/commands/launch/mobile.js +2 -0
  26. package/dist/commands/provision.js +2 -0
  27. package/dist/commands/run-engine.js +109 -14
  28. package/dist/commands/run.js +4 -2
  29. package/dist/commands/runtime-launch.js +178 -10
  30. package/dist/heal-bounds.js +1 -1
  31. package/dist/live-adapter-contract.js +3 -1
  32. package/dist/metamask-action-validation.js +47 -1
  33. package/dist/mm-harness-cli.js +31 -1
  34. package/dist/recipe-security.js +40 -0
  35. package/dist/run-diagnostics.js +1 -1
  36. package/dist/runner.js +3 -0
  37. package/docs/RELEASE-QA-CAPABILITY-MAP.md +125 -0
  38. package/library/actions/extension/assets/finish_send.mjs +128 -0
  39. package/library/actions/extension/assets/import_custom_token.mjs +227 -0
  40. package/library/actions/extension/assets/prepare_send.mjs +53 -0
  41. package/library/actions/extension/assets/read_details.mjs +57 -0
  42. package/library/actions/extension/assets/read_visible_state.mjs +50 -0
  43. package/library/actions/extension/assets/review_send.mjs +23 -0
  44. package/library/actions/extension/assets/select_network_scope.mjs +30 -0
  45. package/library/actions/extension/assets/send.mjs +104 -0
  46. package/library/actions/extension/assets/set_custom_gas.mjs +56 -0
  47. package/library/actions/extension/assets/set_token_visibility.mjs +129 -0
  48. package/library/actions/extension/assets/verify_sorting.mjs +89 -0
  49. package/library/actions/extension/networks/add_chainlist.mjs +94 -0
  50. package/library/actions/extension/networks/add_custom.mjs +123 -0
  51. package/library/actions/extension/networks/custom_network.mjs +103 -0
  52. package/library/actions/extension/networks/read_visible_state.mjs +50 -0
  53. package/library/actions/extension/networks/remove_custom.mjs +68 -0
  54. package/library/actions/extension/perps/close_orders.mjs +1 -0
  55. package/library/actions/extension/perps/close_visible_position.mjs +133 -0
  56. package/library/actions/extension/perps/compare_provider_market.mjs +109 -0
  57. package/library/actions/extension/perps/edit_margin.mjs +82 -0
  58. package/library/actions/extension/perps/open_balance_action.mjs +58 -0
  59. package/library/actions/extension/perps/open_position_action.mjs +80 -0
  60. package/library/actions/extension/perps/perps.mjs +196 -2
  61. package/library/actions/extension/perps/place_order.mjs +5 -2
  62. package/library/actions/extension/perps/read_eligibility.mjs +34 -0
  63. package/library/actions/extension/perps/read_funds_confirmation.mjs +69 -0
  64. package/library/actions/extension/perps/read_snapshot.mjs +470 -0
  65. package/library/actions/extension/perps/read_visible_state.mjs +98 -0
  66. package/library/actions/extension/perps/select_activity_filter.mjs +44 -0
  67. package/library/actions/extension/perps/select_market_filter.mjs +71 -0
  68. package/library/actions/extension/perps/set_market_favorite.mjs +47 -0
  69. package/library/actions/extension/perps/update_position_tpsl.mjs +106 -0
  70. package/library/actions/extension/platform/cdp.mjs +109 -3
  71. package/library/actions/extension/settings/set_basic_functionality.mjs +58 -0
  72. package/library/actions/extension/swap_bridge/read_visible_state.mjs +71 -0
  73. package/library/actions/extension/swap_bridge/select_assets.mjs +103 -0
  74. package/library/actions/extension/swap_bridge/set_amount.mjs +36 -0
  75. package/library/actions/extension/swap_bridge/set_max_amount.mjs +58 -0
  76. package/library/actions/extension/swap_bridge/set_slippage.mjs +121 -0
  77. package/library/actions/extension/swap_bridge/submit_transaction.mjs +363 -0
  78. package/library/actions/extension/ui/activate_and_observe.mjs +96 -0
  79. package/library/actions/extension/ui/navigate.mjs +264 -8
  80. package/library/actions/extension/wallet/ensure_unlocked.mjs +2 -6
  81. package/library/actions/extension/wallet/import.mjs +13 -46
  82. package/library/actions/extension/wallet/lock.mjs +35 -0
  83. package/library/actions/extension/wallet/secret-input.mjs +98 -0
  84. package/library/actions/extension/wallet/select_account.mjs +71 -7
  85. package/library/actions/mobile/assets/read_visible_state.mjs +37 -0
  86. package/library/actions/mobile/perps/read_visible_state.mjs +31 -0
  87. package/library/actions/mobile/platform/native-session-name.mjs +5 -0
  88. package/library/actions/mobile/platform/observe-ui.mjs +86 -2
  89. package/library/actions/mobile/swap_bridge/native-session.mjs +45 -0
  90. package/library/actions/mobile/swap_bridge/read_visible_state.mjs +31 -0
  91. package/library/actions/mobile/swap_bridge/select_assets.mjs +94 -0
  92. package/library/actions/mobile/swap_bridge/set_amount.mjs +73 -0
  93. package/library/actions/mobile/ui/native-navigation.mjs +265 -0
  94. package/library/actions/mobile/ui/navigate.mjs +10 -1
  95. package/library/actions/mobile/wallet/ensure_unlocked.mjs +17 -0
  96. package/library/actions/mobile/wallet/import.mjs +71 -2
  97. package/library/actions/mobile/wallet/native-ui.mjs +512 -0
  98. package/library/actions/mobile/wallet/read_state.mjs +16 -0
  99. package/library/actions/mobile/wallet/reset.mjs +17 -4
  100. package/library/actions/shared/assets/visible-state.mjs +176 -0
  101. package/library/actions/shared/networks/visible-state.mjs +70 -0
  102. package/library/actions/shared/perps/visible-state.mjs +354 -0
  103. package/library/actions/shared/swap-bridge/transaction.mjs +54 -0
  104. package/library/actions/shared/swap-bridge/visible-state.mjs +97 -0
  105. package/library/actions/shared/ui/locators.mjs +7 -0
  106. package/library/manifests/extension.action-manifest.json +1366 -184
  107. package/library/manifests/mobile.action-manifest.json +235 -1
  108. package/library/recipes/assets/release-custom-gas-send.recipe.json +88 -0
  109. package/library/recipes/assets/release-custom-network-token-persistence.recipe.json +106 -0
  110. package/library/recipes/assets/release-native-max-cancel.recipe.json +70 -0
  111. package/library/recipes/assets/release-native-max-send.recipe.json +78 -0
  112. package/library/recipes/assets/release-own-account-send.recipe.json +80 -0
  113. package/library/recipes/assets/release-token-address-import.recipe.json +47 -0
  114. package/library/recipes/assets/release-token-details.recipe.json +48 -0
  115. package/library/recipes/assets/release-token-hide-readd.recipe.json +55 -0
  116. package/library/recipes/assets/release-token-list.recipe.json +75 -0
  117. package/library/recipes/assets/release-token-persistence.recipe.json +73 -0
  118. package/library/recipes/assets/release-token-search-import.recipe.json +40 -0
  119. package/library/recipes/assets/release-token-sorting.recipe.json +29 -0
  120. package/library/recipes/extension/networks/release-balance-stability.recipe.json +51 -0
  121. package/library/recipes/extension/perps/release-live-limit-order.recipe.json +104 -0
  122. package/library/recipes/extension/perps/release-order-entry.recipe.json +155 -0
  123. package/library/recipes/extension/perps/release-order-validation.recipe.json +220 -0
  124. package/library/recipes/extension/runner/action-validation.recipe.json +12 -1
  125. package/library/recipes/perps/release-activity-filters.recipe.json +51 -0
  126. package/library/recipes/perps/release-funds-flows.recipe.json +76 -0
  127. package/library/recipes/perps/release-live-market-order.recipe.json +122 -0
  128. package/library/recipes/perps/release-market-details.recipe.json +83 -0
  129. package/library/recipes/perps/release-market-search.recipe.json +79 -0
  130. package/library/recipes/perps/release-watchlist.recipe.json +99 -0
  131. package/library/recipes/swap-bridge/release-custom-slippage.recipe.json +90 -0
  132. package/library/recipes/swap-bridge/release-live-max-swap.recipe.json +99 -0
  133. package/library/recipes/swap-bridge/release-live-swap.recipe.json +101 -0
  134. package/library/recipes/swap-bridge/release-max-quote.recipe.json +72 -0
  135. package/library/recipes/swap-bridge/release-quote.recipe.json +72 -0
  136. package/library/recipes/swap-bridge/release-surface.recipe.json +43 -0
  137. package/library/recipes/wallet/import.recipe.json +20 -1
  138. package/library/recipes/wallet/reset-import.recipe.json +20 -1
  139. package/package.json +6 -5
  140. package/scripts/validate-release-report.mjs +319 -0
@@ -0,0 +1,97 @@
1
+ function text(value) {
2
+ if (typeof value !== 'string') return undefined;
3
+ const normalized = value.replace(/\s+/gu, ' ').trim();
4
+ return normalized ? normalized.slice(0, 500) : undefined;
5
+ }
6
+
7
+ function symbol(value) {
8
+ if (value == null) return undefined;
9
+ const normalized = String(value).trim().toUpperCase();
10
+ if (!normalized) return undefined;
11
+ return normalized;
12
+ }
13
+
14
+ export function resolveVisibleSwapBridgeOptions(node = {}) {
15
+ return {
16
+ sourceSymbol: symbol(node.source_symbol),
17
+ destinationSymbol: symbol(node.destination_symbol),
18
+ requireAmountInputs: node.require_amount_inputs !== false,
19
+ requireSettings: node.require_settings === true,
20
+ };
21
+ }
22
+
23
+ function normalizedItem(source) {
24
+ const testId = text(source?.testId ?? source?.test_id ?? source?.identifier);
25
+ if (!testId) return undefined;
26
+ return {
27
+ testId,
28
+ label: text(source?.label ?? source?.text) ?? null,
29
+ value: text(source?.value) ?? null,
30
+ enabled: source?.enabled !== false,
31
+ visible: source?.visible !== false,
32
+ };
33
+ }
34
+
35
+ function tokenLabel(item) {
36
+ return text(item?.label ?? item?.value) ?? null;
37
+ }
38
+
39
+ function matchesSymbol(value, expected) {
40
+ if (!expected) return true;
41
+ return new RegExp(`(^|[^A-Z0-9])${expected.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&')}([^A-Z0-9]|$)`, 'u')
42
+ .test(String(value ?? '').toUpperCase());
43
+ }
44
+
45
+ export function normalizeVisibleSwapBridgeState(raw, options, platform) {
46
+ const items = [...(raw.items ?? []), ...(raw.offscreenItems ?? [])]
47
+ .map(normalizedItem)
48
+ .filter(Boolean);
49
+ const unique = Array.from(new Map(items.map((item) => [item.testId, item])).values());
50
+ const byId = new Map(unique.map((item) => [item.testId, item]));
51
+ const sourceAsset = tokenLabel(byId.get(platform === 'extension' ? 'bridge-source-button' : 'source-token-area'));
52
+ const destinationAsset = tokenLabel(byId.get(platform === 'extension' ? 'bridge-destination-button' : 'dest-token-area'));
53
+ const sourceInput = byId.get(platform === 'extension' ? 'from-amount' : 'source-token-area-input');
54
+ const destinationInput = byId.get(platform === 'extension' ? 'to-amount' : 'dest-token-area-input');
55
+ const settings = byId.get(platform === 'extension' ? 'bridge__header-settings-button' : 'bridge-slippage-settings-button');
56
+ const noQuotes = byId.get(platform === 'extension' ? 'prepare-swap-page-error' : 'bridge-no-quotes');
57
+ const missingPrice = byId.get(platform === 'extension' ? 'bridge-missing-price-banner' : 'bridge-missing-price-banner');
58
+ const state = {
59
+ source: 'visible-ui',
60
+ platform,
61
+ surface: 'swap',
62
+ ...(text(raw.route) ? { route: text(raw.route) } : {}),
63
+ ...(text(raw.title) ? { title: text(raw.title) } : {}),
64
+ sourceAsset,
65
+ destinationAsset,
66
+ sourceAmount: text(sourceInput?.value ?? sourceInput?.label) ?? null,
67
+ destinationAmount: text(destinationInput?.value ?? destinationInput?.label) ?? null,
68
+ controls: unique.slice(0, 100),
69
+ features: {
70
+ 'amount-inputs': Boolean(sourceInput && destinationInput),
71
+ settings: Boolean(settings),
72
+ 'asset-selectors': Boolean(sourceAsset && destinationAsset),
73
+ },
74
+ quoteState: noQuotes ? 'unavailable' : missingPrice ? 'missing-price' : 'idle-or-ready',
75
+ truncated: raw.truncated === true || unique.length > 100,
76
+ };
77
+ assertVisibleSwapBridgeState(state, options);
78
+ return state;
79
+ }
80
+
81
+ function assertVisibleSwapBridgeState(state, options) {
82
+ if (!state.features['asset-selectors']) {
83
+ throw new Error('Visible Swap source and destination token selectors were not both observed.');
84
+ }
85
+ if (options.requireAmountInputs && !state.features['amount-inputs']) {
86
+ throw new Error('Visible Swap source and destination amount inputs were not both observed.');
87
+ }
88
+ if (options.requireSettings && !state.features.settings) {
89
+ throw new Error('Visible Swap settings control was not observed.');
90
+ }
91
+ if (!matchesSymbol(state.sourceAsset, options.sourceSymbol)) {
92
+ throw new Error(`Expected visible Swap source ${options.sourceSymbol}, but observed ${JSON.stringify(state.sourceAsset)}.`);
93
+ }
94
+ if (!matchesSymbol(state.destinationAsset, options.destinationSymbol)) {
95
+ throw new Error(`Expected visible Swap destination ${options.destinationSymbol}, but observed ${JSON.stringify(state.destinationAsset)}.`);
96
+ }
97
+ }
@@ -30,6 +30,7 @@ export function locatorsFromObservation(observation) {
30
30
  const leftBest = left.suggestions[0];
31
31
  const rightBest = right.suggestions[0];
32
32
  return (
33
+ locatorPriority(left) - locatorPriority(right) ||
33
34
  confidenceRank(leftBest?.confidence) - confidenceRank(rightBest?.confidence) ||
34
35
  strategyRank(leftBest?.strategy) - strategyRank(rightBest?.strategy)
35
36
  );
@@ -44,6 +45,12 @@ export function locatorsFromObservation(observation) {
44
45
  };
45
46
  }
46
47
 
48
+ function locatorPriority(locator) {
49
+ const role = text(locator.role)?.toLowerCase() ?? '';
50
+ if (INTERACTIVE_ROLE_PARTS.some((part) => role.includes(part))) return 0;
51
+ return locator.description.includes('label=') ? 1 : 2;
52
+ }
53
+
47
54
  export function requireVisibleObservation(result) {
48
55
  const value = record(result);
49
56
  const observations = record(value.observations);