docbrown/app/javascript/crayons/MultiInput/__tests__/MultiInput.test.jsx
2023-10-19 12:59:54 -04:00

234 lines
7.7 KiB
JavaScript

import { h } from 'preact';
import { render, waitFor } from '@testing-library/preact';
import { userEvent } from '@testing-library/user-event';
import '@testing-library/jest-dom';
import { MultiInput } from '../MultiInput';
describe('<MultiInput />', () => {
const getProps = () => ({
labelText: 'Example label',
placeholder: 'Add an email address...',
regex: /([a-zA-Z0-9@.])/,
validationRegex: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/,
});
const renderMultiInput = (props = getProps()) =>
render(<MultiInput {...props} />);
const setup = (getByPlaceholderText) => {
const input = getByPlaceholderText('Add an email address...');
input.focus();
return input;
};
it('renders default UI', () => {
const { container } = renderMultiInput();
expect(container.innerHTML).toMatchSnapshot();
});
describe('different keys', () => {
it('adds selection when enter is pressed', async () => {
const { getByPlaceholderText, getByRole } = renderMultiInput();
const input = setup(getByPlaceholderText);
await waitFor(() => userEvent.type(input, 'forem@gmail.com'));
userEvent.type(input, '{enter}');
await waitFor(() =>
expect(
getByRole('button', { name: 'Edit forem@gmail.com' }),
).toBeInTheDocument(),
);
});
it('adds selection when comma is pressed', async () => {
const { getByPlaceholderText, getByRole } = renderMultiInput();
const input = setup(getByPlaceholderText);
await waitFor(() => userEvent.type(input, 'forem@gmail.com'));
userEvent.keyboard(',');
await waitFor(() =>
expect(
getByRole('button', { name: 'Edit forem@gmail.com' }),
).toBeInTheDocument(),
);
});
it('adds selection when space is pressed', async () => {
const { getByPlaceholderText, getByRole } = renderMultiInput();
const input = setup(getByPlaceholderText);
await waitFor(() => userEvent.type(input, 'forem@gmail.com'));
userEvent.keyboard(' ');
await waitFor(() =>
expect(
getByRole('button', { name: 'Edit forem@gmail.com' }),
).toBeInTheDocument(),
);
});
});
it('edits a selection', async () => {
const { getByLabelText, getByPlaceholderText, getByRole } =
renderMultiInput();
const input = setup(getByPlaceholderText);
await waitFor(() => userEvent.type(input, 'forem@gmail.com'));
userEvent.type(input, '{enter}');
await waitFor(() =>
getByRole('button', { name: 'Edit forem@gmail.com' }).click(),
);
// Input is focused and pre-filled with value
const editInput = getByLabelText('Example label');
await waitFor(() => expect(editInput).toHaveValue('forem@gmail.com'));
expect(editInput).toHaveFocus();
});
it('deletes a selection', async () => {
const { getByLabelText, getByPlaceholderText, getByRole, queryByRole } =
renderMultiInput();
const input = setup(getByPlaceholderText);
await waitFor(() => userEvent.type(input, 'forem@gmail.com'));
userEvent.type(input, '{enter}');
await waitFor(() =>
getByRole('button', { name: 'Remove forem@gmail.com' }).click(),
);
// Selection disappears
await waitFor(() =>
expect(
queryByRole('button', { name: 'Remove forem@gmail.com' }),
).not.toBeInTheDocument(),
);
// Input is re-focused
expect(getByLabelText('Example label')).toHaveFocus();
});
describe('callbacks', () => {
it('passes an edit callback to custom selection template', async () => {
const Selection = ({ name, onEdit }) => (
<button onClick={onEdit}>Selected: {name}</button>
);
const { getByLabelText, getByRole, queryByRole, getByPlaceholderText } =
render(
<MultiInput
labelText="Example label"
placeholder="Add an email address..."
inputRegex={/([a-zA-Z0-9@.])/}
validationRegex={/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/}
SelectionTemplate={Selection}
/>,
);
const input = getByPlaceholderText('Add an email address...');
input.focus();
await waitFor(() => userEvent.type(input, 'one'));
userEvent.type(input, '{enter}');
await waitFor(() =>
getByRole('button', { name: 'Selected: one' }).click(),
);
// Selection disappears
await waitFor(() =>
expect(
queryByRole('button', { name: 'Selected: one' }),
).not.toBeInTheDocument(),
);
// Input is focused and pre-filled with value
const editInput = getByLabelText('Example label');
await waitFor(() => expect(editInput).toHaveValue('one'));
expect(editInput).toHaveFocus();
});
it('passes a deselect callback to custom selection template', async () => {
const Selection = ({ name, onDeselect }) => (
<button onClick={onDeselect}>Selected: {name}</button>
);
const { getByLabelText, getByRole, queryByRole, getByPlaceholderText } =
render(
<MultiInput
labelText="Example label"
placeholder="Add an email address..."
inputRegex={/([a-zA-Z0-9@.])/}
validationRegex={/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/}
SelectionTemplate={Selection}
/>,
);
const input = getByPlaceholderText('Add an email address...');
input.focus();
await waitFor(() => userEvent.type(input, 'one'));
userEvent.type(input, '{enter}');
await waitFor(() =>
getByRole('button', { name: 'Selected: one' }).click(),
);
// Selection disappears
await waitFor(() =>
expect(
queryByRole('button', { name: 'Selected: one' }),
).not.toBeInTheDocument(),
);
// Input is re-focused
expect(getByLabelText('Example label')).toHaveFocus();
});
});
it('edits a previous selection (if exists) on backspace press in empty input', async () => {
const { getByLabelText, getByRole, queryByRole, getByPlaceholderText } =
renderMultiInput();
const input = setup(getByPlaceholderText);
await waitFor(() => userEvent.type(input, 'example'));
userEvent.type(input, '{enter}');
await waitFor(() =>
expect(getByRole('button', { name: 'Edit example' })).toBeInTheDocument(),
);
// Selection should be present as passed as a default value
const editInput = getByLabelText('Example label');
editInput.focus();
await userEvent.type(editInput, '{backspace}');
await waitFor(() => expect(editInput).toHaveValue('example'));
expect(queryByRole('button', { name: 'Edit example' })).toBeNull();
});
it('Adds a description to invalid entries', async () => {
const { getByRole, getByPlaceholderText } = renderMultiInput();
const input = setup(getByPlaceholderText);
await waitFor(() => userEvent.type(input, 'example'));
userEvent.type(input, '{enter}');
await waitFor(() =>
expect(getByRole('button', { name: 'Edit example' })).toBeInTheDocument(),
);
expect(
getByRole('button', { name: 'Edit example' }),
).toHaveAccessibleDescription('Invalid entry');
});
it('Does not add a description to valid entries', async () => {
const { getByRole, getByPlaceholderText } = renderMultiInput();
const input = setup(getByPlaceholderText);
await waitFor(() => userEvent.type(input, 'example@email.com'));
userEvent.type(input, '{enter}');
await waitFor(() =>
expect(
getByRole('button', { name: 'Edit example@email.com' }),
).toBeInTheDocument(),
);
expect(
getByRole('button', { name: 'Edit example@email.com' }),
).not.toHaveAccessibleDescription('Invalid entry');
});
});