import { setOnchainKitConfig } from '@/core/OnchainKitConfig';
import { FundEvent } from '@/core/analytics/types';
import { render, screen, waitFor } from '@testing-library/react';
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest';
import { quoteResponseDataMock } from '../mocks';
import { FundCardProvider, useFundContext } from './FundCardProvider';
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve(quoteResponseDataMock),
}),
) as Mock;
let mockSendAnalytics: Mock;
vi.mock('@/core/analytics/hooks/useAnalytics', () => ({
useAnalytics: () => ({
sendAnalytics: mockSendAnalytics,
}),
}));
const TestComponent = () => {
const context = useFundContext();
return (
{context.asset}
{context.exchangeRate}
{context.exchangeRateLoading ? 'loading' : 'not-loading'}
);
};
describe('FundCardProvider', () => {
beforeEach(() => {
setOnchainKitConfig({ apiKey: '123456789' });
vi.clearAllMocks();
});
describe('analytics', () => {
beforeEach(() => {
mockSendAnalytics = vi.fn();
});
it('tracks fund amount changes for fiat', async () => {
const { result } = await act(async () =>
renderHook(() => useFundContext(), {
wrapper: ({ children }) => (
{children}
),
}),
);
act(() => {
result.current.setFundAmountFiat('100.00');
});
expect(mockSendAnalytics).toHaveBeenCalledWith(
FundEvent.FundAmountChanged,
{
amount: 100,
currency: 'USD',
},
);
});
it('does not track fund amount changes when amount is invalid', async () => {
const { result } = await act(async () =>
renderHook(() => useFundContext(), {
wrapper: ({ children }) => (
{children}
),
}),
);
act(() => {
result.current.setFundAmountFiat('invalid');
});
expect(mockSendAnalytics).not.toHaveBeenCalled();
});
it('tracks payment method selection', async () => {
const { result } = await act(async () =>
renderHook(() => useFundContext(), {
wrapper: ({ children }) => (
{children}
),
}),
);
const mockPaymentMethod = {
id: 'debit_card',
name: 'Debit Card',
description: 'Pay with debit card',
icon: 'card-icon',
disabled: false,
};
act(() => {
result.current.setSelectedPaymentMethod(mockPaymentMethod);
});
expect(mockSendAnalytics).toHaveBeenCalledWith(
FundEvent.FundOptionSelected,
{
option: 'debit_card',
},
);
});
it('handles fund amount changes with different currencies', async () => {
const { result } = await act(async () =>
renderHook(() => useFundContext(), {
wrapper: ({ children }) => (
{children}
),
}),
);
act(() => {
result.current.setFundAmountFiat('50.00');
});
expect(mockSendAnalytics).toHaveBeenCalledWith(
FundEvent.FundAmountChanged,
{
amount: 50,
currency: 'GBP',
},
);
});
});
it('provides default context values', async () => {
await act(async () => {
render(
,
);
});
expect(screen.getByTestId('selected-asset').textContent).toBe('BTC');
});
it('fetches and sets exchange rate on mount', async () => {
act(() => {
render(
,
);
});
// Check initial loading state
expect(screen.getByTestId('loading-state').textContent).toBe('loading');
// Wait for exchange rate to be set
await waitFor(() => {
expect(screen.getByTestId('exchange-rate').textContent).toBe(
'0.0008333333333333334',
);
expect(screen.getByTestId('loading-state').textContent).toBe(
'not-loading',
);
});
// Verify fetch was called with correct parameters
expect(fetch).toHaveBeenCalledWith(
expect.stringContaining('/quote'),
expect.objectContaining({
method: 'POST',
body: expect.stringContaining('"purchase_currency":"BTC"'),
}),
);
});
it('throws error when useFundContext is used outside of FundCardProvider', () => {
const TestOutsideProvider = () => {
useFundContext();
return Test
;
};
expect(() => render()).toThrow(
'useFundContext must be used within a FundCardProvider',
);
});
it('handles exchange rate fetch error', async () => {
const mockError = new Error('Failed to fetch exchange rate');
const mockOnError = vi.fn();
global.fetch = vi.fn(() => Promise.reject(mockError)) as Mock;
render(
,
);
await waitFor(() => {
expect(mockOnError).toHaveBeenCalledWith({
errorType: 'handled_error',
code: 'EXCHANGE_RATE_ERROR',
debugMessage: 'Failed to fetch exchange rate',
});
});
});
});