258 lines
6.6 KiB
TypeScript
258 lines
6.6 KiB
TypeScript
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: <button>Action</button>,
|
|
},
|
|
{
|
|
id: '3',
|
|
content: 'Item 3',
|
|
},
|
|
];
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('renders list items', () => {
|
|
render(<List items={mockItems} />);
|
|
|
|
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(<List items={[]} emptyMessage="No items found" />);
|
|
|
|
expect(screen.getByText('No items found')).toBeInTheDocument();
|
|
});
|
|
|
|
it('returns null when items array is empty and no empty message', () => {
|
|
const { container } = render(<List items={[]} />);
|
|
|
|
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(<List items={itemsWithClick} />);
|
|
|
|
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(<List items={itemsWithDisabled} />);
|
|
|
|
const item = screen.getByText('Disabled Item');
|
|
await user.click(item);
|
|
|
|
expect(mockOnClick).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('renders actions on items', () => {
|
|
render(<List items={mockItems} />);
|
|
|
|
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: <button>Action</button>,
|
|
onClick: mockOnClick,
|
|
},
|
|
];
|
|
|
|
render(<List items={itemsWithActions} />);
|
|
|
|
const actionButton = screen.getByText('Action');
|
|
await user.click(actionButton);
|
|
|
|
expect(mockOnClick).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('applies default variant styles', () => {
|
|
const { container } = render(<List items={mockItems} variant="default" />);
|
|
|
|
const list = container.querySelector('ul');
|
|
expect(list).toHaveClass('space-y-1');
|
|
});
|
|
|
|
it('applies bordered variant styles', () => {
|
|
const { container } = render(<List items={mockItems} variant="bordered" />);
|
|
|
|
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(<List items={mockItems} variant="spaced" />);
|
|
|
|
const list = container.querySelector('ul');
|
|
expect(list).toHaveClass('space-y-2');
|
|
});
|
|
|
|
it('applies custom className', () => {
|
|
const { container } = render(
|
|
<List items={mockItems} className="custom-class" />,
|
|
);
|
|
|
|
const list = container.querySelector('ul');
|
|
expect(list).toHaveClass('custom-class');
|
|
});
|
|
|
|
it('applies custom itemClassName', () => {
|
|
const { container } = render(
|
|
<List items={mockItems} itemClassName="custom-item-class" />,
|
|
);
|
|
|
|
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(<List items={clickableItems} />);
|
|
|
|
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(<List items={disabledItems} />);
|
|
|
|
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: (
|
|
<div>
|
|
<h3>Title</h3>
|
|
<p>Description</p>
|
|
</div>
|
|
),
|
|
},
|
|
];
|
|
|
|
render(<List items={complexItems} />);
|
|
|
|
expect(screen.getByText('Title')).toBeInTheDocument();
|
|
expect(screen.getByText('Description')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders items with multiple actions', () => {
|
|
const itemsWithMultipleActions: ListItem[] = [
|
|
{
|
|
id: '1',
|
|
content: 'Item',
|
|
actions: (
|
|
<>
|
|
<button>Edit</button>
|
|
<button>Delete</button>
|
|
</>
|
|
),
|
|
},
|
|
];
|
|
|
|
render(<List items={itemsWithMultipleActions} />);
|
|
|
|
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(<List items={itemsWithoutClick} />);
|
|
|
|
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(<List items={itemsWithClickOnly} />);
|
|
|
|
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();
|
|
});
|
|
});
|