/** * @vitest-environment jsdom */ import * as React from 'react'; import { describe, it, expect, vi } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; import * as matchers from '@testing-library/jest-dom/matchers'; import { expect as vitestExpect } from 'vitest'; import { Button } from '../../button'; // Extend vitest's expect with jest-dom matchers vitestExpect.extend(matchers); describe('Button', () => { describe('rendering', () => { it('renders a button element', () => { render(); const button = screen.getByRole('button', { name: /click me/i }); expect(button).toBeInTheDocument(); expect(button.tagName).toBe('BUTTON'); }); it('renders children correctly', () => { render(); expect(screen.getByText('Test Content')).toBeInTheDocument(); }); it('applies custom className', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass('custom-class'); }); it('forwards ref correctly', () => { const ref = React.createRef(); render(); expect(ref.current).toBeInstanceOf(HTMLButtonElement); }); it('passes through HTML attributes', () => { render( ); const button = screen.getByTestId('test-button'); expect(button).toHaveAttribute('type', 'submit'); }); }); describe('variants', () => { it('renders default variant by default', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass('bg-primary', 'text-primary-foreground'); }); it('renders secondary variant', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass('bg-secondary', 'text-secondary-foreground'); }); it('renders outline variant', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass('border', 'border-input', 'bg-background'); }); it('renders ghost variant', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass('hover:bg-accent'); }); it('renders destructive variant', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass( 'bg-destructive', 'text-destructive-foreground' ); }); it('renders link variant', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass('text-primary', 'underline-offset-4'); }); }); describe('sizes', () => { it('renders default size by default', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass('h-10', 'px-4', 'py-2'); }); it('renders sm size', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass('h-9', 'px-3'); }); it('renders lg size', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass('h-11', 'px-8'); }); it('renders icon size', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass('h-10', 'w-10'); }); }); describe('disabled state', () => { it('is disabled when disabled prop is true', () => { render(); const button = screen.getByRole('button'); expect(button).toBeDisabled(); }); it('applies disabled styles', () => { render(); const button = screen.getByRole('button'); expect(button).toHaveClass( 'disabled:pointer-events-none', 'disabled:opacity-50' ); }); it('is not disabled by default', () => { render(); const button = screen.getByRole('button'); expect(button).not.toBeDisabled(); }); }); describe('onClick handler', () => { it('calls onClick when clicked', () => { const handleClick = vi.fn(); render(); const button = screen.getByRole('button'); fireEvent.click(button); expect(handleClick).toHaveBeenCalledTimes(1); }); it('does not call onClick when disabled', () => { const handleClick = vi.fn(); render( ); const button = screen.getByRole('button'); fireEvent.click(button); expect(handleClick).not.toHaveBeenCalled(); }); it('passes event object to onClick handler', () => { const handleClick = vi.fn(); render(); const button = screen.getByRole('button'); fireEvent.click(button); expect(handleClick).toHaveBeenCalledWith(expect.any(Object)); }); }); describe('combined props', () => { it('applies multiple props correctly', () => { const handleClick = vi.fn(); render( ); const button = screen.getByRole('button'); expect(button).toHaveClass( 'border', 'border-input', 'h-11', 'px-8', 'custom-class' ); expect(button).toBeDisabled(); }); }); });