import { fireEvent, render, screen } from '@testing-library/react'; import { type Mock, afterEach, beforeEach, describe, expect, it, vi, } from 'vitest'; import { useAccount, useConnect } from 'wagmi'; import { SignatureButton } from '../components/SignatureButton'; import { useSignatureContext } from '../components/SignatureProvider'; vi.mock('wagmi', () => ({ useAccount: vi.fn(), useConnect: vi.fn(), useConnectors: vi.fn(() => ({ connectors: [{ id: 'mockConnector' }] })), })); vi.mock('../components/SignatureProvider', () => ({ useSignatureContext: vi.fn(), })); describe('SignatureButton', () => { const mockHandleSign = vi.fn(); beforeEach(() => { (useConnect as Mock).mockReturnValue({ connectors: [{ id: 'mockConnector' }], connect: vi.fn(), status: 'idle', }); (useSignatureContext as Mock).mockReturnValue({ lifecycleStatus: { statusName: 'init', }, handleSign: mockHandleSign, }); }); afterEach(() => { vi.clearAllMocks(); }); it('should render ConnectWallet when no address is connected', () => { (useAccount as Mock).mockReturnValue({ address: undefined, status: 'disconnected', }); render(); expect(screen.getByText('Connect Wallet')).toBeInTheDocument(); }); it('should render connect label when passed in', () => { (useAccount as Mock).mockReturnValue({ address: undefined, status: 'disconnected', }); render(); expect(screen.getByText('Disconnected Label')).toBeInTheDocument(); }); it('should render sign button when address is connected', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); render(); expect(screen.getByText('Sign Message')).toBeInTheDocument(); }); it('should render pending message when pending', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); (useSignatureContext as Mock).mockReturnValue({ lifecycleStatus: { statusName: 'pending', }, }); render(); expect(screen.getByText('Signing...')).toBeInTheDocument(); }); it('should render pending label when pending', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); (useSignatureContext as Mock).mockReturnValue({ lifecycleStatus: { statusName: 'pending', }, }); render(); expect(screen.getByText('Pending')).toBeInTheDocument(); }); it('should render error message when error', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); (useSignatureContext as Mock).mockReturnValue({ lifecycleStatus: { statusName: 'error', }, }); render(); expect(screen.getByText('Try again')).toBeInTheDocument(); }); it('should render error label when error', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); (useSignatureContext as Mock).mockReturnValue({ lifecycleStatus: { statusName: 'error', }, }); render( , ); expect(screen.getByText('Try more things')).toBeInTheDocument(); }); it('should render success message when success', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); (useSignatureContext as Mock).mockReturnValue({ lifecycleStatus: { statusName: 'success', }, }); render(); expect(screen.getByText('Signed')).toBeInTheDocument(); }); it('should render success label when success', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); (useSignatureContext as Mock).mockReturnValue({ lifecycleStatus: { statusName: 'success', }, }); render(); expect(screen.getByText('done')).toBeInTheDocument(); }); it('should call handleSign when clicked', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); render(); fireEvent.click(screen.getByText('Sign Message')); expect(mockHandleSign).toHaveBeenCalled(); }); it('should apply custom className', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); render(); expect(screen.getByRole('button')).toHaveClass('custom-class'); }); it('should disable button when disabled prop is true', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); render(); expect(screen.getByRole('button')).toBeDisabled(); }); it('should use default label when none provided', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); render(); expect(screen.getByText('Sign')).toBeInTheDocument(); }); it('should use custom render function when provided', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); const mockContext = { lifecycleStatus: { statusName: 'init', }, handleSign: mockHandleSign, }; (useSignatureContext as Mock).mockReturnValue(mockContext); const customRender = vi.fn(({ label, onClick, context }) => { expect(context).toBe(mockContext); return ( ); }); render(); expect(customRender).toHaveBeenCalledWith({ label: 'Sign Message', onClick: mockHandleSign, context: mockContext, }); expect(screen.getByTestId('custom-button')).toBeInTheDocument(); expect(screen.getByText('Custom: Sign Message')).toBeInTheDocument(); }); it('should trigger handleSign when custom rendered button is clicked', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); (useSignatureContext as Mock).mockReturnValue({ lifecycleStatus: { statusName: 'init', }, handleSign: mockHandleSign, }); render( ( )} />, ); fireEvent.click(screen.getByTestId('custom-button')); expect(mockHandleSign).toHaveBeenCalled(); }); it('should pass updated label to render function based on lifecycle status', () => { (useAccount as Mock).mockReturnValue({ address: '0x123' }); (useSignatureContext as Mock).mockReturnValue({ lifecycleStatus: { statusName: 'success', }, handleSign: mockHandleSign, }); const customRender = vi.fn(({ label }) => (
{label}
)); render( , ); expect(screen.getByTestId('rendered-label')).toHaveTextContent( 'Successfully Signed', ); }); });