import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { TagInput } from './tag-input' describe('TagInput', () => { const mockOnChange = vi.fn() beforeEach(() => { vi.clearAllMocks() }) it('should render empty input with placeholder', () => { render( ) expect(screen.getByPlaceholderText('Add equipment...')).toBeInTheDocument() }) it('should add tag on Enter key', async () => { const user = userEvent.setup() render( ) const input = screen.getByRole('textbox') await user.type(input, 'Dumbbells') await user.keyboard('{Enter}') expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells']) }) it('should add tag on blur', async () => { const user = userEvent.setup() render( ) const input = screen.getByRole('textbox') await user.type(input, 'Yoga Mat') await user.tab() // blur expect(mockOnChange).toHaveBeenCalledWith(['Yoga Mat']) }) it('should not add empty tag', async () => { const user = userEvent.setup() render( ) const input = screen.getByRole('textbox') await user.type(input, ' ') await user.keyboard('{Enter}') expect(mockOnChange).not.toHaveBeenCalled() }) it('should not add duplicate tags', async () => { const user = userEvent.setup() render( ) const input = screen.getByRole('textbox') await user.type(input, 'Dumbbells') await user.keyboard('{Enter}') expect(mockOnChange).not.toHaveBeenCalled() }) it('should remove tag on X click', async () => { const user = userEvent.setup() render( ) // Find and click the remove button on first tag const removeButtons = screen.getAllByRole('button', { name: '' }) await user.click(removeButtons[0]) expect(mockOnChange).toHaveBeenCalledWith(['Yoga Mat']) }) it('should remove last tag on Backspace when input empty', async () => { const user = userEvent.setup() render( ) const input = screen.getByRole('textbox') await user.click(input) await user.keyboard('{Backspace}') expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells']) }) it('should not remove tag on Backspace when input has text', async () => { const user = userEvent.setup() render( ) const input = screen.getByRole('textbox') await user.type(input, 'Yoga') await user.keyboard('{Backspace}') expect(mockOnChange).not.toHaveBeenCalled() }) it('should display all tags', () => { render( ) expect(screen.getByText('Dumbbells')).toBeInTheDocument() expect(screen.getByText('Yoga Mat')).toBeInTheDocument() expect(screen.getByText('Resistance Band')).toBeInTheDocument() }) it('should trim whitespace from tags', async () => { const user = userEvent.setup() render( ) const input = screen.getByRole('textbox') await user.type(input, ' Dumbbells ') await user.keyboard('{Enter}') expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells']) }) it('should respect disabled prop', () => { render( ) expect(screen.getByRole('textbox')).toBeDisabled() // Remove button on tag should not be present when disabled const removeButtons = screen.queryAllByRole('button', { name: '' }) expect(removeButtons).toHaveLength(0) }) it('should clear input after adding tag', async () => { const user = userEvent.setup() render( ) const input = screen.getByRole('textbox') as HTMLInputElement await user.type(input, 'Dumbbells') await user.keyboard('{Enter}') expect(input.value).toBe('') }) it('should handle multiple tags addition', async () => { const user = userEvent.setup() const { rerender } = render( ) const input = screen.getByRole('textbox') // Add first tag await user.type(input, 'Dumbbells') await user.keyboard('{Enter}') expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells']) // Rerender with updated value rerender( ) // Add second tag await user.type(input, 'Yoga Mat') await user.keyboard('{Enter}') expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells', 'Yoga Mat']) // Rerender with updated value rerender( ) // Add third tag await user.type(input, 'Resistance Band') await user.keyboard('{Enter}') expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells', 'Yoga Mat', 'Resistance Band']) }) })