import { test, expect, vi } from 'vitest'; import { render, screen, within, fireEvent } from '@testing-library/react'; import TextInput from './TextInput'; const INPUT_ID = 'text-input'; const LABEL_TEXT = 'First name'; test('text input renders correctly', () => { render( ); const textInput = screen.getByRole('textbox'); const label = screen.getByText(LABEL_TEXT); expect(textInput).toHaveClass('ds_input'); expect(textInput).toHaveAttribute('id', INPUT_ID); expect(textInput).toHaveAttribute('name', INPUT_ID); expect(textInput).toHaveAttribute('type', 'text'); expect(textInput.tagName).toEqual('INPUT'); expect(label).toHaveClass('ds_label'); expect(label).toHaveAttribute('for', INPUT_ID); expect(label.tagName).toEqual('LABEL'); expect(label.textContent).toEqual(LABEL_TEXT); expect(textInput.previousSibling).toEqual(label); }); test('text input with custom class(es)', () => { render( ); const textInput = screen.getByRole('textbox'); expect(textInput).toHaveClass('ds_input', 'foo', 'bar'); }); test('text input with character count', () => { const MAX_LENGTH = 100; render( ); const textInput = screen.getByRole('textbox'); const textInputWrapper = textInput.parentElement; expect(textInputWrapper).toHaveAttribute('data-module', 'ds-character-count'); }); test('text input with character count and threshold', () => { const MAX_LENGTH = 100; const COUNT_THRESHOLD = 80; render( ); const textInput = screen.getByRole('textbox'); const textInputWrapper = textInput.parentElement; expect(textInputWrapper).toHaveAttribute('data-threshold', COUNT_THRESHOLD.toString()); }); test('text input with width', () => { const INPUT_WIDTH = 'fixed-10'; render( ); const textInput = screen.getByRole('textbox'); expect(textInput).toHaveClass(`ds_input--${INPUT_WIDTH}`); }); test('text input with currency', () => { render( ); const textInput = screen.getByRole('textbox'); const textInputWrapper = textInput.parentElement; expect(textInputWrapper?.tagName).toEqual('DIV') expect(textInputWrapper).toHaveClass('ds_currency-wrapper'); expect(textInputWrapper).not.toHaveAttribute('data-symbol'); }); test('text input with custom currency symbol', () => { const CURRENCY_SYMBOL = '@'; render( ); const textInput = screen.getByRole('textbox'); const textInputWrapper = textInput.parentElement; expect(textInputWrapper).toHaveAttribute('data-symbol', CURRENCY_SYMBOL); }); test('text input with button', () => { const BUTTON_TEXT = 'Search'; const BUTTON_ICON = 'Search'; render( ); const textInput = screen.getByRole('textbox'); const textInputWrapper = textInput.parentElement; const button = screen.getByRole('button'); const buttonTextElement = within(button).getByText(BUTTON_TEXT); const buttonIconElement = within(button).getByRole('img', { hidden: true }); expect(textInputWrapper).toHaveClass('ds_input__wrapper', 'ds_input__wrapper--has-icon '); expect(button).toBeInTheDocument(); expect(button).toHaveClass('ds_button'); expect(button).toHaveAttribute('type', 'button'); expect(button.tagName).toEqual('BUTTON'); expect(buttonTextElement).toHaveClass('visually-hidden'); expect(buttonTextElement.tagName).toEqual('SPAN'); expect(buttonIconElement).toBeInTheDocument(); }); test('text input with hint text', () => { const HINT_TEXT = 'hint text'; render( ); const hintTextEl = screen.getByText(HINT_TEXT); const textInput = screen.getByRole('textbox'); expect(hintTextEl).toBeInTheDocument(); expect(textInput).toHaveAttribute('aria-describedby', hintTextEl.id); }); test('text input with custom name', () => { const INPUT_NAME = 'foo'; render( ); const textInput = screen.getByRole('textbox'); expect(textInput).toHaveAttribute('name', INPUT_NAME); }); test('text input with blur function', () => { const ONBLUR_FUNCTION = vi.fn(); render( ); const textInput = screen.getByRole('textbox'); fireEvent.blur(textInput); expect(ONBLUR_FUNCTION).toHaveBeenCalled(); }); test('text input with change function', () => { const ONCHANGE_FUNCTION = vi.fn(); render( ); const textInput = screen.getByRole('textbox'); fireEvent.change(textInput, {target: {value: 'foo'}}); expect(ONCHANGE_FUNCTION).toHaveBeenCalled(); }); test('text input with onBlur that is not a function', () => { render( ); const textInput = screen.getByRole('textbox'); fireEvent.blur(textInput); // todo: assertion // success indicated by no errors thrown // error would be thrown on an untestable thread }); test('text input with onChange that is not a function', () => { render( ); const textInput = screen.getByRole('textbox'); fireEvent.change(textInput, {target: {value: 'foo'}}); // todo: assertion // success indicated by no errors thrown // error would be thrown on an untestable thread }); test('text input with placeholder text', () => { const PLACEHOLDER_TEXT = 'foo'; render( ); const textInput = screen.getByRole('textbox'); expect(textInput).toHaveAttribute('placeholder', PLACEHOLDER_TEXT); }); test('text input with different type', () => { const INPUT_TYPE = 'foo'; render( ); const textInput = screen.getByRole('textbox'); expect(textInput).toHaveAttribute('type', INPUT_TYPE); }); test('text input with initial value', () => { const INITIAL_VALUE = 'initial value'; render( ); const textInput = screen.getByRole('textbox'); expect(textInput).toHaveAttribute('value', INITIAL_VALUE); }); test('text input with error message', () => { const ERROR_MESSAGE_TEXT = 'This is a required field'; render( ); const textInput = screen.getByRole('textbox'); const errorMessageElement = screen.getByText(ERROR_MESSAGE_TEXT); expect(textInput).toHaveClass('ds_input--error') expect(textInput).toHaveAttribute('aria-describedby', errorMessageElement.id); expect(textInput).toHaveAttribute('aria-invalid', 'true'); expect(errorMessageElement).toBeInTheDocument(); expect(errorMessageElement).toHaveClass('ds_question__error-message'); }); test('instantiating/initialising DS component script', () => { render( ); const textInput = screen.getByRole('textbox'); const textInputContainer = textInput.parentElement; expect(textInputContainer).toHaveClass('js-initialised'); expect(textInputContainer).toHaveClass('js-instantiated'); }); test('passing additional props', () => { render( ); const textInput = screen.getByRole('textbox'); expect(textInput?.dataset.test).toEqual('foo'); }); test('passing additional CSS classes', () => { render( ); const textInput = screen.getByRole('textbox'); expect(textInput).toHaveClass('foo', 'ds_input'); });