From 35953ea2aa43c7896cfedf90544323cc0b376370 Mon Sep 17 00:00:00 2001 From: Nicholas Gambino Date: Tue, 10 Dec 2024 09:49:34 -0800 Subject: [PATCH 01/12] fix: Token list should respect hidezerobalance setting --- .../app/assets/token-list/token-list.tsx | 43 ++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/ui/components/app/assets/token-list/token-list.tsx b/ui/components/app/assets/token-list/token-list.tsx index d3e67f6204cc..4b0147948a29 100644 --- a/ui/components/app/assets/token-list/token-list.tsx +++ b/ui/components/app/assets/token-list/token-list.tsx @@ -86,8 +86,13 @@ export default function TokenList({ const dispatch = useDispatch(); const currentNetwork = useSelector(getCurrentNetwork); const allNetworks = useSelector(getNetworkConfigurationIdByChainId); - const { tokenSortConfig, tokenNetworkFilter, privacyMode } = - useSelector(getPreferences); + const { + tokenSortConfig, + tokenNetworkFilter, + privacyMode, + hideZeroBalanceTokens, + } = useSelector(getPreferences); + const preferences = useSelector(getPreferences); const selectedAccount = useSelector(getSelectedAccount); const conversionRate = useSelector(getConversionRate); const contractExchangeRates = useSelector( @@ -122,6 +127,8 @@ export default function TokenList({ } }, [Object.keys(allNetworks).length]); + console.log(preferences); + const consolidatedBalances = () => { const tokensWithBalance: TokenWithFiatAmount[] = []; Object.entries(selectedAccountTokensChains).forEach( @@ -147,14 +154,28 @@ export default function TokenList({ currencyRates, }); - // Append processed token with balance and fiat amount - tokensWithBalance.push({ - ...token, - balance, - tokenFiatAmount, - chainId, - string: String(balance), - }); + if (hideZeroBalanceTokens) { + // only hide zero balance tokens if not native gas token + if (!token.isNative && balance === '0') { + return; + } else { + tokensWithBalance.push({ + ...token, + balance, + tokenFiatAmount, + chainId, + string: String(balance), + }); + } + } else { + tokensWithBalance.push({ + ...token, + balance, + tokenFiatAmount, + chainId, + string: String(balance), + }); + } }); }, ); @@ -228,6 +249,8 @@ export default function TokenList({ const showFiat = shouldShowFiat && (isMainnet || (isTestnet && showFiatInTestnets)); + console.log(sortedFilteredTokens); + return (
{sortedFilteredTokens.map((tokenData) => ( From dd5ead4ad26396c7de25f4edc5d6461186fb60be Mon Sep 17 00:00:00 2001 From: Nicholas Gambino Date: Tue, 10 Dec 2024 10:04:11 -0800 Subject: [PATCH 02/12] cleanup --- ui/components/app/assets/token-list/token-list.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/components/app/assets/token-list/token-list.tsx b/ui/components/app/assets/token-list/token-list.tsx index 4b0147948a29..7493a00dac8f 100644 --- a/ui/components/app/assets/token-list/token-list.tsx +++ b/ui/components/app/assets/token-list/token-list.tsx @@ -249,8 +249,6 @@ export default function TokenList({ const showFiat = shouldShowFiat && (isMainnet || (isTestnet && showFiatInTestnets)); - console.log(sortedFilteredTokens); - return (
{sortedFilteredTokens.map((tokenData) => ( From 6726beb527ce93bf7ddb263024ef3060909857b8 Mon Sep 17 00:00:00 2001 From: Nicholas Gambino Date: Tue, 10 Dec 2024 10:07:52 -0800 Subject: [PATCH 03/12] cleanup --- ui/components/app/assets/token-list/token-list.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/components/app/assets/token-list/token-list.tsx b/ui/components/app/assets/token-list/token-list.tsx index 7493a00dac8f..b569c0eaae11 100644 --- a/ui/components/app/assets/token-list/token-list.tsx +++ b/ui/components/app/assets/token-list/token-list.tsx @@ -127,8 +127,6 @@ export default function TokenList({ } }, [Object.keys(allNetworks).length]); - console.log(preferences); - const consolidatedBalances = () => { const tokensWithBalance: TokenWithFiatAmount[] = []; Object.entries(selectedAccountTokensChains).forEach( From 1fbd5a6070d4646385613a2ae69674ae02d5f0e9 Mon Sep 17 00:00:00 2001 From: Nicholas Gambino Date: Tue, 10 Dec 2024 10:13:51 -0800 Subject: [PATCH 04/12] fix: Add unit test to token-list file to ensure zero balances aren't rendered for non-native assets --- .../app/assets/token-list/token-list.spec.tsx | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 ui/components/app/assets/token-list/token-list.spec.tsx diff --git a/ui/components/app/assets/token-list/token-list.spec.tsx b/ui/components/app/assets/token-list/token-list.spec.tsx new file mode 100644 index 000000000000..940c06a84376 --- /dev/null +++ b/ui/components/app/assets/token-list/token-list.spec.tsx @@ -0,0 +1,98 @@ +import React from 'react'; +import { render, screen, act, waitFor } from '@testing-library/react'; +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import { Provider } from 'react-redux'; +import TokenList from './token-list'; +import TokenCell from '../token-cell'; + +// Mock the TokenCell component +jest.mock('../token-cell', () => jest.fn(({ symbol }) =>
{symbol}
)); + +const mockStore = configureMockStore([thunk]); + +describe('TokenList Component', () => { + let store: any; + + beforeEach(() => { + store = mockStore({ + metamask: { + currentNetwork: { chainId: '0x1' }, + preferences: { + tokenSortConfig: null, + tokenNetworkFilter: { '0x1': true }, + privacyMode: false, + hideZeroBalanceTokens: true, + }, + selectedAccount: { + address: '0xAccount', + }, + selectedAccountTokensAcrossChains: { + '0x1': [ + { + address: '0xToken1', + chainId: '0x1', + symbol: 'TOKEN1', + decimals: 18, + isNative: false, + image: 'token1.png', + balance: '1000', + }, + { + address: '0xToken2', + chainId: '0x1', + symbol: 'TOKEN2', + decimals: 18, + isNative: false, + image: 'token2.png', + balance: '0', // Zero balance + }, + ], + '0x2': [ + { + address: '0xToken3', + chainId: '0x2', + symbol: 'TOKEN3', + decimals: 18, + isNative: true, + image: 'token3.png', + balance: '500', + }, + ], + }, + tokenBalances: { + '0xAccount': { + '0x1': '1000', + }, + }, + nativeBalances: { + '0x1': '2000', + }, + marketData: {}, + currencyRates: {}, + showFiatInTestnets: true, + }, + }); + }); + + const renderComponent = (props = {}) => { + render( + + + , + ); + }; + + it('renders tokens with non-zero balances', async () => { + await act(async () => { + renderComponent(); + }); + + // TOKEN1 has a non-zero balance + expect(screen.getByText('TOKEN1')).toBeInTheDocument(); + // TOKEN2 has a zero balance and should not be rendered + expect(screen.queryByText('TOKEN2')).not.toBeInTheDocument(); + // TOKEN3 is native and should always be rendered + expect(screen.getByText('TOKEN3')).toBeInTheDocument(); + }); +}); From 35486c079c2bd6276b1232c54273b276c2565cbf Mon Sep 17 00:00:00 2001 From: Nicholas Gambino Date: Tue, 10 Dec 2024 10:39:08 -0800 Subject: [PATCH 05/12] cleanup --- .../app/assets/token-list/token-list.spec.tsx | 98 ------------------- .../app/assets/token-list/token-list.tsx | 1 - 2 files changed, 99 deletions(-) delete mode 100644 ui/components/app/assets/token-list/token-list.spec.tsx diff --git a/ui/components/app/assets/token-list/token-list.spec.tsx b/ui/components/app/assets/token-list/token-list.spec.tsx deleted file mode 100644 index 940c06a84376..000000000000 --- a/ui/components/app/assets/token-list/token-list.spec.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import { render, screen, act, waitFor } from '@testing-library/react'; -import configureMockStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import { Provider } from 'react-redux'; -import TokenList from './token-list'; -import TokenCell from '../token-cell'; - -// Mock the TokenCell component -jest.mock('../token-cell', () => jest.fn(({ symbol }) =>
{symbol}
)); - -const mockStore = configureMockStore([thunk]); - -describe('TokenList Component', () => { - let store: any; - - beforeEach(() => { - store = mockStore({ - metamask: { - currentNetwork: { chainId: '0x1' }, - preferences: { - tokenSortConfig: null, - tokenNetworkFilter: { '0x1': true }, - privacyMode: false, - hideZeroBalanceTokens: true, - }, - selectedAccount: { - address: '0xAccount', - }, - selectedAccountTokensAcrossChains: { - '0x1': [ - { - address: '0xToken1', - chainId: '0x1', - symbol: 'TOKEN1', - decimals: 18, - isNative: false, - image: 'token1.png', - balance: '1000', - }, - { - address: '0xToken2', - chainId: '0x1', - symbol: 'TOKEN2', - decimals: 18, - isNative: false, - image: 'token2.png', - balance: '0', // Zero balance - }, - ], - '0x2': [ - { - address: '0xToken3', - chainId: '0x2', - symbol: 'TOKEN3', - decimals: 18, - isNative: true, - image: 'token3.png', - balance: '500', - }, - ], - }, - tokenBalances: { - '0xAccount': { - '0x1': '1000', - }, - }, - nativeBalances: { - '0x1': '2000', - }, - marketData: {}, - currencyRates: {}, - showFiatInTestnets: true, - }, - }); - }); - - const renderComponent = (props = {}) => { - render( - - - , - ); - }; - - it('renders tokens with non-zero balances', async () => { - await act(async () => { - renderComponent(); - }); - - // TOKEN1 has a non-zero balance - expect(screen.getByText('TOKEN1')).toBeInTheDocument(); - // TOKEN2 has a zero balance and should not be rendered - expect(screen.queryByText('TOKEN2')).not.toBeInTheDocument(); - // TOKEN3 is native and should always be rendered - expect(screen.getByText('TOKEN3')).toBeInTheDocument(); - }); -}); diff --git a/ui/components/app/assets/token-list/token-list.tsx b/ui/components/app/assets/token-list/token-list.tsx index b569c0eaae11..a18edacfa1f2 100644 --- a/ui/components/app/assets/token-list/token-list.tsx +++ b/ui/components/app/assets/token-list/token-list.tsx @@ -92,7 +92,6 @@ export default function TokenList({ privacyMode, hideZeroBalanceTokens, } = useSelector(getPreferences); - const preferences = useSelector(getPreferences); const selectedAccount = useSelector(getSelectedAccount); const conversionRate = useSelector(getConversionRate); const contractExchangeRates = useSelector( From 73bd217a5c1d0c076df1b6eaa2898deb13e18a04 Mon Sep 17 00:00:00 2001 From: Nicholas Gambino Date: Tue, 10 Dec 2024 10:55:57 -0800 Subject: [PATCH 06/12] fix: Lint --- ui/components/app/assets/token-list/token-list.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/components/app/assets/token-list/token-list.tsx b/ui/components/app/assets/token-list/token-list.tsx index a18edacfa1f2..61e54f795813 100644 --- a/ui/components/app/assets/token-list/token-list.tsx +++ b/ui/components/app/assets/token-list/token-list.tsx @@ -153,9 +153,7 @@ export default function TokenList({ if (hideZeroBalanceTokens) { // only hide zero balance tokens if not native gas token - if (!token.isNative && balance === '0') { - return; - } else { + if (token.isNative || balance !== '0') { tokensWithBalance.push({ ...token, balance, From b6d235d135ff4952bcb65ad6d32c0a4e471f43eb Mon Sep 17 00:00:00 2001 From: Nicholas Gambino Date: Tue, 10 Dec 2024 12:33:43 -0800 Subject: [PATCH 07/12] refactor: clean up logic for hide zero balance token condition --- .../app/assets/token-list/token-list.tsx | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/ui/components/app/assets/token-list/token-list.tsx b/ui/components/app/assets/token-list/token-list.tsx index 61e54f795813..0278f12281d3 100644 --- a/ui/components/app/assets/token-list/token-list.tsx +++ b/ui/components/app/assets/token-list/token-list.tsx @@ -151,18 +151,10 @@ export default function TokenList({ currencyRates, }); - if (hideZeroBalanceTokens) { - // only hide zero balance tokens if not native gas token - if (token.isNative || balance !== '0') { - tokensWithBalance.push({ - ...token, - balance, - tokenFiatAmount, - chainId, - string: String(balance), - }); - } - } else { + // respect hide zero balance setting + // native tokens should still show zero balance regardless + // erc20s with zero balances should be hidden + if (!hideZeroBalanceTokens || token.isNative || balance !== '0') { tokensWithBalance.push({ ...token, balance, From 506b410459bc081813c183c1ff0e4058a1bc8bff Mon Sep 17 00:00:00 2001 From: sahar-fehri Date: Tue, 10 Dec 2024 22:30:30 +0100 Subject: [PATCH 08/12] fix: fix showing native zero balances whe allNetworks selected --- .../app/assets/token-list/token-list.tsx | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/ui/components/app/assets/token-list/token-list.tsx b/ui/components/app/assets/token-list/token-list.tsx index d3e67f6204cc..23a266245e5a 100644 --- a/ui/components/app/assets/token-list/token-list.tsx +++ b/ui/components/app/assets/token-list/token-list.tsx @@ -8,6 +8,7 @@ import { getCurrencyRates, getCurrentNetwork, getIsTestnet, + getIsTokenNetworkFilterEqualCurrentNetwork, getMarketData, getNetworkConfigurationIdByChainId, getNewTokensImported, @@ -96,6 +97,9 @@ export default function TokenList({ ); const newTokensImported = useSelector(getNewTokensImported); const selectedAccountTokensChains = useFilteredAccountTokens(currentNetwork); + const isOnCurrentNetwork = useSelector( + getIsTokenNetworkFilterEqualCurrentNetwork, + ); const { tokenBalances } = useTokenBalances(); const selectedAccountTokenBalancesAcrossChains = @@ -148,13 +152,23 @@ export default function TokenList({ }); // Append processed token with balance and fiat amount - tokensWithBalance.push({ - ...token, - balance, - tokenFiatAmount, - chainId, - string: String(balance), - }); + // We push the token if its not native. + // If it is native, we check balance and if user has currentNetwork selected. + // If user is on current network; we push token. + // else we check the balance and push only if not zero + const shouldShowToken = + !isNative || + (isNative && isOnCurrentNetwork) || + (isNative && !isOnCurrentNetwork && balance !== '0'); + if (shouldShowToken) { + tokensWithBalance.push({ + ...token, + balance, + tokenFiatAmount, + chainId, + string: String(balance), + }); + } }); }, ); From 5f743e95843ba9d26ff9f24e034dc41a369502bd Mon Sep 17 00:00:00 2001 From: Nicholas Gambino Date: Tue, 10 Dec 2024 13:57:10 -0800 Subject: [PATCH 09/12] fix: Unit tests --- .../app/assets/token-list/token-list.test.tsx | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 ui/components/app/assets/token-list/token-list.test.tsx diff --git a/ui/components/app/assets/token-list/token-list.test.tsx b/ui/components/app/assets/token-list/token-list.test.tsx new file mode 100644 index 000000000000..8113d5d01cda --- /dev/null +++ b/ui/components/app/assets/token-list/token-list.test.tsx @@ -0,0 +1,198 @@ +import React from 'react'; +import { screen, act, waitFor } from '@testing-library/react'; +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import { renderWithProvider } from '../../../../../test/jest'; +import { MetaMaskReduxState } from '../../../../store/store'; +import mockState from '../../../../../test/data/mock-state.json'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; +import { useIsOriginalNativeTokenSymbol } from '../../../../hooks/useIsOriginalNativeTokenSymbol'; +import useMultiPolling from '../../../../hooks/useMultiPolling'; +import { getTokenSymbol } from '../../../../store/actions'; +import { getSelectedInternalAccountFromMockState } from '../../../../../test/jest/mocks'; +import { mockNetworkState } from '../../../../../test/stub/networks'; +import AssetList from '.'; +import TokenList from './token-list'; + +// Specific to just the ETH FIAT conversion +const CONVERSION_RATE = 1597.32; +const ETH_BALANCE = '0x041173b2c0e57d'; // 0.0011 ETH ($1.83) + +const USDC_BALANCE = '199.4875'; // @ $1 +const LINK_BALANCE = '122.0005'; // @ $6.75 +const WBTC_BALANCE = '2.38'; // @ $26,601.51 + +const USDC_CONTRACT = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; +const LINK_CONTRACT = '0x514910771AF9Ca656af840dff83E8264EcF986CA'; +const WBTC_CONTRACT = '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599'; + +const mockTokens = [ + { + address: USDC_CONTRACT, + decimals: 6, + symbol: 'USDC', + string: USDC_BALANCE, // $199.36 + }, + { + address: LINK_CONTRACT, + aggregators: [], + decimals: 18, + symbol: 'LINK', + string: LINK_BALANCE, // $824.78 + }, + { + address: WBTC_CONTRACT, + aggregators: [], + decimals: 8, + symbol: 'WBTC', + string: WBTC_BALANCE, // $63,381.02 + }, +]; + +jest.mock('../../../../hooks/useTokenTracker', () => { + return { + useTokenTracker: () => ({ + loading: false, + tokensWithBalances: mockTokens, + error: null, + }), + }; +}); + +jest.mock('../../../../hooks/useIsOriginalNativeTokenSymbol', () => { + return { + useIsOriginalNativeTokenSymbol: jest.fn(), + }; +}); + +jest.mock('../../../../store/actions', () => { + return { + getTokenSymbol: jest.fn(), + setTokenNetworkFilter: jest.fn(() => ({ + type: 'TOKEN_NETWORK_FILTER', + })), + tokenBalancesStartPolling: jest.fn().mockResolvedValue('pollingToken'), + tokenBalancesStopPollingByPollingToken: jest.fn(), + }; +}); + +jest.mock('../../../../hooks/useMultiPolling', () => ({ + __esModule: true, + default: jest.fn(), +})); + +const mockSelectedInternalAccount = getSelectedInternalAccountFromMockState( + mockState as unknown as MetaMaskReduxState, +); + +const render = (balance = ETH_BALANCE, chainId = CHAIN_IDS.MAINNET) => { + const state = { + ...mockState, + metamask: { + ...mockState.metamask, + ...mockNetworkState({ chainId }), + currencyRates: { + ETH: { + conversionRate: CONVERSION_RATE, + }, + }, + accountsByChainId: { + [CHAIN_IDS.MAINNET]: { + [mockSelectedInternalAccount.address]: { balance }, + }, + }, + marketData: { + [CHAIN_IDS.MAINNET]: { + [USDC_CONTRACT]: { price: 0.00062566 }, + [LINK_CONTRACT]: { price: 0.00423239 }, + [WBTC_CONTRACT]: { price: 16.66575 }, + }, + '0x0': { + [USDC_CONTRACT]: { price: 0.00062566 }, + [LINK_CONTRACT]: { price: 0.00423239 }, + [WBTC_CONTRACT]: { price: 16.66575 }, + }, + }, + }, + }; + const store = configureMockStore([thunk])(state); + return renderWithProvider( + undefined} nativeToken={<>} />, + store, + ); +}; + +describe('TokenList', () => { + const state = { + ...mockState, + metamask: { + ...mockState.metamask, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + currencyRates: { + ETH: { + conversionRate: CONVERSION_RATE, + }, + }, + accountsByChainId: { + [CHAIN_IDS.MAINNET]: { + [mockSelectedInternalAccount.address]: { balance }, + }, + }, + marketData: { + [CHAIN_IDS.MAINNET]: { + [USDC_CONTRACT]: { price: 0.00062566 }, + [LINK_CONTRACT]: { price: 0.00423239 }, + [WBTC_CONTRACT]: { price: 16.66575 }, + }, + '0x0': { + [USDC_CONTRACT]: { price: 0.00062566 }, + [LINK_CONTRACT]: { price: 0.00423239 }, + [WBTC_CONTRACT]: { price: 16.66575 }, + }, + }, + }, + }; + (useMultiPolling as jest.Mock).mockClear(); + + // Mock implementation for useMultiPolling + (useMultiPolling as jest.Mock).mockImplementation(({ input }) => { + // Mock startPolling and stopPollingByPollingToken for each input + const startPolling = jest.fn().mockResolvedValue('mockPollingToken'); + const stopPollingByPollingToken = jest.fn(); + + input.forEach((inputItem: string) => { + const key = JSON.stringify(inputItem); + // Simulate returning a unique token for each input + startPolling.mockResolvedValueOnce(`mockToken-${key}`); + }); + + return { startPolling, stopPollingByPollingToken }; + }); + (useIsOriginalNativeTokenSymbol as jest.Mock).mockReturnValue(true); + + (getTokenSymbol as jest.Mock).mockImplementation(async (address) => { + if (address === USDC_CONTRACT) { + return 'USDC'; + } + if (address === LINK_CONTRACT) { + return 'LINK'; + } + if (address === WBTC_CONTRACT) { + return 'WBTC'; + } + return null; + }); + const store = configureMockStore([thunk])(state); + + it('renders AssetList component and shows AssetList control bar', async () => { + const { getByTestId, rerender } = renderWithProvider( + undefined} nativeToken={<>} />, + store, + ); + + await waitFor(() => { + expect(screen.getByTestId('sort-by-popover-toggle')).toBeInTheDocument(); + expect(screen.getByTestId('import-token-button')).toBeInTheDocument(); + }); + }); +}); From 936531ef1e0e63f5e37889c3a490ae67e2126c33 Mon Sep 17 00:00:00 2001 From: sahar-fehri Date: Tue, 10 Dec 2024 23:47:09 +0100 Subject: [PATCH 10/12] fix: fix --- ui/components/app/assets/token-list/token-list.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/components/app/assets/token-list/token-list.tsx b/ui/components/app/assets/token-list/token-list.tsx index 23a266245e5a..62875ed1662c 100644 --- a/ui/components/app/assets/token-list/token-list.tsx +++ b/ui/components/app/assets/token-list/token-list.tsx @@ -113,7 +113,7 @@ export default function TokenList({ const nativeBalances: Record = useSelector( getSelectedAccountNativeTokenCachedBalanceByChainId, ) as Record; - + const isTestnet = useSelector(getIsTestnet); // Ensure newly added networks are included in the tokenNetworkFilter useEffect(() => { if (process.env.PORTFOLIO_VIEW) { @@ -157,6 +157,7 @@ export default function TokenList({ // If user is on current network; we push token. // else we check the balance and push only if not zero const shouldShowToken = + isTestnet || !isNative || (isNative && isOnCurrentNetwork) || (isNative && !isOnCurrentNetwork && balance !== '0'); @@ -230,8 +231,6 @@ export default function TokenList({ console.log(t('loadingTokens')); } - // Check if testnet - const isTestnet = useSelector(getIsTestnet); const shouldShowFiat = useMultichainSelector( getMultichainShouldShowFiat, selectedAccount, From 22bb2e5d6667a86aace4a4aba340a7013fe0181e Mon Sep 17 00:00:00 2001 From: Nicholas Gambino Date: Tue, 10 Dec 2024 15:14:48 -0800 Subject: [PATCH 11/12] chore: Remove wip test file --- .../app/assets/token-list/token-list.test.tsx | 198 ------------------ 1 file changed, 198 deletions(-) delete mode 100644 ui/components/app/assets/token-list/token-list.test.tsx diff --git a/ui/components/app/assets/token-list/token-list.test.tsx b/ui/components/app/assets/token-list/token-list.test.tsx deleted file mode 100644 index 8113d5d01cda..000000000000 --- a/ui/components/app/assets/token-list/token-list.test.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import React from 'react'; -import { screen, act, waitFor } from '@testing-library/react'; -import configureMockStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import { renderWithProvider } from '../../../../../test/jest'; -import { MetaMaskReduxState } from '../../../../store/store'; -import mockState from '../../../../../test/data/mock-state.json'; -import { CHAIN_IDS } from '../../../../../shared/constants/network'; -import { useIsOriginalNativeTokenSymbol } from '../../../../hooks/useIsOriginalNativeTokenSymbol'; -import useMultiPolling from '../../../../hooks/useMultiPolling'; -import { getTokenSymbol } from '../../../../store/actions'; -import { getSelectedInternalAccountFromMockState } from '../../../../../test/jest/mocks'; -import { mockNetworkState } from '../../../../../test/stub/networks'; -import AssetList from '.'; -import TokenList from './token-list'; - -// Specific to just the ETH FIAT conversion -const CONVERSION_RATE = 1597.32; -const ETH_BALANCE = '0x041173b2c0e57d'; // 0.0011 ETH ($1.83) - -const USDC_BALANCE = '199.4875'; // @ $1 -const LINK_BALANCE = '122.0005'; // @ $6.75 -const WBTC_BALANCE = '2.38'; // @ $26,601.51 - -const USDC_CONTRACT = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; -const LINK_CONTRACT = '0x514910771AF9Ca656af840dff83E8264EcF986CA'; -const WBTC_CONTRACT = '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599'; - -const mockTokens = [ - { - address: USDC_CONTRACT, - decimals: 6, - symbol: 'USDC', - string: USDC_BALANCE, // $199.36 - }, - { - address: LINK_CONTRACT, - aggregators: [], - decimals: 18, - symbol: 'LINK', - string: LINK_BALANCE, // $824.78 - }, - { - address: WBTC_CONTRACT, - aggregators: [], - decimals: 8, - symbol: 'WBTC', - string: WBTC_BALANCE, // $63,381.02 - }, -]; - -jest.mock('../../../../hooks/useTokenTracker', () => { - return { - useTokenTracker: () => ({ - loading: false, - tokensWithBalances: mockTokens, - error: null, - }), - }; -}); - -jest.mock('../../../../hooks/useIsOriginalNativeTokenSymbol', () => { - return { - useIsOriginalNativeTokenSymbol: jest.fn(), - }; -}); - -jest.mock('../../../../store/actions', () => { - return { - getTokenSymbol: jest.fn(), - setTokenNetworkFilter: jest.fn(() => ({ - type: 'TOKEN_NETWORK_FILTER', - })), - tokenBalancesStartPolling: jest.fn().mockResolvedValue('pollingToken'), - tokenBalancesStopPollingByPollingToken: jest.fn(), - }; -}); - -jest.mock('../../../../hooks/useMultiPolling', () => ({ - __esModule: true, - default: jest.fn(), -})); - -const mockSelectedInternalAccount = getSelectedInternalAccountFromMockState( - mockState as unknown as MetaMaskReduxState, -); - -const render = (balance = ETH_BALANCE, chainId = CHAIN_IDS.MAINNET) => { - const state = { - ...mockState, - metamask: { - ...mockState.metamask, - ...mockNetworkState({ chainId }), - currencyRates: { - ETH: { - conversionRate: CONVERSION_RATE, - }, - }, - accountsByChainId: { - [CHAIN_IDS.MAINNET]: { - [mockSelectedInternalAccount.address]: { balance }, - }, - }, - marketData: { - [CHAIN_IDS.MAINNET]: { - [USDC_CONTRACT]: { price: 0.00062566 }, - [LINK_CONTRACT]: { price: 0.00423239 }, - [WBTC_CONTRACT]: { price: 16.66575 }, - }, - '0x0': { - [USDC_CONTRACT]: { price: 0.00062566 }, - [LINK_CONTRACT]: { price: 0.00423239 }, - [WBTC_CONTRACT]: { price: 16.66575 }, - }, - }, - }, - }; - const store = configureMockStore([thunk])(state); - return renderWithProvider( - undefined} nativeToken={<>} />, - store, - ); -}; - -describe('TokenList', () => { - const state = { - ...mockState, - metamask: { - ...mockState.metamask, - ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), - currencyRates: { - ETH: { - conversionRate: CONVERSION_RATE, - }, - }, - accountsByChainId: { - [CHAIN_IDS.MAINNET]: { - [mockSelectedInternalAccount.address]: { balance }, - }, - }, - marketData: { - [CHAIN_IDS.MAINNET]: { - [USDC_CONTRACT]: { price: 0.00062566 }, - [LINK_CONTRACT]: { price: 0.00423239 }, - [WBTC_CONTRACT]: { price: 16.66575 }, - }, - '0x0': { - [USDC_CONTRACT]: { price: 0.00062566 }, - [LINK_CONTRACT]: { price: 0.00423239 }, - [WBTC_CONTRACT]: { price: 16.66575 }, - }, - }, - }, - }; - (useMultiPolling as jest.Mock).mockClear(); - - // Mock implementation for useMultiPolling - (useMultiPolling as jest.Mock).mockImplementation(({ input }) => { - // Mock startPolling and stopPollingByPollingToken for each input - const startPolling = jest.fn().mockResolvedValue('mockPollingToken'); - const stopPollingByPollingToken = jest.fn(); - - input.forEach((inputItem: string) => { - const key = JSON.stringify(inputItem); - // Simulate returning a unique token for each input - startPolling.mockResolvedValueOnce(`mockToken-${key}`); - }); - - return { startPolling, stopPollingByPollingToken }; - }); - (useIsOriginalNativeTokenSymbol as jest.Mock).mockReturnValue(true); - - (getTokenSymbol as jest.Mock).mockImplementation(async (address) => { - if (address === USDC_CONTRACT) { - return 'USDC'; - } - if (address === LINK_CONTRACT) { - return 'LINK'; - } - if (address === WBTC_CONTRACT) { - return 'WBTC'; - } - return null; - }); - const store = configureMockStore([thunk])(state); - - it('renders AssetList component and shows AssetList control bar', async () => { - const { getByTestId, rerender } = renderWithProvider( - undefined} nativeToken={<>} />, - store, - ); - - await waitFor(() => { - expect(screen.getByTestId('sort-by-popover-toggle')).toBeInTheDocument(); - expect(screen.getByTestId('import-token-button')).toBeInTheDocument(); - }); - }); -}); From 9970e95665904b56532c14bab914f96aefdfaaa0 Mon Sep 17 00:00:00 2001 From: Nicholas Gambino Date: Tue, 10 Dec 2024 15:29:02 -0800 Subject: [PATCH 12/12] Update comment --- ui/components/app/assets/token-list/token-list.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ui/components/app/assets/token-list/token-list.tsx b/ui/components/app/assets/token-list/token-list.tsx index cb88bbb95dcc..17c523f07c49 100644 --- a/ui/components/app/assets/token-list/token-list.tsx +++ b/ui/components/app/assets/token-list/token-list.tsx @@ -155,10 +155,15 @@ export default function TokenList({ currencyRates, }); - // respect hide zero balance setting - // native tokens should still show zero balance regardless - // erc20s with zero balances should be hidden - // additionally, when all networks filter is shown, do not display zero native tokens + // Respect the "hide zero balance" setting (when true): + // - Native tokens should always display with zero balance when on the current network filter. + // - Native tokens should not display with zero balance when on all networks filter + // - ERC20 tokens with zero balances should respect the setting on both the current and all networks. + + // Respect the "hide zero balance" setting (when false): + // - Native tokens should always display with zero balance when on the current network filter. + // - Native tokens should always display with zero balance when on all networks filter + // - ERC20 tokens always display with zero balance on both the current and all networks filter. if ( !hideZeroBalanceTokens || balance !== '0' ||