diff --git a/ui/hooks/useAccountTrackerPolling.test.ts b/ui/hooks/useAccountTrackerPolling.test.ts new file mode 100644 index 000000000000..010085ddce61 --- /dev/null +++ b/ui/hooks/useAccountTrackerPolling.test.ts @@ -0,0 +1,143 @@ +import { renderHookWithProvider } from '../../test/lib/render-helpers'; +import { + accountTrackerStartPolling, + accountTrackerStopPollingByPollingToken, +} from '../store/actions'; +import useAccountTrackerPolling from './useAccountTrackerPolling'; + +let mockPromises: Promise[]; + +jest.mock('../store/actions', () => ({ + accountTrackerStartPolling: jest.fn().mockImplementation((input) => { + const promise = Promise.resolve(`${input}_tracking`); + mockPromises.push(promise); + return promise; + }), + accountTrackerStopPollingByPollingToken: jest.fn(), +})); + +let originalPortfolioView: string | undefined; + +describe('useAccountTrackerPolling', () => { + beforeEach(() => { + // Mock process.env.PORTFOLIO_VIEW + originalPortfolioView = process.env.PORTFOLIO_VIEW; + process.env.PORTFOLIO_VIEW = 'true'; // Set your desired mock value here + + mockPromises = []; + jest.clearAllMocks(); + }); + + afterEach(() => { + // Restore the original value + process.env.PORTFOLIO_VIEW = originalPortfolioView; + }); + + it('should poll account trackers for network client IDs when enabled and stop on dismount', async () => { + process.env.PORTFOLIO_VIEW = 'true'; + + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: true, + selectedNetworkClientId: 'selectedNetworkClientId', + networkConfigurationsByChainId: { + '0x1': { + chainId: '0x1', + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'selectedNetworkClientId', + }, + ], + }, + '0x89': { + chainId: '0x89', + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'selectedNetworkClientId2', + }, + ], + }, + }, + }, + }; + + const { unmount } = renderHookWithProvider( + () => useAccountTrackerPolling(), + state, + ); + + // Should poll each client ID + await Promise.all(mockPromises); + expect(accountTrackerStartPolling).toHaveBeenCalledTimes(2); + expect(accountTrackerStartPolling).toHaveBeenCalledWith( + 'selectedNetworkClientId', + ); + expect(accountTrackerStartPolling).toHaveBeenCalledWith( + 'selectedNetworkClientId2', + ); + + // Stop polling on dismount + unmount(); + expect(accountTrackerStopPollingByPollingToken).toHaveBeenCalledTimes(2); + expect(accountTrackerStopPollingByPollingToken).toHaveBeenCalledWith( + 'selectedNetworkClientId_tracking', + ); + }); + + it('should not poll if onboarding is not completed', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: false, + networkConfigurationsByChainId: { + '0x1': {}, + }, + }, + }; + + renderHookWithProvider(() => useAccountTrackerPolling(), state); + + await Promise.all(mockPromises); + expect(accountTrackerStartPolling).toHaveBeenCalledTimes(0); + expect(accountTrackerStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + + it('should not poll when locked', async () => { + const state = { + metamask: { + isUnlocked: false, + completedOnboarding: true, + networkConfigurationsByChainId: { + '0x1': {}, + }, + }, + }; + + renderHookWithProvider(() => useAccountTrackerPolling(), state); + + await Promise.all(mockPromises); + expect(accountTrackerStartPolling).toHaveBeenCalledTimes(0); + expect(accountTrackerStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + + it('should not poll when no network client IDs are provided', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: true, + networkConfigurationsByChainId: { + '0x1': {}, + }, + }, + }; + + renderHookWithProvider(() => useAccountTrackerPolling(), state); + + await Promise.all(mockPromises); + expect(accountTrackerStartPolling).toHaveBeenCalledTimes(0); + expect(accountTrackerStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); +}); diff --git a/ui/hooks/useCurrencyRatePolling.test.ts b/ui/hooks/useCurrencyRatePolling.test.ts new file mode 100644 index 000000000000..9ad405a31c46 --- /dev/null +++ b/ui/hooks/useCurrencyRatePolling.test.ts @@ -0,0 +1,128 @@ +import { renderHookWithProvider } from '../../test/lib/render-helpers'; +import { + currencyRateStartPolling, + currencyRateStopPollingByPollingToken, +} from '../store/actions'; +import useCurrencyRatePolling from './useCurrencyRatePolling'; + +let mockPromises: Promise[]; + +jest.mock('../store/actions', () => ({ + currencyRateStartPolling: jest.fn().mockImplementation((input) => { + const promise = Promise.resolve(`${input}_rates`); + mockPromises.push(promise); + return promise; + }), + currencyRateStopPollingByPollingToken: jest.fn(), +})); + +describe('useCurrencyRatePolling', () => { + beforeEach(() => { + mockPromises = []; + jest.clearAllMocks(); + }); + + it('should poll currency rates for native currencies when enabled and stop on dismount', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: true, + useCurrencyRateCheck: true, + selectedNetworkClientId: 'selectedNetworkClientId', + networkConfigurationsByChainId: { + '0x1': { + nativeCurrency: 'ETH', + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'selectedNetworkClientId', + }, + ], + }, + '0x89': { + nativeCurrency: 'BNB', + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'selectedNetworkClientId2', + }, + ], + }, + }, + }, + }; + + const { unmount } = renderHookWithProvider( + () => useCurrencyRatePolling(), + state, + ); + + await Promise.all(mockPromises); + expect(currencyRateStartPolling).toHaveBeenCalledTimes(1); + expect(currencyRateStartPolling).toHaveBeenCalledWith(['ETH', 'BNB']); + + // Stop polling on dismount + unmount(); + expect(currencyRateStopPollingByPollingToken).toHaveBeenCalledTimes(1); + expect(currencyRateStopPollingByPollingToken).toHaveBeenCalledWith( + 'ETH,BNB_rates', + ); + }); + + it('should not poll if onboarding is not completed', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: false, + useCurrencyRateCheck: true, + networkConfigurationsByChainId: { + '0x1': { nativeCurrency: 'ETH' }, + }, + }, + }; + + renderHookWithProvider(() => useCurrencyRatePolling(), state); + + await Promise.all(mockPromises); + expect(currencyRateStartPolling).toHaveBeenCalledTimes(0); + expect(currencyRateStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + + it('should not poll when locked', async () => { + const state = { + metamask: { + isUnlocked: false, + completedOnboarding: true, + useCurrencyRateCheck: true, + networkConfigurationsByChainId: { + '0x1': { nativeCurrency: 'ETH' }, + }, + }, + }; + + renderHookWithProvider(() => useCurrencyRatePolling(), state); + + await Promise.all(mockPromises); + expect(currencyRateStartPolling).toHaveBeenCalledTimes(0); + expect(currencyRateStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + + it('should not poll when currency rate checking is disabled', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: true, + useCurrencyRateCheck: false, + networkConfigurationsByChainId: { + '0x1': { nativeCurrency: 'ETH' }, + }, + }, + }; + + renderHookWithProvider(() => useCurrencyRatePolling(), state); + + await Promise.all(mockPromises); + expect(currencyRateStartPolling).toHaveBeenCalledTimes(0); + expect(currencyRateStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); +}); diff --git a/ui/hooks/useTokenDetectionPolling.test.ts b/ui/hooks/useTokenDetectionPolling.test.ts new file mode 100644 index 000000000000..bae369ffd525 --- /dev/null +++ b/ui/hooks/useTokenDetectionPolling.test.ts @@ -0,0 +1,157 @@ +import { renderHookWithProvider } from '../../test/lib/render-helpers'; +import { + tokenDetectionStartPolling, + tokenDetectionStopPollingByPollingToken, +} from '../store/actions'; +import useTokenDetectionPolling from './useTokenDetectionPolling'; + +let mockPromises: Promise[]; + +jest.mock('../store/actions', () => ({ + tokenDetectionStartPolling: jest.fn().mockImplementation((input) => { + const promise = Promise.resolve(`${input}_detection`); + mockPromises.push(promise); + return promise; + }), + tokenDetectionStopPollingByPollingToken: jest.fn(), +})); +let originalPortfolioView: string | undefined; + +describe('useTokenDetectionPolling', () => { + beforeEach(() => { + // Mock process.env.PORTFOLIO_VIEW + originalPortfolioView = process.env.PORTFOLIO_VIEW; + process.env.PORTFOLIO_VIEW = 'true'; // Set your desired mock value here + + mockPromises = []; + jest.clearAllMocks(); + }); + + afterEach(() => { + // Restore the original value + process.env.PORTFOLIO_VIEW = originalPortfolioView; + }); + + it('should poll token detection for chain IDs when enabled and stop on dismount', async () => { + process.env.PORTFOLIO_VIEW = 'true'; + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: true, + useTokenDetection: true, + selectedNetworkClientId: 'selectedNetworkClientId', + networkConfigurationsByChainId: { + '0x1': { + chainId: '0x1', + rpcEndpoints: [ + { + networkClientId: 'selectedNetworkClientId', + }, + ], + }, + '0x89': { + chainId: '0x89', + rpcEndpoints: [ + { + networkClientId: 'selectedNetworkClientId2', + }, + ], + }, + }, + }, + }; + + const { unmount } = renderHookWithProvider( + () => useTokenDetectionPolling(), + state, + ); + + // Should poll each chain + await Promise.all(mockPromises); + expect(tokenDetectionStartPolling).toHaveBeenCalledTimes(1); + expect(tokenDetectionStartPolling).toHaveBeenCalledWith(['0x1', '0x89']); + + // Stop polling on dismount + unmount(); + expect(tokenDetectionStopPollingByPollingToken).toHaveBeenCalledTimes(1); + expect(tokenDetectionStopPollingByPollingToken).toHaveBeenCalledWith( + '0x1,0x89_detection', + ); + }); + + it('should not poll if onboarding is not completed', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: false, + useTokenDetection: true, + networkConfigurationsByChainId: { + '0x1': {}, + }, + }, + }; + + renderHookWithProvider(() => useTokenDetectionPolling(), state); + + await Promise.all(mockPromises); + expect(tokenDetectionStartPolling).toHaveBeenCalledTimes(0); + expect(tokenDetectionStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + + it('should not poll when locked', async () => { + const state = { + metamask: { + isUnlocked: false, + completedOnboarding: true, + useTokenDetection: true, + networkConfigurationsByChainId: { + '0x1': {}, + }, + }, + }; + + renderHookWithProvider(() => useTokenDetectionPolling(), state); + + await Promise.all(mockPromises); + expect(tokenDetectionStartPolling).toHaveBeenCalledTimes(0); + expect(tokenDetectionStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + + it('should not poll when token detection is disabled', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: true, + useTokenDetection: false, + networkConfigurationsByChainId: { + '0x1': {}, + }, + }, + }; + + renderHookWithProvider(() => useTokenDetectionPolling(), state); + + await Promise.all(mockPromises); + expect(tokenDetectionStartPolling).toHaveBeenCalledTimes(0); + expect(tokenDetectionStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + + it('should not poll when no chains are provided', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: true, + useTokenDetection: true, + networkConfigurationsByChainId: { + '0x1': {}, + }, + }, + }; + + renderHookWithProvider(() => useTokenDetectionPolling(), state); + + await Promise.all(mockPromises); + expect(tokenDetectionStartPolling).toHaveBeenCalledTimes(0); + expect(tokenDetectionStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); +}); diff --git a/ui/hooks/useTokenRatesPolling.test.ts b/ui/hooks/useTokenRatesPolling.test.ts new file mode 100644 index 000000000000..9383527e6fbd --- /dev/null +++ b/ui/hooks/useTokenRatesPolling.test.ts @@ -0,0 +1,160 @@ +import { renderHookWithProvider } from '../../test/lib/render-helpers'; +import { + tokenRatesStartPolling, + tokenRatesStopPollingByPollingToken, +} from '../store/actions'; +import useTokenRatesPolling from './useTokenRatesPolling'; + +let mockPromises: Promise[]; + +jest.mock('../store/actions', () => ({ + tokenRatesStartPolling: jest.fn().mockImplementation((input) => { + const promise = Promise.resolve(`${input}_rates`); + mockPromises.push(promise); + return promise; + }), + tokenRatesStopPollingByPollingToken: jest.fn(), +})); + +let originalPortfolioView: string | undefined; +describe('useTokenRatesPolling', () => { + beforeEach(() => { + // Mock process.env.PORTFOLIO_VIEW + originalPortfolioView = process.env.PORTFOLIO_VIEW; + process.env.PORTFOLIO_VIEW = 'true'; // Set your desired mock value here + + mockPromises = []; + jest.clearAllMocks(); + }); + + afterEach(() => { + // Restore the original value + process.env.PORTFOLIO_VIEW = originalPortfolioView; + }); + + it('should poll token rates when enabled and stop on dismount', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: true, + useCurrencyRateCheck: true, + selectedNetworkClientId: 'selectedNetworkClientId', + networkConfigurationsByChainId: { + '0x1': { + chainId: '0x1', + rpcEndpoints: [ + { + networkClientId: 'selectedNetworkClientId', + }, + ], + }, + '0x89': { + chainId: '0x89', + rpcEndpoints: [ + { + networkClientId: 'selectedNetworkClientId2', + }, + ], + }, + }, + }, + }; + + const { unmount } = renderHookWithProvider( + () => useTokenRatesPolling(), + state, + ); + + // Should poll each chain + await Promise.all(mockPromises); + expect(tokenRatesStartPolling).toHaveBeenCalledTimes(2); + expect(tokenRatesStartPolling).toHaveBeenCalledWith('0x1'); + expect(tokenRatesStartPolling).toHaveBeenCalledWith('0x89'); + // Stop polling on dismount + unmount(); + expect(tokenRatesStopPollingByPollingToken).toHaveBeenCalledTimes(2); + expect(tokenRatesStopPollingByPollingToken).toHaveBeenCalledWith( + '0x1_rates', + ); + expect(tokenRatesStopPollingByPollingToken).toHaveBeenCalledWith( + '0x89_rates', + ); + }); + + it('should not poll if onboarding is not completed', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: false, + useCurrencyRateCheck: true, + networkConfigurationsByChainId: { + '0x1': {}, + '0x89': {}, + }, + }, + }; + + renderHookWithProvider(() => useTokenRatesPolling(), state); + + await Promise.all(mockPromises); + expect(tokenRatesStartPolling).toHaveBeenCalledTimes(0); + expect(tokenRatesStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + + it('should not poll when locked', async () => { + const state = { + metamask: { + isUnlocked: false, + completedOnboarding: true, + useCurrencyRateCheck: true, + networkConfigurationsByChainId: { + '0x1': {}, + '0x89': {}, + }, + }, + }; + + renderHookWithProvider(() => useTokenRatesPolling(), state); + + await Promise.all(mockPromises); + expect(tokenRatesStartPolling).toHaveBeenCalledTimes(0); + expect(tokenRatesStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + + it('should not poll when rate checking is disabled', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: true, + useCurrencyRateCheck: false, + networkConfigurationsByChainId: { + '0x1': {}, + '0x89': {}, + }, + }, + }; + + renderHookWithProvider(() => useTokenRatesPolling(), state); + + await Promise.all(mockPromises); + expect(tokenRatesStartPolling).toHaveBeenCalledTimes(0); + expect(tokenRatesStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + + it('should not poll when no chains are provided', async () => { + const state = { + metamask: { + isUnlocked: true, + completedOnboarding: true, + useCurrencyRateCheck: true, + networkConfigurationsByChainId: {}, + }, + }; + + renderHookWithProvider(() => useTokenRatesPolling(), state); + + await Promise.all(mockPromises); + expect(tokenRatesStartPolling).toHaveBeenCalledTimes(0); + expect(tokenRatesStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); +});