Skip to content

Commit 5a1a3b9

Browse files
authored
test: Add Component View Tests for Send Flow (#26094)
<!-- 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** Sev1 issues addressed in this PR: Test | Issue | What it covers -- | -- | -- TRON send: selecting destination account updates selection and flow moves forward | #22789 #23251| TRON send: choose recipient from list, flow advances (no stuck on recipient list). ERC-721 send: Amount screen shows enabled Continue and user can proceed to Recipient | #12317 | ERC-721 send: Continue enabled on Amount, can go to Recipient. ERC-721 selected from Asset screen navigates to Recipient not Amount | #19002 | Starting Send from Asset with ERC-721 opens Recipient (not Amount). Solana send Recipient screen does not show EVM contacts | #22205 | Non-EVM (Solana) send: Recipient screen has no EVM contacts. Recipient list renders each contact with avatar | #22806 | Each Recipient list row (contacts) has the expected avatar. ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> 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** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **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 - [ ] 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. ## **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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: changes are primarily new/updated tests and test scaffolding, plus additive `testID` props used for automation with no functional logic changes. > > **Overview** > Adds new component-view regression tests for the redesigned `Send` flow, covering TRON recipient selection advancing the flow, ERC-721 navigation/Continue enablement, non-EVM recipient filtering (no EVM contacts), and per-recipient avatar rendering. > > To support these tests, introduces centralized Send `testID` constants/helpers, adds `testID`s to NFT rows (now includes `tokenId` for uniqueness) and recipient avatars, and expands component-view test mocks/presets (Engine controller stubs, Snap `onAmountInput` validation response, and reusable Send/TRON state overrides). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit ecc307b. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 3bf82cf commit 5a1a3b9

7 files changed

Lines changed: 607 additions & 27 deletions

File tree

app/components/Views/confirmations/components/UI/nft/nft.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@ export function Nft({ asset, onPress }: NftProps) {
2929
onPress(asset);
3030
}, [asset, onPress]);
3131

32+
const testID = `nft-${asset.name || asset.collectionName || 'NFT'}-${asset.tokenId}`;
33+
3234
return (
3335
<Pressable
36+
testID={testID}
3437
style={({ pressed }) =>
3538
tw.style(
3639
'w-full flex-row items-center justify-between py-2',

app/components/Views/confirmations/components/UI/recipient/recipient.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ export function Recipient({
8989
accessibilityRole="button"
9090
>
9191
<Box twClassName="flex-row items-center">
92-
<Box twClassName="h-12 justify-center">
92+
<Box
93+
twClassName="h-12 justify-center"
94+
testID={`recipient-avatar-${recipient.address}`}
95+
>
9396
<Avatar
9497
variant={AvatarVariant.Account}
9598
type={accountAvatarType}

app/components/Views/confirmations/components/nft-list/nft-list.test.tsx

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jest.mock('../UI/nft', () => {
3232
onPress: (asset: Nft) => void;
3333
}) => (
3434
<Pressable
35-
testID={`nft-${asset.name || asset.tokenId}`}
35+
testID={`nft-${asset.name || asset.collectionName || 'NFT'}-${asset.tokenId}`}
3636
onPress={() => onPress(asset)}
3737
>
3838
<Text>{asset.name || asset.tokenId}</Text>
@@ -95,30 +95,30 @@ describe('NftList', () => {
9595
it('renders nfts when provided', () => {
9696
const { getByTestId } = render(<NftList nfts={mockNfts} />);
9797

98-
expect(getByTestId('nft-Cool NFT #1')).toBeOnTheScreen();
99-
expect(getByTestId('nft-Awesome NFT #2')).toBeOnTheScreen();
98+
expect(getByTestId('nft-Cool NFT #1-1')).toBeOnTheScreen();
99+
expect(getByTestId('nft-Awesome NFT #2-2')).toBeOnTheScreen();
100100
});
101101

102102
it('renders empty list when no nfts provided', () => {
103103
const { queryByTestId } = render(<NftList nfts={[]} />);
104104

105-
expect(queryByTestId('nft-Cool NFT #1')).toBeNull();
106-
expect(queryByTestId('nft-Awesome NFT #2')).toBeNull();
105+
expect(queryByTestId('nft-Cool NFT #1-1')).toBeNull();
106+
expect(queryByTestId('nft-Awesome NFT #2-2')).toBeNull();
107107
});
108108

109109
it('renders single nft correctly', () => {
110110
const singleNft = [mockNfts[0]];
111111
const { getByTestId } = render(<NftList nfts={singleNft} />);
112112

113-
expect(getByTestId('nft-Cool NFT #1')).toBeOnTheScreen();
113+
expect(getByTestId('nft-Cool NFT #1-1')).toBeOnTheScreen();
114114
});
115115
});
116116

117117
describe('nft selection', () => {
118118
it('calls updateAsset and navigates to recipient screen when ERC721 nft is pressed', () => {
119119
const { getByTestId } = render(<NftList nfts={mockNfts} />);
120120

121-
fireEvent.press(getByTestId('nft-Cool NFT #1'));
121+
fireEvent.press(getByTestId('nft-Cool NFT #1-1'));
122122

123123
expect(mockUpdateAsset).toHaveBeenCalledWith(mockNfts[0]);
124124
expect(mockGotToSendScreen).toHaveBeenCalledWith(Routes.SEND.RECIPIENT);
@@ -134,14 +134,14 @@ describe('NftList', () => {
134134

135135
const { getByTestId } = render(<NftList nfts={mockNfts} />);
136136

137-
fireEvent.press(getByTestId('nft-Cool NFT #1'));
137+
fireEvent.press(getByTestId('nft-Cool NFT #1-1'));
138138
expect(mockUpdateTo).toHaveBeenCalledWith('');
139139
});
140140

141141
it('calls updateAsset and navigates to amount screen when ERC1155 nft is pressed', () => {
142142
const { getByTestId } = render(<NftList nfts={mockNfts} />);
143143

144-
fireEvent.press(getByTestId('nft-Awesome NFT #2'));
144+
fireEvent.press(getByTestId('nft-Awesome NFT #2-2'));
145145

146146
expect(mockUpdateAsset).toHaveBeenCalledWith(mockNfts[1]);
147147
expect(mockGotToSendScreen).toHaveBeenCalledWith(Routes.SEND.AMOUNT);
@@ -158,7 +158,7 @@ describe('NftList', () => {
158158

159159
const { getByTestId } = render(<NftList nfts={mockNfts} />);
160160

161-
fireEvent.press(getByTestId('nft-Cool NFT #1'));
161+
fireEvent.press(getByTestId('nft-Cool NFT #1-1'));
162162

163163
expect(mockCaptureAssetSelected).toHaveBeenCalledWith(mockNfts[0], '0');
164164
});
@@ -174,7 +174,7 @@ describe('NftList', () => {
174174

175175
const { getByTestId } = render(<NftList nfts={mockNfts} />);
176176

177-
fireEvent.press(getByTestId('nft-Awesome NFT #2'));
177+
fireEvent.press(getByTestId('nft-Awesome NFT #2-2'));
178178

179179
expect(mockCaptureAssetSelected).toHaveBeenCalledWith(mockNfts[1], '1');
180180
});
@@ -184,10 +184,10 @@ describe('NftList', () => {
184184
it('shows only first 5 nfts initially', () => {
185185
const { queryByTestId } = render(<NftList nfts={manyNfts} />);
186186

187-
expect(queryByTestId('nft-NFT 0')).toBeOnTheScreen();
188-
expect(queryByTestId('nft-NFT 4')).toBeOnTheScreen();
189-
expect(queryByTestId('nft-NFT 5')).toBeNull();
190-
expect(queryByTestId('nft-NFT 11')).toBeNull();
187+
expect(queryByTestId('nft-NFT 0-0')).toBeOnTheScreen();
188+
expect(queryByTestId('nft-NFT 4-4')).toBeOnTheScreen();
189+
expect(queryByTestId('nft-NFT 5-5')).toBeNull();
190+
expect(queryByTestId('nft-NFT 11-11')).toBeNull();
191191
});
192192

193193
it('shows "Show more NFTs" button when there are more than 5 nfts', () => {
@@ -205,12 +205,12 @@ describe('NftList', () => {
205205
it('shows more nfts when "Show more NFTs" is pressed', () => {
206206
const { getByText, queryByTestId } = render(<NftList nfts={manyNfts} />);
207207

208-
expect(queryByTestId('nft-NFT 5')).toBeNull();
208+
expect(queryByTestId('nft-NFT 5-5')).toBeNull();
209209

210210
fireEvent.press(getByText('Show more NFTs'));
211211

212-
expect(queryByTestId('nft-NFT 5')).toBeOnTheScreen();
213-
expect(queryByTestId('nft-NFT 9')).toBeOnTheScreen();
212+
expect(queryByTestId('nft-NFT 5-5')).toBeOnTheScreen();
213+
expect(queryByTestId('nft-NFT 9-9')).toBeOnTheScreen();
214214
});
215215

216216
it('hides "Show more NFTs" button when all nfts are visible', () => {
@@ -236,13 +236,13 @@ describe('NftList', () => {
236236

237237
fireEvent.press(getByText('Show more NFTs'));
238238

239-
expect(queryByTestId('nft-NFT 9')).toBeOnTheScreen();
240-
expect(queryByTestId('nft-NFT 10')).toBeNull();
239+
expect(queryByTestId('nft-NFT 9-9')).toBeOnTheScreen();
240+
expect(queryByTestId('nft-NFT 10-10')).toBeNull();
241241

242242
fireEvent.press(getByText('Show more NFTs'));
243243

244-
expect(queryByTestId('nft-NFT 14')).toBeOnTheScreen();
245-
expect(queryByTestId('nft-NFT 15')).toBeNull();
244+
expect(queryByTestId('nft-NFT 14-14')).toBeOnTheScreen();
245+
expect(queryByTestId('nft-NFT 15-15')).toBeNull();
246246
});
247247
});
248248
});
Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
11
export const RedesignedSendViewSelectorsIDs = {
2+
SEND_AMOUNT: 'send_amount',
3+
EDIT_AMOUNT_KEYBOARD: 'edit-amount-keyboard',
4+
PERCENTAGE_BUTTON_100: 'percentage-button-100',
25
RECIPIENT_ADDRESS_INPUT: 'recipient-address-input',
36
REVIEW_BUTTON: 'review-button',
4-
};
7+
} as const;
8+
9+
/** TestID for recipient row (unselected). */
10+
export const getRecipientRowTestId = (address: string) =>
11+
`recipient-${address}`;
12+
13+
/** TestID for selected recipient row. */
14+
export const getSelectedRecipientTestId = (address: string) =>
15+
`selected-${address}`;
16+
17+
/** TestID for recipient row avatar. */
18+
export const getRecipientAvatarTestId = (address: string) =>
19+
`recipient-avatar-${address}`;
20+
21+
/** TestID for NFT row in asset list. Includes tokenId for uniqueness. */
22+
export const getNftRowTestId = (name: string, tokenId: string) =>
23+
`nft-${name}-${tokenId}`;

0 commit comments

Comments
 (0)