import React from 'react' import { render, screen, fireEvent } from '@testing-library/react' import { Badge } from '../badge' describe('Badge Component', () => { it('renders correctly with default props', () => { render(Default Badge) const badge = screen.getByText('Default Badge').parentElement! expect(badge).toBeInTheDocument() expect(badge).toHaveClass('inline-flex') expect(badge).toHaveClass('items-center') expect(badge).toHaveClass('h-6') // md size expect(badge).toHaveClass('rounded-full') // default radius }) it('renders with custom text content', () => { render(Custom Text) expect(screen.getByText('Custom Text')).toBeInTheDocument() }) it('applies custom className', () => { render(Test) const badge = screen.getByText('Test').parentElement! expect(badge).toHaveClass('custom-badge') }) describe('Variants', () => { it('renders primary variant correctly (default)', () => { render(Primary) const badge = screen.getByText('Primary').parentElement! expect(badge).toHaveClass('bg-primary') expect(badge).toHaveClass('text-primary-foreground') expect(badge).toHaveClass('border-transparent') }) it('renders secondary variant correctly', () => { render(Secondary) const badge = screen.getByText('Secondary').parentElement! expect(badge).toHaveClass('bg-secondary') expect(badge).toHaveClass('text-secondary-foreground') }) it('renders destructive variant correctly', () => { render(Destructive) const badge = screen.getByText('Destructive').parentElement! expect(badge).toHaveClass('bg-error') expect(badge).toHaveClass('text-white') }) it('renders outline variant correctly', () => { render(Outline) const badge = screen.getByText('Outline').parentElement! expect(badge).toHaveClass('border-gray-200') expect(badge).toHaveClass('bg-transparent') }) it('renders success variant correctly', () => { render(Success) const badge = screen.getByText('Success').parentElement! expect(badge).toHaveClass('bg-success') expect(badge).toHaveClass('text-white') }) it('renders warning variant correctly', () => { render(Warning) const badge = screen.getByText('Warning').parentElement! expect(badge).toHaveClass('bg-warning') expect(badge).toHaveClass('text-white') }) it('renders ghost variant correctly', () => { render(Ghost) const badge = screen.getByText('Ghost').parentElement! expect(badge).toHaveClass('bg-transparent') expect(badge).toHaveClass('text-foreground') }) }) describe('Sizes', () => { it('renders small size correctly', () => { render(Small) const badge = screen.getByText('Small').parentElement! expect(badge).toHaveClass('h-5') expect(badge).toHaveClass('px-2') expect(badge).toHaveClass('text-xs') }) it('renders medium size correctly (default)', () => { render(Medium) const badge = screen.getByText('Medium').parentElement! expect(badge).toHaveClass('h-6') expect(badge).toHaveClass('px-2.5') expect(badge).toHaveClass('text-xs') }) it('renders large size correctly', () => { render(Large) const badge = screen.getByText('Large').parentElement! expect(badge).toHaveClass('h-7') expect(badge).toHaveClass('px-3') expect(badge).toHaveClass('text-sm') }) }) describe('Radius', () => { it('renders default radius correctly', () => { render(Default Radius) const badge = screen.getByText('Default Radius').parentElement! expect(badge).toHaveClass('rounded-full') }) it('renders small radius correctly', () => { render(Small Radius) const badge = screen.getByText('Small Radius').parentElement! expect(badge).toHaveClass('rounded-md') }) it('renders large radius correctly', () => { render(Large Radius) const badge = screen.getByText('Large Radius').parentElement! expect(badge).toHaveClass('rounded-xl') }) it('renders no radius correctly', () => { render(No Radius) const badge = screen.getByText('No Radius').parentElement! expect(badge).toHaveClass('rounded-none') }) }) describe('Dot Feature', () => { it('renders with dot when withDot is true', () => { render(With Dot) const badge = screen.getByText('With Dot').parentElement! const dot = badge.querySelector('span[aria-hidden="true"]') expect(dot).toBeInTheDocument() expect(dot).toHaveClass('h-2') expect(dot).toHaveClass('w-2') expect(dot).toHaveClass('rounded-full') }) it('does not render dot when withDot is false', () => { render(Without Dot) const badge = screen.getByText('Without Dot').parentElement! const dot = badge.querySelector('span[aria-hidden="true"]') expect(dot).not.toBeInTheDocument() }) it('renders dot with custom color', () => { render(Custom Dot) const badge = screen.getByText('Custom Dot').parentElement! const dot = badge.querySelector('span[aria-hidden="true"]') expect(dot).toHaveClass('bg-red-500') }) it('renders dot with variant-specific default colors', () => { const { rerender } = render(Destructive) let dot = screen.getByText('Destructive').parentElement!.querySelector('span[aria-hidden="true"]') expect(dot).toHaveClass('bg-white') rerender(Success) dot = screen.getByText('Success').parentElement!.querySelector('span[aria-hidden="true"]') expect(dot).toHaveClass('bg-white') rerender(Primary) dot = screen.getByText('Primary').parentElement!.querySelector('span[aria-hidden="true"]') expect(dot).toHaveClass('bg-primary-foreground') }) }) describe('Icons', () => { const TestIcon = () => Icon it('renders with left icon', () => { render(}>With Left Icon) expect(screen.getByTestId('test-icon')).toBeInTheDocument() expect(screen.getByText('With Left Icon')).toBeInTheDocument() }) it('renders with right icon', () => { render(}>With Right Icon) expect(screen.getByTestId('test-icon')).toBeInTheDocument() expect(screen.getByText('With Right Icon')).toBeInTheDocument() }) it('renders with both left and right icons', () => { const LeftIcon = () => Left const RightIcon = () => Right render( } rightIcon={}> Both Icons ) expect(screen.getByTestId('left-icon')).toBeInTheDocument() expect(screen.getByTestId('right-icon')).toBeInTheDocument() expect(screen.getByText('Both Icons')).toBeInTheDocument() }) }) describe('Removable Feature', () => { it('renders remove button when removable is true and onRemove is provided', () => { const onRemove = jest.fn() render(Removable) const removeButton = screen.getByRole('button', { name: 'Remove badge' }) expect(removeButton).toBeInTheDocument() expect(removeButton).toHaveAttribute('aria-label', 'Remove badge') }) it('does not render remove button when removable is false', () => { const onRemove = jest.fn() render(Not Removable) const removeButton = screen.queryByRole('button', { name: 'Remove badge' }) expect(removeButton).not.toBeInTheDocument() }) it('does not render remove button when onRemove is not provided', () => { render(No Remove Handler) const removeButton = screen.queryByRole('button', { name: 'Remove badge' }) expect(removeButton).not.toBeInTheDocument() }) it('calls onRemove when remove button is clicked', () => { const onRemove = jest.fn() render(Removable) const removeButton = screen.getByRole('button', { name: 'Remove badge' }) fireEvent.click(removeButton) expect(onRemove).toHaveBeenCalledTimes(1) }) it('stops propagation when remove button is clicked', () => { const onRemove = jest.fn() const onBadgeClick = jest.fn() render( Removable ) const removeButton = screen.getByRole('button', { name: 'Remove badge' }) fireEvent.click(removeButton) expect(onRemove).toHaveBeenCalledTimes(1) expect(onBadgeClick).not.toHaveBeenCalled() }) it('sets data-removable attribute when removable is true', () => { const onRemove = jest.fn() render(Removable) const badge = screen.getByText('Removable').parentElement! expect(badge).toHaveAttribute('data-removable', '') }) }) describe('Complex Combinations', () => { it('renders with all features combined', () => { const onRemove = jest.fn() const LeftIcon = () => L const RightIcon = () => R render( } rightIcon={} removable onRemove={onRemove} className="custom-badge" > Complex Badge ) const badge = screen.getByText('Complex Badge').parentElement! // Check variant, size, radius expect(badge).toHaveClass('bg-success') expect(badge).toHaveClass('h-7') expect(badge).toHaveClass('rounded-md') expect(badge).toHaveClass('custom-badge') // Check dot const dot = badge.querySelector('span[aria-hidden="true"]') expect(dot).toHaveClass('bg-yellow-400') // Check icons expect(screen.getByTestId('left-icon')).toBeInTheDocument() expect(screen.getByTestId('right-icon')).toBeInTheDocument() // Check remove button const removeButton = screen.getByRole('button', { name: 'Remove badge' }) expect(removeButton).toBeInTheDocument() // Test remove functionality fireEvent.click(removeButton) expect(onRemove).toHaveBeenCalledTimes(1) }) }) describe('Accessibility', () => { it('has proper ARIA attributes for remove button', () => { const onRemove = jest.fn() render(Accessible) const removeButton = screen.getByRole('button', { name: 'Remove badge' }) expect(removeButton).toHaveAttribute('aria-label', 'Remove badge') expect(removeButton).toHaveAttribute('type', 'button') }) it('has aria-hidden on dot element', () => { render(With Dot) const dot = screen.getByText('With Dot').parentElement!.querySelector('span[aria-hidden="true"]') expect(dot).toHaveAttribute('aria-hidden', 'true') }) it('has aria-hidden on remove icon', () => { const onRemove = jest.fn() render(Removable) const removeIcon = screen.getByRole('button').querySelector('svg') expect(removeIcon).toHaveAttribute('aria-hidden', 'true') }) it('truncates long text content', () => { render(Very long badge text that should be truncated) const textSpan = screen.getByText('Very long badge text that should be truncated') expect(textSpan).toHaveClass('truncate') }) }) describe('Event Handling', () => { it('handles click events on badge', () => { const onClick = jest.fn() render(Clickable) const badge = screen.getByText('Clickable').parentElement! fireEvent.click(badge) expect(onClick).toHaveBeenCalledTimes(1) }) it('handles focus events', () => { const onFocus = jest.fn() render(Focusable) const badge = screen.getByText('Focusable').parentElement! fireEvent.focus(badge) expect(onFocus).toHaveBeenCalledTimes(1) }) }) })