@deeeed/metamask-harness 0.47.4 → 0.49.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.
- package/CHANGELOG.md +12 -0
- package/README.md +3 -2
- package/adapters/extension/lib/macos-focus.cjs +2 -2
- package/adapters/extension/live.sh +6 -8
- package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +1 -0
- package/adapters/shared/ensure-runner-deps.sh +6 -0
- package/dist/adapters/extension/runtime-decision.js +18 -1
- package/dist/adapters/extension/runtime.js +5 -5
- package/dist/adapters/extension/surface.js +1 -0
- package/dist/app-lifecycle.js +3 -1
- package/dist/command-contract.js +2 -0
- package/dist/commands/call.js +1 -8
- package/dist/commands/device-target.js +12 -3
- package/dist/commands/doctor.js +7 -5
- package/dist/commands/fixtures.js +1 -1
- package/dist/commands/help.js +17 -5
- package/dist/commands/launch/index.js +10 -0
- package/dist/commands/manifest.js +3 -1
- package/dist/commands/mobile-device-view.js +13 -3
- package/dist/commands/parse-args.js +2 -0
- package/dist/commands/run-engine.js +29 -4
- package/dist/commands/run.js +1 -1
- package/dist/commands/runtime-launch.js +10 -1
- package/dist/commands/status.js +8 -4
- package/dist/devices.js +31 -9
- package/dist/execution-provenance.js +8 -7
- package/dist/funding-execution-context.js +62 -17
- package/dist/heal-bounds.js +3 -3
- package/dist/live-adapter-contract.js +4 -0
- package/dist/manifest.js +2 -13
- package/dist/metamask-action-validation.js +3 -2
- package/dist/mm-harness-cli.js +4 -2
- package/dist/runner.js +14 -3
- package/docs/RECIPES.md +135 -24
- package/docs/RELEASE-QA-CAPABILITY-MAP.md +3 -2
- package/library/actions/core/perps/_controller.mjs +24 -24
- package/library/actions/core/perps/assert_orders.mjs +3 -5
- package/library/actions/core/perps/assert_positions.mjs +3 -5
- package/library/actions/core/perps/close_orders.mjs +39 -19
- package/library/actions/core/perps/close_positions.mjs +53 -12
- package/library/actions/core/perps/edit_order.mjs +2 -0
- package/library/actions/core/perps/ensure_orders.mjs +3 -4
- package/library/actions/core/perps/ensure_positions.mjs +4 -5
- package/library/actions/core/perps/place_order.mjs +7 -14
- package/library/actions/core/perps/read_account.mjs +2 -0
- package/library/actions/core/perps/read_orders.mjs +2 -0
- package/library/actions/core/perps/read_positions.mjs +3 -1
- package/library/actions/core/perps/read_snapshot.mjs +3 -0
- package/library/actions/core/perps/start_state.mjs +3 -14
- package/library/actions/core/perps/teardown_state.mjs +3 -14
- package/library/actions/core/perps/update_position_tpsl.mjs +13 -19
- package/library/actions/core/wallet/list_accounts.mjs +3 -0
- package/library/actions/extension/analytics/consent.mjs +2 -0
- package/library/actions/extension/analytics/set_consent.mjs +2 -0
- package/library/actions/extension/assets/home-token-identity.mjs +31 -0
- package/library/actions/extension/assets/import_custom_token.mjs +63 -79
- package/library/actions/extension/assets/open_details.mjs +19 -7
- package/library/actions/extension/assets/prepare_send.mjs +1 -0
- package/library/actions/extension/assets/read_details.mjs +62 -29
- package/library/actions/extension/assets/read_visible_state.mjs +5 -1
- package/library/actions/extension/assets/review_send.mjs +1 -0
- package/library/actions/extension/assets/select_network_scope.mjs +17 -9
- package/library/actions/extension/assets/send.mjs +2 -0
- package/library/actions/extension/assets/set_custom_gas.mjs +1 -0
- package/library/actions/extension/assets/set_token_visibility.mjs +86 -17
- package/library/actions/extension/assets/verify_sorting.mjs +63 -14
- package/library/actions/extension/deeplink/open.mjs +1 -0
- package/library/actions/extension/networks/add_chainlist.mjs +2 -0
- package/library/actions/extension/networks/add_custom.mjs +21 -17
- package/library/actions/extension/networks/custom_network.mjs +10 -3
- package/library/actions/extension/networks/read_visible_state.mjs +2 -0
- package/library/actions/extension/networks/remove_custom.mjs +1 -0
- package/library/actions/extension/performance/_navigation-memory.mjs +37 -6
- package/library/actions/extension/performance/compare_idle_navigation_memory.mjs +5 -2
- package/library/actions/extension/performance/measure_detached_dom.mjs +5 -2
- package/library/actions/extension/performance/measure_navigation_memory.mjs +5 -2
- package/library/actions/extension/perps/assert_orders.mjs +1 -0
- package/library/actions/extension/perps/assert_positions.mjs +1 -0
- package/library/actions/extension/perps/assert_visible_consistency.mjs +11 -4
- package/library/actions/extension/perps/close_orders.mjs +4 -1
- package/library/actions/extension/perps/close_positions.mjs +1 -0
- package/library/actions/extension/perps/close_visible_position.mjs +160 -11
- package/library/actions/extension/perps/compare_provider_market.mjs +2 -0
- package/library/actions/extension/perps/edit_margin.mjs +3 -1
- package/library/actions/extension/perps/ensure_orders.mjs +1 -0
- package/library/actions/extension/perps/ensure_positions.mjs +1 -0
- package/library/actions/extension/perps/mutation-receipt.mjs +54 -3
- package/library/actions/extension/perps/open_balance_action.mjs +17 -7
- package/library/actions/extension/perps/open_position_action.mjs +1 -0
- package/library/actions/extension/perps/perps.mjs +550 -69
- package/library/actions/extension/perps/place_order.mjs +1 -0
- package/library/actions/extension/perps/read_eligibility.mjs +1 -0
- package/library/actions/extension/perps/read_funds_confirmation.mjs +10 -0
- package/library/actions/extension/perps/read_orders.mjs +1 -0
- package/library/actions/extension/perps/read_positions.mjs +1 -0
- package/library/actions/extension/perps/read_snapshot.mjs +4 -1
- package/library/actions/extension/perps/read_visible_state.mjs +12 -6
- package/library/actions/extension/perps/search_markets.mjs +8 -2
- package/library/actions/extension/perps/select_activity_filter.mjs +2 -1
- package/library/actions/extension/perps/select_market_filter.mjs +75 -45
- package/library/actions/extension/perps/set_market_favorite.mjs +7 -3
- package/library/actions/extension/perps/state.mjs +2 -0
- package/library/actions/extension/perps/update_position_tpsl.mjs +3 -1
- package/library/actions/extension/perps/visible-mutation-identity.mjs +6 -2
- package/library/actions/extension/platform/cdp.mjs +50 -142
- package/library/actions/extension/settings/set_basic_functionality.mjs +1 -0
- package/library/actions/extension/swap_bridge/quote-state.mjs +56 -0
- package/library/actions/extension/swap_bridge/read_visible_state.mjs +5 -1
- package/library/actions/extension/swap_bridge/select_assets.mjs +12 -1
- package/library/actions/extension/swap_bridge/set_amount.mjs +11 -8
- package/library/actions/extension/swap_bridge/set_max_amount.mjs +18 -18
- package/library/actions/extension/swap_bridge/set_slippage.mjs +1 -9
- package/library/actions/extension/ui/locators.mjs +1 -0
- package/library/actions/extension/ui/navigate.mjs +60 -14
- package/library/actions/extension/wallet/ensure_unlocked.mjs +2 -0
- package/library/actions/extension/wallet/import.mjs +2 -0
- package/library/actions/extension/wallet/list_accounts.mjs +1 -0
- package/library/actions/extension/wallet/lock.mjs +4 -1
- package/library/actions/extension/wallet/read_state.mjs +1 -0
- package/library/actions/extension/wallet/reset.mjs +2 -0
- package/library/actions/extension/wallet/secret-input.mjs +2 -0
- package/library/actions/extension/wallet/select_account.mjs +2 -0
- package/library/actions/extension/wallet/setup.mjs +1 -0
- package/library/actions/extension/wallet/state.mjs +2 -0
- package/library/actions/extension/wallet/visible-session.mjs +2 -0
- package/library/actions/mobile/analytics/consent-settings.mjs +1 -0
- package/library/actions/mobile/analytics/set_consent.mjs +1 -0
- package/library/actions/mobile/app/network-control.mjs +2 -0
- package/library/actions/mobile/app/network.mjs +1 -0
- package/library/actions/mobile/app/network_assert.mjs +1 -0
- package/library/actions/mobile/app/network_capture.mjs +1 -0
- package/library/actions/mobile/assets/import_custom_token.mjs +3 -0
- package/library/actions/mobile/assets/open_details.mjs +1 -0
- package/library/actions/mobile/assets/read_details.mjs +1 -0
- package/library/actions/mobile/assets/read_visible_state.mjs +4 -1
- package/library/actions/mobile/assets/set_token_visibility.mjs +15 -9
- package/library/actions/mobile/assets/verify_sorting.mjs +90 -7
- package/library/actions/mobile/deeplink/open.mjs +1 -0
- package/library/actions/mobile/networks/add_custom.mjs +1 -0
- package/library/actions/mobile/networks/network-management.mjs +23 -15
- package/library/actions/mobile/networks/read_visible_state.mjs +1 -0
- package/library/actions/mobile/networks/remove_custom.mjs +7 -2
- package/library/actions/mobile/perps/assert_orders.mjs +1 -0
- package/library/actions/mobile/perps/assert_positions.mjs +1 -0
- package/library/actions/mobile/perps/capture_performance.mjs +1 -0
- package/library/actions/mobile/perps/close_orders.mjs +1 -0
- package/library/actions/mobile/perps/close_positions.mjs +1 -0
- package/library/actions/mobile/perps/ensure_orders.mjs +1 -0
- package/library/actions/mobile/perps/ensure_positions.mjs +1 -0
- package/library/actions/mobile/perps/measure_homepage_visible.mjs +1 -0
- package/library/actions/mobile/perps/performance-capture.mjs +2 -0
- package/library/actions/mobile/perps/perps.mjs +8 -1
- package/library/actions/mobile/perps/place_order.mjs +1 -0
- package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +2 -0
- package/library/actions/mobile/perps/read-visible-state-loop.mjs +3 -0
- package/library/actions/mobile/perps/read_orders.mjs +1 -0
- package/library/actions/mobile/perps/read_positions.mjs +1 -0
- package/library/actions/mobile/perps/read_visible_state.mjs +1 -0
- package/library/actions/mobile/perps/search_markets.mjs +1 -0
- package/library/actions/mobile/perps/set_market_favorite.mjs +17 -19
- package/library/actions/mobile/platform/bridge.mjs +16 -4
- package/library/actions/mobile/platform/native-session-name.mjs +1 -0
- package/library/actions/mobile/platform/native-session.mjs +28 -17
- package/library/actions/mobile/platform/observe-ui.mjs +4 -2
- package/library/actions/mobile/platform/tool-paths.mjs +1 -0
- package/library/actions/mobile/swap_bridge/native-session.mjs +1 -0
- package/library/actions/mobile/swap_bridge/read_visible_state.mjs +1 -0
- package/library/actions/mobile/swap_bridge/select_assets.mjs +2 -0
- package/library/actions/mobile/swap_bridge/set_amount.mjs +1 -0
- package/library/actions/mobile/swap_bridge/set_max_amount.mjs +1 -0
- package/library/actions/mobile/swap_bridge/set_slippage.mjs +1 -0
- package/library/actions/mobile/swap_bridge/submit_transaction.mjs +2 -0
- package/library/actions/mobile/ui/locators.mjs +1 -0
- package/library/actions/mobile/ui/native-navigation.mjs +2 -0
- package/library/actions/mobile/ui/navigate.mjs +3 -1
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +2 -0
- package/library/actions/mobile/wallet/home.mjs +1 -0
- package/library/actions/mobile/wallet/import.mjs +2 -0
- package/library/actions/mobile/wallet/list_accounts.mjs +1 -0
- package/library/actions/mobile/wallet/lock.mjs +1 -0
- package/library/actions/mobile/wallet/native-ui.mjs +2 -0
- package/library/actions/mobile/wallet/read_state.mjs +2 -0
- package/library/actions/mobile/wallet/reset-helper.mjs +1 -0
- package/library/actions/mobile/wallet/reset.mjs +1 -0
- package/library/actions/mobile/wallet/select_account.mjs +2 -0
- package/library/actions/mobile/wallet/setup.mjs +2 -0
- package/library/actions/shared/analytics/assert_events.mjs +3 -0
- package/library/actions/shared/analytics/consent.mjs +3 -0
- package/library/actions/shared/analytics/read_events.mjs +2 -0
- package/library/actions/shared/analytics/start_capture.mjs +3 -0
- package/library/actions/shared/app/network-artifact.mjs +2 -0
- package/library/actions/shared/app/network-assert.mjs +2 -0
- package/library/actions/shared/assets/visible-state.mjs +2 -0
- package/library/actions/shared/deeplink/url.mjs +3 -0
- package/library/actions/shared/networks/visible-state.mjs +2 -0
- package/library/actions/shared/perps/visible-state.mjs +16 -3
- package/library/actions/shared/swap-bridge/transaction.mjs +3 -0
- package/library/actions/shared/swap-bridge/visible-state.mjs +2 -0
- package/library/actions/shared/ui/locators.mjs +2 -0
- package/library/actions/shared/wallet/import-source.mjs +3 -0
- package/library/manifests/core.action-manifest.json +34 -2
- package/library/manifests/extension.action-manifest.json +50 -17
- package/library/manifests/mobile.action-manifest.json +11 -10
- package/library/recipes/core/perps/smoke.recipe.json +23 -0
- package/library/recipes/core/perps/snapshot.recipe.json +26 -1
- package/library/recipes/extension/assets/release-custom-network-token-persistence.recipe.json +25 -8
- package/library/recipes/extension/performance/navigation-memory-suite.recipe.json +219 -0
- package/library/recipes/extension/performance/navigation-memory.recipe.json +61 -156
- package/library/recipes/extension/wallet/smoke.recipe.json +17 -8
- package/library/recipes/mobile/app/lifecycle-smoke.recipe.json +89 -0
- package/library/recipes/mobile/networks/release-custom-network-persistence.recipe.json +31 -6
- package/library/recipes/mobile/networks/release-multichain-visibility.recipe.json +5 -4
- package/library/recipes/mobile/wallet/smoke.recipe.json +17 -8
- package/library/recipes/{assets → shared/assets}/release-token-address-import.recipe.json +7 -0
- package/library/recipes/{assets → shared/assets}/release-token-details.recipe.json +2 -2
- package/library/recipes/shared/assets/release-token-hide-readd.recipe.json +91 -0
- package/library/recipes/{assets → shared/assets}/release-token-persistence.recipe.json +3 -0
- package/library/recipes/{assets → shared/assets}/release-token-search-import.recipe.json +4 -1
- package/library/recipes/{assets → shared/assets}/release-token-sorting.recipe.json +13 -2
- package/library/recipes/{swap-bridge → shared/swap-bridge}/release-quote.recipe.json +29 -3
- package/package.json +3 -3
- package/site/assets/help-recipes.json +220 -27
- package/library/recipes/assets/release-token-hide-readd.recipe.json +0 -57
- package/library/recipes/core/perps/clean-market-testnet.recipe.json +0 -44
- package/library/recipes/core/perps/order-lifecycle.recipe.json +0 -82
- package/library/recipes/core/perps/read-markets.recipe.json +0 -36
- package/library/recipes/core/perps/trading-lifecycle.recipe.json +0 -80
- package/library/recipes/extension/perps/release-activity-filters.recipe.json +0 -51
- package/library/recipes/extension/perps/release-funds-flows.recipe.json +0 -76
- package/library/recipes/extension/perps/release-live-limit-order.recipe.json +0 -109
- package/library/recipes/extension/perps/release-market-filters.recipe.json +0 -97
- package/library/recipes/extension/perps/release-order-entry.recipe.json +0 -158
- package/library/recipes/extension/perps/release-order-validation.recipe.json +0 -259
- package/library/recipes/extension/perps/release-visible-consistency.recipe.json +0 -47
- package/library/recipes/extension/perps/source-dev-snapshot-consistency.recipe.json +0 -76
- package/library/recipes/mobile/app/lifecycle.android-smoke.recipe.json +0 -69
- package/library/recipes/mobile/perps/chase-assert-running.recipe.json +0 -110
- package/library/recipes/mobile/perps/chase-place.recipe.json +0 -145
- package/library/recipes/mobile/perps/chase-terminate.recipe.json +0 -98
- package/library/recipes/mobile/perps/performance.recipe.json +0 -722
- package/library/recipes/mobile/perps/pro-order-setup.recipe.json +0 -111
- package/library/recipes/mobile/perps/pro-order-start-state.recipe.json +0 -53
- package/library/recipes/mobile/perps/scale-assert-orders.recipe.json +0 -126
- package/library/recipes/mobile/perps/scale-place.recipe.json +0 -186
- package/library/recipes/mobile/perps/twap-assert-active.recipe.json +0 -97
- package/library/recipes/mobile/perps/twap-place.recipe.json +0 -146
- package/library/recipes/mobile/runner/action-validation.recipe.json +0 -336
- package/library/recipes/perps/clean-market-testnet.recipe.json +0 -49
- package/library/recipes/perps/lifecycle.recipe.json +0 -144
- package/library/recipes/perps/release-live-market-order.recipe.json +0 -139
- package/library/recipes/perps/release-watchlist.recipe.json +0 -111
- package/library/recipes/swap-bridge/release-max-quote.recipe.json +0 -72
- /package/library/recipes/{assets → shared/assets}/release-token-list.recipe.json +0 -0
- /package/library/recipes/{perps → shared/perps}/release-market-details.recipe.json +0 -0
- /package/library/recipes/{perps → shared/perps}/release-market-search.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-custom-slippage.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-live-max-swap.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-live-swap.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-surface.recipe.json +0 -0
- /package/library/recipes/{wallet → shared/wallet}/import.recipe.json +0 -0
- /package/library/recipes/{wallet → shared/wallet}/reset-import.recipe.json +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Opens named wallet screens through UI controls or history, and accepts direct URL/hash navigation.
|
|
2
|
+
// Named navigation may dismiss tutorials or drawers and add a missing network before selecting its filter.
|
|
1
3
|
import { pathToFileURL } from 'node:url';
|
|
2
4
|
|
|
3
5
|
import {
|
|
@@ -8,6 +10,7 @@ import {
|
|
|
8
10
|
} from '../platform/cdp.mjs';
|
|
9
11
|
|
|
10
12
|
const BACK_CONTROLS = [
|
|
13
|
+
'custom-token-import-close-button',
|
|
11
14
|
'perps-order-entry-back-button',
|
|
12
15
|
'perps-market-detail-back-button',
|
|
13
16
|
'perps-activity-back-button',
|
|
@@ -23,6 +26,18 @@ const BACK_CONTROLS = [
|
|
|
23
26
|
].map(dataTestId).concat('button[aria-label="Back"]');
|
|
24
27
|
const UI_POLL_MS = 200;
|
|
25
28
|
const PERPS_HOME_SETTLE_MS = 1000;
|
|
29
|
+
const PERPS_MARKET_LIST = [
|
|
30
|
+
'market-list-view',
|
|
31
|
+
'parent-selector-perps-market-list',
|
|
32
|
+
].map(dataTestId).join(',');
|
|
33
|
+
const PERPS_MARKET_DETAIL = [
|
|
34
|
+
'perps-market-detail-page',
|
|
35
|
+
'parent-selector-perps-market-detail',
|
|
36
|
+
].map(dataTestId).join(',');
|
|
37
|
+
const PERPS_ACTIVITY = [
|
|
38
|
+
'perps-activity-page',
|
|
39
|
+
'parent-selector-perps-activity',
|
|
40
|
+
].map(dataTestId).join(',');
|
|
26
41
|
|
|
27
42
|
function text(value) {
|
|
28
43
|
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
@@ -68,6 +83,7 @@ async function hasVisibleSelector(page, selector) {
|
|
|
68
83
|
return page.evaluate(`(() => {
|
|
69
84
|
const element = document.querySelector(${JSON.stringify(selector)});
|
|
70
85
|
if (!element) return false;
|
|
86
|
+
if (element.closest('dialog')?.open === false) return false;
|
|
71
87
|
const style = getComputedStyle(element);
|
|
72
88
|
const rect = element.getBoundingClientRect();
|
|
73
89
|
return style.display !== 'none' && style.visibility !== 'hidden' && rect.width > 0 && rect.height > 0;
|
|
@@ -116,6 +132,13 @@ export async function openHome(page, timeoutMs) {
|
|
|
116
132
|
let pendingActivation;
|
|
117
133
|
|
|
118
134
|
while (Date.now() < deadline) {
|
|
135
|
+
const importNetworkClose = '[data-testid="custom-token-import-network-selector"] button[aria-label="Close"]';
|
|
136
|
+
if (await hasVisibleSelector(page, importNetworkClose)) {
|
|
137
|
+
await activateSemanticControl(page, importNetworkClose);
|
|
138
|
+
steps.push(importNetworkClose);
|
|
139
|
+
await pauseForUi(page);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
119
142
|
if (await hasVisibleSelector(page, drawerClose)) {
|
|
120
143
|
await activateSemanticControl(page, drawerClose);
|
|
121
144
|
steps.push(drawerClose);
|
|
@@ -274,11 +297,11 @@ export async function openPerpsHome(page, timeoutMs) {
|
|
|
274
297
|
export async function openPerpsMarket(page, market, timeoutMs) {
|
|
275
298
|
const deadline = Date.now() + timeoutMs;
|
|
276
299
|
const normalizedMarket = normalizeMarketSymbol(market.trim());
|
|
277
|
-
if (await hasVisibleSelector(page,
|
|
300
|
+
if (await hasVisibleSelector(page, PERPS_MARKET_LIST)) {
|
|
278
301
|
const marketRow = dataTestId(`market-row-${normalizedMarket.replace(':', '-')}`);
|
|
279
302
|
await page.waitForSelector(marketRow, { timeoutMs: Math.max(1, deadline - Date.now()) });
|
|
280
303
|
const navigation = await activateSemanticControl(page, marketRow);
|
|
281
|
-
await page.waitForSelector(
|
|
304
|
+
await page.waitForSelector(PERPS_MARKET_DETAIL, {
|
|
282
305
|
timeoutMs: Math.max(1, deadline - Date.now()),
|
|
283
306
|
});
|
|
284
307
|
return {
|
|
@@ -308,7 +331,7 @@ export async function openPerpsMarket(page, market, timeoutMs) {
|
|
|
308
331
|
);
|
|
309
332
|
}
|
|
310
333
|
const navigation = await activateSemanticControl(page, marketControl);
|
|
311
|
-
await page.waitForSelector(
|
|
334
|
+
await page.waitForSelector(PERPS_MARKET_DETAIL, {
|
|
312
335
|
timeoutMs: Math.max(1, deadline - Date.now()),
|
|
313
336
|
});
|
|
314
337
|
return {
|
|
@@ -321,7 +344,7 @@ export async function openPerpsMarket(page, market, timeoutMs) {
|
|
|
321
344
|
}
|
|
322
345
|
|
|
323
346
|
export async function openPerpsActivity(page, timeoutMs) {
|
|
324
|
-
if (await hasVisibleSelector(page,
|
|
347
|
+
if (await hasVisibleSelector(page, PERPS_ACTIVITY)) {
|
|
325
348
|
return { method: 'visible-ui', href: await currentHref(page), steps: [] };
|
|
326
349
|
}
|
|
327
350
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -329,7 +352,7 @@ export async function openPerpsActivity(page, timeoutMs) {
|
|
|
329
352
|
const activity = dataTestId('perps-recent-activity-see-all');
|
|
330
353
|
await page.waitForSelector(activity, { timeoutMs: Math.max(1, deadline - Date.now()) });
|
|
331
354
|
const navigation = await activateSemanticControl(page, activity);
|
|
332
|
-
await page.waitForSelector(
|
|
355
|
+
await page.waitForSelector(PERPS_ACTIVITY, {
|
|
333
356
|
timeoutMs: Math.max(1, deadline - Date.now()),
|
|
334
357
|
});
|
|
335
358
|
return {
|
|
@@ -342,7 +365,7 @@ export async function openPerpsActivity(page, timeoutMs) {
|
|
|
342
365
|
}
|
|
343
366
|
|
|
344
367
|
export async function openPerpsMarketList(page, timeoutMs) {
|
|
345
|
-
const marketList =
|
|
368
|
+
const marketList = PERPS_MARKET_LIST;
|
|
346
369
|
if (await hasVisibleSelector(page, marketList)) {
|
|
347
370
|
return { method: 'visible-ui', href: await currentHref(page), steps: [] };
|
|
348
371
|
}
|
|
@@ -417,17 +440,37 @@ export async function openNetwork(page, network, chainId, addIfMissing, timeoutM
|
|
|
417
440
|
throw new Error('Token assets did not become selected before network navigation.');
|
|
418
441
|
}
|
|
419
442
|
}
|
|
420
|
-
await
|
|
421
|
-
|
|
443
|
+
if (!(await hasVisibleSelector(page, dataTestId('home-network-filter-all-default')))) {
|
|
444
|
+
await activateSemanticControl(page, sort);
|
|
445
|
+
steps.push(sort);
|
|
446
|
+
}
|
|
422
447
|
let choice = await waitForFirstVisible(page, [installed, custom, additional, manage], deadline);
|
|
423
448
|
if (!choice) throw new Error(`Network filter did not show ${network} (${chainId}) before timeout.`);
|
|
424
|
-
if (choice
|
|
449
|
+
if (!addIfMissing && (choice === additional || choice === manage)) {
|
|
450
|
+
throw new Error(`Network ${network} (${chainId}) is not added to the wallet.`);
|
|
451
|
+
}
|
|
452
|
+
let added = false;
|
|
453
|
+
if (choice === additional) {
|
|
454
|
+
await page.scroll({ selector: additional, intoView: true });
|
|
455
|
+
await page.waitFor({ selector: additional, expected: 'visible', timeoutMs: Math.max(1, deadline - Date.now()) });
|
|
456
|
+
await activateSemanticControl(page, additional);
|
|
457
|
+
steps.push(additional);
|
|
458
|
+
await page.waitFor({ selector: additional, expected: 'hidden', timeoutMs: Math.max(1, deadline - Date.now()) });
|
|
459
|
+
await activateSemanticControl(page, sort);
|
|
460
|
+
steps.push(sort);
|
|
461
|
+
choice = await waitForFirstVisible(page, [installed, custom], deadline);
|
|
462
|
+
if (!choice) throw new Error(`Added network ${network} (${chainId}) did not appear in the wallet filter.`);
|
|
463
|
+
await page.scroll({ selector: choice, intoView: true });
|
|
464
|
+
await page.waitFor({ selector: choice, expected: 'visible', timeoutMs: Math.max(1, deadline - Date.now()) });
|
|
465
|
+
await activateSemanticControl(page, choice);
|
|
466
|
+
steps.push(choice);
|
|
467
|
+
added = true;
|
|
468
|
+
} else if (choice !== manage) {
|
|
469
|
+
await page.scroll({ selector: choice, intoView: true });
|
|
470
|
+
await page.waitFor({ selector: choice, expected: 'visible', timeoutMs: Math.max(1, deadline - Date.now()) });
|
|
425
471
|
await activateSemanticControl(page, choice);
|
|
426
472
|
steps.push(choice);
|
|
427
473
|
} else {
|
|
428
|
-
if (!addIfMissing) {
|
|
429
|
-
throw new Error(`Network ${network} (${chainId}) is not added to the wallet.`);
|
|
430
|
-
}
|
|
431
474
|
await activateSemanticControl(page, manage);
|
|
432
475
|
steps.push(manage);
|
|
433
476
|
const listItem = dataTestId(`network-list-item-eip155:${chainId}`);
|
|
@@ -437,6 +480,7 @@ export async function openNetwork(page, network, chainId, addIfMissing, timeoutM
|
|
|
437
480
|
await activateSemanticControl(page, popular);
|
|
438
481
|
steps.push(popular);
|
|
439
482
|
await page.waitForSelector(listItem, { timeoutMs: Math.max(1, deadline - Date.now()) });
|
|
483
|
+
added = true;
|
|
440
484
|
}
|
|
441
485
|
const back = dataTestId('page-header-back-button');
|
|
442
486
|
await activateSemanticControl(page, back);
|
|
@@ -444,15 +488,17 @@ export async function openNetwork(page, network, chainId, addIfMissing, timeoutM
|
|
|
444
488
|
await page.waitForSelector(sort, { timeoutMs: Math.max(1, deadline - Date.now()) });
|
|
445
489
|
await activateSemanticControl(page, sort);
|
|
446
490
|
steps.push(sort);
|
|
447
|
-
choice = await waitForFirstVisible(page, [installed, custom
|
|
491
|
+
choice = await waitForFirstVisible(page, [installed, custom], deadline);
|
|
448
492
|
if (!choice) throw new Error(`Added network ${network} (${chainId}) did not appear in the wallet filter.`);
|
|
493
|
+
await page.scroll({ selector: choice, intoView: true });
|
|
494
|
+
await page.waitFor({ selector: choice, expected: 'visible', timeoutMs: Math.max(1, deadline - Date.now()) });
|
|
449
495
|
await activateSemanticControl(page, choice);
|
|
450
496
|
steps.push(choice);
|
|
451
497
|
}
|
|
452
498
|
while (Date.now() < deadline) {
|
|
453
499
|
const label = await page.evaluate(`String(document.querySelector(${JSON.stringify(sort)})?.innerText || '')`);
|
|
454
500
|
if (label.toLowerCase().includes(network.toLowerCase())) {
|
|
455
|
-
return { method: 'visible-ui', href: await currentHref(page), network, chainId, steps };
|
|
501
|
+
return { method: 'visible-ui', href: await currentHref(page), network, chainId, added, steps };
|
|
456
502
|
}
|
|
457
503
|
await pauseForUi(page);
|
|
458
504
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Unlocks an existing wallet with the supplied or fixture password when a password input is present.
|
|
2
|
+
// Rejects onboarding profiles and waits for the password input to disappear after submission.
|
|
1
3
|
import { readFile } from 'node:fs/promises';
|
|
2
4
|
import { dataTestId, runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
3
5
|
import { walletFixturePath } from '../../harness-exports.mjs';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Imports a wallet through onboarding or verifies an existing profile against the expected account.
|
|
2
|
+
// May unlock the wallet, finish onboarding, and select the expected account through visible UI.
|
|
1
3
|
import { resolveWalletImportCredentials, validateWalletImportOptions, walletImportMethod } from '../../shared/wallet/import-source.mjs';
|
|
2
4
|
import { runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
3
5
|
import { pasteSecretRecoveryPhrase, setSecretInput } from './secret-input.mjs';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Locks the wallet through visible menu controls and waits for the unlock page.
|
|
2
|
+
// Opens Home first when the account menu is unavailable.
|
|
1
3
|
import { dataTestId, runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
2
4
|
|
|
3
5
|
const MENU = dataTestId('account-options-menu-button');
|
|
@@ -21,6 +23,7 @@ async function clickUncovered(page, selector, deadline) {
|
|
|
21
23
|
point = await page.evaluate(`(() => {
|
|
22
24
|
const element = document.querySelector(${JSON.stringify(selector)});
|
|
23
25
|
if (!element) return null;
|
|
26
|
+
element.scrollIntoView({ block: 'center', inline: 'center' });
|
|
24
27
|
const style = getComputedStyle(element);
|
|
25
28
|
const rect = element.getBoundingClientRect();
|
|
26
29
|
if (style.display === 'none' || style.visibility === 'hidden' || rect.width <= 0 || rect.height <= 0) return null;
|
|
@@ -74,7 +77,7 @@ export async function lockWallet(page, timeoutMs = 15_000) {
|
|
|
74
77
|
if (Date.now() >= deadline) throw new Error('Wallet lock control did not become visible.');
|
|
75
78
|
await page.evaluate('new Promise((resolve) => setTimeout(resolve, 100))');
|
|
76
79
|
}
|
|
77
|
-
await page
|
|
80
|
+
await clickUncovered(page, LOCK, deadline);
|
|
78
81
|
while (!(await visible(page, UNLOCK_PAGE))) {
|
|
79
82
|
if (Date.now() >= deadline) throw new Error('Wallet did not reach the unlock page after locking.');
|
|
80
83
|
await page.evaluate('new Promise((resolve) => setTimeout(resolve, 100))');
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Clears the wallet through the visible forgot-password reset flow unless it is already fresh.
|
|
2
|
+
// Checks that onboarding is incomplete, the route is onboarding, and no account is selected.
|
|
1
3
|
import { runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
2
4
|
import { readWalletStateExpression } from './state.mjs';
|
|
3
5
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Enters wallet secrets through CDP input events and checks their retained length.
|
|
2
|
+
// Recovery-phrase paste reloads the page to install a temporary clipboard bridge, then removes it.
|
|
1
3
|
export async function setSecretInput(page, selector, value) {
|
|
2
4
|
const found = await page.evaluate(`(() => {
|
|
3
5
|
const element = document.querySelector(${JSON.stringify(selector)});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Selects an account by address, ID, or name through the background request hook.
|
|
2
|
+
// Falls back to visible account search when that hook is unavailable and checks the selected address.
|
|
1
3
|
import { runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
2
4
|
import {
|
|
3
5
|
listWalletAccountsExpression,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Builds page expressions for reading wallet/account state and changing the selected account through state hooks.
|
|
2
|
+
// Account selection sends a background request and polls until the selected ID matches.
|
|
1
3
|
const walletStatePreamble = `
|
|
2
4
|
const readWalletState = async () => {
|
|
3
5
|
const hooks = globalThis.stateHooks || {};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Dismisses onboarding completion and navigates the account list to select and check an expected EVM account.
|
|
2
|
+
// Validates address search with an alternate-address control and returns a hash of the matched identity.
|
|
1
3
|
import { createHash } from 'node:crypto';
|
|
2
4
|
|
|
3
5
|
const ACCOUNT_SEARCH = 'input[data-testid="multichain-account-list-search"], [data-testid="multichain-account-list-search"] input';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Import an ERC-20 token through the native form after checking its resolved metadata.
|
|
2
|
+
// Confirm the imported symbol appears in the token list.
|
|
1
3
|
import { runAdapter } from '../platform/bridge.mjs';
|
|
2
4
|
import {
|
|
3
5
|
closeNativeSession,
|
|
@@ -168,6 +170,7 @@ runAdapter(async (input) => {
|
|
|
168
170
|
const token = await findTokenRow(session, expectedSymbol, timeoutMs);
|
|
169
171
|
return {
|
|
170
172
|
action: input.action,
|
|
173
|
+
changed: true,
|
|
171
174
|
network,
|
|
172
175
|
chainId,
|
|
173
176
|
address,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// Normalize token rows from Mobile accessibility observations into visible asset state.
|
|
1
2
|
import { runAdapter } from '../platform/bridge.mjs';
|
|
2
3
|
import { observeNativeUi } from '../platform/observe-ui.mjs';
|
|
3
4
|
import {
|
|
@@ -17,7 +18,7 @@ runAdapter(async (input) => {
|
|
|
17
18
|
const warning = observed.warnings?.map((entry) => entry.message).filter(Boolean).join('; ');
|
|
18
19
|
throw new Error(`Mobile visible Assets state is unavailable${warning ? `: ${warning}` : '.'}`);
|
|
19
20
|
}
|
|
20
|
-
const items =
|
|
21
|
+
const items = (visible.items ?? [])
|
|
21
22
|
.filter((item) => String(item.test_id ?? '').startsWith('asset-'))
|
|
22
23
|
.map((item) => ({
|
|
23
24
|
testId: item.test_id,
|
|
@@ -32,6 +33,8 @@ runAdapter(async (input) => {
|
|
|
32
33
|
items,
|
|
33
34
|
truncated: visible.truncated,
|
|
34
35
|
}, options, 'mobile'),
|
|
36
|
+
observationProvider: visible.provider ?? screen.provider,
|
|
37
|
+
deviceId: screen.deviceId,
|
|
35
38
|
proofPath: 'native-accessibility',
|
|
36
39
|
};
|
|
37
40
|
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Import or remove a token through native controls and check its resulting visibility state.
|
|
2
|
+
// Reject ambiguous symbol matches and unsupported exact asset IDs.
|
|
1
3
|
import { runAdapter } from '../platform/bridge.mjs';
|
|
2
4
|
import {
|
|
3
5
|
closeNativeSession,
|
|
@@ -25,25 +27,24 @@ function descendants(snapshot, root) {
|
|
|
25
27
|
|
|
26
28
|
function searchedToken(snapshot, query) {
|
|
27
29
|
const normalized = query.toUpperCase();
|
|
28
|
-
const rows = snapshot.nodes.filter((node) => node.identifier === 'searched-token-result');
|
|
30
|
+
const rows = snapshot.nodes.filter((node) => node.identifier === 'searched-token-result' && node.visibleToUser !== false);
|
|
31
|
+
const matches = [];
|
|
29
32
|
for (const row of rows) {
|
|
30
33
|
const children = descendants(snapshot, row);
|
|
31
|
-
const label = children.map(nativeLabel).find((value) => {
|
|
34
|
+
const label = [nativeLabel(row), ...children.map(nativeLabel)].find((value) => {
|
|
32
35
|
const parts = value.split(',').map((part) => part.trim().toUpperCase());
|
|
33
36
|
return parts.includes(normalized);
|
|
34
37
|
});
|
|
35
38
|
if (label) {
|
|
36
|
-
|
|
39
|
+
matches.push({
|
|
37
40
|
row,
|
|
38
41
|
label,
|
|
39
42
|
imported: row.enabled === false && children.some((node) => /CheckBox$/u.test(String(node.type))),
|
|
40
|
-
};
|
|
43
|
+
});
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
);
|
|
46
|
-
return imported ? { row: imported, label: query, imported: true } : null;
|
|
46
|
+
if (matches.length > 1) throw new Error(`Mobile token search matched ${matches.length} rows for ${query}; token identity is ambiguous.\nNext: mm-harness actions --action metamask.assets.set_token_visibility --json`);
|
|
47
|
+
return matches[0] ?? null;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
function visibleNode(snapshot, identifier) {
|
|
@@ -201,7 +202,9 @@ async function findVisibleAsset(session, query, timeoutMs) {
|
|
|
201
202
|
)).snapshot;
|
|
202
203
|
}
|
|
203
204
|
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
204
|
-
|
|
205
|
+
const matches = snapshot.nodes.filter(node => node.identifier === `asset-${query.toUpperCase()}` && node.visibleToUser !== false);
|
|
206
|
+
if (matches.length > 1) throw new Error(`Mobile token list matched ${matches.length} rows for ${query}; removal is ambiguous.\nNext: mm-harness actions --action metamask.assets.set_token_visibility --json`);
|
|
207
|
+
if (matches.length === 1) return true;
|
|
205
208
|
await session.execute('ui.scroll', { direction: 'down', timeout_ms: timeoutMs, settle: false });
|
|
206
209
|
snapshot = await session.snapshot();
|
|
207
210
|
}
|
|
@@ -271,6 +274,9 @@ async function disableToken(session, query, requireUnchanged, requireChange, tim
|
|
|
271
274
|
}
|
|
272
275
|
|
|
273
276
|
runAdapter(async (input) => {
|
|
277
|
+
if (String(input.node?.asset_id ?? '').trim()) {
|
|
278
|
+
throw new Error('Exact asset-ID corroboration is not supported on Mobile.\nNext: mm-harness actions --adapter mobile --action metamask.assets.set_token_visibility --json');
|
|
279
|
+
}
|
|
274
280
|
const query = String(input.node?.query ?? '').trim();
|
|
275
281
|
const timeoutMs = Number(input.node?.timeout_ms ?? 45_000);
|
|
276
282
|
const requested = input.node?.visible;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Switch token sorting to check alphabetical and declining fiat order in visible rows.
|
|
2
|
+
// Restore the original sort preference and report restoration failures.
|
|
1
3
|
import { runAdapter } from '../platform/bridge.mjs';
|
|
2
4
|
import {
|
|
3
5
|
closeNativeSession,
|
|
@@ -31,7 +33,8 @@ function assetRows(snapshot) {
|
|
|
31
33
|
|
|
32
34
|
function isAlphabetical(rows) {
|
|
33
35
|
const names = rows.map((row) => row.name);
|
|
34
|
-
|
|
36
|
+
// MetaMask's SES lockdown compares names by UTF-16 code units.
|
|
37
|
+
const expected = [...names].sort();
|
|
35
38
|
return names.length >= 2 && names.every((name, index) => name === expected[index]);
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -40,18 +43,78 @@ function isDeclining(rows) {
|
|
|
40
43
|
return values.length >= 2 && values.every((value, index) => index === 0 || values[index - 1] >= value);
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
async function
|
|
46
|
+
async function openSortMenu(session, timeoutMs) {
|
|
47
|
+
if (sessionNode(await session.snapshot(), 'sort-alphabetical')) return;
|
|
44
48
|
await session.execute('ui.press', { test_id: 'token-sort-button', timeout_ms: timeoutMs, settle: false });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function selectedSort(session, timeoutMs) {
|
|
52
|
+
await openSortMenu(session, timeoutMs);
|
|
53
|
+
const deadline = Date.now() + timeoutMs;
|
|
54
|
+
do {
|
|
55
|
+
const snapshot = await session.snapshot();
|
|
56
|
+
const byIndex = new Map(snapshot.nodes.map((node) => [node.index, node]));
|
|
57
|
+
const selected = ['sort-alphabetical', 'sort-declining-balance'].filter((option) => {
|
|
58
|
+
const row = sessionNode(snapshot, option);
|
|
59
|
+
if (!row) return false;
|
|
60
|
+
// ListItemSelect renders a checkbox child only for the selected option.
|
|
61
|
+
return snapshot.nodes.some((node) => {
|
|
62
|
+
if (node.visibleToUser === false || !/checkbox$/iu.test(String(node.type))) return false;
|
|
63
|
+
let parent = byIndex.get(node.parentIndex);
|
|
64
|
+
for (let depth = 0; depth < 40 && parent; depth += 1) {
|
|
65
|
+
if (parent.index === row.index) return true;
|
|
66
|
+
parent = byIndex.get(parent.parentIndex);
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
if (selected.length === 1) return selected[0];
|
|
72
|
+
if (Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 200));
|
|
73
|
+
} while (Date.now() < deadline);
|
|
74
|
+
throw new Error('Asset sort preference is unavailable or ambiguous.\nNext: mm-harness call ui.screenshot --adapter mobile');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function choose(session, option, timeoutMs) {
|
|
78
|
+
await openSortMenu(session, timeoutMs);
|
|
45
79
|
await session.execute('ui.press', { test_id: option, timeout_ms: timeoutMs, settle: false });
|
|
46
80
|
const deadline = Date.now() + timeoutMs;
|
|
47
81
|
let rows = [];
|
|
48
82
|
while (Date.now() <= deadline) {
|
|
49
83
|
const snapshot = await session.snapshot();
|
|
50
84
|
rows = assetRows(snapshot);
|
|
51
|
-
|
|
85
|
+
const ordered = option === 'sort-alphabetical' ? isAlphabetical(rows) : isDeclining(rows);
|
|
86
|
+
if (ordered && !sessionNode(snapshot, option)) return rows;
|
|
52
87
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
53
88
|
}
|
|
54
|
-
throw new Error(`Asset sort ${option} did not
|
|
89
|
+
throw new Error(`Asset sort ${option} did not produce the expected visible row order: ${JSON.stringify(rows)}.`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function visibleTokenList(session, deadline) {
|
|
93
|
+
while (Date.now() <= deadline) {
|
|
94
|
+
const snapshot = await session.snapshot();
|
|
95
|
+
if (!sessionNode(snapshot, 'sort-alphabetical')
|
|
96
|
+
&& sessionNode(snapshot, 'token-sort-button') && assetRows(snapshot).length >= 2) return;
|
|
97
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
98
|
+
}
|
|
99
|
+
throw new Error('Asset token list was not visible after restoring the sort preference.\nNext: mm-harness call ui.screenshot --adapter mobile');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function restoreSort(session, option, timeoutMs) {
|
|
103
|
+
const deadline = Date.now() + timeoutMs;
|
|
104
|
+
const remaining = () => {
|
|
105
|
+
const ms = deadline - Date.now();
|
|
106
|
+
if (ms <= 0) throw new Error('Asset sort restoration timed out.\nNext: mm-harness call ui.screenshot --adapter mobile');
|
|
107
|
+
return ms;
|
|
108
|
+
};
|
|
109
|
+
if (await selectedSort(session, remaining()) !== option) {
|
|
110
|
+
await session.execute('ui.press', { test_id: option, timeout_ms: remaining(), settle: false });
|
|
111
|
+
await visibleTokenList(session, deadline);
|
|
112
|
+
if (await selectedSort(session, remaining()) !== option) {
|
|
113
|
+
throw new Error('Asset sort preference was not restored.\nNext: mm-harness call ui.screenshot --adapter mobile');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
await session.execute('ui.press', { test_id: option, timeout_ms: remaining(), settle: false });
|
|
117
|
+
await visibleTokenList(session, deadline);
|
|
55
118
|
}
|
|
56
119
|
|
|
57
120
|
async function revealTokenList(session, timeoutMs) {
|
|
@@ -76,9 +139,13 @@ function sessionNode(snapshot, identifier) {
|
|
|
76
139
|
runAdapter(async (input) => {
|
|
77
140
|
const timeoutMs = Number(input.node?.timeout_ms ?? 30_000);
|
|
78
141
|
const session = createNativeSession(input, 'Assets', `sort-${process.pid}`);
|
|
142
|
+
const errors = [];
|
|
143
|
+
let initialSort;
|
|
144
|
+
let result;
|
|
79
145
|
try {
|
|
80
146
|
await session.open();
|
|
81
147
|
await revealTokenList(session, timeoutMs);
|
|
148
|
+
initialSort = await selectedSort(session, timeoutMs);
|
|
82
149
|
const alphabetical = await choose(session, 'sort-alphabetical', timeoutMs);
|
|
83
150
|
const declining = await choose(session, 'sort-declining-balance', timeoutMs);
|
|
84
151
|
const failures = [];
|
|
@@ -91,14 +158,30 @@ runAdapter(async (input) => {
|
|
|
91
158
|
if (failures.length > 0) {
|
|
92
159
|
throw new Error(`Asset token sorting failed: ${failures.join('; ')}.`);
|
|
93
160
|
}
|
|
94
|
-
|
|
161
|
+
result = {
|
|
95
162
|
action: input.action,
|
|
96
163
|
alphabetical: alphabetical.map((row) => row.name),
|
|
97
164
|
decliningFiat: declining.map((row) => row.fiat),
|
|
98
|
-
|
|
165
|
+
initialSort: initialSort.slice('sort-'.length),
|
|
166
|
+
restoredSort: initialSort.slice('sort-'.length),
|
|
99
167
|
proofPath: 'native-accessibility-token-sorting',
|
|
100
168
|
};
|
|
101
|
-
}
|
|
169
|
+
} catch (error) {
|
|
170
|
+
errors.push(error);
|
|
171
|
+
}
|
|
172
|
+
if (initialSort) {
|
|
173
|
+
try {
|
|
174
|
+
await restoreSort(session, initialSort, timeoutMs);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
errors.push(error);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
102
180
|
await closeNativeSession(session);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
errors.push(error);
|
|
103
183
|
}
|
|
184
|
+
if (errors.length === 1) throw errors[0];
|
|
185
|
+
if (errors.length > 1) throw new AggregateError(errors, `Asset sorting failed: ${errors.map((error) => error.message).join('; ')}`);
|
|
186
|
+
return result;
|
|
104
187
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// Navigate native Network Settings and search for network rows by chain ID and name.
|
|
1
2
|
import { nativeLabel, nativeNode } from '../platform/native-session.mjs';
|
|
2
3
|
|
|
3
4
|
const IDS = {
|
|
@@ -26,6 +27,10 @@ export async function waitForSnapshot(session, timeoutMs, predicate, message) {
|
|
|
26
27
|
const deadline = Date.now() + timeoutMs;
|
|
27
28
|
while (Date.now() <= deadline) {
|
|
28
29
|
const snapshot = await session.snapshot();
|
|
30
|
+
const labels = snapshot.nodes.map(nativeLabel);
|
|
31
|
+
if (labels.includes('Reload') && labels.includes('Go home') && labels.includes('Tools button')) {
|
|
32
|
+
throw new Error('Mobile network navigation opened the Expo developer menu instead of the requested control.\nNext: mm-harness call ui.key_press key=BACK --adapter mobile');
|
|
33
|
+
}
|
|
29
34
|
const value = predicate(snapshot);
|
|
30
35
|
if (value) return { snapshot, value };
|
|
31
36
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
@@ -42,23 +47,26 @@ async function systemBack(session) {
|
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
export async function openNetworkManager(session, timeoutMs) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (visibleNode(snapshot, IDS.details)) {
|
|
49
|
-
await systemBack(session);
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (visibleNode(snapshot, IDS.menu)) {
|
|
53
|
-
await session.execute('ui.press', { test_id: IDS.menu, timeout_ms: timeoutMs, settle: false });
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (IDS.wallet.some((identifier) => nativeNode(snapshot, identifier))) {
|
|
57
|
-
await session.execute('ui.press', { test_id: IDS.hamburger, timeout_ms: timeoutMs, settle: false });
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
50
|
+
const snapshot = await session.snapshot();
|
|
51
|
+
if (visibleNode(snapshot, IDS.manager)) return snapshot;
|
|
52
|
+
if (visibleNode(snapshot, IDS.details)) {
|
|
60
53
|
await systemBack(session);
|
|
54
|
+
return (await waitForSnapshot(
|
|
55
|
+
session, timeoutMs, (current) => visibleNode(current, IDS.manager),
|
|
56
|
+
'Mobile Networks settings did not return from network details.',
|
|
57
|
+
)).snapshot;
|
|
58
|
+
}
|
|
59
|
+
if (!visibleNode(snapshot, IDS.menu)) {
|
|
60
|
+
if (!IDS.wallet.some((identifier) => nativeNode(snapshot, identifier))) {
|
|
61
|
+
throw new Error('Mobile network navigation requires Home, the account menu, or network settings.');
|
|
62
|
+
}
|
|
63
|
+
await session.execute('ui.press', { test_id: IDS.hamburger, timeout_ms: timeoutMs, settle: false });
|
|
64
|
+
await waitForSnapshot(
|
|
65
|
+
session, timeoutMs, (current) => visibleNode(current, IDS.menu),
|
|
66
|
+
'The Mobile account menu did not expose the Networks control after opening.',
|
|
67
|
+
);
|
|
61
68
|
}
|
|
69
|
+
await session.execute('ui.press', { test_id: IDS.menu, timeout_ms: timeoutMs, settle: false });
|
|
62
70
|
return (await waitForSnapshot(
|
|
63
71
|
session,
|
|
64
72
|
timeoutMs,
|