Skip to content

test(e2e): mock NFT API to remove from allowlist (MMQA-1781)#29620

Open
chrisleewilcox wants to merge 2 commits intomainfrom
MMQA-1781-tier-3-nft-api-mocks
Open

test(e2e): mock NFT API to remove from allowlist (MMQA-1781)#29620
chrisleewilcox wants to merge 2 commits intomainfrom
MMQA-1781-tier-3-nft-api-mocks

Conversation

@chrisleewilcox
Copy link
Copy Markdown
Contributor

@chrisleewilcox chrisleewilcox commented May 1, 2026

Description

Tier 3 of MMQA-1364 (allowlist reduction), scoped to the nft.api.cx.metamask.io endpoints. Closes out Tier 3 (signature-insights done in MMQA-1779).

Matcher Method Response
^https://nft\.api\.cx\.metamask\.io/users/0x[0-9a-fA-F]+/tokens\?.*$ GET { tokens: [], continuation: null }
^https://nft\.api\.cx\.metamask\.io/collections\?.*$ GET { collections: [] }
^https://nft\.api\.cx\.metamask\.io/explore/sites\?.*$ GET { dapps: [] }

Why these response shapes.

  • /users/.../tokens shape verified from @metamask/assets-controllers NftDetectionController._getOwnerNfts (consumer calls .tokens.filter(...)).
  • /explore/sites shape verified from app/components/UI/Sites/hooks/useSiteData/useSitesData.ts:146 (data.dapps.map(...) then mergePortfolioSite() falls back to a hard-coded Portfolio entry on empty input).
  • /collections shape is a guess — the caller can't be located in the current SDK (path was likely refactored away from the singular /collections?contract= form to the /tokens POST batch endpoint in NftController._getNftInformationFromApi). The matcher is harmless insurance: either it never fires, or it fires and returns empty. No active spec asserts on collection content.

Why regex, not exact URLs. Same rationale as Tier 2 / Tier 3 Part 1: the previous allowlist enumerated specific contract addresses and a specific user account. Any new NFT in fixtures, any new chainId, or any new test account would re-introduce live requests. Regex covers the entire category.

Why dev-api / uat-api hosts can drop without mocking. Neither is referenced anywhere in app/ or node_modules/@metamask/*. The SDK hardcodes nft.api.cx.metamask.io (prod) only via controller-utils/constants.cjs:193. The dev/uat entries were added defensively in PR #26141 alongside portfolio.dev-api/portfolio.uat-api patterns and are dead.

Spec audit.

  • tests/regression/assets/nft-detection-modal.spec.tsdescribe.skip (not active).
  • tests/smoke/snaps/test-snap-get-preferences.spec.ts — only sets useNftDetection: true as a flag; doesn't assert on detected NFT content.
  • No active spec positively asserts on NFTs being present, collection content, or /explore/sites content. Empty defaults are safe.

Files changed

  • tests/api-mocking/mock-responses/defaults/nft-api.ts — new
  • tests/api-mocking/mock-responses/defaults/index.ts — import + spread into DEFAULT_MOCKS.GET
  • tests/api-mocking/mock-e2e-allowlist.ts — removed 3 hosts + 3 URLs

Out of scope

  • Polymarket hosts (3) — separate Tier 1 follow-up
  • Tier 4 entries (api.avax.network, mainnet.era.zksync.io, rpc.atlantischain.network, testnet-rpc.monad.xyz)
  • metamask.github.io — handled by MMQA-1367

Changelog

CHANGELOG entry: null

Related issues

MMQA-1781 — parent epic MMQA-1364

Manual testing steps

Feature: NFT API default mocks for E2E tests

  Scenario: NFT detection sweep is mocked
    Given the E2E mock server is running with default mocks loaded
    When NftDetectionController fires GET /users/<address>/tokens for the connected account
    Then mockttp returns { tokens: [], continuation: null }
    And no NFTs are added to wallet state
    And validateLiveRequests() does not record a live request to nft.api

  Scenario: NFT collection lookup is mocked
    Given a fixture transaction history references an unknown NFT contract
    When the wallet looks up collection metadata via GET /collections?…
    Then mockttp returns { collections: [] }
    And the wallet falls back to its default rendering

  Scenario: Sites tab is mocked
    Given the user opens the Sites tab
    When useSitesData fetches GET /explore/sites?…
    Then mockttp returns { dapps: [] }
    And the Portfolio entry is shown via the hard-coded fallback

Screenshots/Recordings

Before

mock-e2e-allowlist.ts allowlisted the entire nft.api.cx.metamask.io host plus 3 specific URLs. Every E2E run that exercised the connected account, NFT-bearing tx history, or the Sites tab fired live requests to the production NFT API. The host wildcard silenced validateLiveRequests(), so the leaks were invisible to the test runner. The dev/uat host entries were there defensively but never hit.

After

mockttp intercepts the three known endpoints with empty default responses that match each consumer's shape expectations. The 3 hosts and 3 URLs are gone from the allowlist; validateLiveRequests() records zero leaks for nft.api.cx.metamask.io. New chainIds, new fixture NFT contracts, and new test accounts all stay covered automatically because the matchers are regex-based.

Pre-merge author checklist

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 to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production performance metrics

For performance guidelines and tooling, see the Performance Guide.

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
Low risk: changes are confined to E2E mock infrastructure, replacing previously allowlisted live nft.api calls with deterministic empty mock responses.

Overview
Stops E2E tests from making live requests to nft.api.cx.metamask.io by adding default mockttp GET handlers for /users/.../tokens, /collections, and /explore/sites that return empty-but-shape-correct payloads.

Removes the nft.*.cx.metamask.io hosts and several hardcoded NFT API URLs from mock-e2e-allowlist.ts, and wires the new NFT_API_MOCKS into DEFAULT_MOCKS.GET so these requests are intercepted by default.

Reviewed by Cursor Bugbot for commit cf2524b. Bugbot is set up for automated code reviews on this repo. Configure here.

Adds default GET mocks for the three nft.api.cx.metamask.io endpoints
the wallet hits during E2E runs, then removes the corresponding host
and URL allowlist entries.

- /users/0x*/tokens → empty tokens array (NftDetectionController scan)
- /collections → empty collections array (legacy NFT contract lookup)
- /explore/sites → empty dapps array (Sites tab in useSitesData)

Drops 3 hosts (prod/dev/uat) and 3 URL entries. dev-api/uat-api hosts
are not referenced anywhere in app or SDK code — added defensively in
PR #26141; safe to remove without mocking.

Regex matchers cover the entire category, so new fixture contracts /
new chainIds / new tx-history NFTs won't reintroduce live requests.

Tier 3 of MMQA-1364. Closes out the NFT API portion (signature-insights
done in MMQA-1779).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbotv2 metamaskbotv2 Bot added the team-qa QA team label May 1, 2026
@github-actions github-actions Bot added the size-S label May 1, 2026
@chrisleewilcox chrisleewilcox marked this pull request as ready for review May 1, 2026 18:38
@chrisleewilcox chrisleewilcox requested a review from a team as a code owner May 1, 2026 18:38
@chrisleewilcox chrisleewilcox added no-changelog no-changelog Indicates no external facing user changes, therefore no changelog documentation needed no changelog required No changelog entry is required for this change labels May 1, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokeWalletPlatform, SmokeConfirmations, SmokeAccounts
  • Selected Performance tags: None (no tests recommended)
  • Risk Level: medium
  • AI Confidence: 78%
click to see 🤖 AI reasoning details

E2E Test Selection:
The changes are purely in E2E test infrastructure (api-mocking layer):

  1. mock-e2e-allowlist.ts: Removes NFT API hosts/URLs from the allowlist. Previously, NFT API calls were allowed to pass through to real servers; now they will be intercepted and mocked.

  2. nft-api.ts (new): Adds default mock responses for NFT API endpoints using regex patterns - returning empty tokens, collections, and dapps lists.

  3. defaults/index.ts: Includes the new NFT_API_MOCKS in DEFAULT_MOCKS, making these mocks active globally for all E2E tests.

Impact Assessment:

  • The explore/sites endpoint is used by useSitesData hook in the Sites/Trending component → affects SmokeWalletPlatform (Trending tab with Sites section)
  • NFT collections/tokens endpoints affect NFT display in wallet and confirmations → affects SmokeConfirmations (NFT approvals like ERC-721/ERC-1155 setApprovalForAll)
  • Account-related NFT display could affect SmokeAccounts tests

The change moves from live API calls to mocked empty responses. This is a stabilizing change for tests, but could break tests that previously relied on real NFT data being returned. Running the most directly affected test suites validates the mocking works correctly without breaking existing test flows.

No performance tests are needed as this is purely a test infrastructure change with no impact on app code performance.

Performance Test Selection:
These changes are purely in the E2E test infrastructure (api-mocking layer) and do not touch any app source code. There is no impact on app performance, rendering, or data loading that would warrant performance tests.

View GitHub Actions results

@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.81%. Comparing base (51b6bbd) to head (cf2524b).
⚠️ Report is 33 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #29620      +/-   ##
==========================================
- Coverage   82.15%   81.81%   -0.34%     
==========================================
  Files        5178     5226      +48     
  Lines      137450   138584    +1134     
  Branches    31079    31456     +377     
==========================================
+ Hits       112924   113388     +464     
- Misses      16875    17466     +591     
- Partials     7651     7730      +79     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 1, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no changelog required No changelog entry is required for this change no-changelog no-changelog Indicates no external facing user changes, therefore no changelog documentation needed size-S team-qa QA team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants