ant-design/components/input/__tests__/otp.test.tsx
lijianan 8a4a83ace7
Merge pull request #48471 from ant-design/master
chore: merge master into feature
2024-04-16 10:17:57 +08:00

171 lines
5.2 KiB
TypeScript

import React from 'react';
import Input from '..';
import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
const { OTP } = Input;
describe('Input.OTP', () => {
focusTest(Input.OTP, { refFocus: true });
mountTest(Input.OTP);
rtlTest(Input.OTP);
const getText = (container: HTMLElement) => {
const inputList = container.querySelectorAll<HTMLInputElement>('input');
return Array.from(inputList)
.map((input) => input.value || ' ')
.join('')
.replace(/\s*$/, '');
};
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.clearAllTimers();
jest.useRealTimers();
});
it('paste to fill all', async () => {
const onChange = jest.fn();
const { container } = render(<OTP onChange={onChange} />);
fireEvent.input(container.querySelector('input')!, { target: { value: '123456' } });
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith('123456');
});
it('fill step by step', () => {
const CODE = 'BAMBOO';
const onChange = jest.fn();
render(<OTP onChange={onChange} autoFocus />);
for (let i = 0; i < CODE.length; i += 1) {
expect(onChange).not.toHaveBeenCalled();
fireEvent.input(document.activeElement!, { target: { value: CODE[i] } });
}
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith(CODE);
});
it('backspace to delete', async () => {
const CODE = 'LITTLE';
const onChange = jest.fn();
const { container } = render(<OTP defaultValue={CODE} onChange={onChange} />);
expect(getText(container)).toBe(CODE);
// Focus on the last cell
const inputList = container.querySelectorAll('input');
inputList[inputList.length - 1].focus();
for (let i = 0; i < CODE.length; i += 1) {
fireEvent.keyDown(document.activeElement!, { key: 'Backspace' });
fireEvent.input(document.activeElement!, { target: { value: '' } });
fireEvent.keyUp(document.activeElement!, { key: 'Backspace' });
}
expect(getText(container)).toBe('');
// We do not trigger change if empty. It's safe to modify this logic if needed.
expect(onChange).not.toHaveBeenCalled();
});
it('controlled', () => {
const { container, rerender } = render(<OTP value="BAMBOO" />);
expect(getText(container)).toBe('BAMBOO');
rerender(<OTP value="LITTLE" />);
expect(getText(container)).toBe('LITTLE');
rerender(<OTP value="" />);
expect(getText(container)).toBe('');
rerender(<OTP value="EXCEED-RANGE" />);
expect(getText(container)).toBe('EXCEED');
rerender(<OTP value={null!} />);
expect(getText(container)).toBe('');
});
it('focus to selection', async () => {
const { container } = render(<OTP defaultValue="BAMBOO" />);
const firstInput = container.querySelector('input')!;
const selectSpy = jest.spyOn(firstInput, 'select');
expect(selectSpy).not.toHaveBeenCalled();
// Trigger focus
firstInput.focus();
await waitFakeTimer();
expect(selectSpy).toHaveBeenCalled();
});
it('arrow key to switch', () => {
const { container } = render(<OTP autoFocus />);
const inputList = Array.from(container.querySelectorAll('input'));
expect(document.activeElement).toEqual(inputList[0]);
fireEvent.keyDown(document.activeElement!, { key: 'ArrowRight' });
expect(document.activeElement).toEqual(inputList[1]);
fireEvent.keyDown(document.activeElement!, { key: 'ArrowLeft' });
expect(document.activeElement).toEqual(inputList[0]);
});
it('fill last cell', () => {
const { container } = render(<OTP />);
fireEvent.input(container.querySelectorAll('input')[5], { target: { value: '1' } });
expect(getText(container)).toBe(' 1');
});
it('formatter', () => {
const { container } = render(
<OTP defaultValue="bamboo" formatter={(val) => val.toUpperCase()} />,
);
expect(getText(container)).toBe('BAMBOO');
// Type to trigger formatter
fireEvent.input(container.querySelector('input')!, { target: { value: 'little' } });
expect(getText(container)).toBe('LITTLE');
});
it('support mask prop', () => {
// default
const { container, rerender } = render(<OTP defaultValue="bamboo" />);
expect(getText(container)).toBe('bamboo');
// support string
rerender(<OTP defaultValue="bamboo" mask="*" />);
expect(getText(container)).toBe('******');
// support emoji
rerender(<OTP defaultValue="bamboo" mask="🔒" />);
expect(getText(container)).toBe('🔒🔒🔒🔒🔒🔒');
});
it('should throw Error when mask.length > 1', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<OTP mask="abc" />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Input.OTP] `mask` prop should be a single character.',
);
errSpy.mockRestore();
});
it('should not throw Error when mask.length <= 1', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<OTP mask="x" />);
expect(errSpy).not.toHaveBeenCalled();
errSpy.mockRestore();
});
});