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 React from 'react'; import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest'; import { useAccount } from 'wagmi'; import { useFundCardFundingUrl } from '../hooks/useFundCardFundingUrl'; import { quoteResponseDataMock } from '../mocks'; import { fetchOnrampQuote } from '../utils/fetchOnrampQuote'; import { getFundingPopupSize } from '../utils/getFundingPopupSize'; import { FundCardProvider, useFundContext } from './FundCardProvider'; import { FundCardSubmitButton } from './FundCardSubmitButton'; vi.mock('@/fund/hooks/useGetFundingUrl', () => ({ useGetFundingUrl: vi.fn(), })); vi.mock('@/fund/hooks/useFundCardFundingUrl', () => ({ useFundCardFundingUrl: vi.fn(), })); vi.mock('@/core/hooks/useOnchainKit', () => ({ useOnchainKit: vi.fn(() => ({ apiKey: 'mock-api-key', sessionId: 'mock-session-id', config: {}, })), })); vi.mock('@/fund/utils/setupOnrampEventListeners', () => ({ setupOnrampEventListeners: vi.fn(), })); vi.mock('@/internal/utils/openPopup', () => ({ openPopup: vi.fn(), })); vi.mock('@/fund/hooks/useFundCardFundingUrl', () => ({ useFundCardFundingUrl: vi.fn(), })); vi.mock('@/fund/hooks/useFundCardSetupOnrampEventListeners', () => ({ useFundCardSetupOnrampEventListeners: vi.fn(), })); vi.mock('@/fund/utils/getFundingPopupSize', () => ({ getFundingPopupSize: vi.fn(), })); vi.mock('@/fund/utils/fetchOnrampQuote'); 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 TestHelperComponent = () => { const { fundAmountFiat, fundAmountCrypto, exchangeRate, exchangeRateLoading, setFundAmountFiat, setFundAmountCrypto, } = useFundContext(); return (
{fundAmountFiat} {fundAmountCrypto} {exchangeRate} {exchangeRateLoading ? 'loading' : 'not-loading'}
); }; describe('FundCardSubmitButton', () => { beforeEach(() => { vi.resetAllMocks(); setOnchainKitConfig({ apiKey: 'mock-api-key' }); (getFundingPopupSize as Mock).mockImplementation(() => ({ height: 200, width: 100, })); (useFundCardFundingUrl as Mock).mockReturnValue('mock-funding-url'); (fetchOnrampQuote as Mock).mockResolvedValue(quoteResponseDataMock); (useAccount as Mock).mockReturnValue({ address: '0x123', }); }); const renderComponent = async () => { return await act(async () => render( , ), ); }; it('renders custom content when `render` prop is provided', async () => { await act(async () => render( ( )} /> , ), ); const customButton = screen.getByTestId('custom-render-button'); expect(customButton).toBeInTheDocument(); expect(customButton).toHaveTextContent('Custom Render - default'); }); it('renders disabled by default when no amount is set', async () => { await renderComponent(); expect(screen.getByTestId('ockFundButton')).toBeDisabled(); }); it('displays "Success" text when in success state', async () => { const SuccessStateWrapper = () => { const { setSubmitButtonState } = useFundContext(); React.useEffect(() => { setSubmitButtonState('success'); }, [setSubmitButtonState]); return ; }; render( , ); const setFiatAmountButton = screen.getByTestId('set-fiat-amount'); fireEvent.click(setFiatAmountButton); await waitFor(() => { expect(screen.getByTestId('ockFundButtonTextContent')).toHaveTextContent( 'Success', ); }); }); it('displays "Something went wrong" text when in error state', async () => { const ErrorStateWrapper = () => { const { setSubmitButtonState } = useFundContext(); React.useEffect(() => { setSubmitButtonState('error'); }, [setSubmitButtonState]); return ; }; render( , ); const setFiatAmountButton = screen.getByTestId('set-fiat-amount'); fireEvent.click(setFiatAmountButton); await waitFor(() => { expect(screen.getByTestId('ockFundButtonTextContent')).toHaveTextContent( 'Something went wrong', ); }); }); it('enables when fiat amount is set', async () => { await renderComponent(); const setFiatAmountButton = screen.getByTestId('set-fiat-amount'); fireEvent.click(setFiatAmountButton); const setCryptoAmountButton = screen.getByTestId('set-crypto-amount'); fireEvent.click(setCryptoAmountButton); await waitFor(() => { expect(screen.getByTestId('ockFundButton')).not.toBeDisabled(); }); }); it('disables when fiat amount is set to zero', async () => { await renderComponent(); const button = screen.getByTestId('set-fiat-amount-zero'); fireEvent.click(button); await waitFor(() => { expect(screen.getByTestId('ockFundButton')).toBeDisabled(); }); }); it('disables when crypto amount is set to zero', async () => { await renderComponent(); const setCryptoAmountButton = screen.getByTestId('set-crypto-amount-zero'); fireEvent.click(setCryptoAmountButton); await waitFor(() => { expect(screen.getByTestId('ockFundButton')).toBeDisabled(); }); }); it('shows loading state when clicked', async () => { await renderComponent(); const setFiatAmountButton = screen.getByTestId('set-fiat-amount'); fireEvent.click(setFiatAmountButton); const setCryptoAmountButton = screen.getByTestId('set-crypto-amount'); fireEvent.click(setCryptoAmountButton); const fundButton = screen.getByTestId('ockFundButton'); fireEvent.click(fundButton); expect(screen.getByTestId('ockSpinner')).toBeInTheDocument(); }); it('shows ConnectWallet when no wallet is connected', async () => { (useAccount as Mock).mockReturnValue({ address: undefined }); await renderComponent(); expect( screen.queryByTestId('ockConnectWallet_Container'), ).toBeInTheDocument(); expect(screen.queryByTestId('ockFundButton')).not.toBeInTheDocument(); }); it('sets submit button state to default on popup close', async () => { vi.useFakeTimers(); (openPopup as Mock).mockImplementation(() => ({ closed: true })); await renderComponent(); const button = screen.getByTestId('ockFundButton'); // Simulate entering a valid amount const setFiatAmountButton = screen.getByTestId('set-fiat-amount'); await act(async () => { fireEvent.click(setFiatAmountButton); }); const setCryptoAmountButton = screen.getByTestId('set-crypto-amount'); await act(async () => { fireEvent.click(setCryptoAmountButton); }); // Click the submit button to trigger loading state await act(async () => { fireEvent.click(button); }); await act(async () => { vi.runOnlyPendingTimers(); }); const submitButton = screen.getByTestId('ockFundButton'); // Assert that the submit button state is set to 'default' expect(submitButton).not.toBeDisabled(); }); it('passes sessionToken to FundButton when available in 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); // Set amounts to enable button const setFiatAmountButton = screen.getByTestId('set-fiat-amount'); await act(async () => { fireEvent.click(setFiatAmountButton); }); // Verify the FundButton is rendered with proper context expect(screen.getByTestId('ockFundButton')).toBeInTheDocument(); }); });