import { act } from 'react'; import { getName } from '@/identity'; import { getSlicedAddress } from '@/identity/utils/getSlicedAddress'; import { renderHook, waitFor } from '@testing-library/react'; import { base } from 'viem/chains'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { validateAddressInput } from '../utils/validateAddressInput'; import { useRecipientState } from './useRecipientState'; vi.mock('@/identity', () => ({ getName: vi.fn(), })); vi.mock('@/identity/utils/getSlicedAddress', () => ({ getSlicedAddress: vi.fn(), })); vi.mock('../utils/validateAddressInput', () => ({ validateAddressInput: vi.fn(), })); const mockAddress = '0x1234567890123456789012345678901234567890'; const mockSlicedAddress = '0x1234...7890'; const mockEnsName = 'test.eth'; describe('useRecipientState', () => { beforeEach(() => { vi.clearAllMocks(); }); it('should initialize with default state', () => { const { result } = renderHook(() => useRecipientState()); expect(result.current.recipientState).toEqual({ phase: 'input', input: '', address: null, displayValue: null, }); expect(result.current.deselectRecipient).toBeDefined(); expect(result.current.updateRecipientInput).toBeDefined(); expect(result.current.validateRecipientInput).toBeDefined(); expect(result.current.selectRecipient).toBeDefined(); }); it('should update recipient input', () => { const { result } = renderHook(() => useRecipientState()); act(() => { result.current.updateRecipientInput('test input'); }); expect(result.current.recipientState.input).toBe('test input'); }); it('should validate valid address input', async () => { vi.mocked(validateAddressInput).mockResolvedValue( mockAddress as `0x${string}`, ); const { result } = renderHook(() => useRecipientState()); act(() => { result.current.validateRecipientInput(mockAddress); }); await waitFor(() => { expect(result.current.recipientState).toEqual({ phase: 'validated', input: mockAddress, address: mockAddress, displayValue: null, }); }); }); it('should handle invalid address input', async () => { vi.mocked(validateAddressInput).mockResolvedValue(null); const { result } = renderHook(() => useRecipientState()); act(() => { result.current.validateRecipientInput('invalid'); }); await waitFor(() => { expect(result.current.recipientState).toEqual({ phase: 'input', input: 'invalid', address: null, displayValue: null, }); }); }); it('should select recipient and resolve ENS name', async () => { vi.mocked(getName).mockResolvedValue(mockEnsName); const { result } = renderHook(() => useRecipientState()); await act(async () => { await result.current.selectRecipient({ phase: 'selected', input: mockAddress, address: mockAddress, displayValue: null, }); }); await waitFor(() => { expect(getName).toHaveBeenCalledWith({ address: mockAddress, chain: base, }); expect(result.current.recipientState.displayValue).toBe(mockEnsName); }); }); it('should select recipient and fallback to sliced address when ENS fails', async () => { vi.mocked(getName).mockRejectedValue(new Error('ENS error')); vi.mocked(getSlicedAddress).mockReturnValue(mockSlicedAddress); const { result } = renderHook(() => useRecipientState()); await act(async () => { result.current.selectRecipient({ phase: 'selected', input: mockAddress, address: mockAddress, displayValue: null, }); }); expect(getName).toHaveBeenCalledWith({ address: mockAddress, chain: base, }); expect(result.current.recipientState).toEqual({ phase: 'selected', input: mockAddress, address: mockAddress, displayValue: mockSlicedAddress, }); }); it('should select recipient and use sliced address when ENS returns null', async () => { vi.mocked(getName).mockResolvedValue(null); vi.mocked(getSlicedAddress).mockReturnValue(mockSlicedAddress); const { result } = renderHook(() => useRecipientState()); await act(async () => { result.current.selectRecipient({ phase: 'selected', input: mockAddress, address: mockAddress, displayValue: null, }); }); expect(getName).toHaveBeenCalledWith({ address: mockAddress, chain: base, }); expect(result.current.recipientState).toEqual({ phase: 'selected', input: mockAddress, address: mockAddress, displayValue: mockSlicedAddress, }); }); it('should deselect recipient from selected phase', async () => { const { result } = renderHook(() => useRecipientState()); await act(async () => { result.current.selectRecipient({ phase: 'selected', input: mockAddress, address: mockAddress, displayValue: mockEnsName, }); }); act(() => { result.current.deselectRecipient(); }); expect(result.current.recipientState).toEqual({ phase: 'validated', input: mockAddress, address: mockAddress, displayValue: null, }); }); it('should deselect recipient from non-selected phase', async () => { const { result } = renderHook(() => useRecipientState()); await act(async () => { result.current.validateRecipientInput(mockAddress); }); act(() => { result.current.deselectRecipient(); }); expect(result.current.recipientState.displayValue).toBeNull(); }); });