Skip to content

Commit dadabd0

Browse files
committed
feat: allow add rewards account in predict flow & point estimation
1 parent 2ee8a9f commit dadabd0

File tree

10 files changed

+845
-200
lines changed

10 files changed

+845
-200
lines changed

app/components/UI/Predict/components/PredictFeeSummary/PredictFeeSummary.test.tsx

Lines changed: 180 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,26 @@ jest.mock('../../utils/format', () => ({
1010
),
1111
}));
1212

13+
// Mock i18n strings
14+
jest.mock('../../../../../../locales/i18n', () => ({
15+
strings: jest.fn((key: string) => {
16+
const mockStrings: Record<string, string> = {
17+
'predict.fee_summary.fees': 'Fees',
18+
'predict.fee_summary.total': 'Total',
19+
'predict.fee_summary.estimated_points': 'Est. points',
20+
'predict.fee_summary.points_tooltip': 'Points',
21+
'predict.fee_summary.points_tooltip_content_1':
22+
'Points are how you earn MetaMask Rewards for completing transactions, like when you swap, bridge, or predict.',
23+
'predict.fee_summary.points_tooltip_content_2':
24+
'Keep in mind this value is an estimate and will be finalized once the transaction is complete. Points can take up to 1 hour to be confirmed in your Rewards balance.',
25+
'predict.fee_summary.points_error': "We can't load points right now",
26+
'predict.fee_summary.points_error_content':
27+
"You'll still earn any points for this transaction. We'll notify you once they've been added to your account. You can also check your rewards tab in about an hour.",
28+
};
29+
return mockStrings[key] || key;
30+
}),
31+
}));
32+
1333
// Mock ButtonIcon
1434
jest.mock(
1535
'../../../../../component-library/components/Buttons/ButtonIcon',
@@ -52,14 +72,16 @@ jest.mock(
5272
field,
5373
value,
5474
}: {
55-
field: { label: { text: string } };
56-
value: { label: React.ReactNode };
75+
field: { label: { text: string }; tooltip?: unknown };
76+
value: { label: React.ReactNode; tooltip?: unknown };
5777
}) =>
5878
React.createElement(
5979
View,
6080
{ testID: 'key-value-row' },
6181
React.createElement(RNText, null, field.label.text),
6282
value.label,
83+
value.tooltip &&
84+
React.createElement(View, { testID: 'value-tooltip' }, 'Tooltip'),
6385
);
6486
},
6587
);
@@ -70,10 +92,10 @@ jest.mock('../../../Rewards/components/RewardPointsAnimation', () => {
7092
const { Text: RNText } = jest.requireActual('react-native');
7193
return {
7294
__esModule: true,
73-
default: ({ value }: { value: number }) =>
95+
default: ({ value, state }: { value: number; state: string }) =>
7496
React.createElement(
7597
RNText,
76-
{ testID: 'rewards-animation' },
98+
{ testID: 'rewards-animation', 'data-state': state },
7799
`${value} points`,
78100
),
79101
RewardAnimationState: {
@@ -84,6 +106,24 @@ jest.mock('../../../Rewards/components/RewardPointsAnimation', () => {
84106
};
85107
});
86108

109+
// Mock AddRewardsAccount
110+
jest.mock(
111+
'../../../Rewards/components/AddRewardsAccount/AddRewardsAccount',
112+
() => {
113+
const React = jest.requireActual('react');
114+
const { View } = jest.requireActual('react-native');
115+
return {
116+
__esModule: true,
117+
default: () =>
118+
React.createElement(
119+
View,
120+
{ testID: 'add-rewards-account' },
121+
'Add Rewards Account',
122+
),
123+
};
124+
},
125+
);
126+
87127
describe('PredictFeeSummary', () => {
88128
const defaultProps = {
89129
disabled: false,
@@ -173,10 +213,10 @@ describe('PredictFeeSummary', () => {
173213
});
174214

175215
describe('Rewards Row', () => {
176-
it('does not display rewards row when shouldShowRewards is false', () => {
216+
it('does not display rewards row when shouldShowRewardsRow is false', () => {
177217
const props = {
178218
...defaultProps,
179-
shouldShowRewards: false,
219+
shouldShowRewardsRow: false,
180220
estimatedPoints: 100,
181221
};
182222

@@ -186,12 +226,14 @@ describe('PredictFeeSummary', () => {
186226

187227
expect(queryByText('Est. points')).toBeNull();
188228
expect(queryByTestId('rewards-animation')).toBeNull();
229+
expect(queryByTestId('add-rewards-account')).toBeNull();
189230
});
190231

191-
it('displays rewards row when shouldShowRewards is true', () => {
232+
it('displays rewards row when shouldShowRewardsRow is true', () => {
192233
const props = {
193234
...defaultProps,
194-
shouldShowRewards: true,
235+
shouldShowRewardsRow: true,
236+
accountOptedIn: true,
195237
estimatedPoints: 50,
196238
};
197239

@@ -203,10 +245,11 @@ describe('PredictFeeSummary', () => {
203245
expect(getByTestId('rewards-animation')).toBeOnTheScreen();
204246
});
205247

206-
it('displays correct estimated points value', () => {
248+
it('displays correct estimated points value when account is opted in', () => {
207249
const props = {
208250
...defaultProps,
209-
shouldShowRewards: true,
251+
shouldShowRewardsRow: true,
252+
accountOptedIn: true,
210253
estimatedPoints: 123,
211254
};
212255

@@ -215,24 +258,148 @@ describe('PredictFeeSummary', () => {
215258
expect(getByText('123 points')).toBeOnTheScreen();
216259
});
217260

218-
it('displays zero points when estimatedPoints is 0', () => {
261+
it('displays zero points when estimatedPoints is 0 and account is opted in', () => {
219262
const props = {
220263
...defaultProps,
221-
shouldShowRewards: true,
264+
shouldShowRewardsRow: true,
265+
accountOptedIn: true,
222266
estimatedPoints: 0,
223267
};
224268

225269
const { getByText } = render(<PredictFeeSummary {...props} />);
226270

227271
expect(getByText('0 points')).toBeOnTheScreen();
228272
});
273+
274+
it('displays AddRewardsAccount when accountOptedIn is false', () => {
275+
const props = {
276+
...defaultProps,
277+
shouldShowRewardsRow: true,
278+
accountOptedIn: false,
279+
estimatedPoints: 100,
280+
};
281+
282+
const { getByTestId, queryByTestId } = render(
283+
<PredictFeeSummary {...props} />,
284+
);
285+
286+
expect(getByTestId('add-rewards-account')).toBeOnTheScreen();
287+
expect(queryByTestId('rewards-animation')).toBeNull();
288+
});
289+
290+
it('displays AddRewardsAccount when accountOptedIn is null', () => {
291+
const props = {
292+
...defaultProps,
293+
shouldShowRewardsRow: true,
294+
accountOptedIn: null,
295+
estimatedPoints: 100,
296+
};
297+
298+
const { getByTestId, queryByTestId } = render(
299+
<PredictFeeSummary {...props} />,
300+
);
301+
302+
expect(getByTestId('add-rewards-account')).toBeOnTheScreen();
303+
expect(queryByTestId('rewards-animation')).toBeNull();
304+
});
305+
306+
it('displays loading state when isLoadingRewards is true', () => {
307+
const props = {
308+
...defaultProps,
309+
shouldShowRewardsRow: true,
310+
accountOptedIn: true,
311+
estimatedPoints: 50,
312+
isLoadingRewards: true,
313+
};
314+
315+
const { getByTestId } = render(<PredictFeeSummary {...props} />);
316+
317+
const animation = getByTestId('rewards-animation');
318+
expect(animation).toBeOnTheScreen();
319+
expect(animation.props['data-state']).toBe('Loading');
320+
});
321+
322+
it('displays error state when hasRewardsError is true', () => {
323+
const props = {
324+
...defaultProps,
325+
shouldShowRewardsRow: true,
326+
accountOptedIn: true,
327+
estimatedPoints: 50,
328+
hasRewardsError: true,
329+
};
330+
331+
const { getByTestId } = render(<PredictFeeSummary {...props} />);
332+
333+
const animation = getByTestId('rewards-animation');
334+
expect(animation).toBeOnTheScreen();
335+
expect(animation.props['data-state']).toBe('ErrorState');
336+
});
337+
338+
it('displays idle state when not loading and no error', () => {
339+
const props = {
340+
...defaultProps,
341+
shouldShowRewardsRow: true,
342+
accountOptedIn: true,
343+
estimatedPoints: 50,
344+
isLoadingRewards: false,
345+
hasRewardsError: false,
346+
};
347+
348+
const { getByTestId } = render(<PredictFeeSummary {...props} />);
349+
350+
const animation = getByTestId('rewards-animation');
351+
expect(animation).toBeOnTheScreen();
352+
expect(animation.props['data-state']).toBe('Idle');
353+
});
354+
355+
it('displays error tooltip when hasRewardsError is true', () => {
356+
const props = {
357+
...defaultProps,
358+
shouldShowRewardsRow: true,
359+
accountOptedIn: true,
360+
estimatedPoints: 50,
361+
hasRewardsError: true,
362+
};
363+
364+
const { getByTestId } = render(<PredictFeeSummary {...props} />);
365+
366+
expect(getByTestId('value-tooltip')).toBeOnTheScreen();
367+
});
368+
369+
it('does not display error tooltip when hasRewardsError is false', () => {
370+
const props = {
371+
...defaultProps,
372+
shouldShowRewardsRow: true,
373+
accountOptedIn: true,
374+
estimatedPoints: 50,
375+
hasRewardsError: false,
376+
};
377+
378+
const { queryByTestId } = render(<PredictFeeSummary {...props} />);
379+
380+
expect(queryByTestId('value-tooltip')).toBeNull();
381+
});
382+
383+
it('handles null estimatedPoints when account is opted in', () => {
384+
const props = {
385+
...defaultProps,
386+
shouldShowRewardsRow: true,
387+
accountOptedIn: true,
388+
estimatedPoints: null,
389+
};
390+
391+
const { getByText } = render(<PredictFeeSummary {...props} />);
392+
393+
expect(getByText('0 points')).toBeOnTheScreen();
394+
});
229395
});
230396

231397
describe('Rewards Row Position', () => {
232398
it('renders rewards row after Total row', () => {
233399
const props = {
234400
...defaultProps,
235-
shouldShowRewards: true,
401+
shouldShowRewardsRow: true,
402+
accountOptedIn: true,
236403
estimatedPoints: 50,
237404
};
238405

app/components/UI/Predict/components/PredictFeeSummary/PredictFeeSummary.tsx

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,16 @@ import RewardsAnimations, {
2121
RewardAnimationState,
2222
} from '../../../Rewards/components/RewardPointsAnimation';
2323
import { formatPrice } from '../../utils/format';
24+
import AddRewardsAccount from '../../../Rewards/components/AddRewardsAccount/AddRewardsAccount';
2425

2526
interface PredictFeeSummaryProps {
2627
disabled: boolean;
2728
providerFee: number;
2829
metamaskFee: number;
2930
total: number;
30-
shouldShowRewards?: boolean;
31-
estimatedPoints?: number;
31+
shouldShowRewardsRow?: boolean;
32+
accountOptedIn?: boolean | null;
33+
estimatedPoints?: number | null;
3234
isLoadingRewards?: boolean;
3335
hasRewardsError?: boolean;
3436
onFeesInfoPress?: () => void;
@@ -39,7 +41,8 @@ const PredictFeeSummary: React.FC<PredictFeeSummaryProps> = ({
3941
metamaskFee,
4042
providerFee,
4143
total,
42-
shouldShowRewards = false,
44+
shouldShowRewardsRow = false,
45+
accountOptedIn = null,
4346
estimatedPoints = 0,
4447
isLoadingRewards = false,
4548
hasRewardsError = false,
@@ -87,7 +90,7 @@ const PredictFeeSummary: React.FC<PredictFeeSummaryProps> = ({
8790
</Box>
8891

8992
{/* Estimated Points Row */}
90-
{shouldShowRewards && (
93+
{shouldShowRewardsRow && (
9194
<KeyValueRow
9295
field={{
9396
label: {
@@ -112,16 +115,20 @@ const PredictFeeSummary: React.FC<PredictFeeSummaryProps> = ({
112115
justifyContent={BoxJustifyContent.Center}
113116
gap={1}
114117
>
115-
<RewardsAnimations
116-
value={estimatedPoints}
117-
state={
118-
isLoadingRewards
119-
? RewardAnimationState.Loading
120-
: hasRewardsError
121-
? RewardAnimationState.ErrorState
122-
: RewardAnimationState.Idle
123-
}
124-
/>
118+
{accountOptedIn ? (
119+
<RewardsAnimations
120+
value={estimatedPoints ?? 0}
121+
state={
122+
isLoadingRewards
123+
? RewardAnimationState.Loading
124+
: hasRewardsError
125+
? RewardAnimationState.ErrorState
126+
: RewardAnimationState.Idle
127+
}
128+
/>
129+
) : (
130+
<AddRewardsAccount />
131+
)}
125132
</Box>
126133
),
127134
...(hasRewardsError && {

0 commit comments

Comments
 (0)