import { fireEvent, render, screen } from '@testing-library/react'; import { beforeEach, describe, expect, it, Mock, vi } from 'vitest'; import type { WalletAdvancedQrReceiveProps, WalletAdvancedSwapProps, } from '../types'; import { WalletDropdownContent } from './WalletDropdownContent'; import { useWalletContext } from './WalletProvider'; import { usePortfolio } from '../hooks/usePortfolio'; import { useAccount } from 'wagmi'; vi.mock('wagmi', () => ({ useAccount: vi.fn(), })); vi.mock('@/wallet/hooks/usePortfolio', () => ({ usePortfolio: vi.fn(), })); vi.mock('./WalletAdvancedProvider', () => ({ useWalletAdvancedContext: vi.fn(), WalletAdvancedProvider: ({ children }: { children: React.ReactNode }) => ( <>{children} ), })); vi.mock('./WalletAdvancedQrReceive', () => ({ WalletAdvancedQrReceive: ({ classNames }: WalletAdvancedQrReceiveProps) => (
WalletAdvancedQrReceive
), })); vi.mock('./WalletAdvancedSwap', () => ({ WalletAdvancedSwap: ({ from, to, classNames }: WalletAdvancedSwapProps) => (
WalletAdvancedSwap
), })); vi.mock('./wallet-advanced-send/components/Send', () => ({ Send: ({ className }: { className?: string }) => (
WalletAdvancedSend
), })); vi.mock('./WalletProvider', () => ({ useWalletContext: vi.fn(), WalletProvider: ({ children }: { children: React.ReactNode }) => ( <>{children} ), })); describe('WalletDropdownContent', () => { const mockUseWalletContext = useWalletContext as ReturnType; const defaultMockUseWalletAdvancedContext = { activeFeature: null, isActiveFeatureClosing: false, tokenHoldings: [], animations: { container: '', content: '', }, }; beforeEach(() => { vi.clearAllMocks(); (usePortfolio as Mock).mockReturnValue({ data: { tokenBalances: [], }, }); (useAccount as Mock).mockReturnValue({ address: '0x123', }); }); it('renders null when isSubComponentOpen is false', () => { mockUseWalletContext.mockReturnValue({ isSubComponentOpen: false, ...defaultMockUseWalletAdvancedContext, }); render(
WalletDropdownContent
, ); expect( screen.queryByTestId('ockWalletDropdownContent'), ).not.toBeInTheDocument(); }); it('renders WalletDropdownContent with correct animations when isSubComponentClosing is false and showSubComponentAbove is false', () => { mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, isSubComponentClosing: false, showSubComponentAbove: false, ...defaultMockUseWalletAdvancedContext, animations: { container: 'fade-in slide-in-from-top-1.5 animate-in duration-300 ease-out', content: 'fade-in slide-in-from-top-2.5 animate-in fill-mode-forwards duration-300 ease-out', }, }); render(
WalletDropdownContent
, ); expect(screen.getByTestId('ockWalletDropdownContent')).toBeDefined(); expect(screen.queryByTestId('ockWalletDropdownContent')).toHaveClass( 'fade-in slide-in-from-top-1.5 animate-in duration-300 ease-out', ); expect( screen.queryByTestId('ockWalletAdvancedQrReceive'), ).not.toBeInTheDocument(); expect( screen.queryByTestId('ockWalletAdvancedSwap'), ).not.toBeInTheDocument(); }); it('renders WalletDropdownContent with correct animations when isSubComponentClosing is false and showSubComponentAbove is true', () => { mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, isSubComponentClosing: false, showSubComponentAbove: true, ...defaultMockUseWalletAdvancedContext, animations: { container: 'fade-in slide-in-from-bottom-1.5 animate-in duration-300 ease-out', content: 'fade-in slide-in-from-bottom-2.5 animate-in fill-mode-forwards duration-300 ease-out', }, }); render(
WalletDropdownContent
, ); expect(screen.getByTestId('ockWalletDropdownContent')).toBeDefined(); expect(screen.queryByTestId('ockWalletDropdownContent')).toHaveClass( 'fade-in slide-in-from-bottom-1.5 animate-in duration-300 ease-out', ); expect( screen.queryByTestId('ockWalletAdvancedQrReceive'), ).not.toBeInTheDocument(); expect( screen.queryByTestId('ockWalletAdvancedSwap'), ).not.toBeInTheDocument(); }); it('closes WalletDropdownContent with correct animations when isSubComponentClosing is true and showSubComponentAbove is false', () => { mockUseWalletContext.mockReturnValue({ isSubComponentClosing: true, showSubComponentAbove: false, ...defaultMockUseWalletAdvancedContext, animations: { container: 'fade-out slide-out-to-top-1.5 animate-out fill-mode-forwards ease-in-out', content: '', }, }); render(
WalletDropdownContent
, ); expect(screen.getByTestId('ockWalletDropdownContent')).toBeDefined(); expect(screen.queryByTestId('ockWalletDropdownContent')).toHaveClass( 'fade-out slide-out-to-top-1.5 animate-out fill-mode-forwards ease-in-out', ); expect( screen.queryByTestId('ockWalletAdvancedQrReceive'), ).not.toBeInTheDocument(); expect( screen.queryByTestId('ockWalletAdvancedSwap'), ).not.toBeInTheDocument(); }); it('closes WalletDropdownContent with correct animations when isSubComponentClosing is true and showSubComponentAbove is true', () => { mockUseWalletContext.mockReturnValue({ isSubComponentClosing: true, showSubComponentAbove: true, ...defaultMockUseWalletAdvancedContext, animations: { container: 'fade-out slide-out-to-bottom-1.5 animate-out fill-mode-forwards ease-in-out', content: '', }, }); render(
WalletDropdownContent
, ); expect(screen.getByTestId('ockWalletDropdownContent')).toBeDefined(); expect(screen.queryByTestId('ockWalletDropdownContent')).toHaveClass( 'fade-out slide-out-to-bottom-1.5 animate-out fill-mode-forwards ease-in-out', ); expect( screen.queryByTestId('ockWalletAdvancedQrReceive'), ).not.toBeInTheDocument(); expect( screen.queryByTestId('ockWalletAdvancedSwap'), ).not.toBeInTheDocument(); }); it('closes WalletDropdownContent when mobile tray is closed', () => { const setIsSubComponentOpen = vi.fn(); mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, setIsSubComponentOpen, breakpoint: 'sm', }); render(
WalletDropdownContent
, ); fireEvent.keyDown(document, { key: 'Escape', }); expect(setIsSubComponentOpen).toHaveBeenCalledWith(false); }); it('handles animation end when closing', () => { const setIsSubComponentOpen = vi.fn(); const setIsSubComponentClosing = vi.fn(); mockUseWalletContext.mockReturnValue({ ...defaultMockUseWalletAdvancedContext, isSubComponentOpen: true, setIsSubComponentOpen, isSubComponentClosing: true, setIsSubComponentClosing, }); render(
WalletDropdownContent
, ); const content = screen.getByTestId('ockWalletDropdownContent'); fireEvent.animationEnd(content); expect(setIsSubComponentOpen).toHaveBeenCalledWith(false); expect(setIsSubComponentClosing).toHaveBeenCalledWith(false); }); it('renders WalletAdvancedQrReceive when activeFeature is qr', () => { mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, ...defaultMockUseWalletAdvancedContext, activeFeature: 'qr', }); render(
WalletDropdownContent
, ); expect(screen.getByTestId('ockWalletAdvancedQrReceive')).toBeDefined(); expect( screen.queryByTestId('ockWalletAdvancedQrReceive'), ).toBeInTheDocument(); expect( screen.queryByTestId('ockWalletAdvancedSwap'), ).not.toBeInTheDocument(); expect( screen.queryByTestId('ockWalletAdvancedSend'), ).not.toBeInTheDocument(); }); it('renders WalletAdvancedSwap when activeFeature is swap', () => { mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, ...defaultMockUseWalletAdvancedContext, activeFeature: 'swap', }); render(
WalletDropdownContent
, ); expect(screen.getByTestId('ockWalletAdvancedSwap')).toBeDefined(); expect(screen.queryByTestId('ockWalletAdvancedSwap')).toBeInTheDocument(); expect( screen.queryByTestId('ockWalletAdvancedQrReceive'), ).not.toBeInTheDocument(); expect( screen.queryByTestId('ockWalletAdvancedSend'), ).not.toBeInTheDocument(); }); it('renders WalletAdvancedSend when activeFeature is send', () => { mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, ...defaultMockUseWalletAdvancedContext, activeFeature: 'send', }); render(
WalletDropdownContent
, ); expect(screen.getByTestId('ockWalletAdvancedSend')).toBeDefined(); expect(screen.queryByTestId('ockWalletAdvancedSend')).toBeInTheDocument(); expect( screen.queryByTestId('ockWalletAdvancedQrReceive'), ).not.toBeInTheDocument(); expect( screen.queryByTestId('ockWalletAdvancedSwap'), ).not.toBeInTheDocument(); }); it('handles empty tokenBalances gracefully', () => { (usePortfolio as Mock).mockReturnValue({ data: { tokenBalances: null, }, }); mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, ...defaultMockUseWalletAdvancedContext, activeFeature: 'swap', isSubComponentClosing: false, }); render(
WalletDropdownContent
, ); const swapComponent = screen.getByTestId('ockWalletAdvancedSwap'); const props = JSON.parse( swapComponent.getAttribute('data-props') as string, ); expect(props.from).toEqual([]); }); it('correctly maps token balances to the swap component', () => { const mockTokenBalances = [ { address: '0x123', chainId: 1, symbol: 'TEST', decimals: 18, image: 'test.png', name: 'Test Token', }, { address: '0x456', chainId: 2, symbol: 'TEST2', decimals: 6, image: 'test2.png', name: 'Test Token 2', }, ]; (usePortfolio as Mock).mockReturnValue({ data: { tokenBalances: mockTokenBalances, }, }); mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, ...defaultMockUseWalletAdvancedContext, activeFeature: 'swap', isSubComponentClosing: false, }); render(
WalletDropdownContent
, ); const swapComponent = screen.getByTestId('ockWalletAdvancedSwap'); const props = JSON.parse( swapComponent.getAttribute('data-props') as string, ); expect(props.from).toEqual( mockTokenBalances.map((token) => ({ address: token.address, chainId: token.chainId, symbol: token.symbol, decimals: token.decimals, image: token.image, name: token.name, })), ); }); it('applies custom classNames to components', () => { mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, isSubComponentClosing: false, showSubComponentAbove: false, ...defaultMockUseWalletAdvancedContext, activeFeature: 'qr', }); const customClassNames = { container: 'custom-container', qr: { container: 'custom-qr-container', }, swap: { container: 'custom-swap-container', }, }; const { rerender } = render(
Content
, ); expect(screen.getByTestId('ockWalletDropdownContent')).toHaveClass( 'custom-container', ); // Verify both rendered state and passed props const qrComponent = screen.getByTestId('ockWalletAdvancedQrReceive'); expect(qrComponent).toHaveClass('custom-qr-container'); expect(qrComponent).toHaveProperty('className', 'custom-qr-container'); mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, isSubComponentClosing: false, showSubComponentAbove: false, ...defaultMockUseWalletAdvancedContext, activeFeature: 'swap', }); rerender(
Content
, ); const swapComponent = screen.getByTestId('ockWalletAdvancedSwap'); expect(swapComponent).toHaveClass('custom-swap-container'); expect(swapComponent).toHaveProperty('className', 'custom-swap-container'); }); it('renders BottomSheet when breakpoint is sm', () => { mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, isSubComponentClosing: false, breakpoint: 'sm', }); render(
WalletDropdownContent
, ); expect(screen.getByTestId('ockSheet')).toBeDefined(); }); it('applies custom classNames to BottomSheet when breakpoint is sm', () => { mockUseWalletContext.mockReturnValue({ isSubComponentOpen: true, isSubComponentClosing: false, breakpoint: 'sm', ...defaultMockUseWalletAdvancedContext, activeFeature: 'qr', }); const customClassNames = { container: 'custom-container', }; render(
Content
, ); expect(screen.getByTestId('ockSheet')).toHaveClass('custom-container'); }); });