import React from 'react' import { render, screen, fireEvent, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { Tabs, TabsList, TabsTrigger, TabsContent } from '../tabs' // Mock icons for testing const MockIcon = () => Icon const MockBadge = () => Badge describe('Tabs Components', () => { describe('Tabs Component', () => { describe('Basic Rendering', () => { it('renders correctly with default props', () => { render( Tab 1 Content 1 ) const tabs = screen.getByTestId('tabs') expect(tabs).toBeInTheDocument() expect(tabs).toHaveAttribute('data-orientation', 'horizontal') }) it('applies custom className', () => { render( Tab 1 ) const tabs = screen.getByTestId('tabs') expect(tabs).toHaveClass('custom-tabs') }) it('forwards ref correctly', () => { const ref = React.createRef() render( Tab 1 ) expect(ref.current).toBeInstanceOf(HTMLDivElement) }) it('maintains displayName', () => { expect(Tabs.displayName).toBe('Tabs') }) }) describe('Orientation', () => { it('renders horizontal orientation by default', () => { render( Tab 1 ) const tabs = screen.getByTestId('tabs') expect(tabs).toHaveAttribute('data-orientation', 'horizontal') }) it('renders vertical orientation when vertical prop is true', () => { render( Tab 1 ) const tabs = screen.getByTestId('tabs') expect(tabs).toHaveAttribute('data-orientation', 'vertical') }) }) describe('HTML Attributes', () => { it('passes through HTML attributes', () => { render( Tab 1 ) const tabs = screen.getByTestId('tabs') expect(tabs).toHaveAttribute('aria-label', 'Navigation tabs') }) }) }) describe('TabsList Component', () => { describe('Basic Rendering', () => { it('renders correctly with default props', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toBeInTheDocument() expect(tabsList).toHaveAttribute('role', 'tablist') }) it('applies custom className', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toHaveClass('custom-list') }) it('forwards ref correctly', () => { const ref = React.createRef() render( Tab 1 ) expect(ref.current).toBeInstanceOf(HTMLDivElement) }) it('maintains displayName', () => { expect(TabsList.displayName).toBe('TabsList') }) }) describe('Variants', () => { it('renders default variant correctly', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toHaveClass('bg-muted', 'rounded-md', 'p-1') }) it('renders pills variant correctly', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toHaveClass('bg-transparent', 'gap-2', 'p-0') }) it('renders underline variant correctly', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toHaveClass('bg-transparent', 'border-b', 'gap-4') }) it('renders cards variant correctly', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toHaveClass('bg-transparent', 'gap-2', 'p-0') }) it('renders minimal variant correctly', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toHaveClass('bg-transparent', 'gap-1', 'p-0') }) }) describe('Orientation', () => { it('renders horizontal orientation by default', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toHaveClass('flex-row') }) it('renders vertical orientation correctly', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toHaveClass('flex-col', 'items-start', 'gap-1') }) }) describe('Full Width', () => { it('renders full width when fullWidth is true', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toHaveClass('w-full') }) }) describe('HTML Attributes', () => { it('passes through HTML attributes', () => { render( Tab 1 ) const tabsList = screen.getByTestId('tabs-list') expect(tabsList).toHaveAttribute('aria-label', 'Tab navigation') }) }) }) describe('TabsTrigger Component', () => { describe('Basic Rendering', () => { it('renders correctly with default props', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toBeInTheDocument() expect(trigger).toHaveAttribute('role', 'tab') expect(trigger).toHaveTextContent('Tab 1') }) it('applies custom className', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('custom-trigger') }) it('forwards ref correctly', () => { const ref = React.createRef() render( Tab 1 ) expect(ref.current).toBeInstanceOf(HTMLButtonElement) }) it('maintains displayName', () => { expect(TabsTrigger.displayName).toBe('TabsTrigger') }) }) describe('Variants', () => { it('renders default variant correctly', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('rounded-md') }) it('renders underline variant correctly', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('rounded-none') expect(trigger).toHaveClass('border-b-2') expect(trigger).toHaveClass('border-transparent') }) it('renders pills variant correctly', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('rounded-full', 'bg-muted') }) it('renders cards variant correctly', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('rounded-md', 'bg-muted/50') }) it('renders minimal variant correctly', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('rounded-sm', 'bg-transparent') }) }) describe('Sizes', () => { it('renders sm size correctly', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('h-7', 'px-2', 'text-xs') }) it('renders md size correctly', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('h-9', 'px-3', 'text-sm') }) it('renders lg size correctly', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('h-10', 'px-4', 'text-base') }) }) describe('Icons', () => { it('renders left icon correctly', () => { render( } iconPosition="left" data-testid="tabs-trigger" > Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') const icon = screen.getByTestId('mock-icon') expect(icon).toBeInTheDocument() expect(icon.parentElement).toHaveClass('mr-2') }) it('renders right icon correctly', () => { render( } iconPosition="right" data-testid="tabs-trigger" > Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') const icon = screen.getByTestId('mock-icon') expect(icon).toBeInTheDocument() expect(icon.parentElement).toHaveClass('ml-2') }) it('does not render icon when iconPosition is none', () => { render( } iconPosition="none" data-testid="tabs-trigger" > Tab 1 ) expect(screen.queryByTestId('mock-icon')).not.toBeInTheDocument() }) }) describe('Badge', () => { it('renders badge correctly', () => { render( } data-testid="tabs-trigger" > Tab 1 ) const badge = screen.getByTestId('mock-badge') expect(badge).toBeInTheDocument() expect(badge.parentElement).toHaveClass('ml-2') }) }) describe('Fade Tabs', () => { it('applies fade class when fadeTabs is true', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('data-[state=inactive]:opacity-60') }) }) describe('Orientation', () => { it('renders vertical orientation correctly', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('justify-start', 'w-full', 'text-left') }) }) describe('Full Width', () => { it('renders full width when fullWidth is true', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveClass('w-full') }) }) describe('Interactions', () => { it('handles click events', async () => { const onValueChange = jest.fn() const user = userEvent.setup() render( Tab 1 Tab 2 Content 1 Content 2 ) // Verify initial state - tab2 should be active const trigger1 = screen.getByTestId('tabs-trigger') const trigger2 = screen.getByTestId('tabs-trigger-2') expect(trigger2).toHaveAttribute('data-state', 'active') expect(trigger1).toHaveAttribute('data-state', 'inactive') // Click on tab1 while tab2 is active (should trigger onValueChange) await user.click(trigger1) // Wait for callback to be called await waitFor(() => { expect(onValueChange).toHaveBeenCalledWith('tab1') }) }) }) describe('HTML Attributes', () => { it('passes through HTML attributes', () => { render( Tab 1 ) const trigger = screen.getByTestId('tabs-trigger') expect(trigger).toHaveAttribute('aria-label', 'First tab') }) }) }) describe('TabsContent Component', () => { describe('Basic Rendering', () => { it('renders correctly with default props', () => { render( Tab 1 Content 1 ) const content = screen.getByTestId('tabs-content') expect(content).toBeInTheDocument() expect(content).toHaveTextContent('Content 1') expect(content).toHaveAttribute('role', 'tabpanel') }) it('applies custom className', () => { render( Tab 1 Content 1 ) const content = screen.getByTestId('tabs-content') expect(content).toHaveClass('custom-content') }) it('forwards ref correctly', () => { const ref = React.createRef() render( Tab 1 Content 1 ) expect(ref.current).toBeInstanceOf(HTMLDivElement) }) it('maintains displayName', () => { expect(TabsContent.displayName).toBe('TabsContent') }) }) describe('Animation', () => { it('applies animation classes when animated is true', () => { render( Tab 1 Content 1 ) const content = screen.getByTestId('tabs-content') expect(content).toHaveClass('data-[state=active]:animate-fadeIn', 'data-[state=inactive]:animate-fadeOut') }) }) describe('HTML Attributes', () => { it('passes through HTML attributes', () => { render( Tab 1 Content 1 ) const content = screen.getByTestId('tabs-content') expect(content).toHaveAttribute('aria-label', 'Tab content') }) }) }) describe('Complex Combinations', () => { it('renders complete tabs with all components', () => { render( } badge={} data-testid="tabs-trigger-1" > Tab 1 Tab 2 Content 1 Content 2 ) expect(screen.getByTestId('tabs')).toBeInTheDocument() expect(screen.getByTestId('tabs-list')).toHaveClass('bg-transparent', 'gap-2') expect(screen.getByTestId('tabs-trigger-1')).toHaveClass('h-10', 'px-4') expect(screen.getByTestId('mock-icon')).toBeInTheDocument() expect(screen.getByTestId('mock-badge')).toBeInTheDocument() expect(screen.getByTestId('tabs-content-1')).toBeInTheDocument() }) it('handles vertical tabs with all variants', () => { render( Tab 1 Content 1 ) const tabs = screen.getByTestId('tabs') const tabsList = screen.getByTestId('tabs-list') const trigger = screen.getByTestId('tabs-trigger') expect(tabs).toHaveAttribute('data-orientation', 'vertical') expect(tabsList).toHaveClass('flex-col', 'items-start') expect(trigger).toHaveClass('justify-start', 'w-full', 'data-[state=inactive]:opacity-60') }) it('handles tab switching', () => { render( Tab 1 Tab 2 Content 1 Content 2 ) // Initially tab1 should be active expect(screen.getByTestId('content-1')).toBeInTheDocument() // Click tab2 fireEvent.click(screen.getByTestId('trigger-2')) // Now tab2 content should be visible expect(screen.getByTestId('content-2')).toBeInTheDocument() }) }) describe('Edge Cases', () => { it('handles empty tabs', () => { render() const tabs = screen.getByTestId('tabs') expect(tabs).toBeInTheDocument() }) it('handles tabs without content', () => { render( Tab 1 ) const trigger = screen.getByRole('tab') expect(trigger).toBeInTheDocument() }) it('handles complex nested content', () => { render( Tab 1

Nested Title

Nested paragraph

) const content = screen.getByTestId('tabs-content') expect(content).toHaveTextContent('Nested Title') expect(content).toHaveTextContent('Nested paragraph') expect(screen.getByRole('button', { name: 'Nested button' })).toBeInTheDocument() }) it('passes through HTML attributes for all components', () => { render( Tab 1 Content 1 ) expect(screen.getByRole('tablist').closest('[data-custom="tabs"]')).toBeInTheDocument() expect(screen.getByRole('tablist')).toHaveAttribute('data-custom', 'list') expect(screen.getByRole('tab')).toHaveAttribute('data-custom', 'trigger') expect(screen.getByRole('tabpanel')).toHaveAttribute('data-custom', 'content') }) }) })