Skip to content

Commit 7281395

Browse files
test: add component view tests and skip duplicated smoke E2E (#28911)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** This PR adds **component view tests (CVT)** for flows that were previously covered by **smoke E2E** only, and **skips** those E2E cases (eventually delete them). Smoke specs keep the original test bodies and point to the CV file with `// Moved to cv tests (...)`. Example: In network abstraction shard 1 we see a 4m reduction time (android). ### E2E → component view test mapping | File | Test Name | QA Comment | CV test file | | --- | --- | --- | --- | | view-defi-details.spec.ts | view DeFi position details | just checking some data in the screen | `app/components/UI/DeFiPositions/DeFiProtocolPositionDetails.view.test.tsx` | | view-market-insights.spec.ts | displays market insights content and navigates to swap | | `app/components/UI/MarketInsights/Views/MarketInsightsView/MarketInsightsView.view.test.tsx` | | view-market-insights.spec.ts | does not display entry card when API returns no data | | `app/components/UI/TokenDetails/components/AssetOverviewContent.view.test.tsx` | | view-market-insights.spec.ts | does not display entry card when feature flag is disabled | | `app/components/UI/TokenDetails/components/AssetOverviewContent.view.test.tsx` | | view-market-insights.spec.ts | navigates to buy screen when tapping Buy button | | `app/components/UI/MarketInsights/Views/MarketInsightsView/MarketInsightsView.view.test.tsx` | | view-market-insights.spec.ts | can tap thumbs up feedback button | | `app/components/UI/MarketInsights/Views/MarketInsightsView/MarketInsightsView.view.test.tsx` | | send-btc-token.spec.ts | shows insufficient funds | This only does validation on the input | `app/components/Views/confirmations/components/send/send.non-evm.view.test.tsx` | | send-tron-token.spec.ts | shows insufficient funds | This only does validation on the input | `app/components/Views/confirmations/components/send/send.non-evm.view.test.tsx` | | send-erc20-token.spec.ts | should send USDC amount 50% to an address | CV tests will handle these combinations | `app/components/Views/confirmations/components/send/send.view.test.tsx` | | send-erc20-token.spec.ts | should send USDC send max to an address | CV tests will handle these combinations | `app/components/Views/confirmations/components/send/send.view.test.tsx` | | send-native-token.spec.ts | should send ETH to an address | PARTIALLY: We should only cover ETH send, no need to cover 50% and Max | `app/components/Views/confirmations/components/send/send.view.test.tsx` | | send-solana-token.spec.ts | should send solana to an address | This is not actually sending anything, just checking that the text matches | `app/components/Views/confirmations/components/send/send.non-evm.view.test.tsx` | | alert-system.spec.ts | should sign typed message | Moved to yes as per team review | `app/components/Views/confirmations/components/alert-banner/alert-system-typed-sign-blockaid.view.test.tsx` | | alert-system.spec.ts | should show security alert for malicious request, acknowledge and confirm the signature | Moved to yes as per team review | `app/components/Views/confirmations/components/alert-banner/alert-system-typed-sign-blockaid.view.test.tsx` | | alert-system.spec.ts | should show security alert for error when validating request fails | | `app/components/Views/confirmations/components/alert-banner/alert-system-security-failed.view.test.tsx` | | alert-system.spec.ts | should show mismatch field alert, click the alert, acknowledge and confirm the signature | As long as the component is the same we can do this via CV test | `app/components/Views/confirmations/components/alert-banner/alert-system-siwe-inline-mismatch.view.test.tsx` | | gas-fee-tokens-eip-7702-sponsored.spec.ts | fails transaction if error occurs on API | | `app/components/Views/confirmations/components/activity/eip-7702-sponsored-relay-api-failure.view.test.tsx` | | enable-notifications-after-onboarding.spec.ts | should enable notifications and view feature announcements and wallet notifications | Test is not doing what its title implies; skipped pending owner discussion | `app/components/Views/Notifications/NotificationsView.view.test.tsx` | | notification-settings-flow.spec.ts | should enable notifications and toggle feature announcements and account notifications | UI-only validation, suitable for CV | `app/components/Views/Settings/NotificationsSettings/NotificationsSettings.view.test.tsx` | | add-popular-networks.spec.ts | adds a popular network directly without confirmation modal | This is not in prod anymore | *No matching `*.view.test.tsx` on this branch* | ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: N/A Scenario: Automated tests only Given developer checks out this branch When they run yarn test:view for the touched view test files Then tests pass ``` ## **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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low product risk since changes are test-only, but moderate test-suite risk due to new integration-style view tests, new engine/nock mocks, and `jest.config.view.js` forcing `IS_TEST=true` for feature-gated code paths. > > **Overview** > Adds **component-view (CV) test coverage** for several flows previously validated only by smoke E2E: DeFi protocol position details, token Market Insights (including entry card gating + swap/buy navigation + sources sheet + feedback), notifications list/details and notification settings toggles, confirmation alert-system (typed-sign Blockaid benign/malicious + SIWE domain mismatch inline + validation-failed banner), and EIP-7702 sponsored send (failed activity status + “Paid by MetaMask” fee row). > > Extends CV test infrastructure with new presets/helpers and mocks (notifications state seeding, Market Insights navigation renderer/preset, SnapController request interceptor, Sentinel `/networks` nock mock), adds/normalizes several `testId` constants (send 50% button, confirmation transfer loader, status-icon tooltip), and sets `process.env.IS_TEST=true` at view-jest config load time to satisfy env-inlined feature gates. > > Removes or skips corresponding smoke E2E specs (or individual cases) and updates fixtures/assertions (e.g., SIWE signer address) to align with the new CV coverage. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 6098045. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a492e34 commit 7281395

37 files changed

Lines changed: 2739 additions & 921 deletions
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import '../../../../tests/component-view/mocks';
2+
import React from 'react';
3+
import { FlatList } from 'react-native';
4+
import { act, fireEvent } from '@testing-library/react-native';
5+
import type { GroupedDeFiPositions } from '@metamask/assets-controllers';
6+
7+
import DeFiProtocolPositionDetails, {
8+
DEFI_PROTOCOL_POSITION_DETAILS_BALANCE_TEST_ID,
9+
} from './DeFiProtocolPositionDetails';
10+
import { WalletViewSelectorsIDs } from '../../Views/Wallet/WalletView.testIds';
11+
import { renderComponentViewScreen } from '../../../../tests/component-view/render';
12+
import { describeForPlatforms } from '../../../../tests/component-view/platform';
13+
import { backgroundState } from '../../../util/test/initial-root-state';
14+
15+
/**
16+
* Mirrors smoke `view-defi-details`: tap Aave V3 → read-only position details with
17+
* Supplied tokens and fiat balances (no transaction).
18+
*/
19+
const aaveV3PositionAggregate: GroupedDeFiPositions['protocols'][number] = {
20+
protocolDetails: {
21+
name: 'Aave V3',
22+
iconUrl: '',
23+
},
24+
aggregatedMarketValue: 14.74,
25+
positionTypes: {
26+
supply: {
27+
aggregatedMarketValue: 14.74,
28+
positions: [
29+
[
30+
{
31+
address: '0x23878914efe38d27c4d67ab83ed1b93a74d4086a',
32+
name: 'Aave Ethereum USDT',
33+
symbol: 'aEthUSDT',
34+
decimals: 6,
35+
balance: 0.300112,
36+
balanceRaw: '300112',
37+
marketValue: 14.74,
38+
type: 'protocol',
39+
tokens: [
40+
{
41+
address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
42+
name: 'Tether USD',
43+
symbol: 'USDT',
44+
decimals: 6,
45+
balance: 0.300112,
46+
balanceRaw: '300112',
47+
marketValue: 14.74,
48+
price: 0.99994,
49+
type: 'underlying',
50+
iconUrl: '',
51+
},
52+
],
53+
},
54+
],
55+
[
56+
{
57+
address: '0xfa1fdbbd71b0aa16162d76914d69cd8cb3ef92da',
58+
name: 'Aave Ethereum Lido WETH',
59+
symbol: 'aEthLidoWETH',
60+
decimals: 18,
61+
balance: 1e-5,
62+
balanceRaw: '9030902767263172',
63+
marketValue: 0.3,
64+
type: 'protocol',
65+
tokens: [
66+
{
67+
address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
68+
name: 'Wrapped Ether',
69+
symbol: 'WETH',
70+
decimals: 18,
71+
balance: 1e-5,
72+
balanceRaw: '10000000000000',
73+
marketValue: 0.3,
74+
price: 1599.45,
75+
type: 'underlying',
76+
iconUrl: '',
77+
},
78+
],
79+
},
80+
],
81+
],
82+
},
83+
},
84+
};
85+
86+
const defiDetailsState = {
87+
engine: {
88+
backgroundState: {
89+
...backgroundState,
90+
PreferencesController: {
91+
...backgroundState.PreferencesController,
92+
privacyMode: false,
93+
},
94+
},
95+
},
96+
};
97+
98+
describeForPlatforms('DeFi position details (read-only)', () => {
99+
it('shows Aave V3 supplied assets with token symbols and fiat amounts', () => {
100+
const { getByTestId, getByText, getAllByText, UNSAFE_getByType } =
101+
renderComponentViewScreen(
102+
DeFiProtocolPositionDetails,
103+
{ name: 'DeFiProtocolPositionDetails' },
104+
{ state: defiDetailsState },
105+
{
106+
protocolAggregate: aaveV3PositionAggregate,
107+
networkIconAvatar: undefined,
108+
},
109+
);
110+
111+
expect(
112+
getByTestId(WalletViewSelectorsIDs.DEFI_POSITIONS_DETAILS_CONTAINER),
113+
).toBeOnTheScreen();
114+
115+
expect(getByText('Aave V3')).toBeOnTheScreen();
116+
expect(
117+
getByTestId(DEFI_PROTOCOL_POSITION_DETAILS_BALANCE_TEST_ID),
118+
).toHaveTextContent('$14.74');
119+
120+
// Smoke parity for details checks: Supplied + USDT + WETH + $14.74 + $0.30.
121+
expect(getAllByText('Supplied')).toHaveLength(2);
122+
expect(getAllByText('USDT')).toHaveLength(1);
123+
expect(getAllByText('$14.74').length).toBeGreaterThanOrEqual(2);
124+
125+
const list = UNSAFE_getByType(FlatList);
126+
act(() => {
127+
fireEvent.scroll(list, {
128+
nativeEvent: {
129+
contentOffset: { y: 150 },
130+
contentSize: { height: 500, width: 400 },
131+
layoutMeasurement: { height: 400, width: 400 },
132+
},
133+
});
134+
});
135+
136+
expect(getByText('WETH')).toBeOnTheScreen();
137+
expect(getByText('$0.30')).toBeOnTheScreen();
138+
});
139+
});
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/**
2+
* Component view tests for token (non-Perps) MarketInsightsView: content,
3+
* swap/buy navigation, trend sources sheet, thumbs up.
4+
* Mirrors smoke: tests/smoke/assets/market-insights/view-market-insights.spec.ts
5+
* (cases 7, 10, 11, 12). Entry card visibility cases (8, 9) are covered by
6+
* AssetOverviewContent.view.test.tsx.
7+
* Run: yarn test:view:one MarketInsightsView.view.test.tsx
8+
*/
9+
import '../../../../../../tests/component-view/mocks';
10+
import { fireEvent, screen, waitFor } from '@testing-library/react-native';
11+
import { CHAIN_IDS } from '@metamask/transaction-controller';
12+
import {
13+
MOCK_PERPS_MARKET_INSIGHTS_REPORT,
14+
setupMarketInsightsEngineMock,
15+
} from '../../../../../../tests/component-view/fixtures/perpsMarketInsights';
16+
import { renderMarketInsightsViewWithNavigation } from '../../../../../../tests/component-view/renderers/marketInsights';
17+
import { describeForPlatforms } from '../../../../../../tests/component-view/platform';
18+
import { BuildQuoteSelectors } from '../../../Ramp/Aggregator/Views/BuildQuote/BuildQuote.testIds';
19+
import { MarketInsightsSelectorsIDs } from '../../MarketInsights.testIds';
20+
import { analytics } from '../../../../../util/analytics/analytics';
21+
import { resetFeedbackCache } from './MarketInsightsView';
22+
23+
const ETH_MAINNET_ROUTE_PARAMS = {
24+
assetSymbol: 'ETH',
25+
assetIdentifier: 'eip155:1/slip44:60',
26+
tokenAddress: '0x0000000000000000000000000000000000000000',
27+
tokenDecimals: 18,
28+
tokenName: 'Ethereum',
29+
tokenChainId: CHAIN_IDS.MAINNET,
30+
token: {
31+
address: '0x123',
32+
symbol: 'ETH',
33+
decimals: 18,
34+
name: 'Ethereum',
35+
chainId: '0x1',
36+
image: 'https://example.com/eth.png',
37+
balance: '0',
38+
logo: undefined,
39+
},
40+
};
41+
42+
describeForPlatforms('MarketInsightsView (token flow)', () => {
43+
beforeEach(() => {
44+
setupMarketInsightsEngineMock(MOCK_PERPS_MARKET_INSIGHTS_REPORT);
45+
});
46+
47+
afterEach(() => {
48+
resetFeedbackCache();
49+
});
50+
51+
it('displays market insights content and navigates to swap', async () => {
52+
renderMarketInsightsViewWithNavigation({
53+
initialParams: ETH_MAINNET_ROUTE_PARAMS,
54+
overrides: {
55+
engine: {
56+
backgroundState: {
57+
TokensController: {
58+
allTokens: {
59+
'0x1': {
60+
'0x0000000000000000000000000000000000000001': [
61+
{
62+
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
63+
decimals: 6,
64+
symbol: 'USDC',
65+
name: 'USD Coin',
66+
image: '',
67+
},
68+
],
69+
},
70+
},
71+
allIgnoredTokens: {},
72+
},
73+
TokenBalancesController: {
74+
tokenBalances: {
75+
'0x0000000000000000000000000000000000000001': {
76+
'0x1': {
77+
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': '0x3b9aca00',
78+
},
79+
},
80+
},
81+
},
82+
TokenRatesController: {
83+
marketData: {
84+
'0x1': {
85+
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': {
86+
tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
87+
currency: 'ETH',
88+
price: 0.0005,
89+
},
90+
},
91+
},
92+
},
93+
},
94+
},
95+
},
96+
});
97+
98+
expect(
99+
await screen.findByTestId(MarketInsightsSelectorsIDs.VIEW_CONTAINER),
100+
).toBeOnTheScreen();
101+
expect(
102+
await screen.findByText(
103+
'Ethereum shows strong momentum amid institutional demand',
104+
),
105+
).toBeOnTheScreen();
106+
expect(
107+
await screen.findByText(
108+
'Ethereum continues to attract institutional interest with increasing on-chain activity and a healthy DeFi ecosystem.',
109+
),
110+
).toBeOnTheScreen();
111+
expect(await screen.findByText('Institutional Adoption')).toBeOnTheScreen();
112+
expect(await screen.findByText('DeFi Activity Surge')).toBeOnTheScreen();
113+
114+
fireEvent.press(
115+
await screen.findByTestId(MarketInsightsSelectorsIDs.SWAP_BUTTON),
116+
);
117+
118+
expect(await screen.findByTestId('route-BridgeView')).toBeOnTheScreen();
119+
});
120+
121+
it('navigates to buy flow when tapping Buy button', async () => {
122+
renderMarketInsightsViewWithNavigation({
123+
initialParams: ETH_MAINNET_ROUTE_PARAMS,
124+
});
125+
126+
await screen.findByTestId(MarketInsightsSelectorsIDs.VIEW_CONTAINER);
127+
128+
fireEvent.press(
129+
await screen.findByTestId(MarketInsightsSelectorsIDs.BUY_BUTTON),
130+
);
131+
132+
expect(
133+
await screen.findByTestId(BuildQuoteSelectors.CONTINUE_BUTTON),
134+
).toBeOnTheScreen();
135+
});
136+
137+
it('shows sources bottom sheet when tapping a trend item', async () => {
138+
renderMarketInsightsViewWithNavigation({
139+
initialParams: ETH_MAINNET_ROUTE_PARAMS,
140+
});
141+
142+
await screen.findByTestId(MarketInsightsSelectorsIDs.VIEW_CONTAINER);
143+
144+
fireEvent.press(
145+
await screen.findByTestId(`${MarketInsightsSelectorsIDs.TREND_ITEM}-0`),
146+
);
147+
148+
expect(
149+
await screen.findByText('Spot Ethereum ETFs See Record Weekly Inflows'),
150+
).toBeOnTheScreen();
151+
});
152+
153+
it('can tap thumbs up feedback button', async () => {
154+
const trackEventSpy = jest.spyOn(analytics, 'trackEvent');
155+
try {
156+
renderMarketInsightsViewWithNavigation({
157+
initialParams: ETH_MAINNET_ROUTE_PARAMS,
158+
});
159+
160+
await screen.findByTestId(MarketInsightsSelectorsIDs.VIEW_CONTAINER);
161+
162+
const thumbsUp = await screen.findByTestId(
163+
MarketInsightsSelectorsIDs.THUMBS_UP_BUTTON,
164+
);
165+
trackEventSpy.mockClear();
166+
fireEvent.press(thumbsUp);
167+
168+
await waitFor(() => {
169+
expect(trackEventSpy).toHaveBeenCalledWith(
170+
expect.objectContaining({
171+
name: 'Market Insights Interaction',
172+
properties: expect.objectContaining({
173+
interaction_type: 'thumbs_up',
174+
}),
175+
}),
176+
);
177+
});
178+
} finally {
179+
trackEventSpy.mockRestore();
180+
}
181+
});
182+
});

0 commit comments

Comments
 (0)