import { render, screen, waitFor, fireEvent } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import userEvent from '@testing-library/user-event'; import { Tabs } from './Tabs'; describe('Tabs Component', () => { const mockItems = [ { id: 'tab1', label: 'Tab 1', content:
Content 1
}, { id: 'tab2', label: 'Tab 2', content:
Content 2
}, { id: 'tab3', label: 'Tab 3', content:
Content 3
}, ]; const mockOnChange = vi.fn(); beforeEach(() => { vi.clearAllMocks(); }); it('renders tabs with labels', () => { render(); expect(screen.getByText('Tab 1')).toBeInTheDocument(); expect(screen.getByText('Tab 2')).toBeInTheDocument(); expect(screen.getByText('Tab 3')).toBeInTheDocument(); }); it('renders content of first tab by default', () => { render(); expect(screen.getByText('Content 1')).toBeInTheDocument(); expect(screen.queryByText('Content 2')).not.toBeInTheDocument(); }); it('renders content of defaultActiveId tab', () => { render(); expect(screen.getByText('Content 2')).toBeInTheDocument(); expect(screen.queryByText('Content 1')).not.toBeInTheDocument(); }); it('switches content when tab is clicked', async () => { const user = userEvent.setup(); render(); const tab2 = screen.getByText('Tab 2'); await user.click(tab2); expect(screen.getByText('Content 2')).toBeInTheDocument(); expect(screen.queryByText('Content 1')).not.toBeInTheDocument(); }); it('calls onChange when tab is clicked', async () => { const user = userEvent.setup(); render(); const tab2 = screen.getByText('Tab 2'); await user.click(tab2); expect(mockOnChange).toHaveBeenCalledWith('tab2'); }); it('uses controlled activeId when provided', () => { const { rerender } = render(); expect(screen.getByText('Content 2')).toBeInTheDocument(); rerender(); expect(screen.getByText('Content 3')).toBeInTheDocument(); }); it('navigates to next tab with ArrowRight key', async () => { render(); const tab1 = screen.getByRole('tab', { name: 'Tab 1' }); tab1.focus(); fireEvent.keyDown(tab1, { key: 'ArrowRight', code: 'ArrowRight' }); await waitFor(() => { expect(screen.getByText('Content 2')).toBeInTheDocument(); }); }); it('navigates to previous tab with ArrowLeft key', async () => { render(); const tab2 = screen.getByRole('tab', { name: 'Tab 2' }); tab2.focus(); fireEvent.keyDown(tab2, { key: 'ArrowLeft', code: 'ArrowLeft' }); await waitFor(() => { expect(screen.getByText('Content 1')).toBeInTheDocument(); }); }); it('navigates to first tab with Home key', async () => { render(); const tab3 = screen.getByRole('tab', { name: 'Tab 3' }); tab3.focus(); fireEvent.keyDown(tab3, { key: 'Home', code: 'Home' }); await waitFor(() => { expect(screen.getByText('Content 1')).toBeInTheDocument(); }); }); it('navigates to last tab with End key', async () => { render(); const tab1 = screen.getByRole('tab', { name: 'Tab 1' }); tab1.focus(); fireEvent.keyDown(tab1, { key: 'End', code: 'End' }); await waitFor(() => { expect(screen.getByText('Content 3')).toBeInTheDocument(); }); }); it('wraps around when navigating with ArrowRight at last tab', async () => { render(); const tab3 = screen.getByRole('tab', { name: 'Tab 3' }); tab3.focus(); fireEvent.keyDown(tab3, { key: 'ArrowRight', code: 'ArrowRight' }); await waitFor(() => { expect(screen.getByText('Content 1')).toBeInTheDocument(); }); }); it('wraps around when navigating with ArrowLeft at first tab', async () => { render(); const tab1 = screen.getByRole('tab', { name: 'Tab 1' }); tab1.focus(); fireEvent.keyDown(tab1, { key: 'ArrowLeft', code: 'ArrowLeft' }); await waitFor(() => { expect(screen.getByText('Content 3')).toBeInTheDocument(); }); }); it('disables tab when disabled prop is true', () => { const itemsWithDisabled = [ { id: 'tab1', label: 'Tab 1', content:
Content 1
}, { id: 'tab2', label: 'Tab 2', content:
Content 2
, disabled: true, }, ]; render(); const tab2 = screen.getByRole('tab', { name: 'Tab 2' }); expect(tab2).toBeDisabled(); }); it('skips disabled tabs when navigating with keyboard', async () => { const itemsWithDisabled = [ { id: 'tab1', label: 'Tab 1', content:
Content 1
}, { id: 'tab2', label: 'Tab 2', content:
Content 2
, disabled: true, }, { id: 'tab3', label: 'Tab 3', content:
Content 3
}, ]; render(); const tab1 = screen.getByRole('tab', { name: 'Tab 1' }); tab1.focus(); fireEvent.keyDown(tab1, { key: 'ArrowRight', code: 'ArrowRight' }); // Devrait aller à tab3, en sautant tab2 désactivé await waitFor(() => { expect(screen.getByText('Content 3')).toBeInTheDocument(); }); }); it('does not switch to disabled tab when clicked', async () => { const user = userEvent.setup(); const itemsWithDisabled = [ { id: 'tab1', label: 'Tab 1', content:
Content 1
}, { id: 'tab2', label: 'Tab 2', content:
Content 2
, disabled: true, }, ]; render(); const tab2 = screen.getByText('Tab 2'); await user.click(tab2); expect(screen.getByText('Content 1')).toBeInTheDocument(); expect(screen.queryByText('Content 2')).not.toBeInTheDocument(); }); it('renders tab with icon', () => { const itemsWithIcon = [ { id: 'tab1', label: 'Tab 1', content:
Content 1
, icon: 📊, }, ]; render(); expect(screen.getByTestId('icon')).toBeInTheDocument(); }); it('applies default variant styles', () => { render(); const tabList = screen.getByRole('tablist'); expect(tabList).toHaveClass('border-b'); }); it('applies pills variant styles', () => { render(); const tabList = screen.getByText('Tab 1').closest('[role="tablist"]'); expect(tabList).toHaveClass('bg-muted'); }); it('applies underline variant styles', () => { render(); const tabList = screen.getByText('Tab 1').closest('[role="tablist"]'); expect(tabList).toHaveClass('border-b'); }); it('has correct ARIA attributes', () => { render(); const tab1 = screen.getByRole('tab', { name: 'Tab 1' }); expect(tab1).toHaveAttribute('role', 'tab'); expect(tab1).toHaveAttribute('aria-selected', 'true'); expect(tab1).toHaveAttribute('aria-controls', 'tabpanel-tab1'); const tabpanel = screen.getByRole('tabpanel'); expect(tabpanel).toHaveAttribute('id', 'tabpanel-tab1'); expect(tabpanel).toHaveAttribute('aria-labelledby', 'tab-tab1'); }); it('sets tabIndex correctly', () => { render(); const tab1 = screen.getByRole('tab', { name: 'Tab 1' }); expect(tab1).toHaveAttribute('tabindex', '0'); const tab2 = screen.getByRole('tab', { name: 'Tab 2' }); expect(tab2).toHaveAttribute('tabindex', '-1'); }); it('applies custom className', () => { const { container } = render( , ); const tabsContainer = container.firstChild; expect(tabsContainer).toHaveClass('custom-class'); }); it('handles empty items array', () => { render(); expect(screen.queryByRole('tab')).not.toBeInTheDocument(); }); it('selects first enabled tab when defaultActiveId points to disabled tab', async () => { const itemsWithDisabled = [ { id: 'tab1', label: 'Tab 1', content:
Content 1
, disabled: true, }, { id: 'tab2', label: 'Tab 2', content:
Content 2
}, ]; render(); await waitFor(() => { expect(screen.getByText('Content 2')).toBeInTheDocument(); }); }); });