import { fireEvent, render, screen } from '@testing-library/react';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import type {
WalletAdvancedQrReceiveProps,
WalletAdvancedSwapProps,
} from '../types';
import { WalletDropdownContent } from './WalletDropdownContent';
import { useWalletContext } from './WalletProvider';
import { usePortfolio } from '../hooks/usePortfolio';
import { useAccount } from 'wagmi';
vi.mock('wagmi', () => ({
useAccount: vi.fn(),
}));
vi.mock('@/wallet/hooks/usePortfolio', () => ({
usePortfolio: vi.fn(),
}));
vi.mock('./WalletAdvancedProvider', () => ({
useWalletAdvancedContext: vi.fn(),
WalletAdvancedProvider: ({ children }: { children: React.ReactNode }) => (
<>{children}>
),
}));
vi.mock('./WalletAdvancedQrReceive', () => ({
WalletAdvancedQrReceive: ({ classNames }: WalletAdvancedQrReceiveProps) => (
WalletAdvancedQrReceive
),
}));
vi.mock('./WalletAdvancedSwap', () => ({
WalletAdvancedSwap: ({ from, to, classNames }: WalletAdvancedSwapProps) => (
WalletAdvancedSwap
),
}));
vi.mock('./wallet-advanced-send/components/Send', () => ({
Send: ({ className }: { className?: string }) => (
WalletAdvancedSend
),
}));
vi.mock('./WalletProvider', () => ({
useWalletContext: vi.fn(),
WalletProvider: ({ children }: { children: React.ReactNode }) => (
<>{children}>
),
}));
describe('WalletDropdownContent', () => {
const mockUseWalletContext = useWalletContext as ReturnType;
const defaultMockUseWalletAdvancedContext = {
activeFeature: null,
isActiveFeatureClosing: false,
tokenHoldings: [],
animations: {
container: '',
content: '',
},
};
beforeEach(() => {
vi.clearAllMocks();
(usePortfolio as Mock).mockReturnValue({
data: {
tokenBalances: [],
},
});
(useAccount as Mock).mockReturnValue({
address: '0x123',
});
});
it('renders null when isSubComponentOpen is false', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: false,
...defaultMockUseWalletAdvancedContext,
});
render(
WalletDropdownContent
,
);
expect(
screen.queryByTestId('ockWalletDropdownContent'),
).not.toBeInTheDocument();
});
it('renders WalletDropdownContent with correct animations when isSubComponentClosing is false and showSubComponentAbove is false', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
isSubComponentClosing: false,
showSubComponentAbove: false,
...defaultMockUseWalletAdvancedContext,
animations: {
container:
'fade-in slide-in-from-top-1.5 animate-in duration-300 ease-out',
content:
'fade-in slide-in-from-top-2.5 animate-in fill-mode-forwards duration-300 ease-out',
},
});
render(
WalletDropdownContent
,
);
expect(screen.getByTestId('ockWalletDropdownContent')).toBeDefined();
expect(screen.queryByTestId('ockWalletDropdownContent')).toHaveClass(
'fade-in slide-in-from-top-1.5 animate-in duration-300 ease-out',
);
expect(
screen.queryByTestId('ockWalletAdvancedQrReceive'),
).not.toBeInTheDocument();
expect(
screen.queryByTestId('ockWalletAdvancedSwap'),
).not.toBeInTheDocument();
});
it('renders WalletDropdownContent with correct animations when isSubComponentClosing is false and showSubComponentAbove is true', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
isSubComponentClosing: false,
showSubComponentAbove: true,
...defaultMockUseWalletAdvancedContext,
animations: {
container:
'fade-in slide-in-from-bottom-1.5 animate-in duration-300 ease-out',
content:
'fade-in slide-in-from-bottom-2.5 animate-in fill-mode-forwards duration-300 ease-out',
},
});
render(
WalletDropdownContent
,
);
expect(screen.getByTestId('ockWalletDropdownContent')).toBeDefined();
expect(screen.queryByTestId('ockWalletDropdownContent')).toHaveClass(
'fade-in slide-in-from-bottom-1.5 animate-in duration-300 ease-out',
);
expect(
screen.queryByTestId('ockWalletAdvancedQrReceive'),
).not.toBeInTheDocument();
expect(
screen.queryByTestId('ockWalletAdvancedSwap'),
).not.toBeInTheDocument();
});
it('closes WalletDropdownContent with correct animations when isSubComponentClosing is true and showSubComponentAbove is false', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentClosing: true,
showSubComponentAbove: false,
...defaultMockUseWalletAdvancedContext,
animations: {
container:
'fade-out slide-out-to-top-1.5 animate-out fill-mode-forwards ease-in-out',
content: '',
},
});
render(
WalletDropdownContent
,
);
expect(screen.getByTestId('ockWalletDropdownContent')).toBeDefined();
expect(screen.queryByTestId('ockWalletDropdownContent')).toHaveClass(
'fade-out slide-out-to-top-1.5 animate-out fill-mode-forwards ease-in-out',
);
expect(
screen.queryByTestId('ockWalletAdvancedQrReceive'),
).not.toBeInTheDocument();
expect(
screen.queryByTestId('ockWalletAdvancedSwap'),
).not.toBeInTheDocument();
});
it('closes WalletDropdownContent with correct animations when isSubComponentClosing is true and showSubComponentAbove is true', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentClosing: true,
showSubComponentAbove: true,
...defaultMockUseWalletAdvancedContext,
animations: {
container:
'fade-out slide-out-to-bottom-1.5 animate-out fill-mode-forwards ease-in-out',
content: '',
},
});
render(
WalletDropdownContent
,
);
expect(screen.getByTestId('ockWalletDropdownContent')).toBeDefined();
expect(screen.queryByTestId('ockWalletDropdownContent')).toHaveClass(
'fade-out slide-out-to-bottom-1.5 animate-out fill-mode-forwards ease-in-out',
);
expect(
screen.queryByTestId('ockWalletAdvancedQrReceive'),
).not.toBeInTheDocument();
expect(
screen.queryByTestId('ockWalletAdvancedSwap'),
).not.toBeInTheDocument();
});
it('closes WalletDropdownContent when mobile tray is closed', () => {
const setIsSubComponentOpen = vi.fn();
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
setIsSubComponentOpen,
breakpoint: 'sm',
});
render(
WalletDropdownContent
,
);
fireEvent.keyDown(document, {
key: 'Escape',
});
expect(setIsSubComponentOpen).toHaveBeenCalledWith(false);
});
it('handles animation end when closing', () => {
const setIsSubComponentOpen = vi.fn();
const setIsSubComponentClosing = vi.fn();
mockUseWalletContext.mockReturnValue({
...defaultMockUseWalletAdvancedContext,
isSubComponentOpen: true,
setIsSubComponentOpen,
isSubComponentClosing: true,
setIsSubComponentClosing,
});
render(
WalletDropdownContent
,
);
const content = screen.getByTestId('ockWalletDropdownContent');
fireEvent.animationEnd(content);
expect(setIsSubComponentOpen).toHaveBeenCalledWith(false);
expect(setIsSubComponentClosing).toHaveBeenCalledWith(false);
});
it('renders WalletAdvancedQrReceive when activeFeature is qr', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
...defaultMockUseWalletAdvancedContext,
activeFeature: 'qr',
});
render(
WalletDropdownContent
,
);
expect(screen.getByTestId('ockWalletAdvancedQrReceive')).toBeDefined();
expect(
screen.queryByTestId('ockWalletAdvancedQrReceive'),
).toBeInTheDocument();
expect(
screen.queryByTestId('ockWalletAdvancedSwap'),
).not.toBeInTheDocument();
expect(
screen.queryByTestId('ockWalletAdvancedSend'),
).not.toBeInTheDocument();
});
it('renders WalletAdvancedSwap when activeFeature is swap', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
...defaultMockUseWalletAdvancedContext,
activeFeature: 'swap',
});
render(
WalletDropdownContent
,
);
expect(screen.getByTestId('ockWalletAdvancedSwap')).toBeDefined();
expect(screen.queryByTestId('ockWalletAdvancedSwap')).toBeInTheDocument();
expect(
screen.queryByTestId('ockWalletAdvancedQrReceive'),
).not.toBeInTheDocument();
expect(
screen.queryByTestId('ockWalletAdvancedSend'),
).not.toBeInTheDocument();
});
it('renders WalletAdvancedSend when activeFeature is send', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
...defaultMockUseWalletAdvancedContext,
activeFeature: 'send',
});
render(
WalletDropdownContent
,
);
expect(screen.getByTestId('ockWalletAdvancedSend')).toBeDefined();
expect(screen.queryByTestId('ockWalletAdvancedSend')).toBeInTheDocument();
expect(
screen.queryByTestId('ockWalletAdvancedQrReceive'),
).not.toBeInTheDocument();
expect(
screen.queryByTestId('ockWalletAdvancedSwap'),
).not.toBeInTheDocument();
});
it('handles empty tokenBalances gracefully', () => {
(usePortfolio as Mock).mockReturnValue({
data: {
tokenBalances: null,
},
});
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
...defaultMockUseWalletAdvancedContext,
activeFeature: 'swap',
isSubComponentClosing: false,
});
render(
WalletDropdownContent
,
);
const swapComponent = screen.getByTestId('ockWalletAdvancedSwap');
const props = JSON.parse(
swapComponent.getAttribute('data-props') as string,
);
expect(props.from).toEqual([]);
});
it('correctly maps token balances to the swap component', () => {
const mockTokenBalances = [
{
address: '0x123',
chainId: 1,
symbol: 'TEST',
decimals: 18,
image: 'test.png',
name: 'Test Token',
},
{
address: '0x456',
chainId: 2,
symbol: 'TEST2',
decimals: 6,
image: 'test2.png',
name: 'Test Token 2',
},
];
(usePortfolio as Mock).mockReturnValue({
data: {
tokenBalances: mockTokenBalances,
},
});
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
...defaultMockUseWalletAdvancedContext,
activeFeature: 'swap',
isSubComponentClosing: false,
});
render(
WalletDropdownContent
,
);
const swapComponent = screen.getByTestId('ockWalletAdvancedSwap');
const props = JSON.parse(
swapComponent.getAttribute('data-props') as string,
);
expect(props.from).toEqual(
mockTokenBalances.map((token) => ({
address: token.address,
chainId: token.chainId,
symbol: token.symbol,
decimals: token.decimals,
image: token.image,
name: token.name,
})),
);
});
it('applies custom classNames to components', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
isSubComponentClosing: false,
showSubComponentAbove: false,
...defaultMockUseWalletAdvancedContext,
activeFeature: 'qr',
});
const customClassNames = {
container: 'custom-container',
qr: {
container: 'custom-qr-container',
},
swap: {
container: 'custom-swap-container',
},
};
const { rerender } = render(
Content
,
);
expect(screen.getByTestId('ockWalletDropdownContent')).toHaveClass(
'custom-container',
);
// Verify both rendered state and passed props
const qrComponent = screen.getByTestId('ockWalletAdvancedQrReceive');
expect(qrComponent).toHaveClass('custom-qr-container');
expect(qrComponent).toHaveProperty('className', 'custom-qr-container');
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
isSubComponentClosing: false,
showSubComponentAbove: false,
...defaultMockUseWalletAdvancedContext,
activeFeature: 'swap',
});
rerender(
Content
,
);
const swapComponent = screen.getByTestId('ockWalletAdvancedSwap');
expect(swapComponent).toHaveClass('custom-swap-container');
expect(swapComponent).toHaveProperty('className', 'custom-swap-container');
});
it('renders BottomSheet when breakpoint is sm', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
isSubComponentClosing: false,
breakpoint: 'sm',
});
render(
WalletDropdownContent
,
);
expect(screen.getByTestId('ockSheet')).toBeDefined();
});
it('applies custom classNames to BottomSheet when breakpoint is sm', () => {
mockUseWalletContext.mockReturnValue({
isSubComponentOpen: true,
isSubComponentClosing: false,
breakpoint: 'sm',
...defaultMockUseWalletAdvancedContext,
activeFeature: 'qr',
});
const customClassNames = {
container: 'custom-container',
};
render(
Content
,
);
expect(screen.getByTestId('ockSheet')).toHaveClass('custom-container');
});
});