import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import sdk from '@farcaster/miniapp-sdk'; import { renderHook, waitFor } from '@testing-library/react'; import { act } from 'react'; import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest'; import { useSwapToken } from './useSwapToken'; const swapTokenMock = { success: true, }; vi.mock('@farcaster/miniapp-sdk', () => ({ default: { actions: { swapToken: vi.fn(), }, }, })); const createWrapper = () => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, }, }, }); // eslint-disable-next-line react/display-name return ({ children }: { children: React.ReactNode }) => ( {children} ); }; describe('useSwapToken', () => { beforeEach(() => { (sdk.actions.swapToken as Mock).mockResolvedValue(swapTokenMock); vi.clearAllMocks(); }); it('should return swapToken and swapTokenAsync functions', () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); expect(typeof result.current.swapToken).toBe('function'); expect(typeof result.current.swapTokenAsync).toBe('function'); }); it('should call sdk.actions.swapToken when swapToken is executed', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { sellToken: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', buyToken: 'eip155:10/native', sellAmount: '1000000', }; await act(async () => { result.current.swapToken(params); }); await waitFor(() => { expect(sdk.actions.swapToken).toHaveBeenCalledWith(params); }); }); it('should call sdk.actions.swapToken when swapTokenAsync is executed', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { sellToken: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', buyToken: 'eip155:10/native', sellAmount: '1000000', }; await act(async () => { await result.current.swapTokenAsync(params); }); expect(sdk.actions.swapToken).toHaveBeenCalledWith(params); }); it('should pass sellToken parameter correctly', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { sellToken: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', }; await act(async () => { result.current.swapToken(params); }); await waitFor(() => { expect(sdk.actions.swapToken).toHaveBeenCalledWith(params); }); }); it('should pass buyToken parameter correctly', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { buyToken: 'eip155:10/native', }; await act(async () => { result.current.swapToken(params); }); await waitFor(() => { expect(sdk.actions.swapToken).toHaveBeenCalledWith(params); }); }); it('should pass sellAmount parameter correctly', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { sellAmount: '1000000', }; await act(async () => { result.current.swapToken(params); }); await waitFor(() => { expect(sdk.actions.swapToken).toHaveBeenCalledWith(params); }); }); it('should pass all parameters correctly when provided', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { sellToken: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', buyToken: 'eip155:10/native', sellAmount: '1000000', }; await act(async () => { result.current.swapToken(params); }); await waitFor(() => { expect(sdk.actions.swapToken).toHaveBeenCalledWith(params); }); }); it('should handle empty parameters object', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = {}; await act(async () => { result.current.swapToken(params); }); await waitFor(() => { expect(sdk.actions.swapToken).toHaveBeenCalledWith(params); }); }); it('should pass different token formats correctly', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); // Test with ETH native token on Ethereum mainnet const ethParams = { sellToken: 'eip155:1/native', buyToken: 'eip155:1/erc20:0xA0b86a33E6441D5D56b5FF87d65f2B8e9F90C54c', sellAmount: '1000000000000000000', // 1 ETH in wei }; await act(async () => { result.current.swapToken(ethParams); }); await waitFor(() => { expect(sdk.actions.swapToken).toHaveBeenCalledWith(ethParams); }); }); it('should pass polygon token formats correctly', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); // Test with Polygon tokens const polygonParams = { sellToken: 'eip155:137/erc20:0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', // USDC on Polygon buyToken: 'eip155:137/native', // MATIC sellAmount: '1000000', // 1 USDC }; await act(async () => { result.current.swapToken(polygonParams); }); await waitFor(() => { expect(sdk.actions.swapToken).toHaveBeenCalledWith(polygonParams); }); }); it('should return the swap token result on success', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { sellToken: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', buyToken: 'eip155:10/native', sellAmount: '1000000', }; let response; await act(async () => { response = await result.current.swapTokenAsync(params); }); expect(response).toBe(swapTokenMock); }); it('should handle errors when swapToken fails', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(vi.fn()); (sdk.actions.swapToken as Mock).mockRejectedValue( new Error('Swap token failed'), ); const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { sellToken: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', buyToken: 'eip155:10/native', sellAmount: '1000000', }; await act(async () => { result.current.swapToken(params); }); await waitFor(() => { expect(result.current.isError).toBe(true); }); consoleSpy.mockRestore(); }); it('should handle mutation states correctly', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); expect(result.current.isPending).toBe(false); expect(result.current.isError).toBe(false); expect(result.current.isSuccess).toBe(false); const params = { sellToken: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', buyToken: 'eip155:10/native', sellAmount: '1000000', }; act(() => { result.current.swapToken(params); }); await waitFor(() => { expect(result.current.isSuccess).toBe(true); }); }); it('should handle network error correctly', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(vi.fn()); (sdk.actions.swapToken as Mock).mockRejectedValue( new Error('Network error'), ); const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { sellToken: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', buyToken: 'eip155:10/native', sellAmount: '1000000', }; await act(async () => { try { await result.current.swapTokenAsync(params); } catch (error) { expect(error).toBeInstanceOf(Error); expect((error as Error).message).toBe('Network error'); } }); consoleSpy.mockRestore(); }); it('should handle large sellAmount values', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { sellToken: 'eip155:1/erc20:0xA0b86a33E6441D5D56b5FF87d65f2B8e9F90C54c', buyToken: 'eip155:1/native', sellAmount: '999999999999999999999999', // Very large amount }; await act(async () => { result.current.swapToken(params); }); await waitFor(() => { expect(sdk.actions.swapToken).toHaveBeenCalledWith(params); }); }); it('should handle decimal sellAmount values correctly', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params = { sellToken: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', buyToken: 'eip155:10/native', sellAmount: '1500000', // 1.5 USDC (with 6 decimals) }; await act(async () => { result.current.swapToken(params); }); await waitFor(() => { expect(sdk.actions.swapToken).toHaveBeenCalledWith(params); }); }); it('should reset mutation state on multiple calls', async () => { const { result } = renderHook(() => useSwapToken(), { wrapper: createWrapper(), }); const params1 = { sellToken: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', sellAmount: '1000000', }; const params2 = { buyToken: 'eip155:10/native', sellAmount: '2000000', }; // First call await act(async () => { result.current.swapToken(params1); }); await waitFor(() => { expect(result.current.isSuccess).toBe(true); }); // Reset and second call await act(async () => { result.current.reset(); }); await waitFor(() => { expect(result.current.isSuccess).toBe(false); expect(result.current.isError).toBe(false); }); await act(async () => { result.current.swapToken(params2); }); await waitFor(() => { expect(result.current.isSuccess).toBe(true); expect(sdk.actions.swapToken).toHaveBeenCalledWith(params2); }); }); });