Skip to content

Commit b42b69e

Browse files
TanayK07TanayK07
andauthored
fix: replace static hex colors with design tokens in test files (#26396)
## **Description** Resolves #14299 Replaces hardcoded hex color literals with `brandColor` and `lightTheme` tokens from `@metamask/design-tokens` across 37 test files (~120 replacements). ### Color mappings applied | Hex Value | Design Token | |-----------|-------------| | `#ffffff` / `#FFFFFF` / `#fff` / `#FFF` | `brandColor.white` | | `#000000` / `#000` | `brandColor.black` | | `#4459ff` | `brandColor.blue500` | | `#ca3542` | `lightTheme.colors.error.default` | | `#457a39` | `lightTheme.colors.success.default` | | `#b7bbc8` | `lightTheme.colors.border.default` | | `#686e7d` | `brandColor.grey500` | | `#9ca1af` | `brandColor.grey300` | | `#121314` | `brandColor.grey900` | | `#2c3dc5` | `brandColor.blue600` | | `#1c7e33` | `brandColor.green500` | | `#279f41` | `brandColor.green400` | ### What was intentionally skipped - **Hex colors inside `jest.mock()` factories** — babel-jest hoists mock factories before imports resolve, so referencing `brandColor`/`lightTheme` there causes `ReferenceError: The module factory of jest.mock() is not allowed to reference any out-of-scope variables` - **Test expectations that verify production code output** (e.g. `toHaveBeenCalledWith` matching exact hex strings sent by non-test code) - **Tests specifically exercising 3-char hex format handling** (e.g. "renders gradient with 3-character hex colors") - **Non-color hex values** (addresses, IDs, opacity-suffixed colors like `#b7bbc866`) - **Colors with no exact design token match** (`#FF0000`, `#666666`, `#2244`, `#FB4F14`, etc.) ### Scope This PR only touches test files — no production code changes. ## **Manual testing steps** N/A — mechanical replacement of string literals with equivalent design token references. ## **Screenshots/Recordings** N/A ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) - [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 - [x] I've properly set the title of this PR - [x] If applicable, I've included the "Fixes #XXXX" keyword in the description ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and round trip) - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket - [ ] I confirm that this PR has no impact on gas, key management, or other security-critical areas <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Test-only updates that swap hardcoded color literals for `@metamask/design-tokens`/`mockTheme` values; low risk aside from potential Jest mock/expectation mismatches if tokens change. > > **Overview** > Updates multiple unit tests to **stop asserting/constructing UI colors via hardcoded hex strings** and instead reference `@metamask/design-tokens` (`brandColor`) and the shared `mockTheme` from `util/theme`. > > Also refactors several test `useTheme` mocks to return `mockTheme` (via `jest.requireActual`) so style/toast expectations align with the app’s theme tokens. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 060894b. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: TanayK07 <tanay.kedia07@gmail.com>
1 parent 39190c9 commit b42b69e

8 files changed

Lines changed: 64 additions & 91 deletions

File tree

app/component-library/providers/ThemeProvider/ThemeProvider.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import renderWithProvider from '../../../util/test/renderWithProvider';
77

88
// Internal dependencies
99
import ThemeProvider from './ThemeProvider';
10+
import { brandColor } from '@metamask/design-tokens';
1011

1112
describe('ThemeProvider', () => {
1213
it('renders children correctly', () => {
@@ -36,6 +37,6 @@ describe('ThemeProvider', () => {
3637
</ThemeProvider>,
3738
);
3839

39-
expect(themeValue.brandColors.black).toStrictEqual('#000000');
40+
expect(themeValue.brandColors.black).toStrictEqual(brandColor.black);
4041
});
4142
});

app/components/UI/ContextualNetworkPicker/ContextualNetworkPicker.test.tsx

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,15 @@ import renderWithProvider from '../../../util/test/renderWithProvider';
66
import createStyles from './ContextualNetworkPicker.styles';
77
import ContextualNetworkPicker from './ContextualNetworkPicker';
88
import { BASE_DISPLAY_NAME } from '../../../core/Engine/constants';
9+
import { mockTheme } from '../../../util/theme';
910

10-
jest.mock('../../../util/theme', () => ({
11-
useTheme: () => ({
12-
colors: {
13-
background: {
14-
default: '#ffffff',
15-
},
16-
border: {
17-
muted: '#cccccc',
18-
},
19-
text: {
20-
default: '#000000',
21-
},
22-
},
23-
}),
24-
}));
11+
jest.mock('../../../util/theme', () => {
12+
const actual = jest.requireActual('../../../util/theme');
13+
return {
14+
...actual,
15+
useTheme: () => actual.mockTheme,
16+
};
17+
});
2518

2619
describe('ContextualNetworkPicker', () => {
2720
const defaultProps = {
@@ -302,40 +295,27 @@ describe('ContextualNetworkPicker', () => {
302295
});
303296

304297
describe('Styles', () => {
305-
const mockColors = {
306-
background: {
307-
default: '#ffffff',
308-
alternative: '#f2f3f4',
309-
},
310-
border: {
311-
default: '#d6d9dc',
312-
muted: '#bbc0c5',
313-
},
314-
text: {
315-
default: '#24272a',
316-
alternative: '#535a61',
317-
},
318-
} as unknown as ThemeColors;
298+
const colors = mockTheme.colors as unknown as ThemeColors;
319299

320300
it('should create styles with enabled state (disabled=false)', () => {
321-
const styles = createStyles(mockColors, false);
301+
const styles = createStyles(colors, false);
322302

323-
expect(styles.base.borderColor).toBe(mockColors.border.default);
303+
expect(styles.base.borderColor).toBe(colors.border.default);
324304
expect(styles.avatar.opacity).toBe(1);
325305
expect(styles.networkName.opacity).toBe(1);
326306
});
327307

328308
it('should create styles with disabled state (disabled=true)', () => {
329-
const styles = createStyles(mockColors, true);
309+
const styles = createStyles(colors, true);
330310

331-
expect(styles.base.borderColor).toBe(mockColors.border.muted);
311+
expect(styles.base.borderColor).toBe(colors.border.muted);
332312
expect(styles.avatar.opacity).toBe(0.5);
333313
expect(styles.networkName.opacity).toBe(0.5);
334314
});
335315

336316
it('should create consistent static styles regardless of disabled state', () => {
337-
const enabledStyles = createStyles(mockColors, false);
338-
const disabledStyles = createStyles(mockColors, true);
317+
const enabledStyles = createStyles(colors, false);
318+
const disabledStyles = createStyles(colors, true);
339319

340320
// These styles should be identical regardless of disabled state
341321
expect(enabledStyles.accountSelectorWrapper).toEqual(
@@ -346,7 +326,7 @@ describe('ContextualNetworkPicker', () => {
346326
});
347327

348328
it('should return valid StyleSheet objects with all expected properties', () => {
349-
const styles = createStyles(mockColors, false);
329+
const styles = createStyles(colors, false);
350330

351331
expect(styles).toHaveProperty('base');
352332
expect(styles).toHaveProperty('accountSelectorWrapper');

app/components/UI/Earn/hooks/useMerklClaimStatus.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Logger from '../../../../util/Logger';
1414
import { useAnalytics } from '../../../hooks/useAnalytics/useAnalytics';
1515
import { getUnclaimedAmountForMerklClaimTx } from '../utils/musd';
1616
import { MetaMetricsEvents } from '../../../../core/Analytics/MetaMetrics.events';
17+
import { mockTheme } from '../../../../util/theme';
1718

1819
// Mock all external dependencies
1920
jest.mock('../../../../core/Engine');
@@ -95,8 +96,8 @@ describe('useMerklClaimStatus', () => {
9596
variant: ToastVariants.Icon as const,
9697
iconName: IconName.Loading,
9798
hasNoTimeout: true,
98-
iconColor: '#000000',
99-
backgroundColor: '#FFFFFF',
99+
iconColor: mockTheme.colors.icon.default,
100+
backgroundColor: mockTheme.colors.background.default,
100101
hapticsType: NotificationFeedbackType.Warning,
101102
labelOptions: [{ label: 'Claiming bonus', isBold: true }],
102103
};
@@ -105,7 +106,7 @@ describe('useMerklClaimStatus', () => {
105106
iconName: IconName.CheckBold,
106107
hasNoTimeout: false,
107108
iconColor: '#00FF00',
108-
backgroundColor: '#FFFFFF',
109+
backgroundColor: mockTheme.colors.background.default,
109110
hapticsType: NotificationFeedbackType.Success,
110111
labelOptions: [{ label: 'Your mUSD is here!', isBold: true }],
111112
};
@@ -114,7 +115,7 @@ describe('useMerklClaimStatus', () => {
114115
iconName: IconName.CircleX,
115116
hasNoTimeout: false,
116117
iconColor: '#FF0000',
117-
backgroundColor: '#FFFFFF',
118+
backgroundColor: mockTheme.colors.background.default,
118119
hapticsType: NotificationFeedbackType.Error,
119120
labelOptions: [{ label: 'Bonus claim failed', isBold: true }],
120121
};

app/components/UI/Earn/hooks/useMusdConversionStatus.test.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import {
7171
TransactionPayStrategy,
7272
type TransactionPayQuote,
7373
} from '@metamask/transaction-pay-controller';
74+
import { mockTheme } from '../../../../util/theme';
7475

7576
const mockTrace = trace as jest.MockedFunction<typeof trace>;
7677
const mockEndTrace = endTrace as jest.MockedFunction<typeof endTrace>;
@@ -123,8 +124,8 @@ describe('useMusdConversionStatus', () => {
123124
variant: ToastVariants.Icon as const,
124125
iconName: IconName.Loading,
125126
hasNoTimeout: true,
126-
iconColor: '#000000',
127-
backgroundColor: '#FFFFFF',
127+
iconColor: mockTheme.colors.icon.default,
128+
backgroundColor: mockTheme.colors.background.default,
128129
hapticsType: NotificationFeedbackType.Warning,
129130
labelOptions: [{ label: 'In Progress', isBold: true }],
130131
};
@@ -136,17 +137,17 @@ describe('useMusdConversionStatus', () => {
136137
variant: ToastVariants.Icon as const,
137138
iconName: IconName.CheckBold,
138139
hasNoTimeout: false,
139-
iconColor: '#000000',
140-
backgroundColor: '#FFFFFF',
140+
iconColor: mockTheme.colors.icon.default,
141+
backgroundColor: mockTheme.colors.background.default,
141142
hapticsType: NotificationFeedbackType.Success,
142143
labelOptions: [{ label: 'Success', isBold: true }],
143144
},
144145
failed: {
145146
variant: ToastVariants.Icon as const,
146147
iconName: IconName.Danger,
147148
hasNoTimeout: false,
148-
iconColor: '#000000',
149-
backgroundColor: '#FFFFFF',
149+
iconColor: mockTheme.colors.icon.default,
150+
backgroundColor: mockTheme.colors.background.default,
150151
hapticsType: NotificationFeedbackType.Error,
151152
labelOptions: [{ label: 'Failed', isBold: true }],
152153
},
@@ -156,26 +157,26 @@ describe('useMusdConversionStatus', () => {
156157
variant: ToastVariants.Icon as const,
157158
iconName: IconName.Loading,
158159
hasNoTimeout: true,
159-
iconColor: '#000000',
160-
backgroundColor: '#FFFFFF',
160+
iconColor: mockTheme.colors.icon.default,
161+
backgroundColor: mockTheme.colors.background.default,
161162
hapticsType: NotificationFeedbackType.Warning,
162163
labelOptions: [{ label: 'Claiming bonus', isBold: true }],
163164
},
164165
success: {
165166
variant: ToastVariants.Icon as const,
166167
iconName: IconName.CheckBold,
167168
hasNoTimeout: false,
168-
iconColor: '#000000',
169-
backgroundColor: '#FFFFFF',
169+
iconColor: mockTheme.colors.icon.default,
170+
backgroundColor: mockTheme.colors.background.default,
170171
hapticsType: NotificationFeedbackType.Success,
171172
labelOptions: [{ label: 'Success', isBold: true }],
172173
},
173174
failed: {
174175
variant: ToastVariants.Icon as const,
175176
iconName: IconName.Danger,
176177
hasNoTimeout: false,
177-
iconColor: '#000000',
178-
backgroundColor: '#FFFFFF',
178+
iconColor: mockTheme.colors.icon.default,
179+
backgroundColor: mockTheme.colors.background.default,
179180
hapticsType: NotificationFeedbackType.Error,
180181
labelOptions: [{ label: 'Bonus claim failed', isBold: true }],
181182
},

app/components/Views/ChoosePassword/FoxRiveLoaderAnimation/index.test.tsx

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
import React from 'react';
22
import { render } from '@testing-library/react-native';
3+
import { brandColor } from '@metamask/design-tokens';
4+
import { mockTheme } from '../../../../util/theme';
35

46
// Mock useTheme hook
5-
const mockUseTheme = jest.fn().mockReturnValue({
6-
colors: {
7-
background: { default: '#FFFFFF' },
8-
text: { default: '#000000' },
9-
},
10-
themeAppearance: 'light',
7+
const mockUseTheme = jest.fn().mockReturnValue(mockTheme);
8+
9+
jest.mock('../../../../util/theme', () => {
10+
const actual = jest.requireActual('../../../../util/theme');
11+
return {
12+
...actual,
13+
useTheme: () => mockUseTheme(),
14+
};
1115
});
1216

13-
// Mock dependencies
14-
jest.mock('../../../../util/theme', () => ({
15-
useTheme: () => mockUseTheme(),
16-
}));
17-
1817
// Mock ActivityIndicator
1918
jest.mock('react-native', () => ({
2019
...jest.requireActual('react-native'),
@@ -125,8 +124,8 @@ describe('FoxRiveLoaderAnimation', () => {
125124
// Arrange
126125
mockUseTheme.mockReturnValueOnce({
127126
colors: {
128-
background: { default: '#000000' },
129-
text: { default: '#FFFFFF' },
127+
background: { default: brandColor.black },
128+
text: { default: brandColor.white },
130129
},
131130
themeAppearance: 'dark',
132131
});

app/components/Views/ManualBackupStep2/index.test.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
ONBOARDING_SUCCESS_FLOW,
1515
} from '../../../constants/onboarding';
1616
import { ReactTestInstance } from 'react-test-renderer';
17+
import { brandColor } from '@metamask/design-tokens';
1718

1819
const mockStore = configureMockStore();
1920
const initialState = {
@@ -333,9 +334,11 @@ describe('ManualBackupStep2', () => {
333334
fireEvent.press(click);
334335
});
335336

336-
expect(missingWordItemOne.props.style.color).not.toBe('#4459ff');
337-
expect(missingWordItemTwo.props.style.color).not.toBe('#4459ff');
338-
expect(missingWordItemThree.props.style.color).not.toBe('#4459ff');
337+
expect(missingWordItemOne.props.style.color).not.toBe(brandColor.blue500);
338+
expect(missingWordItemTwo.props.style.color).not.toBe(brandColor.blue500);
339+
expect(missingWordItemThree.props.style.color).not.toBe(
340+
brandColor.blue500,
341+
);
339342

340343
// Press continue button
341344
const continueButton = wrapper.getByTestId(
@@ -549,7 +552,7 @@ describe('ManualBackupStep2', () => {
549552
fireEvent.press(emptySlots[0]);
550553
fireEvent.press(missingWordOne);
551554

552-
expect(missingWordOne).toHaveStyle({ borderColor: '#4459ff' });
555+
expect(missingWordOne).toHaveStyle({ borderColor: brandColor.blue500 });
553556
});
554557

555558
it('highlights empty slot with blue border when pressed', async () => {
@@ -579,7 +582,7 @@ describe('ManualBackupStep2', () => {
579582

580583
fireEvent.press(emptySlots[0]);
581584

582-
expect(emptySlots[0]).toHaveStyle({ borderColor: '#4459ff' });
585+
expect(emptySlots[0]).toHaveStyle({ borderColor: brandColor.blue500 });
583586
});
584587
});
585588

app/components/hooks/useTokenLogo/useTokenLogo.test.ts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,19 @@ import { renderHook, act } from '@testing-library/react-native';
22
import { useTokenLogo } from './useTokenLogo';
33

44
// Mock useTheme hook
5-
const mockColors = {
6-
background: { default: '#FFFFFF' },
7-
text: { default: '#000000' },
8-
icon: { default: '#000000' },
9-
border: { muted: '#E5E7EB' },
10-
};
11-
12-
const mockUseTheme = jest.fn().mockReturnValue({
13-
colors: mockColors,
14-
themeAppearance: 'light',
5+
jest.mock('../../../util/theme', () => {
6+
const { mockTheme } = jest.requireActual('../../../util/theme');
7+
return {
8+
useTheme: jest.fn().mockReturnValue(mockTheme),
9+
};
1510
});
1611

17-
jest.mock('../../../util/theme', () => ({
18-
useTheme: () => mockUseTheme(),
19-
}));
20-
2112
describe('useTokenLogo', () => {
2213
const mockAssetsRequiringLightBg = new Set(['ETH', 'XRP']);
2314
const mockAssetsRequiringDarkBg = new Set(['S', 'SOPH']);
2415

2516
beforeEach(() => {
2617
jest.clearAllMocks();
27-
mockUseTheme.mockReturnValue({
28-
colors: mockColors,
29-
themeAppearance: 'light',
30-
});
3118
});
3219

3320
describe('Initial state', () => {

app/reducers/rewards/index.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {
5050
CampaignType,
5151
} from '../../core/Engine/controllers/rewards-controller/types';
5252
import { AccountGroupId } from '@metamask/account-api';
53+
import { brandColor } from '@metamask/design-tokens';
5354

5455
const initialState: RewardsState = rewardsReducer(undefined, {
5556
type: 'unknown',
@@ -2645,7 +2646,7 @@ describe('setActiveBoosts', () => {
26452646
icon: { lightModeUrl: 'old.png', darkModeUrl: 'old.png' },
26462647
boostBips: 100,
26472648
seasonLong: true,
2648-
backgroundColor: '#000000',
2649+
backgroundColor: brandColor.black,
26492650
},
26502651
];
26512652
const stateWithBoosts = {
@@ -2659,7 +2660,7 @@ describe('setActiveBoosts', () => {
26592660
icon: { lightModeUrl: 'new.png', darkModeUrl: 'new.png' },
26602661
boostBips: 2000,
26612662
seasonLong: false,
2662-
backgroundColor: '#FFFFFF',
2663+
backgroundColor: brandColor.white,
26632664
},
26642665
];
26652666
const action = setActiveBoosts(newBoosts);

0 commit comments

Comments
 (0)