import { render, screen, fireEvent } from '@testing-library/react'
import { describe, it, expect, vi } from 'vitest'
import userEvent from '@testing-library/user-event'
import { Button } from '../button'
describe('Button', () => {
it('renders button with children', () => {
render()
expect(screen.getByRole('button')).toBeInTheDocument()
expect(screen.getByText('Click me')).toBeInTheDocument()
})
it('applies default variant classes', () => {
render()
const button = screen.getByRole('button')
expect(button).toHaveClass('bg-primary', 'text-primary-fg', 'hover:bg-primary/90')
})
it('applies destructive variant classes', () => {
render()
const button = screen.getByRole('button')
expect(button).toHaveClass('bg-destructive', 'text-destructive-fg', 'hover:bg-destructive/90')
})
it('applies link variant classes', () => {
render()
const button = screen.getByRole('button')
expect(button).toHaveClass('underline-offset-4', 'hover:no-underline')
})
it('applies default size classes', () => {
render()
const button = screen.getByRole('button')
expect(button).toHaveClass('h-7', 'px-3', 'py-2')
})
it('applies small size classes', () => {
render()
const button = screen.getByRole('button')
expect(button).toHaveClass('h-6', 'px-2')
})
it('applies large size classes', () => {
render()
const button = screen.getByRole('button')
expect(button).toHaveClass('h-9', 'rounded-md', 'px-4')
})
it('applies icon size classes', () => {
render()
const button = screen.getByRole('button')
expect(button).toHaveClass('size-8')
})
it('handles click events', async () => {
const handleClick = vi.fn()
const user = userEvent.setup()
render()
await user.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
it('can be disabled', () => {
render()
const button = screen.getByRole('button')
expect(button).toBeDisabled()
expect(button).toHaveClass('disabled:pointer-events-none', 'disabled:opacity-50')
})
it('does not trigger click when disabled', async () => {
const handleClick = vi.fn()
const user = userEvent.setup()
render()
await user.click(screen.getByRole('button'))
expect(handleClick).not.toHaveBeenCalled()
})
it('forwards ref correctly', () => {
const ref = vi.fn()
render()
expect(ref).toHaveBeenCalledWith(expect.any(HTMLButtonElement))
})
it('accepts custom className', () => {
render()
const button = screen.getByRole('button')
expect(button).toHaveClass('custom-class')
})
it('accepts custom props', () => {
render()
const button = screen.getByTestId('custom-button')
expect(button).toHaveAttribute('type', 'submit')
})
it('renders as different element when asChild is true', () => {
render(
)
const link = screen.getByRole('link')
expect(link).toHaveAttribute('href', '/test')
expect(link).toHaveClass('bg-primary', 'text-primary-fg') // Should inherit button classes
})
it('combines variant and size classes correctly', () => {
render()
const button = screen.getByRole('button')
expect(button).toHaveClass('bg-destructive', 'text-destructive-fg') // destructive variant
expect(button).toHaveClass('h-9', 'rounded-md', 'px-4') // large size
})
it('handles keyboard events', () => {
const handleKeyDown = vi.fn()
render()
const button = screen.getByRole('button')
fireEvent.keyDown(button, { key: 'Enter' })
expect(handleKeyDown).toHaveBeenCalledWith(expect.objectContaining({
key: 'Enter'
}))
})
it('supports focus events', () => {
const handleFocus = vi.fn()
const handleBlur = vi.fn()
render()
const button = screen.getByRole('button')
fireEvent.focus(button)
fireEvent.blur(button)
expect(handleFocus).toHaveBeenCalledTimes(1)
expect(handleBlur).toHaveBeenCalledTimes(1)
})
it('applies focus-visible styling', () => {
render()
const button = screen.getByRole('button')
expect(button).toHaveClass('focus-visible:border-ring', 'focus-visible:ring-ring/50')
})
})