import React from 'react'
import { render, screen, fireEvent } from '@testing-library/react'
import '@testing-library/jest-dom'
import {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
} from '../table'
describe('Table Components', () => {
describe('Table Component', () => {
describe('Basic Rendering', () => {
it('renders correctly with default props', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toBeInTheDocument()
expect(table).toHaveClass('w-full', 'caption-bottom', 'text-sm')
})
it('applies custom className', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveClass('custom-table')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(
)
expect(ref.current).toBeInstanceOf(HTMLTableElement)
})
it('maintains displayName', () => {
expect(Table.displayName).toBe('Table')
})
})
describe('Variants', () => {
it('renders default variant correctly', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveClass('dark:text-gray-200')
})
it('renders bordered variant correctly', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveClass('border', 'border-border', 'dark:border-gray-700')
})
it('renders striped variant correctly', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveClass('dark:text-gray-200')
// TableBody should have the striped class applied
const tbody = screen.getByTestId('tbody')
expect(tbody).toHaveClass('[&_tr:last-child]:border-0')
})
it('renders card variant correctly', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveClass('border', 'border-border', 'dark:border-gray-700', 'rounded-md', 'shadow-sm')
})
it('renders minimal variant correctly', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveClass('border-none', 'dark:text-gray-200')
})
})
describe('Sizes', () => {
it('renders sm size correctly', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveClass('text-xs')
})
it('renders default size correctly', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveClass('text-sm')
})
it('renders lg size correctly', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveClass('text-base')
})
})
describe('Loading State', () => {
it('shows loading spinner when loading', () => {
render(
)
const spinner = document.querySelector('.animate-spin')
expect(spinner).toBeInTheDocument()
const table = screen.getByTestId('table')
expect(table).toHaveClass('opacity-70')
})
it('does not show loading spinner when not loading', () => {
render(
)
const spinner = document.querySelector('.animate-spin')
expect(spinner).not.toBeInTheDocument()
})
})
describe('Empty Content', () => {
it('shows empty content when no children', () => {
render()
expect(screen.getByText('No data available')).toBeInTheDocument()
})
it('does not show empty content when has children', () => {
render(
)
expect(screen.queryByText('No data available')).not.toBeInTheDocument()
})
})
describe('HTML Attributes', () => {
it('passes through HTML attributes', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveAttribute('id', 'test-table')
expect(table).toHaveAttribute('role', 'table')
})
})
})
describe('TableHeader Component', () => {
it('renders correctly with default props', () => {
render(
)
const header = screen.getByTestId('header')
expect(header).toBeInTheDocument()
expect(header.tagName).toBe('THEAD')
})
it('applies custom className', () => {
render(
)
const header = screen.getByTestId('header')
expect(header).toHaveClass('custom-header')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(
)
expect(ref.current).toBeInstanceOf(HTMLTableSectionElement)
})
it('maintains displayName', () => {
expect(TableHeader.displayName).toBe('TableHeader')
})
})
describe('TableBody Component', () => {
it('renders correctly with default props', () => {
render(
)
const body = screen.getByTestId('body')
expect(body).toBeInTheDocument()
expect(body.tagName).toBe('TBODY')
})
it('applies custom className', () => {
render(
)
const body = screen.getByTestId('body')
expect(body).toHaveClass('custom-body')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(
)
expect(ref.current).toBeInstanceOf(HTMLTableSectionElement)
})
it('maintains displayName', () => {
expect(TableBody.displayName).toBe('TableBody')
})
})
describe('TableFooter Component', () => {
it('renders correctly with default props', () => {
render(
)
const footer = screen.getByTestId('footer')
expect(footer).toBeInTheDocument()
expect(footer.tagName).toBe('TFOOT')
expect(footer).toHaveClass('bg-primary', 'text-primary-foreground', 'font-medium')
})
it('applies custom className', () => {
render(
)
const footer = screen.getByTestId('footer')
expect(footer).toHaveClass('custom-footer')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(
)
expect(ref.current).toBeInstanceOf(HTMLTableSectionElement)
})
it('maintains displayName', () => {
expect(TableFooter.displayName).toBe('TableFooter')
})
})
describe('TableRow Component', () => {
it('renders correctly with default props', () => {
render(
)
const row = screen.getByTestId('row')
expect(row).toBeInTheDocument()
expect(row.tagName).toBe('TR')
expect(row).toHaveClass('border-b', 'transition-colors', 'hover:bg-muted/50')
})
it('applies custom className', () => {
render(
)
const row = screen.getByTestId('row')
expect(row).toHaveClass('custom-row')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(
)
expect(ref.current).toBeInstanceOf(HTMLTableRowElement)
})
it('maintains displayName', () => {
expect(TableRow.displayName).toBe('TableRow')
})
})
describe('TableHead Component', () => {
it('renders correctly with default props', () => {
render(
)
const head = screen.getByTestId('head')
expect(head).toBeInTheDocument()
expect(head.tagName).toBe('TH')
expect(head).toHaveClass('h-10', 'px-4', 'text-left', 'align-middle', 'font-medium', 'text-muted-foreground')
})
it('renders sortable head correctly', () => {
const onSort = jest.fn()
render(
)
const head = screen.getByTestId('head')
expect(head).toHaveClass('cursor-pointer', 'hover:text-foreground', 'select-none')
// Check for sort icon
const sortIcon = head.querySelector('svg')
expect(sortIcon).toBeInTheDocument()
expect(sortIcon).toHaveClass('opacity-30')
})
it('handles sort click', () => {
const onSort = jest.fn()
render(
)
const head = screen.getByTestId('head')
fireEvent.click(head)
expect(onSort).toHaveBeenCalledTimes(1)
})
it('shows ascending sort icon', () => {
render(
)
const head = screen.getByTestId('head')
const sortIcon = head.querySelector('svg')
expect(sortIcon).toBeInTheDocument()
expect(sortIcon).not.toHaveClass('opacity-30')
})
it('shows descending sort icon', () => {
render(
)
const head = screen.getByTestId('head')
const sortIcon = head.querySelector('svg')
expect(sortIcon).toBeInTheDocument()
expect(sortIcon).not.toHaveClass('opacity-30')
})
it('applies custom className', () => {
render(
)
const head = screen.getByTestId('head')
expect(head).toHaveClass('custom-head')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(
)
expect(ref.current).toBeInstanceOf(HTMLTableCellElement)
})
it('maintains displayName', () => {
expect(TableHead.displayName).toBe('TableHead')
})
})
describe('TableCell Component', () => {
it('renders correctly with default props', () => {
render(
)
const cell = screen.getByTestId('cell')
expect(cell).toBeInTheDocument()
expect(cell.tagName).toBe('TD')
expect(cell).toHaveClass('p-4', 'align-middle')
})
it('applies custom className', () => {
render(
)
const cell = screen.getByTestId('cell')
expect(cell).toHaveClass('custom-cell')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(
)
expect(ref.current).toBeInstanceOf(HTMLTableCellElement)
})
it('maintains displayName', () => {
expect(TableCell.displayName).toBe('TableCell')
})
})
describe('TableCaption Component', () => {
it('renders correctly with default props', () => {
render(
)
const caption = screen.getByTestId('caption')
expect(caption).toBeInTheDocument()
expect(caption.tagName).toBe('CAPTION')
expect(caption).toHaveClass('mt-4', 'text-sm', 'text-muted-foreground')
})
it('applies custom className', () => {
render(
)
const caption = screen.getByTestId('caption')
expect(caption).toHaveClass('custom-caption')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(
)
expect(ref.current).toBeInstanceOf(HTMLTableCaptionElement)
})
it('maintains displayName', () => {
expect(TableCaption.displayName).toBe('TableCaption')
})
})
describe('Complex Combinations', () => {
it('renders complete table with all components', () => {
render(
Complete table example
Name
Age
John
25
Total
1
)
expect(screen.getByText('Complete table example')).toBeInTheDocument()
expect(screen.getByText('Name')).toBeInTheDocument()
expect(screen.getByText('John')).toBeInTheDocument()
expect(screen.getByText('Total')).toBeInTheDocument()
})
it('handles striped variant with tbody', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toHaveClass('dark:text-gray-200')
const tbody = screen.getByTestId('tbody')
expect(tbody).toHaveClass('[&_tr:last-child]:border-0')
})
})
describe('Edge Cases', () => {
it('handles empty table', () => {
render()
const table = screen.getByTestId('table')
expect(table).toBeInTheDocument()
})
it('handles table without tbody', () => {
render(
)
const table = screen.getByTestId('table')
expect(table).toBeInTheDocument()
})
it('passes through HTML attributes for all components', () => {
render(
)
expect(screen.getByTestId('table')).toHaveAttribute('id', 'table')
expect(screen.getByTestId('header')).toHaveAttribute('id', 'header')
expect(screen.getByTestId('header-row')).toHaveAttribute('id', 'header-row')
expect(screen.getByTestId('head')).toHaveAttribute('id', 'head')
expect(screen.getByTestId('body')).toHaveAttribute('id', 'body')
expect(screen.getByTestId('body-row')).toHaveAttribute('id', 'body-row')
expect(screen.getByTestId('cell')).toHaveAttribute('id', 'cell')
})
})
})