/**
* @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();
});
});
});