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();
});
});