From ad31020baf93f1254dabfcc0a237c93de26d5979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Tani=C3=A7a?= Date: Thu, 30 Apr 2026 13:07:25 -0600 Subject: [PATCH 1/6] fix(predict): update Polymarket adapter contract addresses (#29573) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Updates the Polymarket adapter contract addresses used by Predict to the new `CtfCollateralAdapter` and `NegRiskCtfCollateralAdapter` values shared by Polymarket. Polymarket added new events on these adapters and will stop accepting relayed transactions through the old adapters on May 1 at 3pm UTC. Without this change, Polymarket transactions routed through the old adapters will start failing. ## **Changelog** CHANGELOG entry: Updated Polymarket adapter contracts so Polymarket prediction transactions continue working after the relayer migration. ## **Related issues** Fixes: - PRED-853 - https://consensyssoftware.atlassian.net/browse/PRED-853 ## **Manual testing steps** ```gherkin Feature: Polymarket adapter migration Scenario: user submits a Polymarket transaction after the adapter migration Given the app is running a build from this branch And the user has access to the Predict/Polymarket experience When the user submits a Polymarket transaction that uses the collateral adapters Then the app should use the updated adapter addresses And the transaction should be accepted by the relayer after the migration cutoff ``` Not run locally; constants-only configuration update. ## **Screenshots/Recordings** Not applicable. ### **Before** Not applicable. ### **After** Not applicable. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > **Medium Risk** > Low code complexity, but changes production on-chain contract targets for Polymarket claim/relay flows; incorrect addresses would cause transactions to fail. > > **Overview** > Updates Predict’s Polymarket configuration to use new `CtfCollateralAdapter` and `NegRiskCtfCollateralAdapter` contract addresses in `polymarket/constants.ts`. > > This shifts v2 claim/relay routing (as referenced by protocol `claim.standardTarget`/`claim.negRiskTarget`) to the new adapter contracts to maintain compatibility with Polymarket’s relayer migration. > > Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 08b08be2963d84291c3593a232e6a1b98d3c8cce. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot). --- app/components/UI/Predict/providers/polymarket/constants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/UI/Predict/providers/polymarket/constants.ts b/app/components/UI/Predict/providers/polymarket/constants.ts index 0d6838c7515..01fe31fb2ae 100644 --- a/app/components/UI/Predict/providers/polymarket/constants.ts +++ b/app/components/UI/Predict/providers/polymarket/constants.ts @@ -101,10 +101,10 @@ export const COLLATERAL_OFFRAMP_ADDRESS = '0x2957922Eb93258b93368531d39fAcCA3B4dC5854'; export const CTF_COLLATERAL_ADAPTER_ADDRESS = - '0xADa100874d00e3331D00F2007a9c336a65009718'; + '0xAdA100Db00Ca00073811820692005400218FcE1f'; export const NEG_RISK_CTF_COLLATERAL_ADAPTER_ADDRESS = - '0xAdA200001000ef00D07553cEE7006808F895c6F1'; + '0xadA2005600Dec949baf300f4C6120000bDB6eAab'; export const POLYGON_USDC_CAIP_ASSET_ID = `${POLYGON_MAINNET_CAIP_CHAIN_ID}/erc20:${MATIC_CONTRACTS.collateral}` as const; From 84250b36b74974988a24de6257eeb748a39cbad3 Mon Sep 17 00:00:00 2001 From: jvbriones <1674192+jvbriones@users.noreply.github.com> Date: Thu, 30 Apr 2026 21:24:16 +0200 Subject: [PATCH 2/6] ci: improve E2E merge gate, add yarn cache, rename flakiness label (#29553) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Three independent improvements to the CI pipeline: - E2E merge gate fix: Gate now checks build jobs too (build-android-apks, build-ios-apps). Previously a failed build caused smoke tests to skip, which the gate treated as passing. This will avoid PRs getting merged with broken builds like: https://github.com/MetaMask/metamask-mobile/actions/runs/25074016847 - Yarn cache: Added node_modules + .yarn/install-state.gz caching to component-view-tests, merge-unit-and-component-view-tests, and setup-e2e-env. Saves ~2–4 min on cache hits. - Label rename: skip-e2e-quality-gate → skip-e2e-flakiness-detection. Post-merge: rename the label manually in GitHub repo settings. - Time-gated auto-labeling: The pr-not-ready-for-e2e label is now only auto-applied to new PRs opened between 13:00–17:00 UTC (15:00–19:00 CEST / 9:00–13:00 EDT), targeting the Europe–US East Coast overlap window. ## **Changelog** CHANGELOG entry: ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > **Medium Risk** > Modifies CI workflow gating and label-driven behavior that can block or permit merges, so misconfiguration could affect release throughput. Changes are scoped to GitHub Actions/scripts and caching, with no app runtime impact. > > **Overview** > **CI merge gating is tightened for E2E** by treating failures/cancellations in `build-android-apks` and `build-ios-apps` as merge-blocking (previously a failed build could cause smoke tests to skip and still pass the gate), while still allowing intentional `skipped` outcomes when E2E is not expected to run. > > **Dependency caching is expanded** by caching `node_modules` plus `.yarn/install-state.gz` in the shared `setup-e2e-env` action and in `ci.yml` jobs that run component-view tests and coverage merge, skipping `yarn install` on cache hits. > > **Labeling/controls are updated**: `skip-e2e-quality-gate` is renamed to `skip-e2e-flakiness-detection` across scripts, rerun triggers, and docs; `auto-label-not-ready-for-e2e` is time-gated (13:00–17:00 UTC) before applying `pr-not-ready-for-e2e`; and documentation/ownership is updated (adds QA ownership for `E2E_DECISION_TREE.md` and clarifies `skip-e2e` as an exceptional “skip builds + all E2E” escape hatch). > > Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ede188d7cfe6bb50f67ad5feb8d3cd0a29abf922. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot). --- .github/CODEOWNERS | 1 + .github/actions/setup-e2e-env/action.yml | 1 + .github/guidelines/E2E_DECISION_TREE.md | 10 +++- .github/guidelines/LABELING_GUIDELINES.md | 2 +- .github/scripts/e2e-split-tags-shards.mjs | 4 +- .../auto-label-not-ready-for-e2e.yml | 13 +++++ .github/workflows/ci.yml | 51 +++++++++++++------ .../rerun-ci-on-skipped-e2e-labels.yml | 9 ++-- 8 files changed, 68 insertions(+), 23 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6eac88a5261..f1d6f77fc2c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -334,6 +334,7 @@ tests/tools/ @MetaMask/qa tests/websocket/ @MetaMask/qa # QA Team - CI +.github/guidelines/E2E_DECISION_TREE.md @MetaMask/qa .github/actions/smart-e2e-selection/ @MetaMask/qa .github/workflows/ai-pr-risk-analysis.yml @MetaMask/qa .github/workflows/auto-label-not-ready-for-e2e.yml @MetaMask/qa diff --git a/.github/actions/setup-e2e-env/action.yml b/.github/actions/setup-e2e-env/action.yml index 227deaba598..0e54460e06e 100644 --- a/.github/actions/setup-e2e-env/action.yml +++ b/.github/actions/setup-e2e-env/action.yml @@ -281,6 +281,7 @@ runs: with: path: | node_modules + .yarn/install-state.gz key: ${{ inputs.cache-prefix }}-yarn-${{ inputs.platform }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Install JavaScript dependencies with retry diff --git a/.github/guidelines/E2E_DECISION_TREE.md b/.github/guidelines/E2E_DECISION_TREE.md index 96441d8f46b..53a9aebb4d7 100644 --- a/.github/guidelines/E2E_DECISION_TREE.md +++ b/.github/guidelines/E2E_DECISION_TREE.md @@ -10,7 +10,7 @@ flowchart TD GR -->|PR label: skip-e2e| HS[No E2E] GR -->|PR label: pr-not-ready-for-e2e| L2[No E2E] L2 -->|ignorable-only changes| NoBlock[No merge block] - L2 -->|non-ignorable changes| Skip2[Merge blocked] + L2 -->|non-ignorable changes| Skip2[⛔️ Merge blocked] GR -->|PR ignorable-only changes| Ignorable[No E2E] GR -->|PR has Android-only changes| Android[Android Build + Tests needed] GR -->|PR has iOS-only changes| iOS[iOS Build + Test needed] @@ -41,9 +41,15 @@ Runs only when all of the following are true: - No hard E2E skip signal (label `skip-e2e`) - No `skip-smart-e2e-selection` label +## (Exceptional) skip builds and all E2E tests + +- Label `skip-e2e` can be added to the PR to skip E2E tests (and builds) in case of infra issues. +- Using this label should be exceptional in case of CI friction and urgencies. Verify new changes and regressions manually before merging. + ## E2E flakiness detection in PRs Flakiness detection is applied to modified E2E test files in PRs: - Modified E2E test files run twice -- It applies to existing test files as well as new test files +- It applies to existing test files as well as new test files added in the PR +- It can be disabled by adding the label `skip-e2e-flakiness-detection`. Useful when making large refactors or when changes don't pose flakiness risk. diff --git a/.github/guidelines/LABELING_GUIDELINES.md b/.github/guidelines/LABELING_GUIDELINES.md index c93d7d4d976..4819b24e8f7 100644 --- a/.github/guidelines/LABELING_GUIDELINES.md +++ b/.github/guidelines/LABELING_GUIDELINES.md @@ -33,7 +33,7 @@ Using any of these labels should be exceptional in case of CI friction and urgen - **skip-sonar-cloud**: The PR will be merged without running SonarCloud checks. - **skip-e2e**: The PR will be merged without running E2E tests. -- **skip-e2e-quality-gate**: This label will disable the default test retries for E2E test files modified in a PR. Useful when making large refactors or when changes don't pose flakiness risk. +- **skip-e2e-flakiness-detection**: This label will disable the default test retries for E2E test files modified in a PR. Useful when making large refactors or when changes don't pose flakiness risk. ### Skip Smart E2E Selection diff --git a/.github/scripts/e2e-split-tags-shards.mjs b/.github/scripts/e2e-split-tags-shards.mjs index 590fcb28af0..b178e0f5b28 100644 --- a/.github/scripts/e2e-split-tags-shards.mjs +++ b/.github/scripts/e2e-split-tags-shards.mjs @@ -108,9 +108,9 @@ async function shouldSkipFlakinessDetection() { ); const labels = data?.repository?.pullRequest?.labels?.nodes || []; - const labelFound = labels.some((l) => String(l?.name).toLowerCase() === 'skip-e2e-quality-gate'); + const labelFound = labels.some((l) => String(l?.name).toLowerCase() === 'skip-e2e-flakiness-detection'); if (labelFound) { - console.log('⏭️ Found "skip-e2e-quality-gate" label → SKIPPING flakiness detection'); + console.log('⏭️ Found "skip-e2e-flakiness-detection" label → SKIPPING flakiness detection'); } return labelFound; } catch (e) { diff --git a/.github/workflows/auto-label-not-ready-for-e2e.yml b/.github/workflows/auto-label-not-ready-for-e2e.yml index 58ea09f5dd3..df0e4bf2b2a 100644 --- a/.github/workflows/auto-label-not-ready-for-e2e.yml +++ b/.github/workflows/auto-label-not-ready-for-e2e.yml @@ -1,3 +1,5 @@ +# Automatically applies the 'pr-not-ready-for-e2e' label to newly opened PRs, +# but only if the PR is opened between 13:00 and 17:00 UTC (15:00–19:00 CEST). name: Auto-apply pr-not-ready-for-e2e label on: @@ -16,7 +18,18 @@ jobs: permissions: pull-requests: write steps: + - name: Check current UTC hour + id: time-check + run: | + HOUR=$(date -u +%H) + echo "Current UTC hour: $HOUR" + if [[ $HOUR -ge 13 && $HOUR -lt 17 ]]; then + echo "in_window=true" >> "$GITHUB_OUTPUT" + else + echo "in_window=false" >> "$GITHUB_OUTPUT" + fi - name: Add pr-not-ready-for-e2e label + if: steps.time-check.outputs.in_window == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a83b8d4de9..96719067513 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -510,11 +510,20 @@ jobs: if: ${{ !cancelled() && github.event_name != 'merge_group' }} steps: - uses: actions/checkout@v6 + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + .yarn/install-state.gz + key: ${{ runner.os }}-node-modules-${{ hashFiles('yarn.lock') }} - uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' cache: yarn - name: Install Yarn dependencies with retry + if: steps.cache-node-modules.outputs.cache-hit != 'true' uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2 with: timeout_minutes: 10 @@ -622,11 +631,20 @@ jobs: shard: [1, 2] steps: - uses: actions/checkout@v6 + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + .yarn/install-state.gz + key: ${{ runner.os }}-node-modules-${{ hashFiles('yarn.lock') }} - uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' cache: yarn - name: Install Yarn dependencies with retry + if: steps.cache-node-modules.outputs.cache-hit != 'true' uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2 with: timeout_minutes: 10 @@ -981,6 +999,8 @@ jobs: needs: - get_requirements - all-jobs-pass + - build-android-apks + - build-ios-apps - e2e-smoke-tests-android - e2e-smoke-tests-ios env: @@ -1004,22 +1024,23 @@ jobs: exit 1 fi - # Check E2E jobs only if they should have run + # Check E2E build + smoke results only if E2E should have run. + # 'skipped' is acceptable — covers merge_group, fork PRs, ignorable-only changes, + # platform-only PRs, and AI selection returning zero tags. + # 'failure'/'cancelled' on any of build or smoke must block merge. if [[ "${{ needs.get_requirements.outputs.skip_e2e }}" != "true" ]]; then - # Accept both 'success' and 'skipped' as valid results - # 'skipped' occurs during merge_group events or when jobs are intentionally skipped - # Only fail on 'failure' or 'cancelled' - ANDROID_RESULT="${{ needs.e2e-smoke-tests-android.result }}" - if [[ "$ANDROID_RESULT" == "failure" ]] || [[ "$ANDROID_RESULT" == "cancelled" ]]; then - echo "Android E2E tests failed (result: $ANDROID_RESULT)" - exit 1 - fi - - IOS_RESULT="${{ needs.e2e-smoke-tests-ios.result }}" - if [[ "$IOS_RESULT" == "failure" ]] || [[ "$IOS_RESULT" == "cancelled" ]]; then - echo "iOS E2E tests failed (result: $IOS_RESULT)" - exit 1 - fi + for entry in \ + "build-android-apks:${{ needs.build-android-apks.result }}" \ + "e2e-smoke-tests-android:${{ needs.e2e-smoke-tests-android.result }}" \ + "build-ios-apps:${{ needs.build-ios-apps.result }}" \ + "e2e-smoke-tests-ios:${{ needs.e2e-smoke-tests-ios.result }}"; do + name="${entry%%:*}" + result="${entry#*:}" + if [[ "$result" == "failure" ]] || [[ "$result" == "cancelled" ]]; then + echo "::error::Required E2E job '$name' did not succeed (result: $result)" + exit 1 + fi + done fi echo "All required jobs passed" diff --git a/.github/workflows/rerun-ci-on-skipped-e2e-labels.yml b/.github/workflows/rerun-ci-on-skipped-e2e-labels.yml index 2986f7fae76..24cea0920f3 100644 --- a/.github/workflows/rerun-ci-on-skipped-e2e-labels.yml +++ b/.github/workflows/rerun-ci-on-skipped-e2e-labels.yml @@ -14,7 +14,7 @@ jobs: if: >- github.event.label.name == 'skip-smart-e2e-selection' || github.event.label.name == 'skip-e2e' || - github.event.label.name == 'skip-e2e-quality-gate' || + github.event.label.name == 'skip-e2e-flakiness-detection' || github.event.label.name == 'pr-not-ready-for-e2e' runs-on: ubuntu-latest permissions: @@ -92,5 +92,8 @@ jobs: run: | RUN_ID="${{ steps.find.outputs.run_id }}" echo "Re-running workflow $RUN_ID..." - gh run rerun "$RUN_ID" --repo "$REPO" - echo "CI workflow re-triggered successfully" + if gh run rerun "$RUN_ID" --repo "$REPO"; then + echo "CI workflow re-triggered successfully" + else + echo "Rerun not possible (run may not be in a retriable state)" + fi From 862bf7619e11a37bd13c54fe5e402b314e1df073 Mon Sep 17 00:00:00 2001 From: sleepytanya <104780023+sleepytanya@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:12:14 -0400 Subject: [PATCH 3/6] test: add automated build environment display to RC and production builds. (#29390) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Adds automated build environment display to RC and production builds. Captures actual environment values used during builds (METAMASK_ENVIRONMENT, METAMASK_BUILD_TYPE, API URLs, etc.) and displays them: - RC builds: In PR comment alongside build links - Prod builds: Extracted via new workflow (Slack notification in follow-up PR). **Changes:** - scripts/apply-build-config.js: Add --write-build-env flag to generate build-env.json with actual env values - build.yml: Generate and upload build-env.json as artifact during builds (used by both RC and prod) - build-rc-auto.yml: Download build-env artifacts in post-rc-build-comment job - scripts/build-announce/: Add env section to RC build PR comments - prod-build-env-notify.yml: New workflow triggered on prod build completion, extracts env values (Slack posting in follow-up PR) ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/MMQA-1108 ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > **Medium Risk** > Changes CI build workflows and the RC PR-comment pipeline to generate, upload, download, and parse a new `build-env.json` artifact; misconfiguration could break build jobs or PR comment posting. Risk is limited to CI/reporting paths and is largely non-blocking (`continue-on-error`/`|| true`) but still touches release build automation. > > **Overview** > Adds generation of a `build-env.json` file during CI builds (via `scripts/apply-build-config.js --write-build-env`) and uploads it as a build artifact for each platform. > > Updates RC automation to download these artifacts and extends `scripts/build-announce` to parse them and include a **Build Environment** section (env/build type, remote FF mapping, key API URLs/flags) in the RC PR comment, with a fallback “not available” section on extraction failure. > > Introduces a new `Prod Build Env Notify` workflow that triggers on successful production workflow runs, downloads the `build-env` artifact, extracts key values with `jq`, and currently prints a placeholder for a future Slack notification; also ignores local `build-env.json` via `.gitignore`. > > Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 0f0c4b58cd73643c90c1cb08fd2aff04bb04a84b. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot). --- .github/workflows/build-rc-auto.yml | 9 ++ .github/workflows/build.yml | 10 ++ .github/workflows/prod-build-env-notify.yml | 122 +++++++++++++++ .gitignore | 3 + scripts/apply-build-config.js | 62 +++++++- .../build-announce/env-validation-section.ts | 141 ++++++++++++++++++ scripts/build-announce/index.ts | 102 ++++++++++++- scripts/build-announce/types.ts | 8 + scripts/build-announce/validate-env.ts | 35 +++++ 9 files changed, 487 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/prod-build-env-notify.yml create mode 100644 scripts/build-announce/env-validation-section.ts create mode 100644 scripts/build-announce/validate-env.ts diff --git a/.github/workflows/build-rc-auto.yml b/.github/workflows/build-rc-auto.yml index 8f9ca3d5f77..95a6dba90f1 100644 --- a/.github/workflows/build-rc-auto.yml +++ b/.github/workflows/build-rc-auto.yml @@ -151,6 +151,15 @@ jobs: cache: yarn - name: Install dependencies run: yarn install --immutable + + - name: Download build environment artifacts + continue-on-error: true + uses: actions/download-artifact@v4 + with: + pattern: build-env-main-rc-* + path: build-env-artifacts + merge-multiple: false + - name: Post RC Build Comment with Test Plan run: node -r esbuild-register scripts/build-announce/index.ts timeout-minutes: 8 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index adf4bcb4a3d..fdf959acbb5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -305,6 +305,8 @@ jobs: eval "$(node scripts/apply-build-config.js ${{ inputs.build_name }} --export)" # Persist to GITHUB_ENV so later steps (e.g. Build) see CONFIGURATION, IS_SIM_BUILD, etc. node scripts/apply-build-config.js ${{ inputs.build_name }} --export-github-env >> "$GITHUB_ENV" + # Generate build-env.json capturing actual env values used in this build (non-critical) + node scripts/apply-build-config.js ${{ inputs.build_name }} --write-build-env || true - name: Validate secrets env: @@ -497,6 +499,14 @@ jobs: path: ${{ steps.rename.outputs.android_sourcemap_dir }} if-no-files-found: warn + - name: Upload build environment JSON + if: success() + uses: actions/upload-artifact@v4 + with: + name: build-env-${{ inputs.build_name }}-${{ matrix.platform }} + path: build-env.json + if-no-files-found: warn + # Single fan-in job so workflow_call outputs work with matrix `build` (matrix jobs cannot feed workflow outputs directly). emit-build-metadata: name: Emit build metadata diff --git a/.github/workflows/prod-build-env-notify.yml b/.github/workflows/prod-build-env-notify.yml new file mode 100644 index 00000000000..c3e631258bb --- /dev/null +++ b/.github/workflows/prod-build-env-notify.yml @@ -0,0 +1,122 @@ +############################################################################################## +# +# Production Build Environment Notification +# +# Automatically triggers when production builds complete. +# Downloads build-env.json artifact and extracts environment values. +# TODO: Post to Slack (follow-up PR) +# +############################################################################################## +name: Prod Build Env Notify + +on: + workflow_run: + workflows: ["Runway iOS Production", "Runway Android Production"] + types: [completed] + +jobs: + notify-env: + name: Post Environment Info + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-node@v6 + with: + node-version-file: '.nvmrc' + + - name: Download build-env artifact from triggering workflow + continue-on-error: true + id: download-artifact + uses: actions/download-artifact@v6 + with: + name: build-env-main-prod-${{ github.event.workflow_run.name == 'Runway iOS Production' && 'ios' || 'android' }} + path: build-env-artifacts + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract and display environment values + if: steps.download-artifact.outcome == 'success' + id: extract-env + run: | + JSON_FILE="build-env-artifacts/build-env.json" + + if [ ! -f "$JSON_FILE" ]; then + echo "ERROR: build-env.json not found" + exit 1 + fi + + echo "=== Build Environment Values ===" + cat "$JSON_FILE" + echo "" + + # Extract values + BUILD_NAME=$(jq -r '.buildName' "$JSON_FILE") + METAMASK_ENV=$(jq -r '.env.METAMASK_ENVIRONMENT' "$JSON_FILE") + BUILD_TYPE=$(jq -r '.env.METAMASK_BUILD_TYPE' "$JSON_FILE") + REWARDS_URL=$(jq -r '.env.REWARDS_API_URL' "$JSON_FILE") + PORTFOLIO_URL=$(jq -r '.env.MM_PORTFOLIO_URL' "$JSON_FILE") + RAMPS_ENV=$(jq -r '.env.RAMPS_ENVIRONMENT' "$JSON_FILE") + + # Map to Remote Feature Flag values + case "$METAMASK_ENV" in + production) REMOTE_FF_ENV="prod" ;; + rc) REMOTE_FF_ENV="rc" ;; + beta) REMOTE_FF_ENV="beta" ;; + test|e2e) REMOTE_FF_ENV="test" ;; + exp) REMOTE_FF_ENV="exp" ;; + *) REMOTE_FF_ENV="dev" ;; + esac + + case "$BUILD_TYPE" in + flask) REMOTE_FF_DIST="flask" ;; + *) REMOTE_FF_DIST="main" ;; + esac + + # Output for next steps + { + echo "build_name=$BUILD_NAME" + echo "environment=$METAMASK_ENV" + echo "build_type=$BUILD_TYPE" + echo "remote_ff_env=$REMOTE_FF_ENV" + echo "remote_ff_dist=$REMOTE_FF_DIST" + echo "rewards_url=$REWARDS_URL" + echo "portfolio_url=$PORTFOLIO_URL" + echo "ramps_env=$RAMPS_ENV" + } >> "$GITHUB_OUTPUT" + + echo "" + echo "=== Extracted Values ===" + echo "Environment: $METAMASK_ENV" + echo "Build Type: $BUILD_TYPE" + echo "Remote Feature Flag Env: $REMOTE_FF_ENV" + echo "Remote Feature Flag Distribution: $REMOTE_FF_DIST" + echo "Rewards API URL: $REWARDS_URL" + echo "MM_PORTFOLIO_URL: $PORTFOLIO_URL" + echo "Ramps Environment: $RAMPS_ENV" + + - name: Post to Slack + if: steps.download-artifact.outcome == 'success' + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + PLATFORM: ${{ github.event.workflow_run.name == 'Runway iOS Production' && 'iOS' || 'Android' }} + ENVIRONMENT: ${{ steps.extract-env.outputs.environment }} + BUILD_TYPE: ${{ steps.extract-env.outputs.build_type }} + REMOTE_FF_ENV: ${{ steps.extract-env.outputs.remote_ff_env }} + REMOTE_FF_DIST: ${{ steps.extract-env.outputs.remote_ff_dist }} + REWARDS_URL: ${{ steps.extract-env.outputs.rewards_url }} + PORTFOLIO_URL: ${{ steps.extract-env.outputs.portfolio_url }} + RAMPS_ENV: ${{ steps.extract-env.outputs.ramps_env }} + WORKFLOW_URL: ${{ github.event.workflow_run.html_url }} + run: | + echo "TODO: Post to Slack" + echo "Platform: $PLATFORM" + echo "Environment: $ENVIRONMENT" + echo "Build Type: $BUILD_TYPE" + echo "Remote FF Env: $REMOTE_FF_ENV" + echo "Remote FF Distribution: $REMOTE_FF_DIST" + echo "Rewards URL: $REWARDS_URL" + echo "Portfolio URL: $PORTFOLIO_URL" + echo "Ramps Environment: $RAMPS_ENV" + echo "Workflow URL: $WORKFLOW_URL" diff --git a/.gitignore b/.gitignore index f9bc9b242ad..060f173e07b 100644 --- a/.gitignore +++ b/.gitignore @@ -196,3 +196,6 @@ runway-artifacts/ release-test-plan.json release-delta.json release-signoffs.json + +# Build environment validation +build-env.json diff --git a/scripts/apply-build-config.js b/scripts/apply-build-config.js index 275929815b2..6b7a8e64c14 100755 --- a/scripts/apply-build-config.js +++ b/scripts/apply-build-config.js @@ -18,6 +18,7 @@ const path = require('path'); const yaml = require('js-yaml'); const BUILDS_PATH = path.join(__dirname, '../builds.yml'); +const BUILD_ENV_PATH = path.join(__dirname, '../build-env.json'); function loadConfig(buildName) { if (!fs.existsSync(BUILDS_PATH)) { @@ -106,16 +107,68 @@ function exportForGitHubEnv(buildName) { return lines.join('\n'); } +/** + * Write build environment to JSON file for display in PR comments. + * This captures the actual env values used during build time. + */ +function writeBuildEnvJson(buildName) { + const config = loadConfig(buildName); + + const envVarsToCapture = [ + 'METAMASK_ENVIRONMENT', + 'METAMASK_BUILD_TYPE', + 'REWARDS_API_URL', + 'MM_PORTFOLIO_URL', + 'RAMPS_ENVIRONMENT', + 'IS_TEST', + // Additional env vars for full environment info + 'PORTFOLIO_API_URL', + 'SECURITY_ALERTS_API_URL', + 'DECODING_API_URL', + 'AUTH_SERVICE_URL', + 'DIGEST_API_URL', + 'SOCIAL_API_URL', + 'BAANX_API_URL', + 'RAMP_DEV_BUILD', + 'BRIDGE_USE_DEV_APIS', + 'RAMP_INTERNAL_BUILD', + ]; + + const buildEnv = { + buildName, + buildTime: new Date().toISOString(), + env: {}, + }; + + if (config.env) { + envVarsToCapture.forEach((key) => { + if (config.env[key] !== undefined) { + buildEnv.env[key] = String(config.env[key]); + } + }); + } + + if (config.code_fencing) { + buildEnv.codeFencing = config.code_fencing; + } + + fs.writeFileSync(BUILD_ENV_PATH, JSON.stringify(buildEnv, null, 2)); + console.log(`Wrote build environment to ${BUILD_ENV_PATH}`); + + return buildEnv; +} + // CLI if (require.main === module) { const args = process.argv.slice(2); const buildName = args.find((a) => !a.startsWith('--')); const exportMode = args.includes('--export'); const exportGitHubEnvMode = args.includes('--export-github-env'); + const writeBuildEnvMode = args.includes('--write-build-env'); if (!buildName) { console.error( - 'Usage: node apply-build-config.js [--export | --export-github-env]', + 'Usage: node apply-build-config.js [--export | --export-github-env | --write-build-env]', ); console.error('Example: node apply-build-config.js main-prod'); process.exit(1); @@ -126,12 +179,14 @@ if (require.main === module) { console.log(exportForGitHubEnv(buildName)); } else if (exportMode) { console.log(exportForShell(buildName)); + } else if (writeBuildEnvMode) { + writeBuildEnvJson(buildName); } else { applyConfig(buildName); - console.log(`✅ Applied config for ${buildName}`); + console.log(`Applied config for ${buildName}`); } } catch (error) { - console.error(`❌ ${error.message}`); + console.error(`Error: ${error.message}`); process.exit(1); } } @@ -141,4 +196,5 @@ module.exports = { applyConfig, exportForShell, exportForGitHubEnv, + writeBuildEnvJson, }; diff --git a/scripts/build-announce/env-validation-section.ts b/scripts/build-announce/env-validation-section.ts new file mode 100644 index 00000000000..4309868d5bd --- /dev/null +++ b/scripts/build-announce/env-validation-section.ts @@ -0,0 +1,141 @@ +/** + * Environment Section Builder + * + * Builds the markdown section displaying build environment values + * to be included in the RC build comment. + */ + +import type { EnvValidationResult } from './types'; + +/** + * Maps METAMASK_ENVIRONMENT to Remote Feature Flag Env display value + */ +function getRemoteFFEnv(env: string | undefined): string { + switch (env) { + case 'production': + return 'prod'; + case 'rc': + return 'rc'; + case 'beta': + return 'beta'; + case 'test': + case 'e2e': + return 'test'; + case 'exp': + return 'exp'; + case 'dev': + default: + return 'dev'; + } +} + +/** + * Maps METAMASK_BUILD_TYPE to Remote Feature Flag Distribution display value + */ +function getRemoteFFDistribution(buildType: string | undefined): string { + switch (buildType) { + case 'flask': + return 'flask'; + case 'main': + default: + return 'main'; + } +} + +/** + * Builds the environment section for the PR comment + * Shows actual environment values similar to "About MetaMask" screen + */ +export function buildEnvValidationSection( + androidResult?: EnvValidationResult, + iosResult?: EnvValidationResult, +): string { + // If no results, return empty + if (!androidResult && !iosResult) { + return ''; + } + + const lines: string[] = []; + + lines.push('### :shield: Build Environment\n'); + + // Use one result to display (they should be identical for same build config) + const result = androidResult || iosResult; + if (!result) return ''; + + const env = result.extractedValues.METAMASK_ENVIRONMENT ?? '—'; + const buildType = result.extractedValues.METAMASK_BUILD_TYPE ?? '—'; + const rewardsUrl = result.extractedValues.REWARDS_API_URL ?? '—'; + const portfolioUrl = result.extractedValues.MM_PORTFOLIO_URL ?? '—'; + const rampsEnv = result.extractedValues.RAMPS_ENVIRONMENT ?? '—'; + + // Main environment info table (like About MetaMask screen) + lines.push('| Setting | Value |'); + lines.push('| :--- | :--- |'); + lines.push(`| **Environment** | \`${env}\` |`); + lines.push(`| **Build Type** | \`${buildType}\` |`); + lines.push(`| **Remote Feature Flag Env** | \`${getRemoteFFEnv(env)}\` |`); + lines.push(`| **Remote Feature Flag Distribution** | \`${getRemoteFFDistribution(buildType)}\` |`); + lines.push(`| **Ramps Environment** | \`${rampsEnv}\` |`); + lines.push(''); + + // Detailed info in collapsible section + lines.push('
'); + lines.push('API URLs & Details\n'); + + lines.push('| API | URL |'); + lines.push('| :--- | :--- |'); + lines.push(`| Rewards API | \`${rewardsUrl}\` |`); + lines.push(`| Portfolio API | \`${portfolioUrl}\` |`); + + // Add more URLs if available + const portfolioApiUrl = result.extractedValues.PORTFOLIO_API_URL; + if (portfolioApiUrl) { + lines.push(`| Portfolio API (alt) | \`${portfolioApiUrl}\` |`); + } + + const securityAlertsUrl = result.extractedValues.SECURITY_ALERTS_API_URL; + if (securityAlertsUrl) { + lines.push(`| Security Alerts API | \`${securityAlertsUrl}\` |`); + } + + lines.push('\n**Build Flags:**\n'); + lines.push(`- Build Name: \`${result.buildName}\``); + lines.push(`- IS_TEST: \`${result.extractedValues.IS_TEST ?? 'false'}\``); + + const rampDevBuild = result.extractedValues.RAMP_DEV_BUILD; + if (rampDevBuild) { + lines.push(`- RAMP_DEV_BUILD: \`${rampDevBuild}\``); + } + + const bridgeDevApis = result.extractedValues.BRIDGE_USE_DEV_APIS; + if (bridgeDevApis) { + lines.push(`- BRIDGE_USE_DEV_APIS: \`${bridgeDevApis}\``); + } + + lines.push('\n
\n'); + + return lines.join('\n'); +} + +/** + * Builds a failure section when environment values could not be extracted + */ +export function buildEnvValidationFailureSection(error: string): string { + return `### :shield: Build Environment + +**Status:** :warning: Not available + +
+Details + +Environment values could not be extracted: ${error} + +This may happen if: +- Build artifacts are not available +- build-env.json was not generated during the build + +
+ +`; +} diff --git a/scripts/build-announce/index.ts b/scripts/build-announce/index.ts index df9ced66c31..ec242736aba 100644 --- a/scripts/build-announce/index.ts +++ b/scripts/build-announce/index.ts @@ -1,5 +1,7 @@ // RC Build Announce - Posts RC build comments to GitHub PRs with build links and AI test plan +import { existsSync, readdirSync } from 'fs'; +import { join } from 'path'; import { Octokit } from '@octokit/rest'; import { RC_BUILD_COMMENT_MARKER, @@ -16,7 +18,12 @@ import { buildTestPlanSection, buildTestPlanFailureSection, } from './test-plan-section'; -import type { BuildInfo, TestPlanResult } from './types'; +import { + buildEnvValidationSection, + buildEnvValidationFailureSection, +} from './env-validation-section'; +import { validateEnv } from './validate-env'; +import type { BuildInfo, TestPlanResult, EnvValidationResult } from './types'; /** * Builds the build links section of the comment @@ -70,12 +77,80 @@ function buildMoreInfoSection(buildInfo: BuildInfo): string { `; } +/** + * Look for build-env.json artifacts and extract environment values + */ +function performEnvValidation(): { + androidResult?: EnvValidationResult; + iosResult?: EnvValidationResult; + error?: string; +} { + const artifactsDir = 'build-env-artifacts'; + + if (!existsSync(artifactsDir)) { + console.log('No build-env-artifacts directory found, skipping env extraction'); + return {}; + } + + const results: { + androidResult?: EnvValidationResult; + iosResult?: EnvValidationResult; + error?: string; + } = {}; + + try { + // Check for flat path first (in case merge-multiple flattens all artifacts) + const flatPath = join(artifactsDir, 'build-env.json'); + if (existsSync(flatPath)) { + console.log(`Found build-env.json at ${flatPath}`); + const result = validateEnv(flatPath); + results.androidResult = result; + return results; + } + + // Otherwise look in subdirectories + const dirs = readdirSync(artifactsDir, { withFileTypes: true }); + + for (const dir of dirs) { + if (!dir.isDirectory()) continue; + + const buildEnvPath = join(artifactsDir, dir.name, 'build-env.json'); + + if (!existsSync(buildEnvPath)) { + continue; + } + + console.log(`Found build-env.json at ${buildEnvPath}`); + + // Determine platform from directory name + const platform = dir.name.includes('android') ? 'android' : 'ios'; + const result = validateEnv(buildEnvPath); + + if (platform === 'android') { + results.androidResult = result; + } else { + results.iosResult = result; + } + } + } catch (error) { + results.error = error instanceof Error ? error.message : String(error); + console.error(`Environment extraction failed: ${results.error}`); + } + + return results; +} + /** * Builds the complete PR comment body */ function buildCommentBody( buildInfo: BuildInfo, testPlan: TestPlanResult | null, + envValidation: { + androidResult?: EnvValidationResult; + iosResult?: EnvValidationResult; + error?: string; + }, testPlanError?: string, ): string { let body = `${RC_BUILD_COMMENT_MARKER} @@ -87,6 +162,15 @@ ${buildMoreInfoSection(buildInfo)} `; + // Add environment section + if (envValidation.androidResult || envValidation.iosResult) { + body += `---\n\n`; + body += buildEnvValidationSection(envValidation.androidResult, envValidation.iosResult); + } else if (envValidation.error) { + body += `---\n\n`; + body += buildEnvValidationFailureSection(envValidation.error); + } + // Add test plan section if (testPlan) { body += `---\n\n`; @@ -163,8 +247,22 @@ async function main(): Promise { console.log('\nNo AI API keys found, skipping test plan generation'); } + // Extract environment values from build artifacts + console.log('\n=== Build Environment ===\n'); + const envValidation = performEnvValidation(); + + if (envValidation.androidResult || envValidation.iosResult) { + const result = envValidation.androidResult || envValidation.iosResult; + console.log(` - Build Name: ${result?.buildName}`); + console.log(` - Environment: ${result?.extractedValues.METAMASK_ENVIRONMENT}`); + } else if (envValidation.error) { + console.log(` - Error: ${envValidation.error}`); + } else { + console.log(' - No build-env artifacts found'); + } + // Build the comment body - const commentBody = buildCommentBody(buildInfo, testPlan, testPlanError); + const commentBody = buildCommentBody(buildInfo, testPlan, envValidation, testPlanError); // Post comment and minimize old ones console.log(`\n=== Posting Comment to PR #${prNumber} ===\n`); diff --git a/scripts/build-announce/types.ts b/scripts/build-announce/types.ts index 8fd1dc93012..3593075bba0 100644 --- a/scripts/build-announce/types.ts +++ b/scripts/build-announce/types.ts @@ -76,3 +76,11 @@ export interface TestPlanResult { excludedFeatures?: string[]; } +/** + * Environment values extracted from build-env.json + */ +export interface EnvValidationResult { + buildName: string; + extractedValues: Record; +} + diff --git a/scripts/build-announce/validate-env.ts b/scripts/build-announce/validate-env.ts new file mode 100644 index 00000000000..add3ef9f0ed --- /dev/null +++ b/scripts/build-announce/validate-env.ts @@ -0,0 +1,35 @@ +/** + * Environment Extraction Script + * + * Reads build environment values from build-env.json generated during the build. + * + * The build-env.json is generated by: node scripts/apply-build-config.js --write-build-env + */ + +import { existsSync, readFileSync } from 'fs'; +import type { EnvValidationResult } from './types'; + +// Build environment JSON structure (generated by apply-build-config.js) +interface BuildEnvJson { + buildName: string; + buildTime: string; + env: Record; + codeFencing?: string[]; +} + +/** + * Load build environment from JSON file and extract values + */ +export function validateEnv(buildEnvPath: string): EnvValidationResult { + if (!existsSync(buildEnvPath)) { + throw new Error(`Build environment file not found: ${buildEnvPath}`); + } + + const content = readFileSync(buildEnvPath, 'utf-8'); + const buildEnv = JSON.parse(content) as BuildEnvJson; + + return { + buildName: buildEnv.buildName, + extractedValues: buildEnv.env, + }; +} From 6e2fa570c76d6127cf3c273156643548637f6a7c Mon Sep 17 00:00:00 2001 From: Jorge Carrasco Date: Thu, 30 Apr 2026 23:00:17 +0200 Subject: [PATCH 4/6] ci: skip E2E for locale translation changes (#29565) ## **Description** This PR updates the post-#29305 CI requirement filters so locale translation JSON changes are treated as E2E-ignorable. Locale-only PRs under `locales/languages/**/*.json` should now skip Smart E2E selection plus Android/iOS E2E builds and smoke tests. The change also adds the same locale rule to the Android/iOS platform-or-ignorable filters, so mixed platform + locale changes keep platform-specific E2E behavior instead of expanding to both platforms. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: MCWP-440 ## **Manual testing steps** ```gherkin Feature: CI requirements for locale translation changes Scenario: locale translation PR skips E2E Given a pull request changes only files under locales/languages/**/*.json When the ci workflow runs get-requirements Then Smart E2E selection is skipped And Android E2E build and smoke test jobs are skipped And iOS E2E build and smoke test jobs are skipped And pr-not-ready-for-e2e does not block merge for the ignorable-only change ``` ## **Screenshots/Recordings** ### **Before** N/A ### **After** N/A ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .github/rules/filter-rules.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/rules/filter-rules.yml b/.github/rules/filter-rules.yml index d1928e2df4f..b3e9d129805 100644 --- a/.github/rules/filter-rules.yml +++ b/.github/rules/filter-rules.yml @@ -24,6 +24,10 @@ low_level_test_files: &low_level_test_files - '**/*.stories.*' - '**/*.snap' +# LOCALE TRANSLATION FILES +locale_translation_files: &locale_translation_files + - 'locales/languages/**/*.json' + # CONFIG FILES config_files: &config_files - '.eslint*' @@ -47,6 +51,7 @@ e2e_ignorable: - *documentation_files - *asset_files - *low_level_test_files + - *locale_translation_files - *config_files - *ci_files @@ -74,6 +79,7 @@ android_or_ignorable: - *documentation_files - *asset_files - *low_level_test_files + - *locale_translation_files - *config_files - *ci_files @@ -82,6 +88,7 @@ ios_or_ignorable: - *documentation_files - *asset_files - *low_level_test_files + - *locale_translation_files - *config_files - *ci_files From 14598698fcfb93335934900ec71d7393f6a0911c Mon Sep 17 00:00:00 2001 From: CW Date: Thu, 30 Apr 2026 15:39:49 -0700 Subject: [PATCH 5/6] test(e2e): mock static asset endpoints to remove from allowlist (MMQA-1778) (#29576) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Tier 2 of MMQA-1364 (allowlist reduction). Adds default mock matchers for three categories of static asset / health-check requests, then removes the corresponding entries from `mock-e2e-allowlist.ts`. | Matcher | Response | | --- | --- | | `^https://clients3\.google\.com/generate_204$` (HEAD) | `204 No Content`, empty body | | `^https://raw\.githubusercontent\.com/MetaMask/contract-metadata/[^/]+/images/.+\.svg$` (GET) | `200`, minimal `` | | `^https://token\.api\.cx\.metamask\.io/assets/nativeCurrencyLogos/.+\.svg$` (GET) | `200`, minimal SVG | **Why regex, not exact URLs.** The previous allowlist enumerated five specific token icons that today's specs happened to load. Any new spec, new default token, branch rename (`master` → `main`), or platform-subset divergence would reintroduce live requests. Regex matchers cover the entire category (any branch, any future token icon) with one entry — a one-time fix instead of a moving target. **`generate_204` origin.** Confirmed it is fired by `@react-native-community/netinfo`'s `InternetReachability._checkInternetReachability` via JS-layer `fetch`. That path is patched by `shim.js`, so the request reaches mockttp at `/proxy?url=…` and the matcher fires. (Method is `HEAD`, hence the new `HEAD` field on `DEFAULT_MOCKS`.) ### Files changed - `tests/api-mocking/mock-responses/defaults/static-assets.ts` — new default mock file with the three matchers - `tests/api-mocking/mock-responses/defaults/index.ts` — imported and spread into `DEFAULT_MOCKS.GET` + new `HEAD` field - `tests/api-mocking/mock-e2e-allowlist.ts` — removed 7 entries (1 generate_204, 5 GitHub raw token SVGs, 1 token.api nativeCurrencyLogos ethereum.svg) ### Out of scope - `https://api.avax.network/ext/bc/C/rpc` — Tier 4 investigation - `https://metamask.github.io/test-dapp/metamask-fox.svg` — handled by MMQA-1367 - Polymarket hosts — separate follow-up tracked in MMQA-1755 ## **Changelog** CHANGELOG entry: null ## **Related issues** [MMQA-1778](https://consensyssoftware.atlassian.net/browse/MMQA-1778) — parent epic [MMQA-1364](https://consensyssoftware.atlassian.net/browse/MMQA-1364) ## **Manual testing steps** ```gherkin Feature: Static asset mocks for E2E tests Scenario: NetInfo reachability probe is mocked Given the E2E mock server is running with default mocks loaded When the app fires the NetInfo reachability probe (HEAD https://clients3.google.com/generate_204) Then mockttp returns 204 with empty body And validateLiveRequests() does not record a live request Scenario: Token icon SVGs are mocked Given a spec loads a token list that includes contract-metadata icons When the app requests https://raw.githubusercontent.com/MetaMask/contract-metadata//images/.svg Then mockttp returns 200 with a placeholder SVG And the request never reaches GitHub live Scenario: Native currency logo SVGs are mocked Given a spec loads a chain whose native currency logo is fetched from token.api When the app requests https://token.api.cx.metamask.io/assets/nativeCurrencyLogos/.svg Then mockttp returns 200 with a placeholder SVG And the request never reaches the live token.api endpoint ``` ## **Screenshots/Recordings** ### **Before** `tests/api-mocking/mock-e2e-allowlist.ts` allowlisted 7 entries that bypassed `validateLiveRequests()`: What this meant on every E2E run: - NetInfo's reachability probe fired a live `HEAD https://clients3.google.com/generate_204` and Google answered. The allowlist silenced the warning, so `validateLiveRequests()` did not flag it. - 5 GitHub raw SVG fetches went live whenever notifications rendered token icons (USDC/SHIB/USDT/stETH/rETH from the mocked notification fixtures in `@metamask/notification-services-controller`). - 1 `token.api` SVG went live whenever the Ethereum native-currency logo was loaded (referenced in `app/constants/urls.ts:144` and the Ramp Quotes constants). ### **After** Allowlist with the 7 entries gone — `ALLOWLISTED_URLS` drops from 16 to 9: What happens on every E2E run now: - **NetInfo reachability probe** → mockttp returns `204` with empty body. NetInfo's `reachabilityTest` (`response.status === 204`) passes; the wallet sees the network as reachable. No live request to Google. - **Notification SVG fetches** → mockttp returns a placeholder SVG (``). Icons render (visual fidelity not asserted in E2E). No live request to `raw.githubusercontent.com` for any token icon — current or future. - **Native currency logo fetch** → same placeholder SVG. No live request to `token.api.cx.metamask.io` for any chain's logo. - `validateLiveRequests()` records **zero** leaks for these endpoints. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [MMQA-1778]: https://consensyssoftware.atlassian.net/browse/MMQA-1778?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --- > [!NOTE] > **Low Risk** > Low risk: changes are confined to the E2E mocking layer, replacing live allowlisted static/health-check requests with deterministic mocks (including adding default `HEAD` mocks). > > **Overview** > Reduces E2E live network allowlisting by removing specific static/health-check URLs (Google `generate_204` and token SVG icon URLs) from `mock-e2e-allowlist.ts` and handling them via default mocks instead. > > Adds `STATIC_ASSETS_MOCKS` with regex-based matchers that return a `204` for `HEAD https://clients3.google.com/generate_204` and a minimal SVG for GitHub contract-metadata and `token.api` native currency logo `.svg` requests, and wires these into `DEFAULT_MOCKS` (including a new `HEAD` entry). > > Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 7c90df25534ddadb858bd37f83b34c87b19f2789. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot). Co-authored-by: Claude Opus 4.7 (1M context) --- tests/api-mocking/mock-e2e-allowlist.ts | 8 ------ .../mock-responses/defaults/index.ts | 3 +++ .../mock-responses/defaults/static-assets.ts | 27 +++++++++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 tests/api-mocking/mock-responses/defaults/static-assets.ts diff --git a/tests/api-mocking/mock-e2e-allowlist.ts b/tests/api-mocking/mock-e2e-allowlist.ts index 46fd155a151..c963fcb71b0 100644 --- a/tests/api-mocking/mock-e2e-allowlist.ts +++ b/tests/api-mocking/mock-e2e-allowlist.ts @@ -22,15 +22,7 @@ export const ALLOWLISTED_HOSTS = [ export const ALLOWLISTED_URLS = [ // Temporarily allow existing live requests during migration - 'https://clients3.google.com/generate_204', 'https://api.avax.network/ext/bc/C/rpc', - // Token SVGs in notifications list - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/usdc.svg', - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/shib.svg', - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/usdt.svg', - 'https://token.api.cx.metamask.io/assets/nativeCurrencyLogos/ethereum.svg', - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/stETH.svg', - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/rETH.svg', 'https://signature-insights.api.cx.metamask.io/v1/signature?chainId=0x539', 'https://mainnet.era.zksync.io/', 'https://rpc.atlantischain.network/', diff --git a/tests/api-mocking/mock-responses/defaults/index.ts b/tests/api-mocking/mock-responses/defaults/index.ts index a660afe2cae..70c52657e53 100644 --- a/tests/api-mocking/mock-responses/defaults/index.ts +++ b/tests/api-mocking/mock-responses/defaults/index.ts @@ -31,6 +31,7 @@ import { TRENDING_API_MOCKS } from '../trending-api-mocks.ts'; import { TX_SENTINEL_NETWORKS_MAP } from '../tx-sentinel-networks-map.ts'; import { DIGEST_API_MOCKS } from './digest-api.ts'; import { MONEY_ACCOUNT_MOCKS } from './money-account.ts'; +import { STATIC_ASSETS_MOCKS } from './static-assets.ts'; // Get auth mocks const authMocks = getAuthMocks(); @@ -59,6 +60,7 @@ export const DEFAULT_MOCKS = { ...(TRENDING_API_MOCKS.GET || []), ...(DIGEST_API_MOCKS.GET || []), ...(MONEY_ACCOUNT_MOCKS.GET || []), + ...(STATIC_ASSETS_MOCKS.GET || []), // Chains Network Mock - Provides blockchain network data { urlEndpoint: 'https://chainid.network/chains.json', @@ -205,4 +207,5 @@ export const DEFAULT_MOCKS = { ], DELETE: [], PATCH: [], + HEAD: [...(STATIC_ASSETS_MOCKS.HEAD || [])], }; diff --git a/tests/api-mocking/mock-responses/defaults/static-assets.ts b/tests/api-mocking/mock-responses/defaults/static-assets.ts new file mode 100644 index 00000000000..939cc679b9a --- /dev/null +++ b/tests/api-mocking/mock-responses/defaults/static-assets.ts @@ -0,0 +1,27 @@ +import { MockEventsObject } from '../../../framework'; + +const MINIMAL_SVG = ''; + +export const STATIC_ASSETS_MOCKS: MockEventsObject = { + HEAD: [ + { + urlEndpoint: /^https:\/\/clients3\.google\.com\/generate_204$/, + responseCode: 204, + response: '', + }, + ], + GET: [ + { + urlEndpoint: + /^https:\/\/raw\.githubusercontent\.com\/MetaMask\/contract-metadata\/[^/]+\/images\/.+\.svg$/, + responseCode: 200, + response: MINIMAL_SVG, + }, + { + urlEndpoint: + /^https:\/\/token\.api\.cx\.metamask\.io\/assets\/nativeCurrencyLogos\/.+\.svg$/, + responseCode: 200, + response: MINIMAL_SVG, + }, + ], +}; From 8d4e444f188b1c10470c510b9afc128c63899fb9 Mon Sep 17 00:00:00 2001 From: Davide Brocchetto Date: Thu, 30 Apr 2026 15:41:38 -0700 Subject: [PATCH 6/6] test: stabilize swap deeplink smoke after sendToHome on iOS (#29523) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** On iOS<16, Detox’s device.sendToHome() brings the home screen to the front. On iOS 16 and later, that path is not used the same way from the simulator tooling, so Detox instead launches and immediately quits the Settings app to put the app under test in the background. That can briefly show Settings and race the next step. This PR tightens tests/smoke/swap/swap-deeplink-smoke.spec.ts: a 1 second wait after sendToHome() before launchApp({ url }), newInstance: false on those launchApp calls so the flow is clearly “same app instance, URL delivered after background,” and short comments above each wait explaining the pre‑16 vs 16+ behavior. ## **Changelog** CHANGELOG entry: ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > **Low Risk** > Test-only changes that adjust Detox app launching behavior and add additional HTTP mocks; low risk to production code, with minor risk of masking real integration issues if mock matching is too broad. > > **Overview** > Stabilizes swap deeplink smoke tests on iOS by adding a short post-`sendToHome()` delay and launching deeplinks with `newInstance: false` to ensure URLs are delivered to the existing app instance. > > Expands swap E2E mocking by introducing reusable `setupSwapSocialAndComplianceMocks()` to stub the Social leaderboard and compliance batch endpoints, wiring it into swap and trending-token smoke setups. Also tightens the swap proxy URL interception so the `insufficientBal` rewrite only targets JSON `getQuote` requests (not `getQuoteStream`). > > Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit edbfe2c5137f11d4968fe4e7a5088ccc3e55de20. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot). --- tests/helpers/swap/swap-mocks.ts | 63 ++++++++++++++++++- tests/helpers/swap/swap-unified-ui.ts | 5 ++ tests/smoke/swap/swap-deeplink-smoke.spec.ts | 10 +++ tests/smoke/swap/swap-trending-tokens.spec.ts | 3 + 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/tests/helpers/swap/swap-mocks.ts b/tests/helpers/swap/swap-mocks.ts index 30caa82c330..2cd14fa55ca 100644 --- a/tests/helpers/swap/swap-mocks.ts +++ b/tests/helpers/swap/swap-mocks.ts @@ -6,6 +6,7 @@ import { setupMockRequest, setupSSEMockRequest, } from '../../api-mocking/helpers/mockHelpers'; +import { getDecodedProxiedURL } from '../../smoke/notifications/utils/helpers'; import { GET_QUOTE_ETH_USDC_RESPONSE, GET_QUOTE_ETH_USDC_RESPONSE_CUSTOM_SLIPPAGE, @@ -30,6 +31,9 @@ const WETH_MAINNET = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; const GOOGLON_MAINNET = '0xba47214edd2bb43099611b208f75e4b42fdcfedc'; const MUSD_MAINNET = '0xaca92e438df0b2401ff60da7e4337b687a2435da'; +/** SocialService leaderboard response shape (empty list is valid for E2E). */ +const SOCIAL_LEADERBOARD_EMPTY_RESPONSE = { traders: [] }; + /** * Mock spot prices so balance display (balance * price) does not show NaN. * Shared by swap and bridge E2E tests. @@ -77,10 +81,64 @@ export async function setupSpotPricesMock(mockServer: Mockttp): Promise { }); } +/** + * Social leaderboard + compliance batch — used by swap `testSpecificMock` and + * bridge/trending specs that do not use swap-mocks’ full mock bundle. + */ +export async function setupSwapSocialAndComplianceMocks( + mockServer: Mockttp, +): Promise { + await setupMockRequest( + mockServer, + { + requestMethod: 'GET', + url: /social\.api\.cx\.metamask\.io\/api\/v1\/leaderboard/, + response: SOCIAL_LEADERBOARD_EMPTY_RESPONSE, + responseCode: 200, + }, + 1001, + ); + + await mockServer + .forPost('/proxy') + .matching((request) => { + try { + const decodedUrl = getDecodedProxiedURL(request.url); + return /compliance\.(dev-api|api|uat-api)\.cx\.metamask\.io\/v1\/wallet\/batch/.test( + decodedUrl, + ); + } catch { + return false; + } + }) + .asPriority(1001) + .thenCallback(async (request) => { + let addresses: string[] = []; + try { + const text = await request.body.getText(); + if (text) { + const parsed = JSON.parse(text) as unknown; + if (Array.isArray(parsed)) { + addresses = parsed.filter( + (a): a is string => typeof a === 'string', + ); + } + } + } catch { + /* ignore malformed body */ + } + return { + statusCode: 200, + json: addresses.map((address) => ({ address, blocked: false })), + }; + }); +} + export const testSpecificMock: TestSpecificMock = async ( mockServer: Mockttp, ) => { await setupSpotPricesMock(mockServer); + await setupSwapSocialAndComplianceMocks(mockServer); // Catch-all for getQuoteStream with no slippage param (initial render before // useInitialSlippage fires). Registered first so specific mocks below at @@ -206,7 +264,10 @@ export const testSpecificMock: TestSpecificMock = async ( await interceptProxyUrl( mockServer, - (url) => url.includes('getQuote') && url.includes('insufficientBal=false'), + (url) => + url.includes('getQuote') && + !url.includes('getQuoteStream') && + url.includes('insufficientBal=false'), (url) => url.replace('insufficientBal=false', 'insufficientBal=true'), ); }; diff --git a/tests/helpers/swap/swap-unified-ui.ts b/tests/helpers/swap/swap-unified-ui.ts index 5cd6c4cbfcd..f78e5f31d52 100644 --- a/tests/helpers/swap/swap-unified-ui.ts +++ b/tests/helpers/swap/swap-unified-ui.ts @@ -1,9 +1,12 @@ import QuoteView from '../../page-objects/swaps/QuoteView'; import SlippageModal from '../../page-objects/swaps/SlippageModal'; import { Assertions } from '../../framework'; +import { createLogger } from '../../framework/logger'; import ActivitiesView from '../../page-objects/Transactions/ActivitiesView'; import { ActivitiesViewSelectorsText } from '../../../app/components/Views/ActivityView/ActivitiesView.testIds'; +const logger = createLogger({ name: 'SwapUnifiedUI' }); + interface SwapOptions { /** Custom slippage percentage (e.g., "2.5" for 2.5%) */ slippage?: string; @@ -30,9 +33,11 @@ export async function submitSwapUnifiedUI( await QuoteView.tapDestinationToken(); await QuoteView.tapToken(chainId, destTokenSymbol); + const getQuoteStarted = Date.now(); await Assertions.expectElementToBeVisible(QuoteView.networkFeeLabel, { timeout: 60000, }); + logger.debug(`⏳ Quote visible after ${Date.now() - getQuoteStarted}ms`); // Dismiss the keypad so quote details (slippage, confirm) are not obscured await QuoteView.dismissKeypad(); diff --git a/tests/smoke/swap/swap-deeplink-smoke.spec.ts b/tests/smoke/swap/swap-deeplink-smoke.spec.ts index 763e422a696..c9ebf45c86a 100644 --- a/tests/smoke/swap/swap-deeplink-smoke.spec.ts +++ b/tests/smoke/swap/swap-deeplink-smoke.spec.ts @@ -11,6 +11,7 @@ import Assertions from '../../framework/Assertions'; import { asDetoxElement } from '../../framework'; import QuoteView from '../../page-objects/swaps/QuoteView'; import { testSpecificMock } from '../../helpers/swap/swap-mocks'; +import TestHelpers from '../../helpers'; import WalletView from '../../page-objects/wallet/WalletView'; // Deep link URLs for testing unified swap/bridge experience @@ -62,7 +63,10 @@ describe( async () => { await loginToApp(); await device.sendToHome(); + // intentional: Detox iOS 16+ sendToHome briefly opens Settings; wait before launchApp({ url }). + if (device.getPlatform() === 'ios') await TestHelpers.delay(1000); await device.launchApp({ + newInstance: false, url: SWAP_DEEPLINK_FULL, }); @@ -121,7 +125,10 @@ describe( async () => { await loginToApp(); await device.sendToHome(); + // intentional: Detox iOS 16+ sendToHome briefly opens Settings; wait before launchApp({ url }). + if (device.getPlatform() === 'ios') await TestHelpers.delay(1000); await device.launchApp({ + newInstance: false, url: SWAP_DEEPLINK_BASE, }); @@ -175,7 +182,10 @@ describe( async () => { await loginToApp(); await device.sendToHome(); + // intentional: Detox iOS 16+ sendToHome briefly opens Settings; wait before launchApp({ url }). + if (device.getPlatform() === 'ios') await TestHelpers.delay(1000); await device.launchApp({ + newInstance: false, url: invalidDeeplink, }); diff --git a/tests/smoke/swap/swap-trending-tokens.spec.ts b/tests/smoke/swap/swap-trending-tokens.spec.ts index 2908f2a8871..b7b625287bd 100644 --- a/tests/smoke/swap/swap-trending-tokens.spec.ts +++ b/tests/smoke/swap/swap-trending-tokens.spec.ts @@ -11,6 +11,7 @@ import TokenOverview from '../../page-objects/wallet/TokenOverview'; import FixtureBuilder from '../../framework/fixtures/FixtureBuilder'; import { prepareSwapsTestEnvironment } from '../../helpers/swap/prepareSwapsTestEnvironment'; import { testSpecificMock } from '../../helpers/swap/bridge-mocks'; +import { setupSwapSocialAndComplianceMocks } from '../../helpers/swap/swap-mocks'; import { GET_QUOTE_ETH_USDC_RESPONSE } from '../../helpers/swap/constants'; import { getDecodedProxiedURL } from '../notifications/utils/helpers'; import { SmokeSwap } from '../../tags'; @@ -77,6 +78,8 @@ const setupSwapsTrendingTokensMock = async (mockServer: Mockttp) => { }, 1001, ); + + await setupSwapSocialAndComplianceMocks(mockServer); }; const setupTrendingTokensMock = async (mockServer: Mockttp) => {