Skip to content

Commit fa6c62a

Browse files
authored
Merge branch 'main' into money_acc_fixes
2 parents 48052f4 + eedc67b commit fa6c62a

4 files changed

Lines changed: 149 additions & 15 deletions

File tree

app/components/UI/Money/Views/MoneyHomeView/MoneyHomeView.test.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { strings } from '../../../../../../locales/i18n';
2323
import MOCK_MONEY_TRANSACTIONS from '../../constants/mockActivityData';
2424
import useMoneyAccountBalance from '../../hooks/useMoneyAccountBalance';
2525
import { selectIsCardholder } from '../../../../../selectors/cardController';
26+
import { getDetectedGeolocation } from '../../../../../reducers/fiatOrders';
2627
import { moneyFormatFiat } from '../../utils/moneyFormatFiat';
2728

2829
const mockGoBack = jest.fn();
@@ -85,7 +86,13 @@ jest.mock('../../../../../selectors/cardController', () => ({
8586
selectIsCardholder: jest.fn(),
8687
}));
8788

89+
jest.mock('../../../../../reducers/fiatOrders', () => ({
90+
...jest.requireActual('../../../../../reducers/fiatOrders'),
91+
getDetectedGeolocation: jest.fn(),
92+
}));
93+
8894
const mockSelectIsCardholder = jest.mocked(selectIsCardholder);
95+
const mockGetDetectedGeolocation = jest.mocked(getDetectedGeolocation);
8996

9097
const mockUseMoneyAccountTransactions = jest.mocked(
9198
useMoneyAccountTransactions,
@@ -134,6 +141,7 @@ describe('MoneyHomeView', () => {
134141
global.alert = jest.fn();
135142

136143
mockSelectIsCardholder.mockReturnValue(false);
144+
mockGetDetectedGeolocation.mockReturnValue('US');
137145

138146
mockUseMoneyAccountBalance.mockReturnValue({
139147
totalFiatFormatted: '$3.00',
@@ -592,4 +600,82 @@ describe('MoneyHomeView', () => {
592600
});
593601
});
594602
});
603+
604+
describe('Metal card geolocation gating', () => {
605+
it('renders the Metal card row when geolocation is US', () => {
606+
mockGetDetectedGeolocation.mockReturnValue('US');
607+
608+
const { getByTestId } = renderWithProvider(<MoneyHomeView />);
609+
610+
expect(
611+
getByTestId(MoneyMetaMaskCardTestIds.METAL_CARD_ROW),
612+
).toBeOnTheScreen();
613+
expect(
614+
getByTestId(MoneyMetaMaskCardTestIds.VIRTUAL_CARD_ROW),
615+
).toBeOnTheScreen();
616+
});
617+
618+
it('renders the Metal card row when geolocation is a US sub-region (e.g. US-CA)', () => {
619+
mockGetDetectedGeolocation.mockReturnValue('us-ca');
620+
621+
const { getByTestId } = renderWithProvider(<MoneyHomeView />);
622+
623+
expect(
624+
getByTestId(MoneyMetaMaskCardTestIds.METAL_CARD_ROW),
625+
).toBeOnTheScreen();
626+
});
627+
628+
it('hides the Metal card row when geolocation is GB', () => {
629+
mockGetDetectedGeolocation.mockReturnValue('GB');
630+
631+
const { queryByTestId, getByTestId } = renderWithProvider(
632+
<MoneyHomeView />,
633+
);
634+
635+
expect(
636+
queryByTestId(MoneyMetaMaskCardTestIds.METAL_CARD_ROW),
637+
).not.toBeOnTheScreen();
638+
expect(
639+
getByTestId(MoneyMetaMaskCardTestIds.VIRTUAL_CARD_ROW),
640+
).toBeOnTheScreen();
641+
});
642+
643+
it('hides the Metal card row when geolocation is undefined (loading/unknown - fail closed)', () => {
644+
mockGetDetectedGeolocation.mockReturnValue(undefined);
645+
646+
const { queryByTestId, getByTestId } = renderWithProvider(
647+
<MoneyHomeView />,
648+
);
649+
650+
expect(
651+
queryByTestId(MoneyMetaMaskCardTestIds.METAL_CARD_ROW),
652+
).not.toBeOnTheScreen();
653+
expect(
654+
getByTestId(MoneyMetaMaskCardTestIds.VIRTUAL_CARD_ROW),
655+
).toBeOnTheScreen();
656+
});
657+
});
658+
659+
describe('Get now navigation', () => {
660+
it('navigates to the card sign-up flow when the virtual card Get now button is pressed', () => {
661+
mockGetDetectedGeolocation.mockReturnValue('GB');
662+
663+
const { getByText } = renderWithProvider(<MoneyHomeView />);
664+
665+
fireEvent.press(getByText(strings('money.metamask_card.get_now')));
666+
667+
expect(mockNavigate).toHaveBeenCalledWith(Routes.CARD.ROOT);
668+
});
669+
670+
it('navigates to the card sign-up flow when the metal card Get now button is pressed', () => {
671+
mockGetDetectedGeolocation.mockReturnValue('US');
672+
673+
const { getAllByText } = renderWithProvider(<MoneyHomeView />);
674+
const buttons = getAllByText(strings('money.metamask_card.get_now'));
675+
676+
fireEvent.press(buttons[1]);
677+
678+
expect(mockNavigate).toHaveBeenCalledWith(Routes.CARD.ROOT);
679+
});
680+
});
595681
});

app/components/UI/Money/Views/MoneyHomeView/MoneyHomeView.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { TokenDetailsSource } from '../../../TokenDetails/constants/constants';
3636
import AppConstants from '../../../../../core/AppConstants';
3737
import NavigationService from '../../../../../core/NavigationService';
3838
import { selectIsCardholder } from '../../../../../selectors/cardController';
39+
import { getDetectedGeolocation } from '../../../../../reducers/fiatOrders';
3940
import Logger from '../../../../../util/Logger';
4041
import { AssetType } from '../../../../Views/confirmations/types/token';
4142
import { Hex } from '@metamask/utils';
@@ -73,6 +74,8 @@ const MoneyHomeView = () => {
7374
const { allTransactions, moneyAddress } = useMoneyAccountTransactions();
7475

7576
const isCardholder = useSelector(selectIsCardholder);
77+
const geolocation = useSelector(getDetectedGeolocation);
78+
const isUS = geolocation?.toUpperCase().split('-')[0] === 'US';
7679

7780
const homeState = getMoneyHomeState(allTransactions.length);
7881
const isMilestone = homeState === 'milestone' || homeState === 'filled';
@@ -296,6 +299,7 @@ const MoneyHomeView = () => {
296299
onHeaderPress={handleHeaderPress}
297300
onLinkPress={handleLinkCardPress}
298301
apy={apyPercent}
302+
showMetalCard={isUS}
299303
/>
300304
<Divider />
301305
{isMilestone && (

app/components/UI/Money/components/MoneyMetaMaskCard/MoneyMetaMaskCard.test.tsx

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ describe('MoneyMetaMaskCard', () => {
3232
).toBeOnTheScreen();
3333
});
3434

35-
it('renders metal card row', () => {
35+
it('renders metal card row when showMetalCard is true', () => {
3636
const { getByText, getByTestId } = render(
37-
<MoneyMetaMaskCard onGetNowPress={jest.fn()} />,
37+
<MoneyMetaMaskCard onGetNowPress={jest.fn()} showMetalCard />,
3838
);
3939

4040
expect(
@@ -48,14 +48,36 @@ describe('MoneyMetaMaskCard', () => {
4848
).toBeOnTheScreen();
4949
});
5050

51+
it('hides metal card row by default (showMetalCard not provided)', () => {
52+
const { queryByTestId, queryByText } = render(
53+
<MoneyMetaMaskCard onGetNowPress={jest.fn()} />,
54+
);
55+
56+
expect(
57+
queryByTestId(MoneyMetaMaskCardTestIds.METAL_CARD_ROW),
58+
).not.toBeOnTheScreen();
59+
expect(
60+
queryByText(strings('money.metamask_card.metal_card')),
61+
).not.toBeOnTheScreen();
62+
});
63+
64+
it('hides metal card row when showMetalCard is false', () => {
65+
const { queryByTestId } = render(
66+
<MoneyMetaMaskCard onGetNowPress={jest.fn()} showMetalCard={false} />,
67+
);
68+
69+
expect(
70+
queryByTestId(MoneyMetaMaskCardTestIds.METAL_CARD_ROW),
71+
).not.toBeOnTheScreen();
72+
});
73+
5174
it('calls onGetNowPress when virtual card Get now is pressed', () => {
5275
const mockGetNow = jest.fn();
53-
const { getAllByText } = render(
76+
const { getByText } = render(
5477
<MoneyMetaMaskCard onGetNowPress={mockGetNow} />,
5578
);
56-
const getNowButtons = getAllByText(strings('money.metamask_card.get_now'));
5779

58-
fireEvent.press(getNowButtons[0]);
80+
fireEvent.press(getByText(strings('money.metamask_card.get_now')));
5981

6082
expect(mockGetNow).toHaveBeenCalledTimes(1);
6183
expect(mockGetNow.mock.calls[0]).toEqual([]);
@@ -64,7 +86,7 @@ describe('MoneyMetaMaskCard', () => {
6486
it('calls onGetNowPress when metal card Get now is pressed', () => {
6587
const mockGetNow = jest.fn();
6688
const { getAllByText } = render(
67-
<MoneyMetaMaskCard onGetNowPress={mockGetNow} />,
89+
<MoneyMetaMaskCard onGetNowPress={mockGetNow} showMetalCard />,
6890
);
6991
const getNowButtons = getAllByText(strings('money.metamask_card.get_now'));
7092

@@ -174,9 +196,9 @@ describe('MoneyMetaMaskCard', () => {
174196
});
175197

176198
describe('upsell mode (default)', () => {
177-
it('renders virtual and metal card rows', () => {
199+
it('renders virtual and metal card rows when showMetalCard is true', () => {
178200
const { getByTestId } = render(
179-
<MoneyMetaMaskCard onGetNowPress={jest.fn()} />,
201+
<MoneyMetaMaskCard onGetNowPress={jest.fn()} showMetalCard />,
180202
);
181203

182204
expect(
@@ -187,6 +209,19 @@ describe('MoneyMetaMaskCard', () => {
187209
).toBeOnTheScreen();
188210
});
189211

212+
it('renders only the virtual card row when showMetalCard is false', () => {
213+
const { getByTestId, queryByTestId } = render(
214+
<MoneyMetaMaskCard onGetNowPress={jest.fn()} showMetalCard={false} />,
215+
);
216+
217+
expect(
218+
getByTestId(MoneyMetaMaskCardTestIds.VIRTUAL_CARD_ROW),
219+
).toBeOnTheScreen();
220+
expect(
221+
queryByTestId(MoneyMetaMaskCardTestIds.METAL_CARD_ROW),
222+
).not.toBeOnTheScreen();
223+
});
224+
190225
it('does not render link mode elements', () => {
191226
const { queryByTestId } = render(
192227
<MoneyMetaMaskCard onGetNowPress={jest.fn()} />,

app/components/UI/Money/components/MoneyMetaMaskCard/MoneyMetaMaskCard.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ interface MoneyMetaMaskCardProps {
3434
onLinkPress?: () => void;
3535
/** Current APY value displayed in the link mode bullet. */
3636
apy?: number;
37+
/**
38+
* Whether to render the Metal card row in upsell mode. Defaults to `false`
39+
* because the Metal card is currently only available to US users; the parent
40+
* is expected to pass the geolocation-derived flag.
41+
*/
42+
showMetalCard?: boolean;
3743
}
3844

3945
const CardRow = ({
@@ -166,6 +172,7 @@ const MoneyMetaMaskCard = ({
166172
onHeaderPress,
167173
onLinkPress,
168174
apy,
175+
showMetalCard = false,
169176
}: MoneyMetaMaskCardProps) => {
170177
const handleLinkPress = useCallback(() => onLinkPress?.(), [onLinkPress]);
171178

@@ -200,13 +207,15 @@ const MoneyMetaMaskCard = ({
200207
onPress={onGetNowPress}
201208
testID={MoneyMetaMaskCardTestIds.VIRTUAL_CARD_ROW}
202209
/>
203-
<CardRow
204-
imageSource={mmCardMetal}
205-
cardName={strings('money.metamask_card.metal_card')}
206-
cashbackPercentage="3"
207-
onPress={onGetNowPress}
208-
testID={MoneyMetaMaskCardTestIds.METAL_CARD_ROW}
209-
/>
210+
{showMetalCard && (
211+
<CardRow
212+
imageSource={mmCardMetal}
213+
cardName={strings('money.metamask_card.metal_card')}
214+
cashbackPercentage="3"
215+
onPress={onGetNowPress}
216+
testID={MoneyMetaMaskCardTestIds.METAL_CARD_ROW}
217+
/>
218+
)}
210219
</>
211220
)}
212221
</Box>

0 commit comments

Comments
 (0)