В веб-приложениях формы играют центральную роль, а корректное
тестирование их поведения — необходимый элемент обеспечения качества.
Одним из часто встречающихся сценариев является проверка очистки формы
после определённых действий пользователя, будь то отправка данных или
сброс полей с помощью кнопки reset. React Testing Library
(RTL) предоставляет инструменты для реализации таких проверок в тестах
на уровне пользовательского взаимодействия, что соответствует принципу
«тестировать так, как использует приложение пользователь».
В React формы обычно управляются через controlled
components, где значения полей хранятся в состоянии компонента
(useState или useReducer). Пример:
import React, { useState } from 'react';
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleReset = () => {
setEmail('');
setPassword('');
};
return (
<form>
<input
type="email"
value={email}
onCha nge={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onCha nge={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="button" onCl ick={handleReset}>Reset</button>
</form>
);
}
export default LoginForm;
Здесь кнопка Reset вызывает функцию
handleReset, которая сбрасывает состояние формы. Это
классический пример, который тестируется в RTL через имитацию действий
пользователя.
Для проверки функциональности reset используется
комбинация render, fireEvent или
userEvent, а также селекторов для поиска элементов формы.
Пример теста:
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import LoginForm from './LoginForm';
test('очистка формы через кнопку reset', async () => {
render(<LoginForm />);
const emailInput = screen.getByPlaceholderText(/email/i);
const passwordInput = screen.getByPlaceholderText(/password/i);
const resetButton = screen.getByRole('button', { name: /reset/i });
await userEvent.type(emailInput, 'test@example.com');
await userEvent.type(passwordInput, '123456');
expect(emailInput).toHaveValue('test@example.com');
expect(passwordInput).toHaveValue('123456');
await userEvent.click(resetButton);
expect(emailInput).toHaveValue('');
expect(passwordInput).toHaveValue('');
});
Ключевые моменты:
userEvent.type имитирует ввод текста
пользователем.userEvent.click позволяет протестировать реакцию кнопки
reset.toHaveValue гарантирует, что
поля очищены корректно.<form reset>Если форма использует стандартный HTML-метод сброса,
reset можно вызывать через атрибут
type="reset" на кнопке:
<form>
<input placeholder="Username" />
<input placeholder="Password" type="password" />
<button type="reset">Reset</button>
</form>
В RTL это проверяется следующим образом:
test('reset через нативную кнопку', async () => {
render(<LoginForm />);
const username = screen.getByPlaceholderText(/username/i);
const password = screen.getByPlaceholderText(/password/i);
const resetButton = screen.getByRole('button', { name: /reset/i });
await userEvent.type(username, 'admin');
await userEvent.type(password, 'qwerty');
await userEvent.click(resetButton);
expect(username).toHaveValue('');
expect(password).toHaveValue('');
});
При этом важно помнить, что нативная кнопка
reset работает только для контролируемых HTML-форм без
программного управления состоянием через useState. Если
состояние управляется React, кнопка должна быть привязана к функции
сброса состояния.
Часто формы создаются через библиотеки типа React Hook
Form или Formik. В таких случаях есть
собственные методы сброса (reset()):
import { useForm } from 'react-hook-form';
function SignupForm() {
const { register, handleSubmit, reset } = useForm();
return (
<form>
<input {...register('username')} placeholder="Username" />
<input {...register('email')} placeholder="Email" />
<button type="button" onCl ick={() => reset()}>Reset</button>
</form>
);
}
Тестирование:
test('React Hook Form reset', async () => {
render(<SignupForm />);
const username = screen.getByPlaceholderText(/username/i);
const email = screen.getByPlaceholderText(/email/i);
const resetButton = screen.getByRole('button', { name: /reset/i });
await userEvent.type(username, 'user123');
await userEvent.type(email, 'user@example.com');
await userEvent.click(resetButton);
expect(username).toHaveValue('');
expect(email).toHaveValue('');
});
Особенности:
reset() библиотеки восстанавливает все
поля сразу, включая значения по умолчанию.Форма может иметь валидацию, состояния ошибок или side-effects при очистке. Пример проверки:
test('очистка формы сбрасывает ошибки', async () => {
render(<LoginForm />);
const emailInput = screen.getByPlaceholderText(/email/i);
const resetButton = screen.getByRole('button', { name: /reset/i });
await userEvent.type(emailInput, 'invalid-email');
await userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
await userEvent.click(resetButton);
expect(emailInput).toHaveValue('');
expect(screen.queryByText(/invalid email/i)).not.toBeInTheDocument();
});
Таким образом, проверяется не только очистка полей, но и удаление сообщений об ошибках, что критично для корректного поведения UX.
resetТщательное тестирование функциональности reset
обеспечивает уверенность в стабильной работе формы и предотвращает
неожиданные сбои при взаимодействии пользователя. Такой подход
соответствует принципам RTL: проверять интерфейс так, как его видит и
использует реальный пользователь.