import { render, screen } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import userEvent from '@testing-library/user-event'; import { List, ListItem } from './List'; describe('List Component', () => { const mockItems: ListItem[] = [ { id: '1', content: 'Item 1', onClick: vi.fn(), }, { id: '2', content: 'Item 2', actions: , }, { id: '3', content: 'Item 3', }, ]; beforeEach(() => { vi.clearAllMocks(); }); it('renders list items', () => { render(); expect(screen.getByText('Item 1')).toBeInTheDocument(); expect(screen.getByText('Item 2')).toBeInTheDocument(); expect(screen.getByText('Item 3')).toBeInTheDocument(); }); it('renders empty message when items array is empty', () => { render(); expect(screen.getByText('No items found')).toBeInTheDocument(); }); it('returns null when items array is empty and no empty message', () => { const { container } = render(); expect(container.firstChild).toBeNull(); }); it('calls onClick when item is clicked', async () => { const user = userEvent.setup(); const mockOnClick = vi.fn(); const itemsWithClick: ListItem[] = [ { id: '1', content: 'Clickable Item', onClick: mockOnClick, }, ]; render(); const item = screen.getByText('Clickable Item'); await user.click(item); expect(mockOnClick).toHaveBeenCalledTimes(1); }); it('does not call onClick when item is disabled', async () => { const user = userEvent.setup(); const mockOnClick = vi.fn(); const itemsWithDisabled: ListItem[] = [ { id: '1', content: 'Disabled Item', onClick: mockOnClick, disabled: true, }, ]; render(); const item = screen.getByText('Disabled Item'); await user.click(item); expect(mockOnClick).not.toHaveBeenCalled(); }); it('renders actions on items', () => { render(); const actionButton = screen.getByText('Action'); expect(actionButton).toBeInTheDocument(); }); it('does not trigger item onClick when clicking actions', async () => { const user = userEvent.setup(); const mockOnClick = vi.fn(); const itemsWithActions: ListItem[] = [ { id: '1', content: 'Item with Actions', actions: , onClick: mockOnClick, }, ]; render(); const actionButton = screen.getByText('Action'); await user.click(actionButton); expect(mockOnClick).not.toHaveBeenCalled(); }); it('applies default variant styles', () => { const { container } = render(); const list = container.querySelector('ul'); expect(list).toHaveClass('space-y-1'); }); it('applies bordered variant styles', () => { const { container } = render(); const list = container.querySelector('ul'); expect(list).toHaveClass('border'); expect(list).toHaveClass('divide-y'); expect(list).toHaveClass('rounded-md'); }); it('applies spaced variant styles', () => { const { container } = render(); const list = container.querySelector('ul'); expect(list).toHaveClass('space-y-2'); }); it('applies custom className', () => { const { container } = render( , ); const list = container.querySelector('ul'); expect(list).toHaveClass('custom-class'); }); it('applies custom itemClassName', () => { const { container } = render( , ); const firstItem = container.querySelector('li'); expect(firstItem).toHaveClass('custom-item-class'); }); it('applies hover styles when item is clickable', () => { const clickableItems: ListItem[] = [ { id: '1', content: 'Clickable', onClick: vi.fn(), }, ]; const { container } = render(); const item = container.querySelector('li'); expect(item).toHaveClass('cursor-pointer'); expect(item).toHaveClass('hover:bg-accent'); }); it('applies disabled styles when item is disabled', () => { const disabledItems: ListItem[] = [ { id: '1', content: 'Disabled', disabled: true, }, ]; const { container } = render(); const item = container.querySelector('li'); expect(item).toHaveClass('cursor-not-allowed'); expect(item).toHaveClass('opacity-50'); }); it('renders items with complex content', () => { const complexItems: ListItem[] = [ { id: '1', content: (

Title

Description

), }, ]; render(); expect(screen.getByText('Title')).toBeInTheDocument(); expect(screen.getByText('Description')).toBeInTheDocument(); }); it('renders items with multiple actions', () => { const itemsWithMultipleActions: ListItem[] = [ { id: '1', content: 'Item', actions: ( <> ), }, ]; render(); expect(screen.getByText('Edit')).toBeInTheDocument(); expect(screen.getByText('Delete')).toBeInTheDocument(); }); it('handles items without onClick', () => { const itemsWithoutClick: ListItem[] = [ { id: '1', content: 'Static Item', }, ]; const { container } = render(); const item = container.querySelector('li'); expect(item).not.toHaveClass('cursor-pointer'); }); it('handles items with onClick but no actions', () => { const itemsWithClickOnly: ListItem[] = [ { id: '1', content: 'Clickable Item', onClick: vi.fn(), }, ]; const { container } = render(); const item = container.querySelector('li'); expect(item).toHaveClass('cursor-pointer'); // Actions container should not be rendered const actionsContainer = item?.querySelector('.ml-4'); expect(actionsContainer).not.toBeInTheDocument(); }); });