Условное отображение полей форм

В веб-приложениях часто встречаются формы с полями, которые появляются или исчезают в зависимости от состояния приложения или выбора пользователя. Тестирование таких форм требует особого подхода, чтобы убедиться, что условная логика отображения работает корректно и взаимодействие с пользователем не вызывает ошибок.

Основные принципы тестирования условного отображения

  1. Проверка начального состояния Перед взаимодействием с формой важно убедиться, что поля, которые должны быть скрыты, действительно отсутствуют в DOM. Для этого используется метод queryBy* из React Testing Library, который возвращает null, если элемент не найден.

    import { render, screen } from '@testing-library/react';
    import FormComponent from './FormComponent';
    
    test('скрытое поле не отображается изначально', () => {
      render(<FormComponent />);
      const conditionalField = screen.queryByLabelText(/Дополнительная информация/i);
      expect(conditionalField).toBeNull();
    });
  2. Симуляция взаимодействия пользователя Для проверки условного отображения необходимо смоделировать действия пользователя, которые активируют показ скрытых полей. В React Testing Library для этого используют fireEvent или userEvent.

    import { render, screen } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    import FormComponent from './FormComponent';
    
    test('появление поля после выбора опции', async () => {
      render(<FormComponent />);
      const toggleCheckbox = screen.getByLabelText(/Показать дополнительное поле/i);
      await userEvent.click(toggleCheckbox);
    
      const conditionalField = screen.getByLabelText(/Дополнительная информация/i);
      expect(conditionalField).toBeInTheDocument();
    });
  3. Проверка правильной работы при обратном действии Важно тестировать не только появление поля, но и его скрытие, если пользователь отменяет действие. Это предотвращает баги с некорректным состоянием DOM.

    test('скрытие поля при снятии галочки', async () => {
      render(<FormComponent />);
      const toggleCheckbox = screen.getByLabelText(/Показать дополнительное поле/i);
      await userEvent.click(toggleCheckbox);
    
      await userEvent.click(toggleCheckbox); // снимаем галочку
      const conditionalField = screen.queryByLabelText(/Дополнительная информация/i);
      expect(conditionalField).toBeNull();
    });

Особенности выбора селекторов

  • getBy* — выбрасывает ошибку, если элемент отсутствует, подходит для проверки обязательных элементов.
  • queryBy* — возвращает null, если элемент отсутствует, идеально для тестирования скрытых полей.
  • findBy* — возвращает промис, подходит для асинхронного появления элементов после действия пользователя.

Использование правильного метода селектора гарантирует, что тесты не будут ложноположительными или ломаться при условной логике отображения.

Асинхронное отображение полей

Если форма загружает поля динамически, например, после API-запроса или таймера, необходимо использовать асинхронные методы и waitFor:

import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import FormComponent from './FormComponent';

test('появление поля после асинхронного действия', async () => {
  render(<FormComponent />);
  const loadButton = screen.getByText(/Загрузить поле/i);
  await userEvent.click(loadButton);

  await waitFor(() => {
    const conditionalField = screen.getByLabelText(/Дополнительная информация/i);
    expect(conditionalField).toBeInTheDocument();
  });
});

waitFor обеспечивает проверку DOM через промежутки времени до появления нужного элемента, предотвращая ошибки в асинхронной логике.

Тестирование взаимодействия с условными полями

После появления условного поля важно убедиться, что с ним можно корректно взаимодействовать:

test('ввод текста в условное поле', async () => {
  render(<FormComponent />);
  const toggleCheckbox = screen.getByLabelText(/Показать дополнительное поле/i);
  await userEvent.click(toggleCheckbox);

  const conditionalField = screen.getByLabelText(/Дополнительная информация/i);
  await userEvent.type(conditionalField, 'Тестовый текст');
  expect(conditionalField).toHaveValue('Тестовый текст');
});

Это проверяет не только отображение поля, но и его функциональность в реальном сценарии использования.

Рекомендации по структуре тестов

  • Разделять тесты на появление, исчезновение и взаимодействие с полями.
  • Использовать describe для логической группировки тестов одного блока формы.
  • Применять beforeEach для рендеринга компонента, если тестов много, чтобы избежать дублирования кода.
describe('Условное отображение дополнительного поля', () => {
  beforeEach(() => render(<FormComponent />));

  test('поле скрыто по умолчанию', () => { /* ... */ });
  test('поле появляется при выборе опции', () => { /* ... */ });
  test('поле исчезает при отмене выбора', () => { /* ... */ });
});

Такой подход повышает читаемость тестов и упрощает поддержку при изменении логики формы.

Заключение по методике

Тестирование условного отображения полей форм требует внимательного подхода к селекторам и имитации действий пользователя. Комбинация queryBy*, getBy*, асинхронных методов и симуляции событий позволяет создавать стабильные тесты, которые точно отражают поведение формы в реальном приложении.