Skip to content

Commit c217cdd

Browse files
authored
fix: use conditional ramp_type based on Unified V1 flag for analytics (MetaMask#24245)
## **Description** The `ramp_type` analytics property was hardcoded to `BUY` in `BalanceEmptyState` and `AssetOverview` components, but when Unified Ramp V1 is enabled, it should be `UNIFIED_BUY` to correctly segment analytics data for the Unified Buy funnel. This PR adds the `useRampsUnifiedV1Enabled` hook to both components and conditionally sets `ramp_type` based on the flag: - When Unified V1 is **enabled** → `ramp_type: "UNIFIED_BUY"` - When Unified V1 is **disabled** → `ramp_type: "BUY"` This is consistent with the existing implementation in `FundActionMenu.tsx`. ## **Changelog** CHANGELOG entry: Fixed analytics `ramp_type` to correctly report `UNIFIED_BUY` when Unified Ramp V1 is enabled ## **Related issues** Fixes: MetaMask#24219 ## **Manual testing steps** ```gherkin Feature: Ramps Button Analytics Scenario: user taps Add Funds from BalanceEmptyState with Unified V1 enabled Given Ramps Unified V1 feature flag is enabled And user has zero balance (BalanceEmptyState is visible) When user taps "Add funds" button Then RAMPS_BUTTON_CLICKED event is tracked with ramp_type: "UNIFIED_BUY" Scenario: user taps Buy from TokenDetails with Unified V1 disabled Given Ramps Unified V1 feature flag is disabled And user is viewing token details (AssetOverview) When user taps "Buy" button Then RAMPS_BUTTON_CLICKED event is tracked with ramp_type: "BUY" ``` ## **Screenshots/Recordings** N/A - No UI changes, analytics fix only. ### **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. ## **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] > Aligns buy-flow analytics with Unified Ramp V1. > > - In `AssetOverview.tsx` and `BalanceEmptyState.tsx`, add `useRampsUnifiedV1Enabled` and set `ramp_type` to `UNIFIED_BUY` when enabled, otherwise `BUY`, for `RAMPS_BUTTON_CLICKED` > - Update tests (`AssetOverview.test.tsx`, `BalanceEmptyState.test.tsx`) to mock the flag and assert `ramp_type` for both enabled/disabled scenarios > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 3d0409a. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 55500a9 commit c217cdd

4 files changed

Lines changed: 119 additions & 3 deletions

File tree

app/components/UI/AssetOverview/AssetOverview.test.tsx

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,12 @@ jest.mock('../../../components/hooks/useAddNetwork', () => ({
273273
})),
274274
}));
275275

276+
const mockUseRampsUnifiedV1Enabled = jest.fn();
277+
jest.mock('../Ramp/hooks/useRampsUnifiedV1Enabled', () => ({
278+
__esModule: true,
279+
default: () => mockUseRampsUnifiedV1Enabled(),
280+
}));
281+
276282
const asset = {
277283
balance: '400',
278284
balanceFiat: '1500',
@@ -327,6 +333,9 @@ describe('AssetOverview', () => {
327333
address: MOCK_ADDRESS_2,
328334
});
329335
selectSelectedInternalAccountByScope.mockReturnValue(mockGetAccountByScope);
336+
337+
// Default mock for unified V1 flag - disabled
338+
mockUseRampsUnifiedV1Enabled.mockReturnValue(false);
330339
});
331340

332341
afterEach(() => {
@@ -449,6 +458,78 @@ describe('AssetOverview', () => {
449458
expect(mockTrackEvent).toHaveBeenCalledTimes(2);
450459
});
451460

461+
it('tracks RAMPS_BUTTON_CLICKED with ramp_type BUY when unified V1 is disabled', async () => {
462+
mockUseRampsUnifiedV1Enabled.mockReturnValue(false);
463+
const { getByTestId } = renderWithProvider(
464+
<AssetOverview
465+
asset={asset}
466+
displayBuyButton
467+
displaySwapsButton
468+
networkName="Ethereum Mainnet"
469+
/>,
470+
{ state: mockInitialState },
471+
);
472+
473+
const buyButton = getByTestId(TokenOverviewSelectorsIDs.BUY_BUTTON);
474+
fireEvent.press(buyButton);
475+
476+
const navigationCall = navigate.mock.calls[0];
477+
const onBuyFunction = navigationCall[1].params.onBuy;
478+
479+
jest.clearAllMocks();
480+
mockBuild.mockReturnValue({ category: 'test' });
481+
mockCreateEventBuilder.mockReturnValue({
482+
addProperties: mockAddProperties,
483+
});
484+
485+
onBuyFunction();
486+
487+
expect(mockCreateEventBuilder).toHaveBeenCalledWith(
488+
MetaMetricsEvents.RAMPS_BUTTON_CLICKED,
489+
);
490+
expect(mockAddProperties).toHaveBeenCalledWith(
491+
expect.objectContaining({
492+
ramp_type: 'BUY',
493+
}),
494+
);
495+
});
496+
497+
it('tracks RAMPS_BUTTON_CLICKED with ramp_type UNIFIED_BUY when unified V1 is enabled', async () => {
498+
mockUseRampsUnifiedV1Enabled.mockReturnValue(true);
499+
const { getByTestId } = renderWithProvider(
500+
<AssetOverview
501+
asset={asset}
502+
displayBuyButton
503+
displaySwapsButton
504+
networkName="Ethereum Mainnet"
505+
/>,
506+
{ state: mockInitialState },
507+
);
508+
509+
const buyButton = getByTestId(TokenOverviewSelectorsIDs.BUY_BUTTON);
510+
fireEvent.press(buyButton);
511+
512+
const navigationCall = navigate.mock.calls[0];
513+
const onBuyFunction = navigationCall[1].params.onBuy;
514+
515+
jest.clearAllMocks();
516+
mockBuild.mockReturnValue({ category: 'test' });
517+
mockCreateEventBuilder.mockReturnValue({
518+
addProperties: mockAddProperties,
519+
});
520+
521+
onBuyFunction();
522+
523+
expect(mockCreateEventBuilder).toHaveBeenCalledWith(
524+
MetaMetricsEvents.RAMPS_BUTTON_CLICKED,
525+
);
526+
expect(mockAddProperties).toHaveBeenCalledWith(
527+
expect.objectContaining({
528+
ramp_type: 'UNIFIED_BUY',
529+
}),
530+
);
531+
});
532+
452533
it('should handle send button press', async () => {
453534
const { getByTestId } = renderWithProvider(
454535
<AssetOverview

app/components/UI/AssetOverview/AssetOverview.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ import { createStakedTrxAsset } from './utils/createStakedTrxAsset';
104104
///: END:ONLY_INCLUDE_IF
105105
import { getDetectedGeolocation } from '../../../reducers/fiatOrders';
106106
import { useRampsButtonClickData } from '../Ramp/hooks/useRampsButtonClickData';
107+
import useRampsUnifiedV1Enabled from '../Ramp/hooks/useRampsUnifiedV1Enabled';
107108

108109
interface AssetOverviewProps {
109110
asset: TokenI;
@@ -188,6 +189,7 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
188189
const currentAddress = asset.address as Hex;
189190
const { goToBuy } = useRampNavigation();
190191
const rampsButtonClickData = useRampsButtonClickData();
192+
const rampUnifiedV1Enabled = useRampsUnifiedV1Enabled();
191193
const { data: prices = [], isLoading } = useTokenHistoricalPrices({
192194
asset,
193195
address: currentAddress,
@@ -362,7 +364,7 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
362364
text: 'Buy',
363365
location: 'TokenDetails',
364366
chain_id_destination: getDecimalChainId(chainId),
365-
ramp_type: 'BUY',
367+
ramp_type: rampUnifiedV1Enabled ? 'UNIFIED_BUY' : 'BUY',
366368
region: rampGeodetectedRegion,
367369
ramp_routing: rampsButtonClickData.ramp_routing,
368370
is_authenticated: rampsButtonClickData.is_authenticated,

app/components/UI/BalanceEmptyState/BalanceEmptyState.test.tsx

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ jest.mock('../Ramp/hooks/useRampsButtonClickData', () => ({
2424
useRampsButtonClickData: jest.fn(() => mockButtonClickData),
2525
}));
2626

27+
const mockUseRampsUnifiedV1Enabled = jest.fn();
28+
jest.mock('../Ramp/hooks/useRampsUnifiedV1Enabled', () => ({
29+
__esModule: true,
30+
default: () => mockUseRampsUnifiedV1Enabled(),
31+
}));
32+
2733
const mockTrackEvent = jest.fn();
2834
const mockCreateEventBuilder = jest.fn();
2935
const mockEventBuilder = {
@@ -50,6 +56,7 @@ describe('BalanceEmptyState', () => {
5056
trackEvent: mockTrackEvent,
5157
createEventBuilder: mockCreateEventBuilder,
5258
});
59+
mockUseRampsUnifiedV1Enabled.mockReturnValue(false);
5360
});
5461

5562
const renderComponent = (props: Partial<BalanceEmptyStateProps> = {}) =>
@@ -87,7 +94,8 @@ describe('BalanceEmptyState', () => {
8794
expect(mockGoToBuy).toHaveBeenCalled();
8895
});
8996

90-
it('tracks RAMPS_BUTTON_CLICKED event when action button is pressed', () => {
97+
it('tracks RAMPS_BUTTON_CLICKED event with ramp_type BUY when unified V1 is disabled', () => {
98+
mockUseRampsUnifiedV1Enabled.mockReturnValue(false);
9199
const { getByTestId } = renderComponent();
92100
const actionButton = getByTestId('balance-empty-state-action-button');
93101

@@ -108,4 +116,27 @@ describe('BalanceEmptyState', () => {
108116
);
109117
expect(mockTrackEvent).toHaveBeenCalled();
110118
});
119+
120+
it('tracks RAMPS_BUTTON_CLICKED event with ramp_type UNIFIED_BUY when unified V1 is enabled', () => {
121+
mockUseRampsUnifiedV1Enabled.mockReturnValue(true);
122+
const { getByTestId } = renderComponent();
123+
const actionButton = getByTestId('balance-empty-state-action-button');
124+
125+
fireEvent.press(actionButton);
126+
127+
expect(mockCreateEventBuilder).toHaveBeenCalledWith('ramps_button_clicked');
128+
expect(mockEventBuilder.addProperties).toHaveBeenCalledWith(
129+
expect.objectContaining({
130+
text: 'Add funds',
131+
location: 'BalanceEmptyState',
132+
chain_id_destination: 1,
133+
ramp_type: 'UNIFIED_BUY',
134+
ramp_routing: undefined,
135+
is_authenticated: false,
136+
preferred_provider: undefined,
137+
order_count: 0,
138+
}),
139+
);
140+
expect(mockTrackEvent).toHaveBeenCalled();
141+
});
111142
});

app/components/UI/BalanceEmptyState/BalanceEmptyState.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { BalanceEmptyStateProps } from './BalanceEmptyState.types';
2525
import bankTransferImage from '../../../images/bank-transfer.png';
2626
import { getDetectedGeolocation } from '../../../reducers/fiatOrders';
2727
import { useRampsButtonClickData } from '../Ramp/hooks/useRampsButtonClickData';
28+
import useRampsUnifiedV1Enabled from '../Ramp/hooks/useRampsUnifiedV1Enabled';
2829

2930
/**
3031
* BalanceEmptyState smart component displays an empty state for wallet balance
@@ -40,6 +41,7 @@ const BalanceEmptyState: React.FC<BalanceEmptyStateProps> = ({
4041
const rampGeodetectedRegion = useSelector(getDetectedGeolocation);
4142
const { goToBuy } = useRampNavigation();
4243
const buttonClickData = useRampsButtonClickData();
44+
const rampUnifiedV1Enabled = useRampsUnifiedV1Enabled();
4345

4446
const handleAction = () => {
4547
goToBuy();
@@ -50,7 +52,7 @@ const BalanceEmptyState: React.FC<BalanceEmptyStateProps> = ({
5052
text: 'Add funds',
5153
location: 'BalanceEmptyState',
5254
chain_id_destination: getDecimalChainId(chainId),
53-
ramp_type: 'BUY',
55+
ramp_type: rampUnifiedV1Enabled ? 'UNIFIED_BUY' : 'BUY',
5456
region: rampGeodetectedRegion,
5557
ramp_routing: buttonClickData.ramp_routing,
5658
is_authenticated: buttonClickData.is_authenticated,

0 commit comments

Comments
 (0)