diff --git a/src/shared/ui/Input/Input.svelte.test.ts b/src/shared/ui/Input/Input.svelte.test.ts new file mode 100644 index 0000000..3b65cfd --- /dev/null +++ b/src/shared/ui/Input/Input.svelte.test.ts @@ -0,0 +1,108 @@ +import { + fireEvent, + render, + screen, +} from '@testing-library/svelte'; +import Input from './Input.svelte'; + +describe('Input', () => { + describe('Rendering', () => { + it('renders an input element', () => { + render(Input); + expect(screen.getByRole('textbox')).toBeInTheDocument(); + }); + + it('renders placeholder text', () => { + render(Input, { placeholder: 'Search fonts…' }); + expect(screen.getByPlaceholderText('Search fonts…')).toBeInTheDocument(); + }); + + it('renders helper text when provided', () => { + render(Input, { helperText: 'Enter a font name' }); + expect(screen.getByText('Enter a font name')).toBeInTheDocument(); + }); + + it('does not render helper text by default', () => { + render(Input); + const input = screen.getByRole('textbox'); + expect(input.closest('div')?.parentElement?.querySelector('span')).toBeNull(); + }); + }); + + describe('Value', () => { + it('renders with initial value', () => { + render(Input, { value: 'Roboto' }); + expect(screen.getByDisplayValue('Roboto')).toBeInTheDocument(); + }); + + it('updates displayed value on user input', async () => { + render(Input); + const input = screen.getByRole('textbox') as HTMLInputElement; + await fireEvent.input(input, { target: { value: 'Inter' } }); + expect(input.value).toBe('Inter'); + }); + }); + + describe('Disabled state', () => { + it('is not disabled by default', () => { + render(Input); + expect(screen.getByRole('textbox')).not.toBeDisabled(); + }); + + it('is disabled when disabled prop is true', () => { + render(Input, { disabled: true }); + expect(screen.getByRole('textbox')).toBeDisabled(); + }); + }); + + describe('Error state', () => { + it('renders helper text with error styling when error=true', () => { + const { container } = render(Input, { error: true, helperText: 'Invalid value' }); + const helperSpan = container.querySelector('span'); + expect(helperSpan).toBeInTheDocument(); + expect(helperSpan).toHaveClass('text-brand'); + }); + }); + + describe('Clear button', () => { + it('does not show clear button by default', () => { + render(Input, { value: 'Roboto' }); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + it('shows clear button when showClearButton=true and value is set and onclear is provided', () => { + render(Input, { + value: 'Roboto', + showClearButton: true, + onclear: vi.fn(), + }); + expect(screen.getByRole('button')).toBeInTheDocument(); + }); + + it('calls onclear when clear button is clicked', async () => { + const onclear = vi.fn(); + render(Input, { value: 'Roboto', showClearButton: true, onclear }); + await fireEvent.click(screen.getByRole('button')); + expect(onclear).toHaveBeenCalledOnce(); + }); + + it('hides clear button when value is empty', () => { + render(Input, { value: '', showClearButton: true, onclear: vi.fn() }); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + }); + + describe('Variants', () => { + it.each(['default', 'underline', 'filled'] as const)('renders %s variant without error', variant => { + render(Input, { variant }); + expect(screen.getByRole('textbox')).toBeInTheDocument(); + }); + }); + + describe('Sizes', () => { + it.each(['sm', 'md', 'lg', 'xl'] as const)('renders %s size without error', size => { + render(Input, { size }); + expect(screen.getByRole('textbox')).toBeInTheDocument(); + }); + }); +});