Skip to content

Commit

Permalink
fix: hide amount in simulations for testnets if its opt out (#9918)
Browse files Browse the repository at this point in the history
## **Description**


This PR aims to hide testnet fiat values if user opt out in the
settings.

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25167?quickstart=1)

## **Related issues**

- Fixes: #9906

## **Manual testing steps**

1. Change current network to any testnet
2. Go to setting, turn on "Show conversion on test networks"
3. Try simple send - see fiat values displayed in simulations
4. Go to setting, turn off "Show conversion on test networks"
5. Try simple send - see no fiat values displayed in simulations
6. Change current network to mainnet or any other network which is not
testnet
7. Try simple send - see fiat values displayed in simulations


## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [X] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.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-extension/blob/develop/.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.
  • Loading branch information
OGPoyraz authored Jun 20, 2024
1 parent 3864504 commit 08a7949
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 30 deletions.
114 changes: 85 additions & 29 deletions app/components/UI/SimulationDetails/FiatDisplay/FiatDisplay.test.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,102 @@
import React from 'react';
import useFiatFormatter from './useFiatFormatter';

import { merge } from 'lodash';
import renderWithProvider from '../../../../util/test/renderWithProvider';
import initialBackgroundState from '../../../../util/test/initial-background-state.json';

import { IndividualFiatDisplay, TotalFiatDisplay } from './FiatDisplay';
import { FIAT_UNAVAILABLE } from '../types';
import { IndividualFiatDisplay, TotalFiatDisplay } from './FiatDisplay';
import useFiatFormatter from './useFiatFormatter';
import { NETWORKS_CHAIN_ID } from '../../../../constants/network';

jest.mock('./useFiatFormatter');
(useFiatFormatter as jest.Mock).mockReturnValue((value: number) => `$${value}`);

const mockInitialState = {
engine: {
backgroundState: initialBackgroundState,
},
};

describe('IndividualFiatDisplay', () => {
it.each([
[FIAT_UNAVAILABLE, 'Not Available'],
[100, '$100'],
[-100, '$100'],
])('when fiatAmount is %s it renders %s', (fiatAmount, expected) => {
const { getByText } = renderWithProvider(
<IndividualFiatDisplay fiatAmount={fiatAmount} />,
{ state: mockInitialState },
);
expect(getByText(expected)).toBeDefined();
});
const mockStateWithTestnet = merge({}, mockInitialState, {
engine: {
backgroundState: {
NetworkController: {
providerConfig: {
chainId: NETWORKS_CHAIN_ID.SEPOLIA,
},
},
},
},
});

const mockStateWithShowingFiatOnTestnets = merge({}, mockStateWithTestnet, {
engine: {
backgroundState: {
PreferencesController: {
showFiatInTestnets: true,
},
},
},
});

const mockStateWithHidingFiatOnTestnets = merge({}, mockStateWithTestnet, {
engine: {
backgroundState: {
PreferencesController: {
showFiatInTestnets: false,
},
},
},
});

describe('TotalFiatDisplay', () => {
it.each([
[[FIAT_UNAVAILABLE, FIAT_UNAVAILABLE], 'Not Available'],
[[], 'Not Available'],
[[100, 200, FIAT_UNAVAILABLE, 300], 'Total = $600'],
[[-100, -200, FIAT_UNAVAILABLE, -300], 'Total = $600'],
])('when fiatAmounts is %s it renders %s', (fiatAmounts, expected) => {
const { getByText } = renderWithProvider(
<TotalFiatDisplay fiatAmounts={fiatAmounts} />,
{ state: mockInitialState },
);
expect(getByText(expected)).toBeDefined();
describe('FiatDisplay', () => {
const mockUseFiatFormatter = jest.mocked(useFiatFormatter);

beforeEach(() => {
jest.resetAllMocks();
mockUseFiatFormatter.mockReturnValue((value: number) => `$${value}`);
});

describe('IndividualFiatDisplay', () => {
it.each([
[FIAT_UNAVAILABLE, 'Not Available'],
[100, '$100'],
[-100, '$100'],
])('when fiatAmount is %s it renders %s', (fiatAmount, expected) => {
const { queryByText } = renderWithProvider(
<IndividualFiatDisplay fiatAmount={fiatAmount} />,
{ state: mockStateWithShowingFiatOnTestnets },
);
expect(queryByText(expected)).toBeDefined();
});

it('does not render anything if hideFiatForTestnet is true', () => {
const { queryByText } = renderWithProvider(
<IndividualFiatDisplay fiatAmount={100} />,
{ state: mockStateWithHidingFiatOnTestnets },
);
expect(queryByText('100')).toBe(null);
});
});

describe('TotalFiatDisplay', () => {
it.each([
[[FIAT_UNAVAILABLE, FIAT_UNAVAILABLE], 'Not Available'],
[[], 'Not Available'],
[[100, 200, FIAT_UNAVAILABLE, 300], 'Total = $600'],
[[-100, -200, FIAT_UNAVAILABLE, -300], 'Total = $600'],
])('when fiatAmounts is %s it renders %s', (fiatAmounts, expected) => {
const { queryByText } = renderWithProvider(
<TotalFiatDisplay fiatAmounts={fiatAmounts} />,
{ state: mockStateWithShowingFiatOnTestnets },
);
expect(queryByText(expected)).toBeDefined();
});

it('does not render anything if hideFiatForTestnet is true', () => {
const { queryByText } = renderWithProvider(
<TotalFiatDisplay fiatAmounts={[100, 200, 300]} />,
{ state: mockStateWithHidingFiatOnTestnets },
);
expect(queryByText('600')).toBe(null);
});
});
});
11 changes: 11 additions & 0 deletions app/components/UI/SimulationDetails/FiatDisplay/FiatDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Text, {
import { strings } from '../../../../../locales/i18n';
import useFiatFormatter from './useFiatFormatter';
import { FIAT_UNAVAILABLE, FiatAmount } from '../types';
import useHideFiatForTestnet from '../../../hooks/useHideFiatForTestnet';

const styleSheet = () =>
StyleSheet.create({
Expand Down Expand Up @@ -48,9 +49,14 @@ export function calculateTotalFiat(fiatAmounts: FiatAmount[]): number {
export const IndividualFiatDisplay: React.FC<{ fiatAmount: FiatAmount }> = ({
fiatAmount,
}) => {
const hideFiatForTestnet = useHideFiatForTestnet();
const { styles } = useStyles(styleSheet, {});
const fiatFormatter = useFiatFormatter();

if (hideFiatForTestnet) {
return null;
}

if (fiatAmount === FIAT_UNAVAILABLE) {
return <FiatNotAvailableDisplay />;
}
Expand All @@ -72,10 +78,15 @@ export const IndividualFiatDisplay: React.FC<{ fiatAmount: FiatAmount }> = ({
export const TotalFiatDisplay: React.FC<{
fiatAmounts: FiatAmount[];
}> = ({ fiatAmounts }) => {
const hideFiatForTestnet = useHideFiatForTestnet();
const { styles } = useStyles(styleSheet, {});
const fiatFormatter = useFiatFormatter();
const totalFiat = calculateTotalFiat(fiatAmounts);

if (hideFiatForTestnet) {
return null;
}

return totalFiat === 0 ? (
<FiatNotAvailableDisplay />
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const ERC721_TOKEN_MOCK = '0x06012c8cf97bead5deae237070f9587f8e7a266d'; // Crypt
const ERC1155_TOKEN_MOCK = '0x60e4d786628fea6478f785a6d7e704777c86a7c6'; // MAYC

const preloadedEngineState = {
settings: { useBlockieIcon: false },
settings: { useBlockieIcon: false, showFiatInTestnets: true },
engine: {
backgroundState: {
PreferencesController: {
Expand Down
72 changes: 72 additions & 0 deletions app/components/hooks/useHideFiatForTestnet/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { renderHook } from '@testing-library/react-hooks';
import { selectChainId } from '../../../selectors/networkController';
import { TEST_NETWORK_IDS } from '../../../constants/network';
import selectShowFiatInTestnets from '../../../selectors/settings';
import useHideFiatForTestnet from './index';

jest.mock('react-redux', () => ({
useSelector: jest.fn().mockImplementation((selector) => selector()),
}));

jest.mock('../../../selectors/networkController', () => ({
selectChainId: jest.fn(),
}));

jest.mock('../../../selectors/settings', () => ({
__esModule: true,
default: jest.fn(),
}));

describe('useHideFiatForTestnet', () => {
const mockSelectShowFiatInTestnets = jest.mocked(selectShowFiatInTestnets);
const mockSelectChainId = jest.mocked(selectChainId);

beforeEach(() => {
jest.clearAllMocks();
});

it('utilizes the specified chain id', () => {
mockSelectShowFiatInTestnets.mockReturnValue(false);
mockSelectChainId.mockReturnValue(TEST_NETWORK_IDS[0]);

const { result } = renderHook(() => useHideFiatForTestnet('0x1'));

expect(result.current).toBe(false);
});

it('returns true if current network is a testnet and showFiatInTestnets is false', () => {
mockSelectShowFiatInTestnets.mockReturnValue(false);
mockSelectChainId.mockReturnValue(TEST_NETWORK_IDS[0]);

const { result } = renderHook(() => useHideFiatForTestnet());

expect(result.current).toBe(true);
});

it('returns false if current network is a testnet and showFiatInTestnets is true', () => {
mockSelectShowFiatInTestnets.mockReturnValue(true);
mockSelectChainId.mockReturnValue(TEST_NETWORK_IDS[0]);

const { result } = renderHook(() => useHideFiatForTestnet());

expect(result.current).toBe(false);
});

it('returns false if current network is not a testnet', () => {
mockSelectShowFiatInTestnets.mockReturnValue(false);
mockSelectChainId.mockReturnValue('0x1');

const { result } = renderHook(() => useHideFiatForTestnet());

expect(result.current).toBe(false);
});

it('returns false if current network is not a testnet but showFiatInTestnets is true', () => {
mockSelectShowFiatInTestnets.mockReturnValue(true);
mockSelectChainId.mockReturnValue('0x1');

const { result } = renderHook(() => useHideFiatForTestnet());

expect(result.current).toBe(false);
});
});
18 changes: 18 additions & 0 deletions app/components/hooks/useHideFiatForTestnet/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useSelector } from 'react-redux';
import type { Hex } from '@metamask/utils';
import { selectChainId } from '../../../selectors/networkController';
import selectShowFiatInTestnets from '../../../selectors/settings';
import { TEST_NETWORK_IDS } from '../../../constants/network';

/**
* Returns true if the fiat value should be hidden for testnet networks.
*
* @param providedChainId - Optional chainId to use for the check
* @returns boolean
*/
export default function useHideFiatForTestnet(providedChainId?: Hex): boolean {
const showFiatInTestnets = useSelector(selectShowFiatInTestnets);
const selectedChainId = useSelector(selectChainId);
const chainId = providedChainId ?? selectedChainId;
return TEST_NETWORK_IDS.includes(chainId) && !showFiatInTestnets;
}
7 changes: 7 additions & 0 deletions app/constants/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,10 @@ export const CURRENCY_SYMBOL_BY_CHAIN_ID = {
CHAINLIST_CURRENCY_SYMBOLS_MAP.LINEA_MAINNET,
[NETWORKS_CHAIN_ID.ZKSYNC_ERA]: CHAINLIST_CURRENCY_SYMBOLS_MAP.ZKSYNC_ERA,
};

export const TEST_NETWORK_IDS = [
NETWORKS_CHAIN_ID.GOERLI,
NETWORKS_CHAIN_ID.SEPOLIA,
NETWORKS_CHAIN_ID.LINEA_GOERLI,
NETWORKS_CHAIN_ID.LINEA_SEPOLIA,
];
15 changes: 15 additions & 0 deletions app/selectors/settings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { RootState } from '../reducers';

import selectShowFiatInTestnets from './settings';

describe('selectShowFiatInTestnets', () => {
it('returns showFiatOnTestnets from state', () => {
const mockState = {
settings: {
showFiatOnTestnets: true,
},
};

expect(selectShowFiatInTestnets(mockState as RootState)).toBe(true);
});
});
5 changes: 5 additions & 0 deletions app/selectors/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { RootState } from '../reducers';

export default function (state: RootState) {
return state.settings.showFiatOnTestnets;
}

0 comments on commit 08a7949

Please sign in to comment.