import { fireEvent, render, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import TextInput from './TextInput'
describe('TextInput Component', () => {
const defaultProps = {
value: '',
callout:
vi.fn<
(stateName: string | number | undefined, value: string | number) => void
>(),
labelText: 'Test Input',
stateName: 'test',
}
it('renders basic text input with label', () => {
render()
const container = screen.getByTestId('text-input-container')
const label = screen.getByTestId('form-label')
const input = screen.getByTestId('text-input-field')
expect(container).toBeInTheDocument()
expect(label).toBeInTheDocument()
expect(input).toBeInTheDocument()
expect(label).toHaveTextContent('Test Input')
})
it('handles text input changes', () => {
render()
const input = screen.getByTestId('text-input-field')
fireEvent.change(input, { target: { value: 'test value' } })
expect(input).toHaveValue('test value')
expect(defaultProps.callout).toHaveBeenCalledWith('test', 'test value')
})
it('renders textarea when type is textarea', () => {
render()
const textarea = screen.getByTestId('text-input-textarea')
expect(textarea).toBeInTheDocument()
})
it('handles password visibility toggle', () => {
render()
const input = screen.getByTestId('text-input-field')
expect(input).toHaveAttribute('type', 'password')
const toggleButton = screen.getByRole('button')
fireEvent.click(toggleButton)
expect(input).toHaveAttribute('type', 'text')
})
it('shows error state and message', () => {
const errorText = 'This field is required'
render(
,
)
const error = screen.getByTestId('text-input-error')
expect(error).toBeInTheDocument()
expect(error).toHaveTextContent(errorText)
})
it('handles currency input format', () => {
render(
,
)
const currencySymbol = screen.getByTestId('text-input-currency')
expect(currencySymbol).toBeInTheDocument()
expect(currencySymbol).toHaveTextContent('$')
})
it('handles disabled state', () => {
render()
const input = screen.getByTestId('text-input-field')
expect(input).toBeDisabled()
})
it('handles required state', () => {
render()
const input = screen.getByTestId('text-input-field')
expect(input).toBeRequired()
})
it('handles clear button functionality', () => {
const clearCallout = vi.fn<() => void>()
render(
,
)
const xIcon = screen.getByTestId('icon-x')
expect(xIcon).toBeInTheDocument()
fireEvent.click(xIcon)
expect(clearCallout).toHaveBeenCalled()
})
it('handles input with end text', () => {
render()
const endText = screen.getByTestId('text-input-end-text')
expect(endText).toBeInTheDocument()
expect(endText).toHaveValue('%')
})
it('handles number input with min/max constraints', () => {
render(
,
)
const input = screen.getByTestId('text-input-field')
fireEvent.change(input, { target: { value: '150' } })
expect(input).toHaveValue(100)
})
it('handles debounced input', async () => {
vi.useFakeTimers()
render()
defaultProps.callout.mockClear() // this is needed to avoid the initial callout when the input is rendered
const input = screen.getByTestId('text-input-field')
fireEvent.change(input, { target: { value: 'test' } })
expect(defaultProps.callout).not.toHaveBeenCalled()
vi.advanceTimersByTime(500)
expect(defaultProps.callout).toHaveBeenCalledTimes(1)
expect(defaultProps.callout).toHaveBeenCalledWith('test', 'test')
vi.useRealTimers()
})
it('handles auto-resize textarea', () => {
render()
const textarea = screen.getByTestId('text-input-textarea')
expect(textarea).toHaveClass('textareaAutoResize')
})
it('handles input with suffix label', () => {
render()
const suffix = screen.getByTestId('text-input-suffix')
expect(suffix).toBeInTheDocument()
expect(suffix).toHaveTextContent('kg')
})
it('handles onBlur callback', () => {
const onBlurCallout =
vi.fn<
(stateName: string | number | undefined, value: string | number) => void
>()
render()
const input = screen.getByTestId('text-input-field')
fireEvent.blur(input)
expect(onBlurCallout).toHaveBeenCalledWith('test', '')
})
it('handles onReturn callback', () => {
const onReturnCallout = vi.fn<() => void>()
render()
const input = screen.getByTestId('text-input-field')
fireEvent.keyDown(input, { key: 'Enter' })
expect(onReturnCallout).toHaveBeenCalled()
})
})