diff --git a/app/components/UI/Ramp/hooks/useCryptoCurrencies.test.ts b/app/components/UI/Ramp/hooks/useCryptoCurrencies.test.ts new file mode 100644 index 000000000000..fbede28d2f2c --- /dev/null +++ b/app/components/UI/Ramp/hooks/useCryptoCurrencies.test.ts @@ -0,0 +1,366 @@ +import { NATIVE_ADDRESS } from '../../../../constants/on-ramp'; +import { renderHookWithProvider } from '../../../../util/test/renderWithProvider'; +import { RampSDK } from '../sdk'; +import useCryptoCurrencies from './useCryptoCurrencies'; +import useSDKMethod from './useSDKMethod'; + +type DeepPartial = { + [key in keyof BaseType]?: DeepPartial; +}; + +const mockuseRampSDKInitialValues: DeepPartial = { + selectedRegion: { id: 'test-region-id' }, + selectedPaymentMethodId: 'test-payment-method-id', + selectedFiatCurrencyId: 'test-fiat-currency-id', + selectedAsset: null, + setSelectedAsset: jest.fn(), + selectedChainId: '1', + isBuy: true, +}; + +let mockUseRampSDKValues: DeepPartial = { + ...mockuseRampSDKInitialValues, +}; + +jest.mock('../sdk', () => ({ + useRampSDK: () => mockUseRampSDKValues, +})); + +jest.mock('./useSDKMethod'); // replace with actual path + +describe('useCryptoCurrencies', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockUseRampSDKValues = { + ...mockuseRampSDKInitialValues, + }; + }); + + it('should call useSDKMethod with the correct parameters for buy', () => { + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: [], + error: null, + isFetching: false, + }, + jest.fn(), + ]); + + renderHookWithProvider(() => useCryptoCurrencies()); + + expect(useSDKMethod).toHaveBeenCalledWith( + 'getCryptoCurrencies', + 'test-region-id', + 'test-payment-method-id', + 'test-fiat-currency-id', + ); + }); + + it('should call useSDKMethod with the correct parameters for sell', () => { + mockUseRampSDKValues.isBuy = false; + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: [], + error: null, + isFetching: false, + }, + jest.fn(), + ]); + + renderHookWithProvider(() => useCryptoCurrencies()); + + expect(useSDKMethod).toHaveBeenCalledWith( + 'getSellCryptoCurrencies', + 'test-region-id', + 'test-payment-method-id', + 'test-fiat-currency-id', + ); + }); + + it('should return loading state', () => { + const mockQueryGetCryptoCurrencies = jest.fn(); + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: [], + error: null, + isFetching: true, + }, + mockQueryGetCryptoCurrencies, + ]); + + const { result } = renderHookWithProvider(() => useCryptoCurrencies()); + + expect(result.current).toEqual({ + cryptoCurrencies: null, + errorCryptoCurrencies: null, + isFetchingCryptoCurrencies: true, + queryGetCryptoCurrencies: mockQueryGetCryptoCurrencies, + }); + }); + + it('should return error state', () => { + const mockQueryGetCryptoCurrencies = jest.fn(); + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: null, + error: 'test error message', + isFetching: false, + }, + mockQueryGetCryptoCurrencies, + ]); + + const { result } = renderHookWithProvider(() => useCryptoCurrencies()); + + expect(result.current).toEqual({ + cryptoCurrencies: null, + errorCryptoCurrencies: 'test error message', + isFetchingCryptoCurrencies: false, + queryGetCryptoCurrencies: mockQueryGetCryptoCurrencies, + }); + }); + + it('should filter list by selectedChainId', () => { + const mockQueryGetCryptoCurrencies = jest.fn(); + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '2' }, address: 'test-address-2' }, + ], + error: null, + isFetching: false, + }, + mockQueryGetCryptoCurrencies, + ]); + + mockUseRampSDKValues.selectedChainId = '2'; + + const { result, rerender } = renderHookWithProvider(() => + useCryptoCurrencies(), + ); + + expect(result.current).toEqual({ + cryptoCurrencies: [ + { network: { chainId: '2' }, address: 'test-address-2' }, + ], + errorCryptoCurrencies: null, + isFetchingCryptoCurrencies: false, + queryGetCryptoCurrencies: mockQueryGetCryptoCurrencies, + }); + + mockUseRampSDKValues.selectedChainId = '1'; + rerender({}); + expect(result.current).toEqual({ + cryptoCurrencies: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + ], + errorCryptoCurrencies: null, + isFetchingCryptoCurrencies: false, + queryGetCryptoCurrencies: mockQueryGetCryptoCurrencies, + }); + }); + + it('should not call setSelectedAsset if current selection is available', () => { + const mockQueryGetCryptoCurrencies = jest.fn(); + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '1' }, address: 'test-address-2' }, + ], + error: null, + isFetching: false, + }, + mockQueryGetCryptoCurrencies, + ]); + + mockUseRampSDKValues.selectedAsset = { + // TODO(ramp, chainId-string): remove this ts-expect-error when chainId is string + // See https://github.com/MetaMask/metamask-mobile/pull/9415 + // @ts-expect-error ts(2322) + network: { chainId: '1' }, + address: 'test-address-2', + }; + + const { result } = renderHookWithProvider(() => useCryptoCurrencies()); + + expect(mockUseRampSDKValues.setSelectedAsset).not.toHaveBeenCalled(); + expect(result.current).toEqual({ + cryptoCurrencies: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '1' }, address: 'test-address-2' }, + ], + errorCryptoCurrencies: null, + isFetchingCryptoCurrencies: false, + queryGetCryptoCurrencies: mockQueryGetCryptoCurrencies, + }); + }); + + it('should select the native crypto currency if available and current selection is null', () => { + const mockQueryGetCryptoCurrencies = jest.fn(); + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '1' }, address: 'test-address-2' }, + { network: { chainId: '1' }, address: NATIVE_ADDRESS }, + ], + error: null, + isFetching: false, + }, + mockQueryGetCryptoCurrencies, + ]); + + const { result } = renderHookWithProvider(() => useCryptoCurrencies()); + + expect(mockUseRampSDKValues.setSelectedAsset).toHaveBeenCalledWith({ + network: { chainId: '1' }, + address: NATIVE_ADDRESS, + }); + expect(result.current).toEqual({ + cryptoCurrencies: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '1' }, address: 'test-address-2' }, + { network: { chainId: '1' }, address: NATIVE_ADDRESS }, + ], + errorCryptoCurrencies: null, + isFetchingCryptoCurrencies: false, + queryGetCryptoCurrencies: mockQueryGetCryptoCurrencies, + }); + }); + + it('should select the native crypto currency if available and current selection is not available', () => { + const mockQueryGetCryptoCurrencies = jest.fn(); + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '1' }, address: 'test-address-2' }, + { network: { chainId: '1' }, address: NATIVE_ADDRESS }, + ], + error: null, + isFetching: false, + }, + mockQueryGetCryptoCurrencies, + ]); + + mockUseRampSDKValues.selectedAsset = { + // TODO(ramp, chainId-string): remove this ts-expect-error when chainId is string + // See https://github.com/MetaMask/metamask-mobile/pull/9415 + // @ts-expect-error ts(2322) + network: { chainId: '1' }, + address: 'test-address-3', + }; + + const { result } = renderHookWithProvider(() => useCryptoCurrencies()); + + expect(mockUseRampSDKValues.setSelectedAsset).toHaveBeenCalledWith({ + network: { chainId: '1' }, + address: NATIVE_ADDRESS, + }); + expect(result.current).toEqual({ + cryptoCurrencies: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '1' }, address: 'test-address-2' }, + { network: { chainId: '1' }, address: NATIVE_ADDRESS }, + ], + errorCryptoCurrencies: null, + isFetchingCryptoCurrencies: false, + queryGetCryptoCurrencies: mockQueryGetCryptoCurrencies, + }); + }); + + it('should select the first available crypto currency if native is not available and current selection is null', () => { + const mockQueryGetCryptoCurrencies = jest.fn(); + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '1' }, address: 'test-address-2' }, + ], + error: null, + isFetching: false, + }, + mockQueryGetCryptoCurrencies, + ]); + + const { result } = renderHookWithProvider(() => useCryptoCurrencies()); + + expect(mockUseRampSDKValues.setSelectedAsset).toHaveBeenCalledWith({ + network: { chainId: '1' }, + address: 'test-address-1', + }); + expect(result.current).toEqual({ + cryptoCurrencies: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '1' }, address: 'test-address-2' }, + ], + errorCryptoCurrencies: null, + isFetchingCryptoCurrencies: false, + queryGetCryptoCurrencies: mockQueryGetCryptoCurrencies, + }); + }); + + it('should select the first available crypto currency if native is not available and current selection is not found', () => { + const mockQueryGetCryptoCurrencies = jest.fn(); + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '1' }, address: 'test-address-2' }, + ], + error: null, + isFetching: false, + }, + mockQueryGetCryptoCurrencies, + ]); + + mockUseRampSDKValues.selectedAsset = { + // TODO(ramp, chainId-string): remove this ts-expect-error when chainId is string + // See https://github.com/MetaMask/metamask-mobile/pull/9415 + // @ts-expect-error ts(2322) + network: { chainId: '1' }, + address: 'test-address-3', + }; + + const { result } = renderHookWithProvider(() => useCryptoCurrencies()); + + expect(mockUseRampSDKValues.setSelectedAsset).toHaveBeenCalledWith({ + network: { chainId: '1' }, + address: 'test-address-1', + }); + expect(result.current).toEqual({ + cryptoCurrencies: [ + { network: { chainId: '1' }, address: 'test-address-1' }, + { network: { chainId: '1' }, address: 'test-address-2' }, + ], + errorCryptoCurrencies: null, + isFetchingCryptoCurrencies: false, + queryGetCryptoCurrencies: mockQueryGetCryptoCurrencies, + }); + }); + + it('should set selectedAsset to undefined if no crypto currencies are available', () => { + const mockQueryGetCryptoCurrencies = jest.fn(); + (useSDKMethod as jest.Mock).mockReturnValue([ + { + data: [], + error: null, + isFetching: false, + }, + mockQueryGetCryptoCurrencies, + ]); + + const { result } = renderHookWithProvider(() => useCryptoCurrencies()); + + expect(mockUseRampSDKValues.setSelectedAsset).toHaveBeenCalledWith( + undefined, + ); + expect(result.current).toEqual({ + cryptoCurrencies: [], + errorCryptoCurrencies: null, + isFetchingCryptoCurrencies: false, + queryGetCryptoCurrencies: mockQueryGetCryptoCurrencies, + }); + }); +});