import sdk from '@farcaster/miniapp-sdk';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { render } from '@testing-library/react';
import { act } from 'react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { http, WagmiProvider, createConfig } from 'wagmi';
import { base } from 'wagmi/chains';
import { AutoConnect } from './AutoConnect';
vi.mock('@farcaster/miniapp-sdk', () => ({
default: {
isInMiniApp: vi.fn(),
},
}));
const mockConnect = vi.fn();
const mockFarcasterFrame = {
type: 'farcasterFrame',
};
const mockFarcasterMiniApp = {
type: 'farcasterMiniApp',
};
const mockOtherConnector = {
type: 'otherConnector',
};
vi.mock('@farcaster/miniapp-wagmi-connector', () => ({
farcasterMiniApp: {
type: 'farcasterMiniApp',
},
}));
// We'll override this mock in individual tests
const mockUseAccount = vi.fn();
const mockUseConnect = vi.fn();
vi.mock('wagmi', async () => {
const actual = await vi.importActual('wagmi');
return {
...actual,
useConnect: () => mockUseConnect(),
useAccount: () => mockUseAccount(),
};
});
const mockConfig = {
chains: [base],
connectors: [],
transports: { [base.id]: http() },
} as const;
const queryClient = new QueryClient();
describe('AutoConnect', () => {
beforeEach(() => {
vi.clearAllMocks();
// Default mocks
mockUseAccount.mockReturnValue({
isConnected: false,
isConnecting: false,
});
mockUseConnect.mockReturnValue({
connectors: [mockFarcasterFrame],
connect: mockConnect,
});
});
afterEach(() => {
vi.clearAllMocks();
});
it('should not attempt connection if not in Mini App', async () => {
vi.mocked(sdk.isInMiniApp).mockResolvedValue(false);
render(
Test Child
,
);
await act(() => Promise.resolve());
expect(mockConnect).not.toHaveBeenCalled();
});
it('should not attempt connection if already connected', async () => {
vi.mocked(sdk.isInMiniApp).mockResolvedValue(true);
// Mock account to be already connected
mockUseAccount.mockReturnValue({
isConnected: true,
isConnecting: false,
});
render(
Test Child
,
);
await act(() => Promise.resolve());
expect(mockConnect).not.toHaveBeenCalled();
});
it('should not attempt connection if connector is not a supported Farcaster connector', async () => {
Object.defineProperty(sdk, 'context', {
value: Promise.resolve({ user: { fid: 123 } }),
writable: true,
configurable: true,
});
// Mock connectors to have a different type of connector
mockUseConnect.mockReturnValue({
connectors: [mockOtherConnector],
connect: mockConnect,
});
render(
Test Child
,
);
await act(() => Promise.resolve());
expect(mockConnect).not.toHaveBeenCalled();
});
it('should not attempt connection when disabled', async () => {
vi.mocked(sdk.isInMiniApp).mockResolvedValue(true);
render(
Test Child
,
);
await act(() => Promise.resolve());
expect(mockConnect).not.toHaveBeenCalled();
});
it('should attempt connection when in Mini App, not connected, and enabled with farcasterFrame connector', async () => {
Object.defineProperty(sdk, 'context', {
value: Promise.resolve({ user: { fid: 123 } }),
writable: true,
configurable: true,
});
render(
Test Child
,
);
await act(() => Promise.resolve());
expect(mockConnect).toHaveBeenCalledWith({ connector: mockFarcasterFrame });
});
it('should attempt connection when in Mini App, not connected, and enabled with farcasterMiniApp connector', async () => {
Object.defineProperty(sdk, 'context', {
value: Promise.resolve({ user: { fid: 123 } }),
writable: true,
configurable: true,
});
// Mock connectors to have farcasterMiniApp type
mockUseConnect.mockReturnValue({
connectors: [mockFarcasterMiniApp],
connect: mockConnect,
});
render(
Test Child
,
);
await act(() => Promise.resolve());
expect(mockConnect).toHaveBeenCalledWith({
connector: mockFarcasterMiniApp,
});
});
it('should only attempt connection once', async () => {
vi.mocked(sdk.isInMiniApp).mockResolvedValue(true);
const { rerender } = render(
Test Child
,
);
await act(() => Promise.resolve());
// Rerender to trigger useEffect again
rerender(
Test Child
,
);
await act(() => Promise.resolve());
// Should only be called once despite rerender
expect(mockConnect).toHaveBeenCalledTimes(1);
expect(mockConnect).toHaveBeenCalledWith({ connector: mockFarcasterFrame });
});
it('should call connect with connector when in Mini App and all conditions are met', async () => {
// Mock isInMiniApp to return true
vi.mocked(sdk.isInMiniApp).mockResolvedValue(true);
// Render the component
render(
Test Child
,
);
// Wait for the async effect to complete
await act(() => Promise.resolve());
// Verify that connect was called with the correct connector
expect(mockConnect).toHaveBeenCalledWith({ connector: mockFarcasterFrame });
});
it('should not attempt connection if currently connecting', async () => {
vi.mocked(sdk.isInMiniApp).mockResolvedValue(true);
// Mock account to be currently connecting
mockUseAccount.mockReturnValue({
isConnected: false,
isConnecting: true,
});
render(
Test Child
,
);
await act(() => Promise.resolve());
expect(mockConnect).not.toHaveBeenCalled();
});
it('should not attempt connection if no connectors available', async () => {
vi.mocked(sdk.isInMiniApp).mockResolvedValue(true);
// Mock empty connectors array
mockUseConnect.mockReturnValue({
connectors: [],
connect: mockConnect,
});
render(
Test Child
,
);
await act(() => Promise.resolve());
expect(mockConnect).not.toHaveBeenCalled();
});
});