diff --git a/src/shared/ui/Button/Button.svelte.test.ts b/src/shared/ui/Button/Button.svelte.test.ts new file mode 100644 index 0000000..6a3f726 --- /dev/null +++ b/src/shared/ui/Button/Button.svelte.test.ts @@ -0,0 +1,89 @@ +import { + fireEvent, + render, + screen, +} from '@testing-library/svelte'; +import { createRawSnippet } from 'svelte'; +import Button from './Button.svelte'; + +/** + * Create a plain text snippet for passing as children/icon prop + */ +function textSnippet(text: string) { + return createRawSnippet(() => ({ + render: () => `${text}`, + })); +} + +describe('Button', () => { + describe('Rendering', () => { + it('renders with text label', () => { + render(Button, { children: textSnippet('Click me') }); + expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument(); + }); + + it('has type="button" by default', () => { + render(Button, { children: textSnippet('Submit') }); + expect(screen.getByRole('button')).toHaveAttribute('type', 'button'); + }); + + it('renders with custom type', () => { + render(Button, { children: textSnippet('Submit'), type: 'submit' }); + expect(screen.getByRole('button')).toHaveAttribute('type', 'submit'); + }); + + it('renders icon-only when icon snippet is provided without children', () => { + render(Button, { icon: textSnippet('×') }); + const btn = screen.getByRole('button'); + expect(btn).toBeInTheDocument(); + }); + }); + + describe('Disabled state', () => { + it('is not disabled by default', () => { + render(Button, { children: textSnippet('Click') }); + expect(screen.getByRole('button')).not.toBeDisabled(); + }); + + it('is disabled when disabled prop is true', () => { + render(Button, { children: textSnippet('Click'), disabled: true }); + expect(screen.getByRole('button')).toBeDisabled(); + }); + }); + + describe('Click interaction', () => { + it('calls onclick when clicked', async () => { + const handler = vi.fn(); + render(Button, { children: textSnippet('Click'), onclick: handler }); + await fireEvent.click(screen.getByRole('button')); + expect(handler).toHaveBeenCalledOnce(); + }); + + it('calls onclick multiple times on repeated clicks', async () => { + const handler = vi.fn(); + render(Button, { children: textSnippet('Click'), onclick: handler }); + const btn = screen.getByRole('button'); + await fireEvent.click(btn); + await fireEvent.click(btn); + await fireEvent.click(btn); + expect(handler).toHaveBeenCalledTimes(3); + }); + }); + + describe('Variants', () => { + it.each(['primary', 'secondary', 'outline', 'ghost', 'icon', 'tertiary'] as const)( + 'renders %s variant without error', + variant => { + render(Button, { children: textSnippet('Click'), variant }); + expect(screen.getByRole('button')).toBeInTheDocument(); + }, + ); + }); + + describe('Sizes', () => { + it.each(['xs', 'sm', 'md', 'lg', 'xl'] as const)('renders %s size without error', size => { + render(Button, { children: textSnippet('Click'), size }); + expect(screen.getByRole('button')).toBeInTheDocument(); + }); + }); +});