Skip to content

Commit f20d286

Browse files
authored
Merge branch 'main' into fix-tmcu-311-manual-dapp-network-selection-switch-non-permitted-network
2 parents a58841f + c702a36 commit f20d286

File tree

2,463 files changed

+145672
-88340
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

2,463 files changed

+145672
-88340
lines changed

.agents/skills/component-view-test/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ tests/component-view/
5656
├── mocks.ts ← Engine + native mocks (import this first, always)
5757
├── render.tsx ← renderComponentViewScreen, renderScreenWithRoutes
5858
├── stateFixture.ts ← StateFixtureBuilder (createStateFixture)
59+
├── platform.ts ← describeForPlatforms, itForPlatforms (run per iOS/Android)
60+
├── api-mocking/ ← HTTP API mocks (nock) — extensible, one file per feature
5961
├── presets/ ← initialState<Feature>() builders — one file per feature area
6062
└── renderers/ ← render<Feature>View() functions — one file per feature area
6163
```

.agents/skills/component-view-test/references/navigation-mocking.md

Lines changed: 42 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -134,102 +134,62 @@ Route names live in `app/constants/navigation/Routes.ts`.
134134

135135
## External Service / API Mocking
136136

137-
Some views call external services **directly** (not through Engine controllers) — e.g. a `getTrendingTokens()` function imported from a package, or a `fetch()` call to an external API. These cannot be driven through Redux state overrides.
137+
Some views call **external HTTP APIs** (e.g. `fetch()` to a REST endpoint). Those requests cannot be driven through Redux state. The framework provides an **api-mocking** layer using [nock](https://github.com/nock/nock) so tests intercept HTTP at the network level **without** using `jest.mock` on service modules (which would violate the “only Engine and allowed native mocks” rule).
138138

139-
### Current pattern — jest.mock on the service module
139+
### Preferred pattern — nock (api-mocking folder)
140140

141-
When a view calls an external service function directly, mock the module in a dedicated file under `tests/component-view/mocks/` and expose setup/clear helpers:
141+
All HTTP API mocks for component view tests live under `tests/component-view/api-mocking/`. Each feature has one file (e.g. `trending.ts`) that exports:
142142

143-
```typescript
144-
// tests/component-view/mocks/myFeatureApiMocks.ts
145-
import { getMyFeatureData } from '@metamask/some-package';
146-
147-
export const getMyFeatureDataMock = getMyFeatureData as jest.Mock;
143+
- Mock response data (e.g. `mockTrendingTokensData`)
144+
- A **setup** function (e.g. `setupTrendingApiFetchMock(responseData?, customReply?)`) that uses nock to intercept the endpoint
145+
- A **clear** function (e.g. `clearTrendingApiMocks()`) to call in `afterEach`
148146

149-
export const mockFeatureData = [
150-
{ id: 'item-1', name: 'Token A', price: '100.00', change24h: 5.2 },
151-
{ id: 'item-2', name: 'Token B', price: '200.00', change24h: -1.8 },
152-
];
147+
Shared nock lifecycle helpers (`clearAllNockMocks`, `disableNetConnect`, `teardownNock`) are in `api-mocking/nockHelpers.ts`. To **add a new API mock** for another view, add a file `api-mocking/<feature>.ts` following the pattern in `api-mocking/trending.ts` (mock data, `setupXxxApiMock`, `clearXxxApiMocks` using `nockHelpers`), and call setup/clear in the view test’s `beforeEach`/`afterEach`.
153148

154-
export const setupMyFeatureApiMock = (data = mockFeatureData) => {
155-
getMyFeatureDataMock.mockImplementation(async () => data);
156-
};
157-
158-
export const clearMyFeatureApiMocks = () => {
159-
jest.clearAllMocks();
160-
};
161-
```
162-
163-
In the test file, declare the `jest.mock` at module scope and use `beforeEach`/`afterEach` for lifecycle:
149+
**Example (trending):**
164150

165151
```typescript
166-
// NOTE: antipattern — only Engine and native modules should be mocked in view tests.
167-
// This is a temporary workaround for service functions called directly from components,
168-
// not through Engine. Track removal in the linked issue.
169-
// eslint-disable-next-line no-restricted-syntax
170-
jest.mock('@metamask/some-package', () => {
171-
const actual = jest.requireActual('@metamask/some-package');
172-
return { ...actual, getMyFeatureData: jest.fn().mockResolvedValue([]) };
173-
});
174-
175152
import {
176-
setupMyFeatureApiMock,
177-
clearMyFeatureApiMocks,
178-
mockFeatureData,
179-
getMyFeatureDataMock,
180-
} from '../../../../tests/component-view/mocks/myFeatureApiMocks';
181-
182-
describe('MyFeatureView', () => {
183-
beforeEach(() => {
184-
setupMyFeatureApiMock(mockFeatureData);
185-
});
186-
187-
afterEach(() => {
188-
clearMyFeatureApiMocks();
189-
});
190-
191-
it('shows token list after data loads from the external service', async () => {
192-
const { findByText } = renderMyFeatureWithRoutes();
153+
setupTrendingApiFetchMock,
154+
clearTrendingApiMocks,
155+
mockTrendingTokensData,
156+
mockBnbChainToken,
157+
} from '../../../../tests/component-view/api-mocking/trending';
158+
159+
beforeEach(() => {
160+
setupTrendingApiFetchMock(mockTrendingTokensData);
161+
});
162+
afterEach(() => {
163+
clearTrendingApiMocks();
164+
});
193165

194-
expect(await findByText('Token A')).toBeOnTheScreen();
166+
it('user sees trending tokens section with mocked data', async () => {
167+
const { findByText, queryByTestId } = renderTrendingViewWithRoutes();
168+
await waitFor(async () => {
169+
expect(await findByText('Ethereum')).toBeOnTheScreen();
195170
});
171+
// assert rows with assertTrendingTokenRowsVisibility(...)
172+
});
196173

197-
it('shows only filtered results when a specific param is passed', async () => {
198-
getMyFeatureDataMock.mockImplementation(async (params) => {
199-
if (params?.chainId === 'eip155:56') return [mockBnbData];
200-
return mockFeatureData;
201-
});
202-
203-
const { findByText } = renderMyFeatureWithRoutes();
204-
// ... interact to trigger the filter, then assert
174+
it('displays only BNB tokens when BNB Chain network filter is selected', async () => {
175+
setupTrendingApiFetchMock(mockTrendingTokensData, (uri) => {
176+
const url = new URL(uri, 'https://token.api.cx.metamask.io');
177+
const chainIdsParam = url.searchParams.get('chainIds') ?? '';
178+
const chainIds = chainIdsParam.split(',').map((s) => s.trim());
179+
if (chainIds.length === 1 && chainIds[0] === 'eip155:56') {
180+
return mockBnbChainToken;
181+
}
182+
return mockTrendingTokensData;
205183
});
184+
const { getByTestId, findByText, queryByTestId } =
185+
renderTrendingViewWithRoutes();
186+
// ... navigate to full view, open network filter, select BNB Chain
187+
// assert visible: [BNB], missing: [ETH, BTC, UNI]
206188
});
207189
```
208190

209-
> ⚠️ **This is a known antipattern.** The golden rule is that only Engine and allowed native modules should be mocked in `*.view.test.*` files. Mocking a service module directly bypasses the ESLint guard (note the `eslint-disable` comment). Always link to a tracking issue and plan to migrate to a proper solution.
210-
211-
### Future pattern — Mock Service Worker (MSW)
212-
213-
> 📌 **Placeholder — no example exists yet in this codebase.**
191+
### Fallback — jest.mock on the service module (antipattern)
214192

215-
For views that call HTTP endpoints directly (via `fetch`), the intended approach is [Mock Service Worker (msw)](https://mswjs.io/), which intercepts requests at the network level without needing `jest.mock`. This keeps tests closer to real behavior and avoids the module-mock antipattern.
193+
When a view calls an external **function** (not `fetch`) from a package and that function cannot be replaced by nock (e.g. no HTTP), you may mock the module in a file under `tests/component-view/mocks/` and use setup/clear helpers. This requires an `eslint-disable` and is a **known antipattern**; prefer moving the integration to an HTTP API and using api-mocking, or drive data through Engine/Redux when possible.
216194

217-
When the first MSW-based view test is written, document the setup here:
218-
219-
```typescript
220-
// TODO: Add MSW setup example once the first test using it is merged.
221-
// Expected shape:
222-
//
223-
// import { setupServer } from 'msw/node';
224-
// import { http, HttpResponse } from 'msw';
225-
//
226-
// const server = setupServer(
227-
// http.get('https://api.example.com/tokens', () =>
228-
// HttpResponse.json(mockTokensData),
229-
// ),
230-
// );
231-
//
232-
// beforeAll(() => server.listen());
233-
// afterEach(() => server.resetHandlers());
234-
// afterAll(() => server.close());
235-
```
195+
> ⚠️ Only Engine and allowed native modules should be mocked in `*.view.test.*` files. Mocking a service module directly bypasses the ESLint guard. Always link to a tracking issue and plan to migrate to nock (api-mocking) or Engine/Redux.

.agents/skills/component-view-test/references/reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ yarn eslint <path/to/test.tsx>
203203
| Engine + native mocks | `tests/component-view/mocks.ts` |
204204
| render, renderScreenWithRoutes | `tests/component-view/render.tsx` |
205205
| StateFixtureBuilder | `tests/component-view/stateFixture.ts` |
206+
| HTTP API mocks (nock) | `tests/component-view/api-mocking/` (per-feature) |
206207
| Feature renderers (per view) | `tests/component-view/renderers/` (e.g. bridge, wallet) |
207208
| Feature presets (per view) | `tests/component-view/presets/` (e.g. bridge, wallet) |
208209
| DeepPartial type | `app/util/test/renderWithProvider` |

.agents/skills/component-view-test/references/writing-tests.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Before writing any test, read:
1212
- Any existing `*.view.test.tsx` for the same component
1313
- The relevant preset(s) in `tests/component-view/presets/`
1414
- The relevant renderer(s) in `tests/component-view/renderers/`
15+
- If the view calls an external HTTP API: `tests/component-view/api-mocking/` and any existing `api-mocking/<feature>.ts` for that API (see navigation-mocking.md, External Service / API Mocking)
1516

1617
---
1718

@@ -194,6 +195,38 @@ const defaultBridgeWithTokens = (overrides?: Record<string, unknown>) => {
194195

195196
Then each test only specifies its delta from this baseline.
196197

198+
### describe / it and platform (iOS + Android)
199+
200+
Import from `tests/component-view/platform`. All helpers accept an optional **filter** (3rd arg): `'ios'` | `'android'` | `['ios','android']` | `{ only: 'ios' }` | `{ skip: ['android'] }`. Env: `TEST_OS=ios` or `TEST_OS=android` to run only one OS.
201+
202+
| Helper | Use |
203+
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
204+
| `describeForPlatforms(name, define, filter?)` | One describe per OS. Inside, `define({ os })`; use `it()` or `itForPlatforms()` — each runs once per that OS. |
205+
| `itForPlatforms(name, (ctx) => {}, filter?)` | One `it` per OS. Callback receives `{ os }`. |
206+
| `itOnlyForPlatforms(name, fn, filter?)` | Same as `itForPlatforms` but registers `it.only`. |
207+
| `itEach(table)(name, (row) => {}, filter?)` | One `it` per table row × per OS. Use `$key` in name to interpolate row fields. |
208+
| `describeEach(table)(name, (row) => { it('...', () => {}); }, filter?)` | One describe per row × per OS. Use `$key` in name. |
209+
| `getTargetPlatforms(filter?)` | Returns `['ios','android']` (or filtered list) for custom loops. |
210+
211+
Example — `itEach` (each case runs on iOS and Android):
212+
213+
```typescript
214+
import { itEach } from '../../../../../../tests/component-view/platform';
215+
216+
const cases = [
217+
{ name: 'renders empty', amount: '0' },
218+
{ name: 'displays fiat', amount: '1' },
219+
];
220+
itEach(cases)('$name', ({ amount }) => {
221+
const { findByDisplayValue } = renderDefault({
222+
bridge: { sourceAmount: amount },
223+
});
224+
expect(findByDisplayValue(amount)).toBeOnTheScreen();
225+
});
226+
```
227+
228+
Jest modifiers (`it.only`, `it.skip`, `describe.only`, `describe.skip`) work as usual inside these blocks.
229+
197230
### Minimal template
198231

199232
```typescript

.agents/skills/e2e-test/SKILL.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ name: e2e-test
33
description:
44
Add and fix Detox E2E tests (smoke and regression) for MetaMask Mobile using
55
withFixtures, Page Objects, and tests/framework. Use when creating a new spec,
6-
fixing a failing E2E test, or adding page objects and selectors.
6+
fixing a failing E2E test, adding page objects and selectors, or adding
7+
MetaMetrics analytics expectations (analyticsExpectations).
78
---
89

910
# E2E Test Builder — Skill
@@ -44,6 +45,10 @@ Task → What do you need?
4445
│ → Open references/mocking.md (testSpecificMock, setupRemoteFeatureFlagsMock, setupMockRequest)
4546
│ → When writing the spec: open references/writing-tests.md
4647
48+
├─ MetaMetrics / Segment analytics assertions (`analyticsExpectations` on `withFixtures`)
49+
│ → Open [tests/docs/analytics-e2e.md](../../../tests/docs/analytics-e2e.md) (config shape, teardown order, presets under `tests/helpers/analytics/expectations/`, `runAnalyticsExpectations`)
50+
│ → When wiring a spec: still follow references/writing-tests.md for `withFixtures` usage
51+
4752
└─ Run tests, debug failures, or self-review
4853
→ Open references/running-tests.md (build check, detox commands, common failures, retry patterns)
4954
```
@@ -84,9 +89,10 @@ Step 5 → Iterate (fix → lint → run) until green
8489

8590
Documentation is split by **action**. Open only the reference that matches what you are doing.
8691

87-
| Action | File | When to open it |
88-
| --------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
89-
| **Writing or updating a spec** | [references/writing-tests.md](references/writing-tests.md) | New spec file, spec structure, FixtureBuilder patterns, smoke/regression templates. |
90-
| **Page Objects and selectors** | [references/page-objects.md](references/page-objects.md) | Create or update POM classes, selector/testId conventions, Matchers/Gestures/Assertions API. |
91-
| **API and feature flag mocking** | [references/mocking.md](references/mocking.md) | testSpecificMock, setupRemoteFeatureFlagsMock, setupMockRequest, shared mock files. |
92-
| **Running tests, debugging, fixing failures** | [references/running-tests.md](references/running-tests.md) | Build check, detox run commands, lint/tsc, common failures table, retry patterns, iteration loop. |
92+
| Action | File | When to open it |
93+
| --------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
94+
| **Writing or updating a spec** | [references/writing-tests.md](references/writing-tests.md) | New spec file, spec structure, FixtureBuilder patterns, smoke/regression templates. |
95+
| **Page Objects and selectors** | [references/page-objects.md](references/page-objects.md) | Create or update POM classes, selector/testId conventions, Matchers/Gestures/Assertions API. |
96+
| **API and feature flag mocking** | [references/mocking.md](references/mocking.md) | testSpecificMock, setupRemoteFeatureFlagsMock, setupMockRequest, shared mock files. |
97+
| **MetaMetrics / analytics expectations** | [tests/docs/analytics-e2e.md](../../../tests/docs/analytics-e2e.md) | `analyticsExpectations` on `withFixtures`, declarative checks, presets in `tests/helpers/analytics/expectations/`. |
98+
| **Running tests, debugging, fixing failures** | [references/running-tests.md](references/running-tests.md) | Build check, detox run commands, lint/tsc, common failures table, retry patterns, iteration loop. |

0 commit comments

Comments
 (0)