@deeeed/metamask-harness 0.48.0 → 0.49.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 (177) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/commands/help.js +17 -5
  3. package/dist/funding-execution-context.js +19 -4
  4. package/dist/metamask-action-validation.js +3 -2
  5. package/dist/mm-harness-cli.js +2 -1
  6. package/docs/RECIPES.md +2 -2
  7. package/library/actions/core/perps/_controller.mjs +14 -24
  8. package/library/actions/core/perps/assert_orders.mjs +3 -5
  9. package/library/actions/core/perps/assert_positions.mjs +3 -5
  10. package/library/actions/core/perps/close_orders.mjs +28 -4
  11. package/library/actions/core/perps/close_positions.mjs +53 -12
  12. package/library/actions/core/perps/edit_order.mjs +2 -0
  13. package/library/actions/core/perps/ensure_orders.mjs +3 -4
  14. package/library/actions/core/perps/ensure_positions.mjs +4 -5
  15. package/library/actions/core/perps/place_order.mjs +7 -14
  16. package/library/actions/core/perps/read_account.mjs +2 -0
  17. package/library/actions/core/perps/read_orders.mjs +2 -0
  18. package/library/actions/core/perps/read_positions.mjs +3 -1
  19. package/library/actions/core/perps/read_snapshot.mjs +3 -0
  20. package/library/actions/core/perps/start_state.mjs +3 -14
  21. package/library/actions/core/perps/teardown_state.mjs +3 -14
  22. package/library/actions/core/perps/update_position_tpsl.mjs +13 -19
  23. package/library/actions/core/wallet/list_accounts.mjs +3 -0
  24. package/library/actions/extension/analytics/consent.mjs +2 -0
  25. package/library/actions/extension/analytics/set_consent.mjs +2 -0
  26. package/library/actions/extension/assets/home-token-identity.mjs +1 -0
  27. package/library/actions/extension/assets/import_custom_token.mjs +2 -0
  28. package/library/actions/extension/assets/open_details.mjs +1 -0
  29. package/library/actions/extension/assets/prepare_send.mjs +1 -0
  30. package/library/actions/extension/assets/read_details.mjs +1 -0
  31. package/library/actions/extension/assets/read_visible_state.mjs +1 -0
  32. package/library/actions/extension/assets/review_send.mjs +1 -0
  33. package/library/actions/extension/assets/select_network_scope.mjs +1 -0
  34. package/library/actions/extension/assets/send.mjs +2 -0
  35. package/library/actions/extension/assets/set_custom_gas.mjs +1 -0
  36. package/library/actions/extension/assets/set_token_visibility.mjs +2 -0
  37. package/library/actions/extension/assets/verify_sorting.mjs +1 -0
  38. package/library/actions/extension/deeplink/open.mjs +1 -0
  39. package/library/actions/extension/networks/add_chainlist.mjs +2 -0
  40. package/library/actions/extension/networks/add_custom.mjs +2 -0
  41. package/library/actions/extension/networks/custom_network.mjs +2 -0
  42. package/library/actions/extension/networks/read_visible_state.mjs +1 -0
  43. package/library/actions/extension/networks/remove_custom.mjs +1 -0
  44. package/library/actions/extension/performance/_navigation-memory.mjs +2 -0
  45. package/library/actions/extension/performance/compare_idle_navigation_memory.mjs +2 -0
  46. package/library/actions/extension/performance/measure_detached_dom.mjs +2 -0
  47. package/library/actions/extension/performance/measure_navigation_memory.mjs +2 -0
  48. package/library/actions/extension/perps/assert_orders.mjs +1 -0
  49. package/library/actions/extension/perps/assert_positions.mjs +1 -0
  50. package/library/actions/extension/perps/assert_visible_consistency.mjs +2 -0
  51. package/library/actions/extension/perps/close_orders.mjs +4 -1
  52. package/library/actions/extension/perps/close_positions.mjs +1 -0
  53. package/library/actions/extension/perps/close_visible_position.mjs +159 -10
  54. package/library/actions/extension/perps/compare_provider_market.mjs +2 -0
  55. package/library/actions/extension/perps/edit_margin.mjs +2 -0
  56. package/library/actions/extension/perps/ensure_orders.mjs +1 -0
  57. package/library/actions/extension/perps/ensure_positions.mjs +1 -0
  58. package/library/actions/extension/perps/mutation-receipt.mjs +54 -3
  59. package/library/actions/extension/perps/open_balance_action.mjs +1 -0
  60. package/library/actions/extension/perps/open_position_action.mjs +1 -0
  61. package/library/actions/extension/perps/perps.mjs +401 -25
  62. package/library/actions/extension/perps/place_order.mjs +1 -0
  63. package/library/actions/extension/perps/read_eligibility.mjs +1 -0
  64. package/library/actions/extension/perps/read_funds_confirmation.mjs +2 -0
  65. package/library/actions/extension/perps/read_orders.mjs +1 -0
  66. package/library/actions/extension/perps/read_positions.mjs +1 -0
  67. package/library/actions/extension/perps/read_snapshot.mjs +2 -0
  68. package/library/actions/extension/perps/read_visible_state.mjs +2 -0
  69. package/library/actions/extension/perps/search_markets.mjs +1 -0
  70. package/library/actions/extension/perps/select_activity_filter.mjs +1 -0
  71. package/library/actions/extension/perps/select_market_filter.mjs +1 -0
  72. package/library/actions/extension/perps/set_market_favorite.mjs +1 -0
  73. package/library/actions/extension/perps/state.mjs +1 -0
  74. package/library/actions/extension/perps/update_position_tpsl.mjs +2 -0
  75. package/library/actions/extension/perps/visible-mutation-identity.mjs +2 -0
  76. package/library/actions/extension/platform/cdp.mjs +29 -2
  77. package/library/actions/extension/settings/set_basic_functionality.mjs +1 -0
  78. package/library/actions/extension/swap_bridge/quote-state.mjs +2 -0
  79. package/library/actions/extension/swap_bridge/read_visible_state.mjs +1 -0
  80. package/library/actions/extension/swap_bridge/select_assets.mjs +1 -0
  81. package/library/actions/extension/swap_bridge/set_amount.mjs +1 -0
  82. package/library/actions/extension/swap_bridge/set_max_amount.mjs +1 -0
  83. package/library/actions/extension/swap_bridge/set_slippage.mjs +1 -0
  84. package/library/actions/extension/ui/locators.mjs +1 -0
  85. package/library/actions/extension/ui/navigate.mjs +2 -0
  86. package/library/actions/extension/wallet/ensure_unlocked.mjs +2 -0
  87. package/library/actions/extension/wallet/import.mjs +2 -0
  88. package/library/actions/extension/wallet/list_accounts.mjs +1 -0
  89. package/library/actions/extension/wallet/lock.mjs +2 -0
  90. package/library/actions/extension/wallet/read_state.mjs +1 -0
  91. package/library/actions/extension/wallet/reset.mjs +2 -0
  92. package/library/actions/extension/wallet/secret-input.mjs +2 -0
  93. package/library/actions/extension/wallet/select_account.mjs +2 -0
  94. package/library/actions/extension/wallet/setup.mjs +1 -0
  95. package/library/actions/extension/wallet/state.mjs +2 -0
  96. package/library/actions/extension/wallet/visible-session.mjs +2 -0
  97. package/library/actions/mobile/analytics/consent-settings.mjs +1 -0
  98. package/library/actions/mobile/analytics/set_consent.mjs +1 -0
  99. package/library/actions/mobile/app/network-control.mjs +2 -0
  100. package/library/actions/mobile/app/network.mjs +1 -0
  101. package/library/actions/mobile/app/network_assert.mjs +1 -0
  102. package/library/actions/mobile/app/network_capture.mjs +1 -0
  103. package/library/actions/mobile/assets/import_custom_token.mjs +2 -0
  104. package/library/actions/mobile/assets/open_details.mjs +1 -0
  105. package/library/actions/mobile/assets/read_details.mjs +1 -0
  106. package/library/actions/mobile/assets/read_visible_state.mjs +1 -0
  107. package/library/actions/mobile/assets/set_token_visibility.mjs +2 -0
  108. package/library/actions/mobile/assets/verify_sorting.mjs +90 -7
  109. package/library/actions/mobile/deeplink/open.mjs +1 -0
  110. package/library/actions/mobile/networks/add_custom.mjs +1 -0
  111. package/library/actions/mobile/networks/network-management.mjs +1 -0
  112. package/library/actions/mobile/networks/read_visible_state.mjs +1 -0
  113. package/library/actions/mobile/networks/remove_custom.mjs +1 -0
  114. package/library/actions/mobile/perps/assert_orders.mjs +1 -0
  115. package/library/actions/mobile/perps/assert_positions.mjs +1 -0
  116. package/library/actions/mobile/perps/capture_performance.mjs +1 -0
  117. package/library/actions/mobile/perps/close_orders.mjs +1 -0
  118. package/library/actions/mobile/perps/close_positions.mjs +1 -0
  119. package/library/actions/mobile/perps/ensure_orders.mjs +1 -0
  120. package/library/actions/mobile/perps/ensure_positions.mjs +1 -0
  121. package/library/actions/mobile/perps/measure_homepage_visible.mjs +1 -0
  122. package/library/actions/mobile/perps/performance-capture.mjs +2 -0
  123. package/library/actions/mobile/perps/perps.mjs +6 -2
  124. package/library/actions/mobile/perps/place_order.mjs +1 -0
  125. package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +2 -0
  126. package/library/actions/mobile/perps/read-visible-state-loop.mjs +2 -0
  127. package/library/actions/mobile/perps/read_orders.mjs +1 -0
  128. package/library/actions/mobile/perps/read_positions.mjs +1 -0
  129. package/library/actions/mobile/perps/read_visible_state.mjs +1 -0
  130. package/library/actions/mobile/perps/search_markets.mjs +1 -0
  131. package/library/actions/mobile/perps/set_market_favorite.mjs +2 -0
  132. package/library/actions/mobile/platform/bridge.mjs +16 -4
  133. package/library/actions/mobile/platform/native-session-name.mjs +1 -0
  134. package/library/actions/mobile/platform/native-session.mjs +2 -0
  135. package/library/actions/mobile/platform/observe-ui.mjs +2 -0
  136. package/library/actions/mobile/platform/tool-paths.mjs +1 -0
  137. package/library/actions/mobile/swap_bridge/native-session.mjs +1 -0
  138. package/library/actions/mobile/swap_bridge/read_visible_state.mjs +1 -0
  139. package/library/actions/mobile/swap_bridge/select_assets.mjs +2 -0
  140. package/library/actions/mobile/swap_bridge/set_amount.mjs +1 -0
  141. package/library/actions/mobile/swap_bridge/set_max_amount.mjs +1 -0
  142. package/library/actions/mobile/swap_bridge/set_slippage.mjs +1 -0
  143. package/library/actions/mobile/swap_bridge/submit_transaction.mjs +2 -0
  144. package/library/actions/mobile/ui/locators.mjs +1 -0
  145. package/library/actions/mobile/ui/native-navigation.mjs +2 -0
  146. package/library/actions/mobile/ui/navigate.mjs +2 -0
  147. package/library/actions/mobile/wallet/ensure_unlocked.mjs +2 -0
  148. package/library/actions/mobile/wallet/home.mjs +1 -0
  149. package/library/actions/mobile/wallet/import.mjs +2 -0
  150. package/library/actions/mobile/wallet/list_accounts.mjs +1 -0
  151. package/library/actions/mobile/wallet/lock.mjs +1 -0
  152. package/library/actions/mobile/wallet/native-ui.mjs +2 -0
  153. package/library/actions/mobile/wallet/read_state.mjs +2 -0
  154. package/library/actions/mobile/wallet/reset-helper.mjs +1 -0
  155. package/library/actions/mobile/wallet/reset.mjs +1 -0
  156. package/library/actions/mobile/wallet/select_account.mjs +2 -0
  157. package/library/actions/mobile/wallet/setup.mjs +2 -0
  158. package/library/actions/shared/analytics/assert_events.mjs +3 -0
  159. package/library/actions/shared/analytics/consent.mjs +3 -0
  160. package/library/actions/shared/analytics/read_events.mjs +2 -0
  161. package/library/actions/shared/analytics/start_capture.mjs +3 -0
  162. package/library/actions/shared/app/network-artifact.mjs +2 -0
  163. package/library/actions/shared/app/network-assert.mjs +2 -0
  164. package/library/actions/shared/assets/visible-state.mjs +2 -0
  165. package/library/actions/shared/deeplink/url.mjs +3 -0
  166. package/library/actions/shared/networks/visible-state.mjs +2 -0
  167. package/library/actions/shared/perps/visible-state.mjs +2 -0
  168. package/library/actions/shared/swap-bridge/transaction.mjs +3 -0
  169. package/library/actions/shared/swap-bridge/visible-state.mjs +2 -0
  170. package/library/actions/shared/ui/locators.mjs +2 -0
  171. package/library/actions/shared/wallet/import-source.mjs +3 -0
  172. package/library/manifests/core.action-manifest.json +20 -0
  173. package/library/manifests/extension.action-manifest.json +2 -2
  174. package/library/manifests/mobile.action-manifest.json +3 -3
  175. package/library/recipes/mobile/app/{lifecycle.android-smoke.recipe.json → lifecycle-smoke.recipe.json} +12 -11
  176. package/package.json +1 -1
  177. package/site/assets/help-recipes.json +188 -35
@@ -1,3 +1,4 @@
1
+ // Enter a query in the native Perps market search and wait for visible market rows.
1
2
  import { runAdapter } from '../platform/bridge.mjs';
2
3
  import {
3
4
  closeNativeSession,
@@ -1,3 +1,5 @@
1
+ // Set a visible market's watchlist state through native controls and verify the result.
2
+ // With require_unchanged, fail on a mismatched state without toggling the control.
1
3
  import { runAdapter } from '../platform/bridge.mjs';
2
4
  import {
3
5
  closeNativeSession,
@@ -1,3 +1,5 @@
1
+ // Run Mobile actions and device-targeted bridge commands with deadlines and classified errors.
2
+ // Provide runtime evaluation, verified navigation, external deeplink launch, and screenshot capture.
1
3
  import { randomUUID } from 'node:crypto';
2
4
  import { constants as fsConstants, existsSync } from 'node:fs';
3
5
  import { mkdir, mkdtemp, open, readFile, rename, rm, writeFile } from 'node:fs/promises';
@@ -666,8 +668,16 @@ async function waitForNextRouteProbe(deadline) {
666
668
 
667
669
  export async function navigate(input, route, params = {}, expectedRoute) {
668
670
  const timeoutMs = Number(input.node?.navigation_timeout_ms ?? 15000);
669
- const requestedRoute = String(expectedRoute ?? route);
670
- const requestedParams = requestedRoute === String(route) ? params : undefined;
671
+ let requestedRoute = String(expectedRoute ?? route);
672
+ let requestedParams = requestedRoute === String(route) ? params : undefined;
673
+ const nestedTarget = expectedRoute == null && typeof params?.screen === 'string';
674
+ if (nestedTarget) {
675
+ // React Navigation reports the active leaf, not its enclosing navigator.
676
+ while (typeof requestedParams?.screen === 'string') {
677
+ requestedRoute = requestedParams.screen;
678
+ requestedParams = requestedParams.params ?? {};
679
+ }
680
+ }
671
681
  const transitionCodes = [];
672
682
  let lastTransitionError = null;
673
683
  let navigateAttempts = 0;
@@ -754,13 +764,15 @@ export async function navigate(input, route, params = {}, expectedRoute) {
754
764
  input,
755
765
  ['navigate', route, JSON.stringify(params)],
756
766
  );
757
- const verifiedRoute = String(
767
+ const verifiedRoute = nestedTarget ? requestedRoute : String(
758
768
  expectedRoute ??
759
769
  (navigation && typeof navigation === 'object' && navigation.navigated
760
770
  ? navigation.navigated
761
771
  : route),
762
772
  );
763
- const verifiedParams = verifiedRoute === String(route) ? params : undefined;
773
+ const verifiedParams = nestedTarget
774
+ ? requestedParams
775
+ : verifiedRoute === String(route) ? params : undefined;
764
776
  const currentRoute = await waitForRoute(
765
777
  input,
766
778
  verifiedRoute,
@@ -1,3 +1,4 @@
1
+ // Derive native session names and device-specific state directories, honoring configured overrides.
1
2
  import path from 'node:path';
2
3
 
3
4
  export function nativeSessionName(env, device, app = 'io.metamask') {
@@ -1,3 +1,5 @@
1
+ // Create device-targeted native UI sessions for actions and accessibility snapshots.
2
+ // Opening an instrumented iOS session hides the accessibility-masking HUD.
1
3
  import { createAgentDeviceUiTransport } from '@farmslot/expo-recipe';
2
4
  import { createAgentDeviceClient } from 'agent-device';
3
5
 
@@ -1,3 +1,5 @@
1
+ // Read and normalize native accessibility hierarchies into screen and visible-item observations.
2
+ // Attempt to hide the masking HUD first and return observation failures as warnings.
1
3
  import { execFile } from 'node:child_process';
2
4
  import { promisify } from 'node:util';
3
5
  import { createAgentDeviceClient } from 'agent-device';
@@ -1,3 +1,4 @@
1
+ // Locate executable adb and idb clients, cache their paths, and export tool-path environment overrides.
1
2
  import { constants as fsConstants, accessSync, readdirSync, realpathSync, statSync } from 'node:fs';
2
3
  import os from 'node:os';
3
4
  import path from 'node:path';
@@ -1,3 +1,4 @@
1
+ // Bind shared native UI session helpers to Swap actions.
1
2
  import {
2
3
  closeNativeSession,
3
4
  createNativeSession,
@@ -1,3 +1,4 @@
1
+ // Normalize Mobile accessibility observations into visible Swap and Bridge quote state.
1
2
  import { runAdapter } from '../platform/bridge.mjs';
2
3
  import { observeNativeUi } from '../platform/observe-ui.mjs';
3
4
  import {
@@ -1,3 +1,5 @@
1
+ // Select source and destination assets through native token pickers and verify their symbols.
2
+ // Exact asset-ID selection is unsupported.
1
3
  import { runAdapter } from '../platform/bridge.mjs';
2
4
  import { resolveAssetSelectionOptions } from '../../shared/swap-bridge/transaction.mjs';
3
5
  import {
@@ -1,3 +1,4 @@
1
+ // Replace the Swap source amount through its keypad and optionally wait for a quote or no-quote state.
1
2
  import { runAdapter } from '../platform/bridge.mjs';
2
3
  import { resolveAmountOptions } from '../../shared/swap-bridge/transaction.mjs';
3
4
  import {
@@ -1,3 +1,4 @@
1
+ // Press Swap Max, verify a positive source amount, and optionally wait for the quote result.
1
2
  import { runAdapter } from '../platform/bridge.mjs';
2
3
  import { resolveAmountOptions } from '../../shared/swap-bridge/transaction.mjs';
3
4
  import {
@@ -1,3 +1,4 @@
1
+ // Apply custom Swap slippage through native controls and verify the refreshed quote displays it.
1
2
  import { runAdapter } from '../platform/bridge.mjs';
2
3
  import {
3
4
  closeNativeSession,
@@ -1,3 +1,5 @@
1
+ // Submit a live Swap or Bridge with confirm_live, including any price-impact confirmation.
2
+ // Require a stable matching quote, a completion status, and a visible Activity entry.
1
3
  import { runAdapter } from '../platform/bridge.mjs';
2
4
  import {
3
5
  closeNativeSession,
@@ -1,3 +1,4 @@
1
+ // Turn visible Mobile accessibility observations into UI locator candidates.
1
2
  import { observeNativeUi } from '../platform/observe-ui.mjs';
2
3
  import { runAdapter } from '../platform/bridge.mjs';
3
4
  import {
@@ -1,3 +1,5 @@
1
+ // Navigate Android Home, Swap, and Perps through native controls and verify visible destinations.
2
+ // Switch Perps Lite or Pro mode through visible controls on Android and iOS.
1
3
  import { createAgentDeviceUiTransport } from '@farmslot/expo-recipe';
2
4
  import { createAgentDeviceClient } from 'agent-device';
3
5
  import {
@@ -1,3 +1,5 @@
1
+ // Resolve page intents or raw routes into Mobile navigation, using native controls in opaque mode.
2
+ // Verify requested Perps market mode through visible controls.
1
3
  import {
2
4
  navigate,
3
5
  resolveMobileTarget,
@@ -1,3 +1,5 @@
1
+ // Unlock an existing wallet with a supplied or fixture password through the bridge or native UI.
2
+ // Verify wallet readiness and, for bridge sessions, a stable unlocked controller state.
1
3
  import { readFile } from 'node:fs/promises';
2
4
  import { pathToFileURL } from 'node:url';
3
5
  import {
@@ -1,3 +1,4 @@
1
+ // Open wallet home through WalletView, with a verified HomeNav fallback for older navigators.
1
2
  import { bridgeCommand, navigate, waitForRoute } from '../platform/bridge.mjs';
2
3
 
3
4
  /**
@@ -1,3 +1,5 @@
1
+ // Import a recovery phrase through onboarding or unlock an existing profile, then verify the expected account.
2
+ // Fresh imports also handle consent, interests, and supported onboarding prompts.
1
3
  import { createHash } from 'node:crypto';
2
4
  import { mkdtemp, rm, writeFile } from 'node:fs/promises';
3
5
  import os from 'node:os';
@@ -1,3 +1,4 @@
1
+ // List controller accounts with selection markers, filtered by selected, named, EVM, or all accounts.
1
2
  import { bridgeCommand, runAdapter } from '../platform/bridge.mjs';
2
3
 
3
4
  runAdapter(async (input) => {
@@ -1,3 +1,4 @@
1
+ // Lock the wallet through Android native UI and verify the login screen, or accept an already locked wallet.
1
2
  import { runAdapter } from '../platform/bridge.mjs';
2
3
  import { lockWalletThroughNativeUi } from './native-ui.mjs';
3
4
 
@@ -1,3 +1,5 @@
1
+ // Drive Android wallet onboarding, lock, unlock, and reset through native accessibility.
2
+ // Wallet observations can navigate home and open the address list to verify the selected account.
1
3
  import { createAgentDeviceUiTransport } from '@farmslot/expo-recipe';
2
4
  import { createAgentDeviceClient } from 'agent-device';
3
5
  import {
@@ -1,3 +1,5 @@
1
+ // Report the selected account and route from bridge status or native accessibility.
2
+ // Native reads navigate to wallet home and may open the address list to observe the account.
1
3
  import { bridgeCommand, runAdapter, selectBridgeStatusEntry } from '../platform/bridge.mjs';
2
4
  import { isOpaqueMobileRuntime, readWalletThroughNativeUi } from './native-ui.mjs';
3
5
 
@@ -1,3 +1,4 @@
1
+ // Reset the wallet through bridge-driven login UI and wait for the fresh onboarding control.
1
2
  import {
2
3
  bridgeCommand,
3
4
  selectBridgeStatusEntry,
@@ -1,3 +1,4 @@
1
+ // Reset the wallet through visible UI and verify fresh onboarding using the bridge or native accessibility.
1
2
  import { runAdapter } from '../platform/bridge.mjs';
2
3
  import { resetMobileWalletThroughUi } from './reset-helper.mjs';
3
4
  import { isOpaqueMobileRuntime, resetWalletThroughNativeUi } from './native-ui.mjs';
@@ -1,3 +1,5 @@
1
+ // Select a wallet account by address, ID, or name and verify the resulting selection.
2
+ // Observe ambiguous bridge write failures until the deadline without repeating the switch.
1
3
  import { pathToFileURL } from 'node:url';
2
4
  import {
3
5
  bridgeCommand,
@@ -1,3 +1,5 @@
1
+ // Validate the wallet fixture, run the Mobile setup script, and verify a selected account.
2
+ // Unlock with the fixture password when setup leaves no selected account observable.
1
3
  import { readFile } from 'node:fs/promises';
2
4
  import { spawn } from 'node:child_process';
3
5
  import path from 'node:path';
@@ -1,3 +1,6 @@
1
+ // Assert captured event counts and property values within the requested flow window.
2
+ // Observe the settling interval before deciding whether duplicate emissions occurred.
3
+
1
4
  import { runAdapter } from './_adapter.mjs';
2
5
  import { readCollected } from './collector.mjs';
3
6
 
@@ -1,3 +1,6 @@
1
+ // Normalize analytics consent inputs and reject marketing consent without participation.
2
+ // This helper validates options; platform actions perform the settings changes.
3
+
1
4
  export function consentParams(node) {
2
5
  const optional = (value, fallback) =>
3
6
  value === undefined || value === null || value === '' ? fallback : value;
@@ -1,3 +1,5 @@
1
+ // Read checkout-local collected analytics events, optionally filtered by cursor and event name.
2
+
1
3
  import { runAdapter } from './_adapter.mjs';
2
4
  import { readCollected } from './collector.mjs';
3
5
 
@@ -1,3 +1,6 @@
1
+ // Start or reuse the local analytics collector and return a cursor for this flow.
2
+ // Client routing and consent must be configured separately.
3
+
1
4
  import { runAdapter } from './_adapter.mjs';
2
5
  import { startCollector } from './collector.mjs';
3
6
 
@@ -1,3 +1,5 @@
1
+ // Resolve a network-evidence path without allowing it to escape the run's artifact directory.
2
+
1
3
  import path from 'node:path';
2
4
 
3
5
  export function resolveNetworkArtifact(artifactsDir, relativePath) {
@@ -1,3 +1,5 @@
1
+ // Validate a captured network summary and assert requested status and request-count bounds.
2
+
1
3
  import { constants } from 'node:fs';
2
4
  import { open } from 'node:fs/promises';
3
5
 
@@ -1,3 +1,5 @@
1
+ // Normalize platform asset observations and enforce symbol, count and display requirements.
2
+
1
3
  function boundedInteger(value, fallback, minimum, maximum, name) {
2
4
  const resolved = value == null ? fallback : Number(value);
3
5
  if (!Number.isInteger(resolved) || resolved < minimum || resolved > maximum) {
@@ -1,3 +1,6 @@
1
+ // Validate external HTTPS deeplink inputs and their bounded timeout.
2
+ // Signature authorization belongs to the receiving product, not this URL-shape check.
3
+
1
4
  export function externalDeeplinkUrl(node) {
2
5
  const raw = node?.url;
3
6
  if (typeof raw !== 'string' || raw.trim().length === 0) {
@@ -1,3 +1,5 @@
1
+ // Normalize network observations and compare asset state across the requested observation window.
2
+
1
3
  import {
2
4
  normalizeVisibleAssetsState,
3
5
  resolveVisibleAssetsOptions,
@@ -1,3 +1,5 @@
1
+ // Normalize platform Perps observations and enforce the requested screen, content and count claims.
2
+
1
3
  const SURFACES = new Set([
2
4
  'auto',
3
5
  'home',
@@ -1,3 +1,6 @@
1
+ // Validate cross-platform swap asset selections and positive amounts, including decimal normalization.
2
+ // Platform actions perform the actual form changes and submission.
3
+
1
4
  function requiredText(value, name) {
2
5
  const resolved = String(value ?? '').trim();
3
6
  if (!resolved) throw new Error(`metamask.swap_bridge ${name} is required.`);
@@ -1,3 +1,5 @@
1
+ // Normalize observed swap controls and enforce token, quote, settings and slippage requirements.
2
+
1
3
  function text(value) {
2
4
  if (typeof value !== 'string') return undefined;
3
5
  const normalized = value.replace(/\s+/gu, ' ').trim();
@@ -1,3 +1,5 @@
1
+ // Rank locator suggestions from observed interactive elements and enforce observation visibility.
2
+
1
3
  const LOCATOR_LIMIT = 20;
2
4
  const INTERACTIVE_ROLE_PARTS = [
3
5
  'button',
@@ -1,3 +1,6 @@
1
+ // Resolve wallet-import credentials from explicit environment or fixture sources.
2
+ // Validate method/source combinations before platform onboarding runs.
3
+
1
4
  import { readFile } from 'node:fs/promises';
2
5
  import path from 'node:path';
3
6
 
@@ -1161,6 +1161,21 @@
1161
1161
  "schema": {
1162
1162
  "type": "object",
1163
1163
  "properties": {
1164
+ "expected_positions": {
1165
+ "type": "array",
1166
+ "maxItems": 1,
1167
+ "items": {
1168
+ "type": "object",
1169
+ "required": ["coin", "size", "entryPrice"],
1170
+ "properties": {
1171
+ "coin": { "type": "string" },
1172
+ "size": { "type": ["string", "number"] },
1173
+ "entryPrice": { "type": ["string", "number"] }
1174
+ },
1175
+ "additionalProperties": true
1176
+ },
1177
+ "description": "Optional seed receipt guard. Require the selected live market, signed size and entry price to match before dispatch; close at most that quantity in its reducing direction. Empty requires no selected position. Identical netted replacements cannot be distinguished."
1178
+ },
1164
1179
  "market": {
1165
1180
  "type": "string",
1166
1181
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1569,6 +1584,11 @@
1569
1584
  "schema": {
1570
1585
  "type": "object",
1571
1586
  "properties": {
1587
+ "expected_order_ids": {
1588
+ "type": "array",
1589
+ "items": { "type": "string" },
1590
+ "description": "Optional exact receipt set. Refuse a changed selected order set before dispatch and cancel only these IDs. Empty requires no selected open orders."
1591
+ },
1572
1592
  "account": {
1573
1593
  "type": "string",
1574
1594
  "description": "Wallet fixture account name used for signing; defaults to dev1."
@@ -593,7 +593,7 @@
593
593
  "string",
594
594
  "number"
595
595
  ],
596
- "description": "Text or a typed numeric recipe parameter; numbers are entered as their decimal text."
596
+ "description": "Text or a typed numeric recipe parameter; numbers are entered as their decimal text. Use an empty string to clear the field."
597
597
  },
598
598
  "text": {
599
599
  "type": "string"
@@ -4191,7 +4191,7 @@
4191
4191
  "from_current_form": {
4192
4192
  "type": "boolean",
4193
4193
  "default": false,
4194
- "description": "Submit the already configured visible form once without navigation, field edits, or controller placement. Requires an empty position/order baseline, verified provider/network, matching market/direction/amount/leverage, and trusted funding. Preserves the current protection settings and observes the product's resulting route."
4194
+ "description": "Submit the already configured visible form once without navigation, field edits, or controller placement. Requires an empty position/order baseline, verified provider/network, matching market/direction/amount/leverage, and trusted funding. Prepared limit sizing currently requires 1x leverage. Preserves the current protection settings and observes the product's resulting route."
4195
4195
  },
4196
4196
  "market": {
4197
4197
  "type": "string",
@@ -1814,7 +1814,7 @@
1814
1814
  "execution_capabilities": ["host-read-export"]
1815
1815
  },
1816
1816
  "metamask.assets.verify_sorting": {
1817
- "description": "Select alphabetical and declining-fiat token sorting and assert the visible native row order.",
1817
+ "description": "Verify native name order under MetaMask's SES collation and declining-fiat order, then restore the original sort preference.",
1818
1818
  "schema": {
1819
1819
  "type": "object",
1820
1820
  "properties": {
@@ -3299,7 +3299,7 @@
3299
3299
  "execution_capabilities": ["app-mutation"]
3300
3300
  },
3301
3301
  "metamask.perps.ensure_mode": {
3302
- "description": "Ensure the viewport-visible Perps market-detail root is Lite or Pro. The action presses the active mode control at most once and fails closed if that press opens another chooser or does not converge directly.",
3302
+ "description": "Ensure the viewport-visible Perps market-detail root is Lite or Pro. Press the active mode control at most once and select the requested first-entry choice if shown; fail if the requested detail root does not appear before the deadline.",
3303
3303
  "schema": {
3304
3304
  "type": "object",
3305
3305
  "properties": {
@@ -3341,7 +3341,7 @@
3341
3341
  },
3342
3342
  "account": {
3343
3343
  "type": "string",
3344
- "description": "Optional exact account name. Omit it to keep the currently selected account."
3344
+ "description": "Optional exact account name or EVM address. Prefer an address when the current product does not expose account names. Omit it to keep the currently selected account."
3345
3345
  },
3346
3346
  "provider": {
3347
3347
  "type": "string",
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
- "title": "MetaMask Mobile Android lifecycle smoke",
4
- "description": "Cold-launch the installed Android dev client, verify its bridge, unlock the fixture-backed wallet, then prove same-process background/foreground recovery and capture the final app state. Requires a source-dev runtime; does not rebuild or reset wallet data.",
3
+ "title": "MetaMask Mobile lifecycle smoke",
4
+ "description": "Cold-launch the installed dev client on the selected Android device or iOS simulator, verify its bridge, unlock the fixture-backed wallet, then prove same-process background/foreground recovery and capture the final app state. Requires a source-dev runtime; does not rebuild or reset wallet data.",
5
5
  "paramsSchema": {
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "properties": {
9
+ "capture_path": {
10
+ "type": "string",
11
+ "default": "screenshots/app-lifecycle-smoke.png",
12
+ "description": "Use a distinct path when composing repeated lifecycle checks."
13
+ },
9
14
  "runtime_ready_timeout_ms": {
10
15
  "type": "integer",
11
16
  "minimum": 1000,
@@ -19,20 +24,18 @@
19
24
  "nodes": {
20
25
  "background-app": {
21
26
  "action": "app.lifecycle",
22
- "platform": "android",
23
27
  "command": "background",
24
28
  "settle_ms": 1000,
25
29
  "require_process_continuity": true,
26
- "intent": "Put the selected Android device on the launcher",
30
+ "intent": "Background the selected app without terminating its process",
27
31
  "next": "foreground-app"
28
32
  },
29
33
  "foreground-app": {
30
34
  "action": "app.lifecycle",
31
- "platform": "android",
32
35
  "command": "foreground",
33
36
  "runtime_ready_timeout_ms": "{{params.runtime_ready_timeout_ms}}",
34
37
  "require_process_continuity": true,
35
- "intent": "Resume the installed Android app in the same process and await its selected bridge",
38
+ "intent": "Resume the selected app in the same process and await its bridge",
36
39
  "next": "bridge-after"
37
40
  },
38
41
  "bridge-after": {
@@ -43,7 +46,7 @@
43
46
  },
44
47
  "screenshot-after": {
45
48
  "action": "ui.screenshot",
46
- "path": "screenshots/app.lifecycle.android-smoke.png",
49
+ "path": "{{params.capture_path}}",
47
50
  "intent": "Record the foreground app state as evidence",
48
51
  "next": "end"
49
52
  },
@@ -69,18 +72,16 @@
69
72
  },
70
73
  "launch-before": {
71
74
  "action": "app.lifecycle",
72
- "platform": "android",
73
75
  "command": "launch",
74
76
  "runtime_ready_timeout_ms": "{{params.runtime_ready_timeout_ms}}",
75
- "intent": "Launch the installed Android app through the Expo dev-client deep link from a cold process",
77
+ "intent": "Launch the selected app through the Expo dev-client deep link from a cold process",
76
78
  "next": "bridge-before"
77
79
  },
78
80
  "terminate-before": {
79
81
  "action": "app.lifecycle",
80
- "platform": "android",
81
82
  "command": "terminate",
82
83
  "settle_ms": 3000,
83
- "intent": "Terminate the installed Android app before lifecycle smoke",
84
+ "intent": "Terminate the selected app before lifecycle smoke",
84
85
  "next": "launch-before"
85
86
  }
86
87
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.48.0",
3
+ "version": "0.49.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"