import React from 'react'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import PinPut from './pinput.component'; describe('PinPut Component', () => { const defaultProps = { value: '', onChange: jest.fn(), numInputs: 5, }; beforeEach(() => { jest.clearAllMocks(); }); it('renders with default props', () => { render(); // Should render 5 input fields by default const inputs = screen.getAllByRole('textbox'); expect(inputs).toHaveLength(5); }); it('renders with custom number of inputs', () => { render(); const inputs = screen.getAllByRole('textbox'); expect(inputs).toHaveLength(6); }); it('displays label when provided', () => { const label = 'Enter your PIN'; render(); expect(screen.getByText(label)).toBeInTheDocument(); }); it('calls onChange when input value changes', async () => { const onChange = jest.fn(); const user = userEvent.setup(); render(); const firstInput = screen.getAllByRole('textbox')[0]; await user.type(firstInput, '1'); expect(onChange).toHaveBeenCalledWith('1'); }); it('handles obscureText prop correctly', () => { render(); const inputs = screen.getAllByDisplayValue(''); inputs.forEach((input) => { expect(input).toHaveAttribute('type', 'password'); }); }); it('handles centerBoxes prop correctly', () => { render(); // The centerBoxes prop affects styling, so we just verify it renders expect(screen.getAllByRole('textbox')).toHaveLength(5); }); it('displays error message when provided', () => { const errorMessage = 'Invalid PIN'; render(); expect(screen.getByText(errorMessage)).toBeInTheDocument(); expect(screen.getByText(errorMessage)).toHaveClass('errorMessage'); }); it('displays helper text when provided', () => { const helperText = 'Enter the 6-digit code'; render(); expect(screen.getByText(helperText)).toBeInTheDocument(); expect(screen.getByText(helperText)).toHaveClass('helperText'); }); it('applies error styling when invalid prop is true', () => { render(); const inputs = screen.getAllByRole('textbox'); inputs.forEach((input) => { expect(input).toHaveClass('inputError'); }); }); it('applies error styling when error prop is provided', () => { render(); const inputs = screen.getAllByRole('textbox'); inputs.forEach((input) => { expect(input).toHaveClass('inputError'); }); }); it('handles disabled state correctly', () => { render(); const inputs = screen.getAllByRole('textbox'); inputs.forEach((input) => { expect(input).toBeDisabled(); expect(input).toHaveClass('inputDisabled'); }); }); it('calls onComplete when all inputs are filled', async () => { const onComplete = jest.fn(); // Test with a pre-filled value that matches the number of inputs render(); // The onComplete should be called when the component mounts with a complete value expect(onComplete).toHaveBeenCalledWith('123'); }); it('calls onFocus when input gains focus', async () => { const onFocus = jest.fn(); const user = userEvent.setup(); render(); const firstInput = screen.getAllByRole('textbox')[0]; await user.click(firstInput); expect(onFocus).toHaveBeenCalled(); }); it('calls onBlur when input loses focus', async () => { const onBlur = jest.fn(); const user = userEvent.setup(); render(); const firstInput = screen.getAllByRole('textbox')[0]; const secondInput = screen.getAllByRole('textbox')[1]; await user.click(firstInput); await user.click(secondInput); expect(onBlur).toHaveBeenCalled(); }); it('applies custom className when provided', () => { const customClass = 'custom-pin-input'; render(); const container = screen.getByRole('group'); expect(container).toHaveClass('container', customClass); }); it('handles value prop correctly', () => { const value = '12345'; render(); const inputs = screen.getAllByRole('textbox'); expect(inputs[0]).toHaveValue('1'); expect(inputs[1]).toHaveValue('2'); expect(inputs[2]).toHaveValue('3'); expect(inputs[3]).toHaveValue('4'); expect(inputs[4]).toHaveValue('5'); }); it('maintains accessibility attributes', () => { const label = 'Security Code'; const error = 'Invalid code'; render(); const inputs = screen.getAllByRole('textbox'); inputs.forEach((input, index) => { expect(input).toHaveAttribute('aria-label', `${label} input ${index + 1} of 4`); expect(input).toHaveAttribute('aria-invalid', 'true'); }); // Check that error message has proper ARIA attributes const errorMessage = screen.getByText(error); expect(errorMessage).toHaveAttribute('role', 'alert'); expect(errorMessage).toHaveAttribute('aria-live', 'polite'); }); it('handles keyboard navigation correctly', async () => { const user = userEvent.setup(); render(); const inputs = screen.getAllByRole('textbox'); // Focus first input await user.click(inputs[0]); expect(inputs[0]).toHaveFocus(); // Tab to next input await user.tab(); expect(inputs[1]).toHaveFocus(); // Tab to next input await user.tab(); expect(inputs[2]).toHaveFocus(); }); it('renders separators between inputs', () => { render(); const separators = screen.getAllByText('-'); expect(separators).toHaveLength(4); // 5 inputs = 4 separators }); it('handles empty value gracefully', () => { render(); const inputs = screen.getAllByRole('textbox'); inputs.forEach((input) => { expect(input).toHaveValue(''); }); }); it('handles undefined value gracefully', () => { render(); const inputs = screen.getAllByRole('textbox'); inputs.forEach((input) => { expect(input).toHaveValue(''); }); }); });