Работа с textarea

<textarea> в React используется для ввода многострочного текста и часто управляется через контролируемые компоненты, где значение хранится в состоянии. Тестирование <textarea> в React Testing Library требует понимания взаимодействия с DOM и событий ввода.


Основы тестирования <textarea>

В React Testing Library для работы с <textarea> применяются те же подходы, что и с <input>, но с учётом особенностей многострочного текста.

Основные моменты:

  • Значение <textarea> хранится в атрибуте value.
  • Изменения значения инициируют событие change.
  • Доступ к элементу осуществляется через селекторы, например getByRole, getByLabelText или getByPlaceholderText.

Пример:

import { render, screen, fireEvent } from '@testing-library/react';
import React, { useState } from 'react';

function CommentBox() {
  const [comment, setComment] = useState('');

  return (
    <textarea
      value={comment}
      onCha nge={(e) => setComment(e.target.value)}
      placeholder="Введите комментарий"
    />
  );
}

test('ввод текста в textarea', () => {
  render(<CommentBox />);
  const textarea = screen.getByPlaceholderText('Введите комментарий');

  fireEvent.change(textarea, { target: { value: 'Новый комментарий' } });
  expect(textarea.value).toBe('Новый комментарий');
});

Выбор селектора для <textarea>

1. getByRole

<textarea> имеет роль textbox, что позволяет находить элемент через:

const textarea = screen.getByRole('textbox');

Если на странице несколько текстовых полей, можно использовать имя (name или aria-label):

const textarea = screen.getByRole('textbox', { name: /комментарий/i });

2. getByLabelText

Для связанного с <label> <textarea>:

<label htmlFor="comment">Комментарий</label>
<textarea id="comment" />

Тогда тест:

const textarea = screen.getByLabelText('Комментарий');

3. getByPlaceholderText

Полезно, когда <textarea> имеет placeholder:

const textarea = screen.getByPlaceholderText('Введите комментарий');

Симуляция ввода текста

Основные способы изменения значения <textarea>:

1. fireEvent.change

fireEvent.change(textarea, { target: { value: 'Тестовый текст' } });

2. userEvent.type (предпочтительный для имитации пользовательского ввода)

import userEvent from '@testing-library/user-event';

await userEvent.type(textarea, 'Тестовый текст');

userEvent.type имитирует ввод построчно, триггерит события keydown, keypress, input и keyup, что делает тест более приближенным к реальному поведению пользователя.


Работа с многострочным текстом

В <textarea> строки разделяются символом новой строки \n. При тестировании важно учитывать это:

const multilineText = 'Первая строка\nВторая строка\nТретья строка';
fireEvent.change(textarea, { target: { value: multilineText } });
expect(textarea.value).toBe(multilineText);

Проверка реактивного состояния

Контролируемый <textarea> обновляет значение состояния компонента. Проверка часто включает:

  1. Проверку DOM элемента (textarea.value)
  2. Проверку состояния родительского компонента через эффект на странице:
function CommentBoxWithPreview() {
  const [comment, setComment] = useState('');

  return (
    <>
      <textarea value={comment} onCha nge={(e) => setComment(e.target.value)} />
      <div data-testid="preview">{comment}</div>
    </>
  );
}

test('textarea обновляет превью', async () => {
  render(<CommentBoxWithPreview />);
  const textarea = screen.getByRole('textbox');
  const preview = screen.getByTestId('preview');

  fireEvent.change(textarea, { target: { value: 'Тест' } });
  expect(preview.textContent).toBe('Тест');
});

Тестирование событий фокуса и blur

Иногда важно проверить поведение при фокусе или потере фокуса:

textarea.focus();
expect(document.activeElement).toBe(textarea);

textarea.blur();
expect(document.activeElement).not.toBe(textarea);

userEvent также поддерживает tab() для навигации по элементам.


Валидация и ошибки

Для <textarea> с валидацией можно тестировать отображение ошибок:

function CommentForm() {
  const [comment, setComment] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = () => {
    if (!comment) setError('Комментарий обязателен');
  };

  return (
    <>
      <textarea value={comment} onCha nge={(e) => setComment(e.target.value)} />
      {error && <span role="alert">{error}</span>}
      <button onCl ick={handleSubmit}>Отправить</button>
    </>
  );
}

test('отображает ошибку при пустом комментарии', () => {
  render(<CommentForm />);
  const button = screen.getByText('Отправить');

  fireEvent.click(button);
  expect(screen.getByRole('alert').textContent).toBe('Комментарий обязателен');
});

Рекомендации по тестированию <textarea>

  • Предпочтительно использовать userEvent.type для имитации реального пользовательского ввода.
  • Всегда проверять значение через textarea.value, а не напрямую через состояние компонента.
  • Для многострочного текста учитывать символы \n.
  • Использовать семантические селекторы (getByRole, getByLabelText) для надежности тестов.
  • Включать проверку ошибок и поведения при фокусе, если функциональность зависит от этих событий.

Эти подходы обеспечивают комплексное покрытие тестами <textarea> и гарантируют корректную проверку пользовательского взаимодействия, реактивного состояния и отображения ошибок в приложениях на React.