import { act } from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest'; import { useShowCallsStatus } from 'wagmi/experimental'; import { useTransactionContext } from './TransactionProvider'; import { TransactionToast } from './TransactionToast'; vi.mock('./TransactionProvider', () => ({ useTransactionContext: vi.fn(), })); vi.mock('wagmi', () => ({ useAccount: vi.fn(), useConnect: vi.fn(), useConfig: vi.fn(), useChainId: vi.fn(), })); vi.mock('wagmi/experimental', () => ({ useShowCallsStatus: vi.fn(), })); describe('TransactionToast', () => { beforeEach(() => { vi.clearAllMocks(); (useShowCallsStatus as Mock).mockReturnValue({ showCallsStatus: vi.fn(), }); }); it('renders children correctly', async () => { (useTransactionContext as Mock).mockReturnValue({ isLoading: true, isToastVisible: true, lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render( Transaction Toast Content , ); }); const contentElement = screen.getByText('Transaction Toast Content'); expect(contentElement).toBeInTheDocument(); }); it('renders default children correctly', async () => { (useTransactionContext as Mock).mockReturnValue({ isLoading: true, isToastVisible: true, lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(); }); expect(screen.getByText('Transaction in progress')).toBeInTheDocument(); }); it('does not render when not visible', async () => { (useTransactionContext as Mock).mockReturnValue({ errorMessage: '', isLoading: false, isToastVisible: false, receipt: null, setIsToastVisible: vi.fn(), transactionHash: '', transactionId: '', lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Test Message); }); expect(screen.queryByText('Test Message')).toBeNull(); }); it('closes when the close button is clicked', async () => { const setIsToastVisible = vi.fn(); (useTransactionContext as Mock).mockReturnValue({ errorMessage: '', isLoading: false, isToastVisible: true, receipt: null, setIsToastVisible, transactionHash: '0x123', transactionId: '', lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Test Message); }); fireEvent.click(screen.getByTestId('ockCloseButton')); expect(setIsToastVisible).toHaveBeenCalledWith(false); }); it('displays loading state correctly', async () => { (useTransactionContext as Mock).mockReturnValue({ isLoading: true, isToastVisible: true, transactionHash: '', errorMessage: '', lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Transaction in progress); }); expect(screen.getByText('Transaction in progress')).toBeInTheDocument(); }); it('displays transaction hash when available', async () => { const mockTransactionHash = '0x123'; (useTransactionContext as Mock).mockReturnValue({ isLoading: false, isToastVisible: true, transactionHash: mockTransactionHash, errorMessage: '', lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Transaction completed); }); expect(screen.getByText('Transaction completed')).toBeInTheDocument(); }); it('displays error message when present', async () => { const setIsToastVisible = vi.fn(); const mockErrorMessage = 'Transaction failed'; (useTransactionContext as Mock).mockReturnValue({ isLoading: false, isToastVisible: true, transactionHash: '', errorMessage: mockErrorMessage, setIsToastVisible, lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Error occurred); }); expect(screen.getByText('Error occurred')).toBeInTheDocument(); }); it('does not render when in progress', async () => { (useTransactionContext as Mock).mockReturnValue({ isLoading: false, isToastVisible: true, transactionHash: '', errorMessage: '', receipt: null, transactionId: '', lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(In Progress); }); expect(screen.queryByText('In Progress')).not.toBeInTheDocument(); }); it('applies correct position class for bottom-right', async () => { (useTransactionContext as Mock).mockReturnValue({ isLoading: false, isToastVisible: true, transactionHash: '0x123', errorMessage: '', receipt: null, transactionId: 'test-id', lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Test); }); const toastElement = screen.getByTestId('ockToastViewport'); expect(toastElement).toHaveClass('bottom-5 left-3/4'); }); it('applies correct position class for top-right', async () => { (useTransactionContext as Mock).mockReturnValue({ isLoading: false, isToastVisible: true, transactionHash: '0x123', errorMessage: '', receipt: null, transactionId: 'test-id', lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Test); }); const toastElement = screen.getByTestId('ockToastViewport'); expect(toastElement).toHaveClass('top-[100px] left-3/4'); }); it('applies correct position class for top-center', async () => { (useTransactionContext as Mock).mockReturnValue({ isLoading: false, isToastVisible: true, transactionHash: '0x123', errorMessage: '', receipt: null, transactionId: 'test-id', lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Test); }); const toastElement = screen.getByTestId('ockToastViewport'); expect(toastElement).toHaveClass('top-[100px] left-2/4'); }); it('applies default position class when not specified', async () => { (useTransactionContext as Mock).mockReturnValue({ isLoading: false, isToastVisible: true, transactionHash: '0x123', errorMessage: '', receipt: null, transactionId: 'test-id', lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Test); }); const toastElement = screen.getByTestId('ockToastViewport'); expect(toastElement).toHaveClass('bottom-5 left-2/4'); }); it('hides toast after specified duration when receipt is available', async () => { vi.useFakeTimers(); const setIsToastVisible = vi.fn(); (useTransactionContext as Mock).mockReturnValue({ isLoading: false, isToastVisible: true, transactionHash: '', errorMessage: '', receipt: {}, setIsToastVisible, lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Test); }); await act(async () => { vi.advanceTimersByTime(2000); }); expect(setIsToastVisible).toHaveBeenCalledWith(false); vi.useRealTimers(); }); it('hides toast after specified duration when error message is present', async () => { vi.useFakeTimers(); const setIsToastVisible = vi.fn(); (useTransactionContext as Mock).mockReturnValue({ isLoading: false, isToastVisible: true, transactionHash: '', errorMessage: 'Error', receipt: null, setIsToastVisible, lifecycleStatus: { statusName: 'test-status', }, }); await act(async () => { render(Test); }); await act(async () => { vi.advanceTimersByTime(2000); }); expect(setIsToastVisible).toHaveBeenCalledWith(false); vi.useRealTimers(); }); });