import '@testing-library/jest-dom'; import { setOnchainKitConfig } from '@/core/OnchainKitConfig'; import type { AppConfig } from '@/core/types'; import type { MiniKitOptions } from '@/minikit/types'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { render, screen, waitFor } from '@testing-library/react'; import { base } from 'viem/chains'; import { type Mock, afterEach, beforeEach, describe, expect, it, vi, } from 'vitest'; import { http, WagmiProvider, createConfig } from 'wagmi'; import { useConfig } from 'wagmi'; import { mock } from 'wagmi/connectors'; import { OnchainKitProvider } from './OnchainKitProvider'; import { useProviderDependencies } from './internal/hooks/useProviderDependencies'; import { useOnchainKit } from './useOnchainKit'; vi.mock('wagmi', async (importOriginal) => { const actual = (await importOriginal()) as typeof import('wagmi'); return { ...actual, useConfig: vi.fn(), }; }); vi.mock('@/internal/hooks/useProviderDependencies', () => ({ useProviderDependencies: vi.fn(() => ({ providedWagmiConfig: null, providedQueryClient: null, })), })); vi.mock('@/internal/hooks/useTheme', () => ({ useTheme: vi.fn(() => 'default-light'), useThemeRoot: vi.fn(() => 'default-light'), })); vi.mock('@farcaster/miniapp-sdk', () => ({ default: { context: Promise.resolve({ client: { clientFid: null, }, }), }, })); // Mock MiniKitProvider vi.mock('@/minikit/MiniKitProvider', () => ({ MiniKitProvider: vi.fn(({ children, enabled, notificationProxyUrl }) => (
{children}
)), MiniKitContext: { _currentValue: null, }, })); // Mock useSessionStorage vi.mock('usehooks-ts', () => ({ useSessionStorage: vi.fn(() => ['test-session-id', vi.fn()]), })); const queryClient = new QueryClient(); const mockConfig = createConfig({ chains: [base], connectors: [ mock({ accounts: ['0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'], }), ], transports: { [base.id]: http(), }, }); const TestComponent = () => { const { apiKey } = useOnchainKit(); return ( <>
{apiKey}
); }; vi.mock('@/core/OnchainKitConfig', () => ({ setOnchainKitConfig: vi.fn(), ONCHAIN_KIT_CONFIG: { apiKey: null, chain: { name: 'base', id: 8453 }, rpcUrl: null, projectId: null, }, })); describe('OnchainKitProvider', () => { const apiKey = 'test-api-key'; const paymasterUrl = 'https://api.developer.coinbase.com/rpc/v1/base/test-api-key'; const appLogo = ''; const appName = 'Dapp'; beforeEach(() => { vi.clearAllMocks(); (useConfig as Mock).mockReturnValue(mockConfig); (useProviderDependencies as Mock).mockReturnValue({ providedWagmiConfig: mockConfig, providedQueryClient: queryClient, }); }); it('provides the context value correctly', async () => { render( , ); await waitFor(() => { expect(screen.getByText(apiKey)).toBeInTheDocument(); }); }); it('provides the context value correctly without WagmiProvider', async () => { (useProviderDependencies as Mock).mockReturnValue({ providedWagmiConfig: null, providedQueryClient: null, }); render( , ); await waitFor(() => { expect(screen.getByText(apiKey)).toBeInTheDocument(); }); }); it('renders successfully with minimal props', () => { expect(() => { render( , ); }).not.toThrow(); }); it('does not throw an error if api key is not provided', () => { expect(() => { render( , ); }).not.toThrow(); }); it('should wrap children with MiniKitProvider with default options', async () => { render( , ); await waitFor(() => { const miniKitProvider = screen.getByTestId('minikit-provider'); expect(miniKitProvider).toBeInTheDocument(); expect(miniKitProvider).toHaveAttribute('data-enabled', 'true'); }); }); it('should wrap children with MiniKitProvider with custom options', async () => { const miniKitOptions: MiniKitOptions = { enabled: true, notificationProxyUrl: 'https://example.com/proxy', }; render( , ); await waitFor(() => { const miniKitProvider = screen.getByTestId('minikit-provider'); expect(miniKitProvider).toBeInTheDocument(); expect(miniKitProvider).toHaveAttribute('data-enabled', 'true'); expect(miniKitProvider).toHaveAttribute( 'data-notification-proxy-url', 'https://example.com/proxy', ); }); }); it('should call setOnchainKitConfig with the correct values', async () => { render( , ); await waitFor(() => { expect(setOnchainKitConfig).toHaveBeenCalledWith( expect.objectContaining({ apiKey, config: { analytics: true, analyticsUrl: null, appearance: { logo: appLogo, name: appName, mode: 'auto', theme: 'default', }, paymaster: paymasterUrl, wallet: { display: 'classic', preference: 'all', termsUrl: 'https://base.org/terms-of-service', privacyUrl: 'https://base.org/privacy-policy', supportedWallets: { rabby: false, trust: false, frame: false, }, }, }, chain: base, rpcUrl: null, projectId: null, sessionId: 'test-session-id', }), ); }); }); it('should use default values for config when not provided', async () => { render( , ); await waitFor(() => { expect(setOnchainKitConfig).toHaveBeenCalledWith( expect.objectContaining({ config: { analytics: true, analyticsUrl: null, appearance: { logo: appLogo, name: appName, mode: 'auto', theme: 'default', }, paymaster: null, wallet: { display: 'classic', preference: 'all', termsUrl: 'https://base.org/terms-of-service', privacyUrl: 'https://base.org/privacy-policy', supportedWallets: { rabby: false, trust: false, frame: false, }, }, }, }), ); }); }); it('should respect analytics override when provided', async () => { render( , ); await waitFor(() => { expect(setOnchainKitConfig).toHaveBeenCalledWith( expect.objectContaining({ config: expect.objectContaining({ analytics: false, }), }), ); }); }); it('should use custom values when override in config is provided', async () => { const customConfig = { analyticsUrl: 'https://example.com', appearance: { name: 'custom name', logo: 'https://example.com/logo.png', }, paymaster: 'https://example.com', }; render( , ); await waitFor(() => { expect(setOnchainKitConfig).toHaveBeenCalledWith( expect.objectContaining({ apiKey: apiKey, chain: base, config: { analytics: false, analyticsUrl: 'https://example.com', appearance: { name: 'custom name', logo: 'https://example.com/logo.png', mode: 'auto', theme: 'default', }, paymaster: 'https://example.com', wallet: { display: 'classic', preference: 'all', termsUrl: 'https://base.org/terms-of-service', privacyUrl: 'https://base.org/privacy-policy', supportedWallets: { rabby: false, trust: false, frame: false, }, }, }, projectId: null, rpcUrl: null, sessionId: 'test-session-id', }), ); }); }); it('should set default supportedWallets configuration when not provided', async () => { render( , ); await waitFor(() => { expect(setOnchainKitConfig).toHaveBeenCalledWith( expect.objectContaining({ config: expect.objectContaining({ wallet: expect.objectContaining({ supportedWallets: { rabby: false, trust: false, frame: false, }, }), }), }), ); }); }); it('should use custom supportedWallets configuration when provided', async () => { const customConfig: AppConfig = { wallet: { supportedWallets: { rabby: true, trust: true, frame: true, }, }, }; render( , ); await waitFor(() => { expect(setOnchainKitConfig).toHaveBeenCalledWith( expect.objectContaining({ config: expect.objectContaining({ wallet: expect.objectContaining({ supportedWallets: { rabby: true, trust: true, frame: true, }, }), }), }), ); }); }); it('should use partial supportedWallets configuration when provided', async () => { const customConfig: AppConfig = { wallet: { supportedWallets: { trust: true, }, }, }; render( , ); await waitFor(() => { expect(setOnchainKitConfig).toHaveBeenCalledWith( expect.objectContaining({ config: expect.objectContaining({ wallet: expect.objectContaining({ supportedWallets: expect.objectContaining({ trust: true, frame: false, rabby: false, }), }), }), chain: expect.any(Object), sessionId: 'test-session-id', apiKey: null, projectId: null, rpcUrl: null, }), ); }); }); it('should use custom wallet config when provided', async () => { const customConfig: AppConfig = { wallet: { display: 'modal', preference: 'eoaOnly', termsUrl: 'https://example.com/terms', privacyUrl: 'https://example.com/privacy', supportedWallets: { rabby: true, trust: true, frame: true, }, }, }; render( , ); await waitFor(() => { expect(setOnchainKitConfig).toHaveBeenCalledWith( expect.objectContaining({ config: expect.objectContaining({ wallet: { display: 'modal', preference: 'eoaOnly', termsUrl: 'https://example.com/terms', privacyUrl: 'https://example.com/privacy', supportedWallets: { rabby: true, trust: true, frame: true, }, }, }), }), ); }); }); afterEach(() => { vi.resetModules(); }); it('should set data-ock-theme attribute on document root', async () => { const setAttributeSpy = vi.spyOn(document.documentElement, 'setAttribute'); render( , ); await waitFor(() => { expect(setAttributeSpy).toHaveBeenCalledWith( 'data-ock-theme', 'default-light', ); }); setAttributeSpy.mockRestore(); }); });