import React from 'react' import { render, screen, fireEvent } from '@testing-library/react' import '@testing-library/jest-dom' import { Checkbox, CheckboxGroup, CheckboxLabel, CheckboxWithLabel } from '../checkbox' describe('Checkbox Components', () => { describe('Checkbox Component', () => { describe('Basic Rendering', () => { it('renders correctly with default props', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toBeInTheDocument() expect(checkbox).toHaveAttribute('role', 'checkbox') expect(checkbox).toHaveAttribute('aria-checked', 'false') }) it('applies custom className', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('custom-checkbox') }) it('forwards ref correctly', () => { const ref = React.createRef() render() expect(ref.current).toBeInstanceOf(HTMLButtonElement) }) it('maintains displayName', () => { expect(Checkbox.displayName).toBe('Checkbox') }) }) describe('Variants', () => { it('renders default variant correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('border-border') expect(checkbox).toHaveClass('bg-background') }) it('renders outline variant correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('border-border') expect(checkbox).toHaveClass('bg-transparent') }) it('renders muted variant correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('border-border') expect(checkbox).toHaveClass('bg-accent') }) it('renders ghost variant correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('border-transparent') expect(checkbox).toHaveClass('bg-transparent') }) it('uses default variant as default', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('border-border') expect(checkbox).toHaveClass('bg-background') }) }) describe('Sizes', () => { it('renders sm size correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('h-3.5', 'w-3.5') }) it('renders default size correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('h-4', 'w-4') }) it('renders md size correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('h-5', 'w-5') }) it('renders lg size correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('h-6', 'w-6') }) it('uses default size as default', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('h-4', 'w-4') }) }) describe('Radius', () => { it('renders none radius correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('rounded-none') }) it('renders sm radius correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('rounded-sm') }) it('renders default radius correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('rounded-sm') }) it('renders md radius correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('rounded-md') }) it('renders full radius correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('rounded-full') }) }) describe('Animation', () => { it('renders none animation correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).not.toHaveClass('transition-all') }) it('renders subtle animation correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('transition-all') }) it('renders default animation correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('transition-all') }) it('renders bounce animation correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('transition-all') }) }) describe('States', () => { it('renders unchecked state correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveAttribute('aria-checked', 'false') expect(checkbox).toHaveAttribute('data-state', 'unchecked') }) it('renders checked state correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveAttribute('aria-checked', 'true') expect(checkbox).toHaveAttribute('data-state', 'checked') }) it('renders disabled state correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toBeDisabled() expect(checkbox).toHaveClass('disabled:cursor-not-allowed') expect(checkbox).toHaveClass('disabled:opacity-50') }) it('handles controlled state', () => { const handleChange = jest.fn() render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveAttribute('aria-checked', 'true') }) }) describe('Indeterminate State', () => { it('renders indeterminate state correctly', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveAttribute('aria-checked', 'false') expect(checkbox).toHaveAttribute('data-state', 'unchecked') }) it('shows minus icon when indeterminate and checked', () => { render() const checkbox = screen.getByTestId('checkbox') // Even with checked=true, indeterminate overrides to false expect(checkbox).toHaveAttribute('aria-checked', 'false') expect(checkbox).toHaveAttribute('data-state', 'unchecked') }) it('overrides checked state when indeterminate', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveAttribute('aria-checked', 'false') }) it('updates indeterminate state via prop', () => { const { rerender } = render() let checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveAttribute('aria-checked', 'false') rerender() checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveAttribute('aria-checked', 'false') }) }) describe('Custom Icon', () => { it('renders custom icon when provided', () => { render(★} checked data-testid="checkbox" />) expect(screen.getByTestId('custom-icon')).toBeInTheDocument() }) it('uses default check icon when no custom icon', () => { render() const checkbox = screen.getByTestId('checkbox') const checkIcon = checkbox.querySelector('svg') expect(checkIcon).toBeInTheDocument() }) it('prioritizes indeterminate over custom icon', () => { render( ★} data-testid="checkbox" /> ) expect(screen.queryByTestId('custom-icon')).not.toBeInTheDocument() const checkbox = screen.getByTestId('checkbox') // Check that custom icon is not rendered expect(screen.queryByTestId('custom-icon')).not.toBeInTheDocument() // Indeterminate state should show as unchecked expect(checkbox).toHaveAttribute('aria-checked', 'false') }) }) describe('Interactions', () => { it('handles click events', () => { const handleChange = jest.fn() render() const checkbox = screen.getByTestId('checkbox') fireEvent.click(checkbox) expect(handleChange).toHaveBeenCalledWith(true) }) it('does not trigger events when disabled', () => { const handleChange = jest.fn() render() const checkbox = screen.getByTestId('checkbox') fireEvent.click(checkbox) expect(handleChange).not.toHaveBeenCalled() }) it('is focusable for keyboard navigation', () => { render() const checkbox = screen.getByTestId('checkbox') checkbox.focus() expect(checkbox).toHaveFocus() }) }) describe('Accessibility', () => { it('has correct ARIA attributes', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveAttribute('role', 'checkbox') expect(checkbox).toHaveAttribute('aria-checked', 'false') }) it('supports custom ARIA attributes', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveAttribute('aria-label', 'Accept terms') }) it('has focus-visible styles', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('focus-visible:outline-none') expect(checkbox).toHaveClass('focus-visible:ring-2') }) }) }) describe('CheckboxGroup Component', () => { it('renders correctly with default props', () => { render( ) const group = screen.getByTestId('checkbox-group') expect(group).toBeInTheDocument() expect(group).toHaveAttribute('role', 'group') expect(group).toHaveClass('flex', 'flex-col') }) it('renders horizontal orientation correctly', () => { render( ) const group = screen.getByTestId('checkbox-group') expect(group).toHaveClass('flex-row', 'flex-wrap') }) it('renders vertical orientation correctly', () => { render( ) const group = screen.getByTestId('checkbox-group') expect(group).toHaveClass('flex-col') }) it('applies custom spacing', () => { render( ) const group = screen.getByTestId('checkbox-group') expect(group).toHaveStyle({ gap: '2rem' }) }) it('applies custom spacing as number', () => { render( ) const group = screen.getByTestId('checkbox-group') expect(group).toHaveStyle({ gap: '24px' }) }) it('forwards ref correctly', () => { const ref = React.createRef() render( ) expect(ref.current).toBeInstanceOf(HTMLDivElement) }) it('maintains displayName', () => { expect(CheckboxGroup.displayName).toBe('CheckboxGroup') }) }) describe('CheckboxLabel Component', () => { it('renders correctly with default props', () => { render(Label text) const label = screen.getByTestId('checkbox-label') expect(label).toBeInTheDocument() expect(label.tagName).toBe('LABEL') expect(label).toHaveTextContent('Label text') }) it('applies htmlFor attribute', () => { render(Label) const label = screen.getByTestId('checkbox-label') expect(label).toHaveAttribute('for', 'checkbox-1') }) it('renders start position correctly', () => { render(Label) const label = screen.getByTestId('checkbox-label') expect(label).toHaveClass('mr-2') }) it('renders end position correctly', () => { render(Label) const label = screen.getByTestId('checkbox-label') expect(label).toHaveClass('ml-2') }) it('renders disabled state correctly', () => { render(Label) const label = screen.getByTestId('checkbox-label') expect(label).toHaveClass('cursor-not-allowed', 'opacity-70') }) it('forwards ref correctly', () => { const ref = React.createRef() render(Label) expect(ref.current).toBeInstanceOf(HTMLLabelElement) }) it('maintains displayName', () => { expect(CheckboxLabel.displayName).toBe('CheckboxLabel') }) }) describe('CheckboxWithLabel Component', () => { it('renders correctly with default props', () => { render() const container = screen.getByTestId('checkbox-with-label').parentElement expect(container).toHaveClass('flex', 'items-center') expect(screen.getByText('Accept terms')).toBeInTheDocument() }) it('renders label at start position', () => { render() const container = screen.getByTestId('checkbox-with-label').parentElement const label = container?.querySelector('label') const checkbox = container?.querySelector('[role="checkbox"]') expect(label).toBeInTheDocument() expect(checkbox).toBeInTheDocument() // Label should come before checkbox in DOM const elements = Array.from(container?.children || []) const labelIndex = elements.indexOf(label!) const checkboxIndex = elements.indexOf(checkbox!) expect(labelIndex).toBeLessThan(checkboxIndex) }) it('renders label at end position', () => { render() const container = screen.getByTestId('checkbox-with-label').parentElement const label = container?.querySelector('label') const checkbox = container?.querySelector('[role="checkbox"]') expect(label).toBeInTheDocument() expect(checkbox).toBeInTheDocument() // Checkbox should come before label in DOM const elements = Array.from(container?.children || []) const labelIndex = elements.indexOf(label!) const checkboxIndex = elements.indexOf(checkbox!) expect(checkboxIndex).toBeLessThan(labelIndex) }) it('applies custom label className', () => { render( ) const label = screen.getByText('Accept terms') expect(label).toHaveClass('custom-label') }) it('generates unique id when not provided', () => { render() const checkbox = screen.getByTestId('checkbox-with-label') const id = checkbox.getAttribute('id') expect(id).toMatch(/^checkbox-[a-z0-9]+$/) }) it('uses provided id', () => { render() const checkbox = screen.getByTestId('checkbox-with-label') expect(checkbox).toHaveAttribute('id', 'custom-id') const label = screen.getByText('Accept terms') expect(label).toHaveAttribute('for', 'custom-id') }) it('forwards ref correctly', () => { const ref = React.createRef() render() expect(ref.current).toBeInstanceOf(HTMLButtonElement) }) it('passes checkbox props correctly', () => { render( ) const checkbox = screen.getByTestId('checkbox-with-label') expect(checkbox).toHaveAttribute('aria-checked', 'true') expect(checkbox).toBeDisabled() expect(checkbox).toHaveClass('bg-transparent') }) it('maintains displayName', () => { expect(CheckboxWithLabel.displayName).toBe('CheckboxWithLabel') }) }) describe('Complex Combinations', () => { it('renders checkbox with all props correctly', () => { const handleChange = jest.fn() render( ) const checkbox = screen.getByTestId('checkbox') expect(checkbox).toHaveClass('bg-transparent') expect(checkbox).toHaveClass('h-6', 'w-6') expect(checkbox).toHaveClass('rounded-full') expect(checkbox).toHaveClass('transition-all') expect(checkbox).toHaveClass('custom-class') expect(checkbox).toHaveAttribute('aria-checked', 'true') }) it('renders checkbox group with multiple checkboxes', () => { render( ) const group = screen.getByTestId('checkbox-group') expect(group).toHaveClass('flex-row') expect(group).toHaveStyle({ gap: '1.5rem' }) expect(screen.getByText('Option 1')).toBeInTheDocument() expect(screen.getByText('Option 2')).toBeInTheDocument() expect(screen.getByText('Option 3')).toBeInTheDocument() }) }) describe('Edge Cases', () => { it('passes through HTML attributes', () => { render() const checkbox = screen.getByTestId('checkbox-test') expect(checkbox).toHaveAttribute('id', 'checkbox-1') }) it('handles null icon gracefully', () => { render() const checkbox = screen.getByTestId('checkbox') expect(checkbox).toBeInTheDocument() }) it('handles empty label in CheckboxWithLabel', () => { render() const container = screen.getByTestId('checkbox-with-label').parentElement const label = container?.querySelector('label') expect(label).toBeInTheDocument() expect(label).toHaveTextContent('') }) it('handles zero spacing in CheckboxGroup', () => { render( ) const group = screen.getByTestId('checkbox-group') expect(group).toHaveStyle({ gap: 0 }) }) }) })