2022-10-26 00:23:18 +02:00
|
|
|
/* eslint-disable jest/require-top-level-describe */
|
2022-11-15 17:49:02 +01:00
|
|
|
import React from 'react';
|
2023-03-07 00:45:23 +01:00
|
|
|
import PropTypes from 'prop-types';
|
2022-10-26 00:23:18 +02:00
|
|
|
import { fireEvent, render } from '@testing-library/react';
|
2023-02-16 00:43:51 +01:00
|
|
|
import { renderWithUserEvent } from '../../../../test/lib/render-helpers';
|
2022-10-26 00:23:18 +02:00
|
|
|
|
2023-02-16 00:43:51 +01:00
|
|
|
import { Size } from '../../../helpers/constants/design-system';
|
|
|
|
|
|
|
|
import Box from '../../ui/box';
|
2022-11-15 17:49:02 +01:00
|
|
|
|
2022-10-26 00:23:18 +02:00
|
|
|
import { TextField } from './text-field';
|
|
|
|
|
|
|
|
describe('TextField', () => {
|
|
|
|
it('should render correctly', () => {
|
2022-12-06 20:52:03 +01:00
|
|
|
const { getByRole, container } = render(<TextField />);
|
2022-10-26 00:23:18 +02:00
|
|
|
expect(getByRole('textbox')).toBeDefined();
|
2022-12-06 20:52:03 +01:00
|
|
|
expect(container).toMatchSnapshot();
|
2022-10-26 00:23:18 +02:00
|
|
|
});
|
2023-02-16 00:43:51 +01:00
|
|
|
it('should render and be able to input text', () => {
|
|
|
|
const { getByTestId } = render(
|
|
|
|
<TextField inputProps={{ 'data-testid': 'text-field' }} />,
|
|
|
|
);
|
|
|
|
const textField = getByTestId('text-field');
|
|
|
|
|
|
|
|
expect(textField.value).toBe(''); // initial value is empty string
|
|
|
|
fireEvent.change(textField, { target: { value: 'text value' } });
|
|
|
|
expect(textField.value).toBe('text value');
|
|
|
|
fireEvent.change(textField, { target: { value: '' } }); // reset value
|
|
|
|
expect(textField.value).toBe(''); // value is empty string after reset
|
|
|
|
});
|
|
|
|
it('should render with focused state when clicked', async () => {
|
|
|
|
const { getByTestId, user } = renderWithUserEvent(
|
|
|
|
<TextField
|
|
|
|
data-testid="text-field"
|
|
|
|
inputProps={{ 'data-testid': 'input' }}
|
|
|
|
/>,
|
|
|
|
);
|
|
|
|
const textField = getByTestId('input');
|
|
|
|
|
|
|
|
await user.click(textField);
|
|
|
|
expect(getByTestId('input')).toHaveFocus();
|
|
|
|
expect(getByTestId('text-field')).toHaveClass('mm-text-field--focused ');
|
2022-10-26 00:23:18 +02:00
|
|
|
});
|
2023-02-16 00:43:51 +01:00
|
|
|
it('should render and fire onFocus and onBlur events', async () => {
|
2022-10-26 00:23:18 +02:00
|
|
|
const onFocus = jest.fn();
|
|
|
|
const onBlur = jest.fn();
|
2023-02-16 00:43:51 +01:00
|
|
|
const { getByTestId, user } = renderWithUserEvent(
|
2022-10-26 00:23:18 +02:00
|
|
|
<TextField
|
|
|
|
inputProps={{ 'data-testid': 'text-field' }}
|
|
|
|
onFocus={onFocus}
|
|
|
|
onBlur={onBlur}
|
|
|
|
/>,
|
|
|
|
);
|
|
|
|
|
2023-02-16 00:43:51 +01:00
|
|
|
const textField = getByTestId('text-field');
|
|
|
|
await user.click(textField);
|
2022-10-26 00:23:18 +02:00
|
|
|
expect(onFocus).toHaveBeenCalledTimes(1);
|
|
|
|
fireEvent.blur(textField);
|
|
|
|
expect(onBlur).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
2022-11-10 20:13:15 +01:00
|
|
|
it('should render and fire onChange event', async () => {
|
2022-10-26 00:23:18 +02:00
|
|
|
const onChange = jest.fn();
|
2023-02-16 00:43:51 +01:00
|
|
|
const { getByTestId, user } = renderWithUserEvent(
|
2022-10-26 00:23:18 +02:00
|
|
|
<TextField
|
|
|
|
inputProps={{ 'data-testid': 'text-field' }}
|
|
|
|
onChange={onChange}
|
|
|
|
/>,
|
|
|
|
);
|
2023-02-16 00:43:51 +01:00
|
|
|
const textField = getByTestId('text-field');
|
2022-11-10 20:13:15 +01:00
|
|
|
await user.type(textField, '123');
|
|
|
|
expect(textField).toHaveValue('123');
|
|
|
|
expect(onChange).toHaveBeenCalledTimes(3);
|
2022-10-26 00:23:18 +02:00
|
|
|
});
|
2022-11-10 20:13:15 +01:00
|
|
|
it('should render and fire onClick event', async () => {
|
2022-10-26 00:23:18 +02:00
|
|
|
const onClick = jest.fn();
|
2023-02-16 00:43:51 +01:00
|
|
|
const { getByTestId, user } = renderWithUserEvent(
|
|
|
|
<TextField
|
|
|
|
inputProps={{ 'data-testid': 'text-field' }}
|
|
|
|
onClick={onClick}
|
|
|
|
/>,
|
2022-10-26 00:23:18 +02:00
|
|
|
);
|
2023-02-16 00:43:51 +01:00
|
|
|
const textField = getByTestId('text-field');
|
|
|
|
|
|
|
|
await user.click(textField);
|
2022-10-26 00:23:18 +02:00
|
|
|
expect(onClick).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
2023-02-16 00:43:51 +01:00
|
|
|
it('should render with different size classes', () => {
|
2022-11-10 20:13:15 +01:00
|
|
|
const { getByTestId } = render(
|
2023-02-16 00:43:51 +01:00
|
|
|
<>
|
|
|
|
<TextField size={Size.SM} data-testid="sm" />
|
|
|
|
<TextField size={Size.MD} data-testid="md" />
|
|
|
|
<TextField size={Size.LG} data-testid="lg" />
|
|
|
|
</>,
|
|
|
|
);
|
|
|
|
expect(getByTestId('sm')).toHaveClass('mm-text-field--size-sm');
|
|
|
|
expect(getByTestId('md')).toHaveClass('mm-text-field--size-md');
|
|
|
|
expect(getByTestId('lg')).toHaveClass('mm-text-field--size-lg');
|
|
|
|
});
|
|
|
|
it('should render with different types', () => {
|
|
|
|
const { getByTestId } = render(
|
|
|
|
<>
|
|
|
|
<TextField inputProps={{ 'data-testid': 'text-field-text' }} />
|
|
|
|
<TextField
|
|
|
|
type="number"
|
|
|
|
inputProps={{ 'data-testid': 'text-field-number' }}
|
|
|
|
/>
|
|
|
|
<TextField
|
|
|
|
type="password"
|
|
|
|
inputProps={{ 'data-testid': 'text-field-password' }}
|
|
|
|
/>
|
|
|
|
</>,
|
|
|
|
);
|
|
|
|
expect(getByTestId('text-field-text')).toHaveAttribute('type', 'text');
|
|
|
|
expect(getByTestId('text-field-number')).toHaveAttribute('type', 'number');
|
|
|
|
expect(getByTestId('text-field-password')).toHaveAttribute(
|
|
|
|
'type',
|
|
|
|
'password',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
it('should render with truncate class as true by default and remove it when truncate is false', () => {
|
|
|
|
const { getByTestId } = render(
|
|
|
|
<>
|
|
|
|
<TextField data-testid="truncate" />
|
|
|
|
<TextField truncate={false} data-testid="no-truncate" />
|
|
|
|
</>,
|
|
|
|
);
|
|
|
|
expect(getByTestId('truncate')).toHaveClass('mm-text-field--truncate');
|
|
|
|
expect(getByTestId('no-truncate')).not.toHaveClass(
|
|
|
|
'mm-text-field--truncate',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
it('should render with right and left accessories', () => {
|
|
|
|
const { getByRole, getByText } = render(
|
|
|
|
<TextField
|
|
|
|
startAccessory={<div>start accessory</div>}
|
|
|
|
endAccessory={<div>end accessory</div>}
|
|
|
|
/>,
|
2022-10-26 00:23:18 +02:00
|
|
|
);
|
2023-02-16 00:43:51 +01:00
|
|
|
expect(getByRole('textbox')).toBeDefined();
|
|
|
|
expect(getByText('start accessory')).toBeDefined();
|
|
|
|
expect(getByText('end accessory')).toBeDefined();
|
|
|
|
});
|
|
|
|
it('should render with working ref using inputRef prop', () => {
|
|
|
|
// Because the 'ref' attribute wont flow down to the DOM
|
|
|
|
// I'm not exactly sure how to test this?
|
|
|
|
const mockRef = jest.fn();
|
|
|
|
const { getByRole } = render(<TextField inputRef={mockRef} />);
|
|
|
|
expect(getByRole('textbox')).toBeDefined();
|
|
|
|
expect(mockRef).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
it('should render with autoComplete', () => {
|
|
|
|
const { getByTestId } = render(
|
|
|
|
<TextField
|
|
|
|
autoComplete
|
|
|
|
inputProps={{ 'data-testid': 'text-field-auto-complete' }}
|
|
|
|
/>,
|
|
|
|
);
|
|
|
|
expect(getByTestId('text-field-auto-complete')).toHaveAttribute(
|
|
|
|
'autocomplete',
|
|
|
|
'on',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
it('should render with autoFocus', () => {
|
|
|
|
const { getByRole } = render(<TextField autoFocus />);
|
|
|
|
expect(getByRole('textbox')).toHaveFocus();
|
|
|
|
});
|
|
|
|
it('should render with a defaultValue', () => {
|
|
|
|
const { getByRole } = render(
|
|
|
|
<TextField
|
|
|
|
defaultValue="default value"
|
|
|
|
inputProps={{ 'data-testid': 'text-field-default-value' }}
|
|
|
|
/>,
|
|
|
|
);
|
|
|
|
expect(getByRole('textbox').value).toBe('default value');
|
|
|
|
});
|
|
|
|
it('should render in disabled state and not focus or be clickable', async () => {
|
|
|
|
const mockOnClick = jest.fn();
|
|
|
|
const mockOnFocus = jest.fn();
|
|
|
|
const { getByRole, getByTestId, user } = renderWithUserEvent(
|
|
|
|
<TextField
|
|
|
|
disabled
|
|
|
|
onFocus={mockOnFocus}
|
|
|
|
onClick={mockOnClick}
|
|
|
|
data-testid="text-field"
|
|
|
|
/>,
|
|
|
|
);
|
|
|
|
|
|
|
|
const textField = getByTestId('text-field');
|
|
|
|
|
|
|
|
await user.click(textField);
|
|
|
|
expect(getByRole('textbox')).toBeDisabled();
|
|
|
|
expect(mockOnClick).toHaveBeenCalledTimes(0);
|
|
|
|
expect(mockOnFocus).toHaveBeenCalledTimes(0);
|
|
|
|
});
|
|
|
|
it('should render with error className when error is true', () => {
|
|
|
|
const { getByTestId } = render(
|
|
|
|
<TextField error data-testid="text-field-error" />,
|
|
|
|
);
|
|
|
|
expect(getByTestId('text-field-error')).toHaveClass('mm-text-field--error');
|
|
|
|
});
|
|
|
|
it('should render with maxLength and not allow more than the set characters', async () => {
|
|
|
|
const { getByRole, user } = renderWithUserEvent(
|
|
|
|
<TextField maxLength={5} />,
|
|
|
|
);
|
|
|
|
const textField = getByRole('textbox');
|
|
|
|
await user.type(textField, '1234567890');
|
|
|
|
expect(getByRole('textbox')).toBeDefined();
|
|
|
|
expect(textField.maxLength).toBe(5);
|
|
|
|
expect(textField.value).toBe('12345');
|
|
|
|
expect(textField.value).toHaveLength(5);
|
|
|
|
});
|
|
|
|
it('should render with readOnly attr when readOnly is true', async () => {
|
|
|
|
const { getByTestId, getByRole, user } = renderWithUserEvent(
|
|
|
|
<TextField readOnly data-testid="read-only" />,
|
|
|
|
);
|
|
|
|
const textField = getByTestId('read-only');
|
|
|
|
await user.type(textField, '1234567890');
|
|
|
|
expect(getByRole('textbox').value).toBe('');
|
|
|
|
expect(getByRole('textbox')).toHaveAttribute('readonly', '');
|
|
|
|
});
|
|
|
|
it('should render with required attr when required is true', () => {
|
|
|
|
const { getByTestId } = render(
|
|
|
|
<TextField
|
|
|
|
required
|
|
|
|
inputProps={{ 'data-testid': 'text-field-required' }}
|
|
|
|
/>,
|
|
|
|
);
|
|
|
|
expect(getByTestId('text-field-required')).toHaveAttribute('required', '');
|
|
|
|
});
|
|
|
|
it('should render with a custom input and still work', async () => {
|
2023-03-07 00:45:23 +01:00
|
|
|
const CustomInputComponent = React.forwardRef(
|
|
|
|
({ disableStateStyles, ...props }, ref) => (
|
|
|
|
<Box
|
|
|
|
ref={ref}
|
|
|
|
as="input"
|
|
|
|
{...props}
|
|
|
|
placeholder={`Removing ${disableStateStyles} from ...props spread to prevent error in test`}
|
|
|
|
/>
|
|
|
|
),
|
|
|
|
);
|
|
|
|
CustomInputComponent.propTypes = {
|
|
|
|
disableStateStyles: PropTypes.bool,
|
|
|
|
};
|
2023-02-16 00:43:51 +01:00
|
|
|
CustomInputComponent.displayName = 'CustomInputComponent'; // fixes eslint error
|
|
|
|
const { getByTestId, user } = renderWithUserEvent(
|
|
|
|
<TextField
|
|
|
|
InputComponent={CustomInputComponent}
|
|
|
|
inputProps={{ 'data-testid': 'text-field', className: 'test' }}
|
|
|
|
/>,
|
|
|
|
);
|
|
|
|
const textField = getByTestId('text-field');
|
|
|
|
|
|
|
|
expect(textField.value).toBe(''); // initial value is empty string
|
|
|
|
await user.type(textField, 'text value');
|
|
|
|
expect(textField.value).toBe('text value');
|
|
|
|
fireEvent.change(textField, { target: { value: '' } }); // reset value
|
|
|
|
expect(textField.value).toBe(''); // value is empty string after reset
|
2022-10-26 00:23:18 +02:00
|
|
|
});
|
2023-02-22 18:14:40 +01:00
|
|
|
it('should render the child Input with disableStateStyles to prevent multiple focus outlines', async () => {
|
|
|
|
const { getByTestId, user } = renderWithUserEvent(
|
|
|
|
<TextField inputProps={{ 'data-testid': 'input' }} />,
|
|
|
|
);
|
|
|
|
const InputComponent = getByTestId('input');
|
|
|
|
|
|
|
|
await user.click(InputComponent);
|
|
|
|
expect(getByTestId('input')).toHaveFocus();
|
|
|
|
expect(getByTestId('input')).toHaveClass('mm-input--disable-state-styles');
|
|
|
|
});
|
2022-10-26 00:23:18 +02:00
|
|
|
});
|