168 lines
5.1 KiB
TypeScript
168 lines
5.1 KiB
TypeScript
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(<Button>Click me</Button>)
|
|
|
|
expect(screen.getByRole('button')).toBeInTheDocument()
|
|
expect(screen.getByText('Click me')).toBeInTheDocument()
|
|
})
|
|
|
|
it('applies default variant classes', () => {
|
|
render(<Button>Default Button</Button>)
|
|
|
|
const button = screen.getByRole('button')
|
|
expect(button).toHaveClass('bg-primary', 'text-primary-fg', 'hover:bg-primary/90')
|
|
})
|
|
|
|
it('applies destructive variant classes', () => {
|
|
render(<Button variant="destructive">Destructive Button</Button>)
|
|
|
|
const button = screen.getByRole('button')
|
|
expect(button).toHaveClass('bg-destructive', 'text-destructive-fg', 'hover:bg-destructive/90')
|
|
})
|
|
|
|
it('applies link variant classes', () => {
|
|
render(<Button variant="link">Link Button</Button>)
|
|
|
|
const button = screen.getByRole('button')
|
|
expect(button).toHaveClass('underline-offset-4', 'hover:no-underline')
|
|
})
|
|
|
|
it('applies default size classes', () => {
|
|
render(<Button>Default Size</Button>)
|
|
|
|
const button = screen.getByRole('button')
|
|
expect(button).toHaveClass('h-7', 'px-3', 'py-2')
|
|
})
|
|
|
|
it('applies small size classes', () => {
|
|
render(<Button size="sm">Small Button</Button>)
|
|
|
|
const button = screen.getByRole('button')
|
|
expect(button).toHaveClass('h-6', 'px-2')
|
|
})
|
|
|
|
it('applies large size classes', () => {
|
|
render(<Button size="lg">Large Button</Button>)
|
|
|
|
const button = screen.getByRole('button')
|
|
expect(button).toHaveClass('h-9', 'rounded-md', 'px-4')
|
|
})
|
|
|
|
it('applies icon size classes', () => {
|
|
render(<Button size="icon">Icon</Button>)
|
|
|
|
const button = screen.getByRole('button')
|
|
expect(button).toHaveClass('size-8')
|
|
})
|
|
|
|
it('handles click events', async () => {
|
|
const handleClick = vi.fn()
|
|
const user = userEvent.setup()
|
|
|
|
render(<Button onClick={handleClick}>Click me</Button>)
|
|
|
|
await user.click(screen.getByRole('button'))
|
|
|
|
expect(handleClick).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('can be disabled', () => {
|
|
render(<Button disabled>Disabled Button</Button>)
|
|
|
|
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(<Button disabled onClick={handleClick}>Disabled Button</Button>)
|
|
|
|
await user.click(screen.getByRole('button'))
|
|
|
|
expect(handleClick).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('forwards ref correctly', () => {
|
|
const ref = vi.fn()
|
|
|
|
render(<Button ref={ref}>Button with ref</Button>)
|
|
|
|
expect(ref).toHaveBeenCalledWith(expect.any(HTMLButtonElement))
|
|
})
|
|
|
|
it('accepts custom className', () => {
|
|
render(<Button className="custom-class">Custom Button</Button>)
|
|
|
|
const button = screen.getByRole('button')
|
|
expect(button).toHaveClass('custom-class')
|
|
})
|
|
|
|
it('accepts custom props', () => {
|
|
render(<Button data-testid="custom-button" type="submit">Custom Button</Button>)
|
|
|
|
const button = screen.getByTestId('custom-button')
|
|
expect(button).toHaveAttribute('type', 'submit')
|
|
})
|
|
|
|
it('renders as different element when asChild is true', () => {
|
|
render(
|
|
<Button asChild>
|
|
<a href="/test">Link Button</a>
|
|
</Button>
|
|
)
|
|
|
|
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(<Button variant="destructive" size="lg">Large Destructive Button</Button>)
|
|
|
|
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(<Button onKeyDown={handleKeyDown}>Keyboard Button</Button>)
|
|
|
|
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(<Button onFocus={handleFocus} onBlur={handleBlur}>Focus Button</Button>)
|
|
|
|
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(<Button>Focus Button</Button>)
|
|
|
|
const button = screen.getByRole('button')
|
|
expect(button).toHaveClass('focus-visible:border-ring', 'focus-visible:ring-ring/50')
|
|
})
|
|
}) |