import { setOnchainKitConfig } from '@/core/OnchainKitConfig'; import { openPopup } from '@/internal/utils/openPopup'; import '@testing-library/jest-dom'; import { act } from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest'; import { useAccount } from 'wagmi'; import { useFundCardFundingUrl } from '../hooks/useFundCardFundingUrl'; import { optionsResponseDataMock, quoteResponseDataMock } from '../mocks'; import type { PresetAmountInputs } from '../types'; import { fetchOnrampOptions } from '../utils/fetchOnrampOptions'; import { fetchOnrampQuote } from '../utils/fetchOnrampQuote'; import { getFundingPopupSize } from '../utils/getFundingPopupSize'; import { FundCard } from './FundCard'; import { FundCardProvider, useFundContext } from './FundCardProvider'; const mockUpdateInputWidth = vi.fn(); vi.mock('../../internal/hooks/useInputResize', () => ({ useInputResize: () => mockUpdateInputWidth, })); vi.mock('../hooks/useGetFundingUrl', () => ({ useGetFundingUrl: vi.fn(), })); vi.mock('../hooks/useFundCardFundingUrl', () => ({ useFundCardFundingUrl: vi.fn(), })); vi.mock('@/core/hooks/useOnchainKit', () => ({ useOnchainKit: () => ({ apiKey: 'mock-api-key', sessionId: 'mock-session-id', config: {}, }), })); vi.mock('@/internal/utils/openPopup', () => ({ openPopup: vi.fn(), })); vi.mock('../utils/getFundingPopupSize', () => ({ getFundingPopupSize: vi.fn(), })); vi.mock('../hooks/useFundCardSetupOnrampEventListeners'); vi.mock('../utils/fetchOnrampQuote'); vi.mock('../utils/fetchOnrampOptions'); vi.mock('wagmi', () => ({ useAccount: vi.fn(), useConnect: vi.fn(), })); vi.mock('../../wallet/components/ConnectWallet', () => ({ ConnectWallet: ({ className }: { className?: string }) => (
Connect Wallet
), })); // Test component to access context values const TestComponent = () => { const { fundAmountFiat, fundAmountCrypto, exchangeRate, exchangeRateLoading, setFundAmountFiat, setSelectedInputType, } = useFundContext(); return (
{fundAmountFiat} {fundAmountCrypto} {exchangeRate} {exchangeRateLoading ? 'loading' : 'not-loading'}
); }; const renderComponent = async (presetAmountInputs?: PresetAmountInputs) => { await act(async () => { render( , ); }); }; describe('FundCard', () => { beforeEach(() => { vi.clearAllMocks(); setOnchainKitConfig({ apiKey: 'mock-api-key' }); mockUpdateInputWidth.mockClear(); (getFundingPopupSize as Mock).mockImplementation(() => ({ height: 200, width: 100, })); (useFundCardFundingUrl as Mock).mockReturnValue('mock-funding-url'); (fetchOnrampQuote as Mock).mockResolvedValue(quoteResponseDataMock); (fetchOnrampOptions as Mock).mockResolvedValue(optionsResponseDataMock); (useAccount as Mock).mockReturnValue({ address: '0x123', }); }); it('renders without crashing', async () => { await renderComponent(); expect(screen.getByTestId('ockFundCardHeader')).toBeInTheDocument(); expect(screen.getByTestId('ockFundButtonTextContent')).toBeInTheDocument(); }); it('displays the correct header text', async () => { await renderComponent(); expect(screen.getByTestId('ockFundCardHeader')).toHaveTextContent( 'Buy BTC', ); }); it('displays the correct button text', async () => { await renderComponent(); expect(screen.getByTestId('ockFundButtonTextContent')).toHaveTextContent( 'Buy', ); }); it('handles input changes for fiat amount', async () => { await renderComponent(); const input = screen.getByTestId('ockTextInput_Input') as HTMLInputElement; await act(async () => { fireEvent.change(input, { target: { value: '100' } }); }); expect(input.value).toBe('100'); }); it('switches input type from fiat to crypto', async () => { await renderComponent(); await waitFor(() => { const switchButton = screen.getByTestId('ockAmountTypeSwitch'); fireEvent.click(switchButton); }); expect(screen.getByTestId('ockCurrencySpan')).toHaveTextContent('BTC'); }); it('disables the submit button when fund amount is zero and type is fiat', async () => { await renderComponent(); const setFiatAmountButton = screen.getByTestId('set-fiat-amount'); fireEvent.click(setFiatAmountButton); const button = screen.getByTestId('ockFundButton'); expect(button).toBeDisabled(); }); it('disables the submit button when fund amount is zero and input type is crypto', async () => { await renderComponent(); const setCryptoInputTypeButton = screen.getByTestId( 'set-crypto-input-type', ); fireEvent.click(setCryptoInputTypeButton); const button = screen.getByTestId('ockFundButton'); expect(button).toBeDisabled(); }); it('enables the submit button when fund amount is greater than zero and type is fiat', async () => { await renderComponent(); const setFiatAmountButton = screen.getByTestId('set-fiat-amount'); fireEvent.click(setFiatAmountButton); await waitFor(() => { expect(screen.getByTestId('loading-state').textContent).toBe( 'not-loading', ); const input = screen.getByTestId('ockTextInput_Input'); fireEvent.change(input, { target: { value: '1000' } }); const button = screen.getByTestId('ockFundButton'); expect(button).not.toBeDisabled(); }); }); it('enables the submit button when fund amount is greater than zero and type is crypto', async () => { await renderComponent(); const setCryptoInputTypeButton = screen.getByTestId( 'set-crypto-input-type', ); fireEvent.click(setCryptoInputTypeButton); await waitFor(() => { expect(screen.getByTestId('loading-state').textContent).toBe( 'not-loading', ); const input = screen.getByTestId('ockTextInput_Input'); fireEvent.change(input, { target: { value: '1000' } }); const button = screen.getByTestId('ockFundButton'); expect(button).not.toBeDisabled(); }); }); it('shows loading state when submitting', async () => { await renderComponent(); await waitFor(() => { expect(screen.getByTestId('loading-state').textContent).toBe( 'not-loading', ); const input = screen.getByTestId('ockTextInput_Input'); fireEvent.change(input, { target: { value: '1000' } }); const button = screen.getByTestId('ockFundButton'); expect(screen.queryByTestId('ockSpinner')).not.toBeInTheDocument(); act(() => { fireEvent.click(button); }); expect(screen.getByTestId('ockSpinner')).toBeInTheDocument(); }); }); it('sets submit button state to default on popup close', async () => { (openPopup as Mock).mockImplementation(() => ({ closed: true })); await renderComponent(); const button = screen.getByTestId('ockFundButton'); const submitButton = screen.getByTestId('ockFundButton'); await waitFor(() => { expect(screen.getByTestId('loading-state').textContent).toBe( 'not-loading', ); // Simulate entering a valid amount const input = screen.getByTestId( 'ockTextInput_Input', ) as HTMLInputElement; fireEvent.change(input, { target: { value: '100' } }); fireEvent.click(button); // Assert that the submit button state is set to 'default' expect(submitButton).not.toBeDisabled(); }); }); it('renders custom children instead of default children', async () => { await act(async () => { render(
Custom Content
, ); }); expect(screen.getByTestId('custom-child')).toBeInTheDocument(); expect(screen.queryByTestId('ockFundCardHeader')).not.toBeInTheDocument(); }); it('handles preset amount input click correctly', async () => { const presetAmountInputs: PresetAmountInputs = ['12345', '20', '30']; await renderComponent(presetAmountInputs); await waitFor(() => { expect(screen.getByTestId('loading-state').textContent).toBe( 'not-loading', ); // Click the preset amount input const presetAmountInput = screen.getByText('$12,345'); // Verify the input value was updated expect(presetAmountInput).toBeInTheDocument(); }); }); it('passes sessionToken to FundCardProvider', async () => { const sessionToken = 'test-session-token'; await act(async () => { render( , ); }); // Verify the component renders correctly with sessionToken await waitFor(() => { expect(screen.getByTestId('loading-state').textContent).toBe( 'not-loading', ); }); }); it('handles sessionToken correctly in FundCardProvider context', async () => { const sessionToken = 'test-session-token'; const TestSessionComponent = () => { const context = useFundContext(); return
{context.sessionToken}
; }; await act(async () => { render( , ); }); expect(screen.getByTestId('session-token').textContent).toBe(sessionToken); }); });