Skip to content

Commit 2163cc9

Browse files
committed
Merge branch 'main' into mikesposito/feat/qr-keyring
2 parents e3eaec0 + 3527c21 commit 2163cc9

File tree

48 files changed

+1156
-386
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1156
-386
lines changed

.github/guidelines/LABELING_GUIDELINES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ The check can be bypassed when necessary, either by setting the changelog entry
1717

1818
## Optional labels (manual addition)
1919

20-
Any label can be manually added on demand depending on the PR's content. For instance, the label **QA passed** will indicate that a thorough manual testing has been performed and the PR is ready to be merged. In addition, following labels have some specific use cases.
20+
Any label can be manually added on demand depending on the PR's content. For instance, the label **QA passed** will indicate that a thorough manual testing has been performed and the PR is ready to be merged. In addition, following labels have some specific use cases.
2121

2222
### Run iOS E2E tests
2323

app/components/UI/Rewards/components/SeasonStatus/SeasonStatus.test.tsx

Lines changed: 101 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ const mockGetTimeDifferenceFromNow =
7676
typeof getTimeDifferenceFromNow
7777
>;
7878

79+
// Import types
80+
import { SeasonTierDto } from '../../../../../core/Engine/controllers/rewards-controller/types';
81+
7982
// Mock i18n
8083
jest.mock('../../../../../../locales/i18n', () => ({
8184
strings: jest.fn((key: string) => {
@@ -151,24 +154,6 @@ jest.mock('../../../../../component-library/components/Skeleton', () => ({
151154
},
152155
}));
153156

154-
// Mock SeasonTierImage
155-
jest.mock('../SeasonTierImage', () => {
156-
const ReactActual = jest.requireActual('react');
157-
const { View } = jest.requireActual('react-native');
158-
return ReactActual.forwardRef(
159-
(
160-
{ tierOrder, testID, ...props }: { tierOrder?: number; testID?: string },
161-
ref: unknown,
162-
) =>
163-
ReactActual.createElement(View, {
164-
testID: testID || 'season-tier-image',
165-
ref,
166-
'data-tier-order': tierOrder,
167-
...props,
168-
}),
169-
);
170-
});
171-
172157
// Mock lodash capitalize but preserve the rest of lodash
173158
jest.mock('lodash', () => {
174159
const actual = jest.requireActual('lodash');
@@ -178,6 +163,32 @@ jest.mock('lodash', () => {
178163
};
179164
});
180165

166+
// Mock RewardsThemeImageComponent
167+
jest.mock('../ThemeImageComponent', () => {
168+
const ReactActual = jest.requireActual('react');
169+
const { View } = jest.requireActual('react-native');
170+
return {
171+
__esModule: true,
172+
default: ReactActual.forwardRef(
173+
(
174+
props: {
175+
themeImage?: { lightModeUrl?: string; darkModeUrl?: string };
176+
style?: unknown;
177+
testID?: string;
178+
},
179+
ref: unknown,
180+
) =>
181+
ReactActual.createElement(View, {
182+
testID: props.testID || 'season-tier-image',
183+
'data-light-mode-url': props.themeImage?.lightModeUrl,
184+
'data-dark-mode-url': props.themeImage?.darkModeUrl,
185+
style: props.style,
186+
ref,
187+
}),
188+
),
189+
};
190+
});
191+
181192
describe('SeasonStatus', () => {
182193
// Default mock values
183194
const defaultMockValues = {
@@ -308,27 +319,6 @@ describe('SeasonStatus', () => {
308319
expect(getByTestId('metamask-rewards-points-svg')).toBeTruthy();
309320
});
310321

311-
it('should render correct tier order for different tiers', () => {
312-
// Test silver tier (2nd in array)
313-
mockSelectCurrentTier.mockReturnValue({
314-
id: 'silver',
315-
name: 'silver',
316-
pointsNeeded: 2000,
317-
image: {
318-
lightModeUrl: 'lightModeUrl',
319-
darkModeUrl: 'darkModeUrl',
320-
},
321-
levelNumber: 'Level 2',
322-
rewards: [],
323-
});
324-
325-
const { getByText, getByTestId } = render(<SeasonStatus />);
326-
327-
expect(getByText('Level 2')).toBeTruthy();
328-
expect(getByText('Silver')).toBeTruthy();
329-
expect(getByTestId('season-tier-image')).toHaveProp('data-tier-order', 2);
330-
});
331-
332322
it('should capitalize tier names correctly', () => {
333323
mockSelectCurrentTier.mockReturnValue({
334324
id: 'gold',
@@ -539,6 +529,78 @@ describe('SeasonStatus', () => {
539529
});
540530
});
541531

532+
describe('RewardsThemeImageComponent Integration', () => {
533+
it('should render RewardsThemeImageComponent with correct testID when tier has image', () => {
534+
const { getByTestId } = render(<SeasonStatus />);
535+
536+
expect(getByTestId('season-tier-image')).toBeTruthy();
537+
});
538+
539+
it('should pass correct themeImage prop to RewardsThemeImageComponent', () => {
540+
const { getByTestId } = render(<SeasonStatus />);
541+
542+
const tierImage = getByTestId('season-tier-image');
543+
expect(tierImage.props['data-light-mode-url']).toBe('lightModeUrl');
544+
expect(tierImage.props['data-dark-mode-url']).toBe('darkModeUrl');
545+
});
546+
547+
it('should pass correct style prop to RewardsThemeImageComponent', () => {
548+
const { getByTestId } = render(<SeasonStatus />);
549+
550+
const tierImage = getByTestId('season-tier-image');
551+
expect(tierImage.props.style).toBeDefined();
552+
});
553+
554+
it('should render fallback Image when tier has no image', () => {
555+
// Given: tier without image
556+
mockSelectCurrentTier.mockReturnValue({
557+
id: 'bronze',
558+
name: 'bronze',
559+
pointsNeeded: 0,
560+
image: undefined,
561+
levelNumber: 'Level 1',
562+
rewards: [],
563+
} as unknown as SeasonTierDto);
564+
565+
const { queryByTestId } = render(<SeasonStatus />);
566+
567+
// RewardsThemeImageComponent should not be rendered
568+
expect(queryByTestId('season-tier-image')).toBeNull();
569+
});
570+
571+
it('should render RewardsThemeImageComponent with updated image when tier changes', () => {
572+
// Given: initial tier with image
573+
const { getByTestId, rerender } = render(<SeasonStatus />);
574+
const initialTierImage = getByTestId('season-tier-image');
575+
expect(initialTierImage.props['data-light-mode-url']).toBe(
576+
'lightModeUrl',
577+
);
578+
579+
// When: tier changes to different image URLs
580+
mockSelectCurrentTier.mockReturnValue({
581+
id: 'silver',
582+
name: 'silver',
583+
pointsNeeded: 2000,
584+
image: {
585+
lightModeUrl: 'newLightModeUrl',
586+
darkModeUrl: 'newDarkModeUrl',
587+
},
588+
levelNumber: 'Level 2',
589+
rewards: [],
590+
});
591+
rerender(<SeasonStatus />);
592+
593+
// Then: new image URLs are used
594+
const updatedTierImage = getByTestId('season-tier-image');
595+
expect(updatedTierImage.props['data-light-mode-url']).toBe(
596+
'newLightModeUrl',
597+
);
598+
expect(updatedTierImage.props['data-dark-mode-url']).toBe(
599+
'newDarkModeUrl',
600+
);
601+
});
602+
});
603+
542604
describe('Memoized Values', () => {
543605
it('should update displayed points when balance changes', () => {
544606
// Given: initial balance of 1500

app/components/UI/Rewards/components/SeasonStatus/SeasonStatus.tsx

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { strings } from '../../../../../../locales/i18n';
1212
import { useTheme } from '../../../../../util/theme';
1313
import MetamaskRewardsPointsImage from '../../../../../images/rewards/metamask-rewards-points.svg';
1414
import { Skeleton } from '../../../../../component-library/components/Skeleton';
15-
import SeasonTierImage from '../SeasonTierImage';
1615
import { capitalize } from 'lodash';
1716
import { useSelector } from 'react-redux';
1817
import {
@@ -26,6 +25,9 @@ import {
2625
} from '../../../../../reducers/rewards/selectors';
2726
import { formatNumber, formatTimeRemaining } from '../../utils/formatUtils';
2827
import { useTailwind } from '@metamask/design-system-twrnc-preset';
28+
import RewardsThemeImageComponent from '../ThemeImageComponent';
29+
import { Image } from 'react-native';
30+
import fallbackTierImage from '../../../../../images/rewards/tiers/rewards-s1-tier-1.png';
2931

3032
const SeasonStatus: React.FC = () => {
3133
const tw = useTailwind();
@@ -75,29 +77,31 @@ const SeasonStatus: React.FC = () => {
7577
return tiers.findIndex((tier) => tier.id === currentTier.id) + 1;
7678
}, [tiers, currentTier]);
7779

78-
if (seasonStatusLoading) {
80+
if (seasonStatusLoading || !currentTier) {
7981
return <Skeleton height={115} width="100%" />;
8082
}
8183

8284
return (
8385
<Box flexDirection={BoxFlexDirection.Column} twClassName="gap-4 w-full">
8486
{/* Top Row - season name, tier name, and tier image */}
85-
<Box twClassName="flex-row justify-between items-center">
86-
<Box flexDirection={BoxFlexDirection.Row} twClassName="gap-4">
87+
<Box twClassName="flex-row justify-between items-center -mb-2">
88+
<Box
89+
flexDirection={BoxFlexDirection.Row}
90+
twClassName="gap-4 items-center"
91+
>
8792
{/* Tier image */}
88-
<Box twClassName="h-[42px] w-[55px] flex align-center">
89-
<SeasonTierImage
90-
tierOrder={currentTierOrder}
91-
twClassName="w-full h-full"
93+
{currentTier?.image ? (
94+
<RewardsThemeImageComponent
95+
themeImage={currentTier.image}
96+
style={tw.style('h-15 w-15')}
9297
/>
93-
</Box>
98+
) : (
99+
<Image source={fallbackTierImage} style={tw.style('h-15 w-15')} />
100+
)}
94101

95102
{/* Tier name */}
96103
<Box flexDirection={BoxFlexDirection.Column}>
97-
<Text
98-
variant={TextVariant.BodySm}
99-
twClassName="text-alternative -mb-1"
100-
>
104+
<Text variant={TextVariant.BodySm} twClassName="text-alternative">
101105
{strings('rewards.level')} {currentTierOrder}
102106
</Text>
103107
<Text variant={TextVariant.BodyMd} twClassName="text-default">
@@ -109,10 +113,7 @@ const SeasonStatus: React.FC = () => {
109113
{/* Season ends */}
110114
{!!seasonEndDate && !!timeRemaining && (
111115
<Box flexDirection={BoxFlexDirection.Column}>
112-
<Text
113-
variant={TextVariant.BodySm}
114-
twClassName="text-alternative -mb-1"
115-
>
116+
<Text variant={TextVariant.BodySm} twClassName="text-alternative">
116117
{strings('rewards.season_ends')}
117118
</Text>
118119
<Text
@@ -166,7 +167,7 @@ const SeasonStatus: React.FC = () => {
166167
{/* Bottom Row - Points Summary */}
167168
<Box
168169
flexDirection={BoxFlexDirection.Row}
169-
twClassName="gap-2 justify-between items-center mt-1"
170+
twClassName="gap-2 justify-between items-center"
170171
>
171172
<Box
172173
alignItems={BoxAlignItems.Center}

app/components/UI/Rewards/components/SeasonTierImage/SeasonTierImage.test.tsx

Lines changed: 0 additions & 121 deletions
This file was deleted.

0 commit comments

Comments
 (0)