|
import { useForm } from 'react-hook-form'; |
|
import { EpicInput } from './EpicInput'; |
|
import { render, waitFor } from '@testing-library/react'; |
|
import userEvent from '@testing-library/user-event'; |
|
import { EpicInputProps } from './EpicInputProps'; |
|
import { emailRule } from './rules'; |
|
|
|
type Model = { |
|
inputField?: string | number; |
|
}; |
|
|
|
const onSubmit = jest.fn(); |
|
|
|
function EpicInputWithFormContext({ |
|
initialValue, |
|
...props |
|
}: Omit<EpicInputProps, 'formContext' | 'name'> & { initialValue?: Model }) { |
|
const formContext = useForm({ |
|
defaultValues: initialValue, |
|
}); |
|
const { |
|
handleSubmit, |
|
formState: { errors }, |
|
} = formContext; |
|
|
|
const submit = (data: Model) => { |
|
onSubmit(data); |
|
}; |
|
|
|
return ( |
|
<> |
|
<div data-testid={'formErrors'}>{JSON.stringify(errors)}</div> |
|
<form |
|
onSubmit={(e) => { |
|
e.stopPropagation(); |
|
e.preventDefault(); |
|
handleSubmit(submit)(e); |
|
}} |
|
> |
|
<EpicInput |
|
label="Input Label" |
|
name={'inputField'} |
|
formContext={formContext} |
|
error={!!errors.inputField} |
|
helperText={errors.inputField?.message} |
|
{...props} |
|
/> |
|
<button type="submit">Submit</button> |
|
</form> |
|
</> |
|
); |
|
} |
|
|
|
beforeEach(() => { |
|
onSubmit.mockClear(); |
|
}); |
|
|
|
describe('Appearance', function () { |
|
it('should set size to small by default and can be set to medium', function () { |
|
const { getByLabelText, rerender } = render(<EpicInputWithFormContext canEdit />); |
|
const input = getByLabelText(/input label/i); |
|
expect(input.className).toMatch(/inputSizeSmall/i); |
|
|
|
rerender(<EpicInputWithFormContext canEdit={false} />); |
|
const input1 = getByLabelText(/input label/i); |
|
expect(input1.className).toMatch(/inputSizeSmall/i); |
|
|
|
rerender(<EpicInputWithFormContext size={'medium'} />); |
|
const input2 = getByLabelText(/input label/i); |
|
expect(input2.className).not.toMatch(/inputSizeSmall/i); |
|
}); |
|
|
|
it('should always shrink label by default', function () { |
|
const { getByText } = render(<EpicInputWithFormContext />); |
|
const label = getByText(/input label/i, { selector: 'label' }); |
|
|
|
expect(label).toHaveAttribute('data-shrink', 'true'); |
|
}); |
|
|
|
it('should be full width by default', function () { |
|
const { getByLabelText } = render(<EpicInputWithFormContext />); |
|
const input = getByLabelText(/input label/i); |
|
expect(input.closest('div')?.className).toMatch(/fullwidth/i); |
|
}); |
|
|
|
it('should render outlined style in edit mode', function () { |
|
const { getByLabelText } = render(<EpicInputWithFormContext />); |
|
const input = getByLabelText(/input label/i); |
|
expect(input.className).toMatch(/outlined/i); |
|
}); |
|
|
|
it('should apple standard style(underline) in read mode', function () { |
|
const { getByLabelText } = render(<EpicInputWithFormContext canEdit={false} />); |
|
const input = getByLabelText(/input label/i); |
|
expect(input.closest('div')?.className).toMatch(/underline/i); |
|
}); |
|
|
|
it('should not show * on the label on required field in read mode', function () { |
|
const { getByLabelText, queryByLabelText, rerender } = render( |
|
<EpicInputWithFormContext required canEdit={false} /> |
|
); |
|
expect(getByLabelText(/input label/i)).toBeInTheDocument(); |
|
expect(queryByLabelText(/\*/i)).toBeNull(); |
|
|
|
rerender(<EpicInputWithFormContext required canEdit />); |
|
expect(getByLabelText(/input label/i)).toBeInTheDocument(); |
|
expect(queryByLabelText(/\*/i)).toBeInTheDocument(); |
|
}); |
|
}); |
|
|
|
describe('Default behaviours', function () { |
|
it('should have name as test id', function () { |
|
const { getByTestId } = render(<EpicInputWithFormContext />); |
|
expect(getByTestId(/inputField/i)).toBeInTheDocument(); |
|
}); |
|
|
|
it('should be default to edit mode if canEdit not specified', async function () { |
|
const { getByLabelText } = render(<EpicInputWithFormContext />); |
|
const input = getByLabelText(/input label/i); |
|
|
|
expect(input).not.toHaveAttribute('disabled'); |
|
}); |
|
|
|
it('should be disabled if canEdit is false', async function () { |
|
const { getByLabelText } = render(<EpicInputWithFormContext canEdit={false} />); |
|
const input = getByLabelText(/input label/i); |
|
|
|
expect(input).toHaveAttribute('disabled'); |
|
}); |
|
|
|
it('should be readonly if specified', async function () { |
|
const { getByLabelText } = render(<EpicInputWithFormContext canEdit readOnly />); |
|
const input = getByLabelText(/input label/i); |
|
|
|
expect(input).toHaveAttribute('readonly'); |
|
}); |
|
|
|
it('should initial value', function () { |
|
const { getByLabelText } = render( |
|
<EpicInputWithFormContext initialValue={{ inputField: 'initial value' }} /> |
|
); |
|
const input = getByLabelText(/input label/i); |
|
|
|
expect(input).toHaveValue('initial value'); |
|
}); |
|
|
|
it('should hide if hidden is true', function () { |
|
const { getByLabelText } = render(<EpicInputWithFormContext hidden />); |
|
const input = getByLabelText(/input label/i); |
|
expect(input.closest('div[style="display: none;"]')).toBeInTheDocument(); |
|
}); |
|
|
|
it('should submit value and call onChange event', async function () { |
|
const onChange = jest.fn(); |
|
const { getByLabelText, getByText } = render(<EpicInputWithFormContext onChange={onChange} />); |
|
const input = getByLabelText(/input label/i); |
|
|
|
userEvent.type(input, 'test'); |
|
['t', 'te', 'tes', 'test'].forEach((char) => { |
|
expect(onChange).toHaveBeenCalledWith(char); |
|
}); |
|
|
|
userEvent.click(getByText(/submit/i)); |
|
|
|
await waitFor(() => expect(onSubmit).toHaveBeenCalledWith({ inputField: 'test' })); |
|
}); |
|
}); |
|
|
|
describe('Validations', function () { |
|
it('should show and hide required validation messages as appropriate', async function () { |
|
const { getByLabelText, getByText, queryByText, getByTestId, findByTestId } = render( |
|
<EpicInputWithFormContext canEdit required /> |
|
); |
|
const input = getByLabelText(/\*/); |
|
|
|
userEvent.click(getByText(/submit/i)); |
|
|
|
expect((await findByTestId('formErrors')).innerHTML).toMatchInlineSnapshot( |
|
`"{\\"inputField\\":{\\"type\\":\\"required\\",\\"message\\":\\"Required\\",\\"ref\\":{}}}"` |
|
); |
|
|
|
expect(input).toHaveAttribute('aria-invalid', 'true'); |
|
expect(onSubmit).not.toHaveBeenCalled(); |
|
expect(getByText('Required')).toBeInTheDocument(); |
|
|
|
userEvent.type(input, 'I am valid'); |
|
userEvent.click(getByText(/submit/i)); |
|
|
|
await waitFor(() => expect(onSubmit).toHaveBeenCalledWith({ inputField: 'I am valid' })); |
|
expect(getByTestId('formErrors').innerHTML).toBe('{}'); |
|
expect(input).toHaveAttribute('aria-invalid', 'false'); |
|
expect(queryByText('Required')).toBeNull(); |
|
}); |
|
|
|
it('should enforce validation rules passed in as props', async function () { |
|
const { getByLabelText, getByTestId, queryByText, findByTestId, getByText } = render( |
|
<EpicInputWithFormContext canEdit rules={emailRule} /> |
|
); |
|
const input = getByLabelText(/input label/i); |
|
userEvent.type(input, 'invalid email'); |
|
userEvent.click(getByText('Submit')); |
|
|
|
expect((await findByTestId('formErrors')).innerHTML).toMatchInlineSnapshot( |
|
`"{\\"inputField\\":{\\"type\\":\\"pattern\\",\\"message\\":\\"Email is not valid\\",\\"ref\\":{}}}"` |
|
); |
|
|
|
const validationMessage = 'Email is not valid'; |
|
|
|
expect(onSubmit).not.toHaveBeenCalled(); |
|
expect(input).toHaveAttribute('aria-invalid', 'true'); |
|
expect(getByText(validationMessage)).toBeInTheDocument(); |
|
|
|
userEvent.clear(input); |
|
userEvent.type(input, 'valid@gmail.com'); |
|
userEvent.click(getByText('Submit')); |
|
|
|
await waitFor(() => expect(onSubmit).toHaveBeenCalledWith({ inputField: 'valid@gmail.com' })); |
|
expect(getByTestId('formErrors').innerHTML).toBe('{}'); |
|
expect(input).toHaveAttribute('aria-invalid', 'false'); |
|
expect(queryByText(validationMessage)).toBeNull(); |
|
}); |
|
}); |
|
|
|
describe('Number Formatting', function () { |
|
it('should format number', async function () { |
|
const { getByText, getByLabelText } = render( |
|
<EpicInputWithFormContext canEdit type={'number'} /> |
|
); |
|
const input = getByLabelText(/input label/i); |
|
|
|
userEvent.type(input, '1000563.50689034'); |
|
userEvent.click(getByText('Submit')); |
|
|
|
await waitFor(() => expect(onSubmit).toHaveBeenCalledWith({ inputField: '1000563.50689034' })); |
|
expect(input).toHaveValue('1,000,563.50689034'); |
|
}); |
|
|
|
it('should add prefix and limit decimal scale as specified', async function () { |
|
const { getByText, getByLabelText } = render( |
|
<EpicInputWithFormContext |
|
canEdit |
|
type={'number'} |
|
numericProps={{ prefix: '£', decimalScale: 2 }} |
|
/> |
|
); |
|
const input = getByLabelText(/input label/i); |
|
|
|
userEvent.type(input, '1000563.50689034'); |
|
userEvent.click(getByText('Submit')); |
|
|
|
await waitFor(() => expect(onSubmit).toHaveBeenCalledWith({ inputField: '1000563.50' })); |
|
expect(input).toHaveValue('£1,000,563.50'); |
|
}); |
|
}); |
Top comments (0)