Клавиатурные события: специальные клавиши

При тестировании интерфейсов на React часто требуется проверять поведение компонентов при взаимодействии с клавиатурой. Это особенно важно для форм, модальных окон, кастомных селекторов и любых элементов, где пользователь может использовать не только мышь, но и клавиши. React Testing Library предоставляет инструменты для эмуляции таких событий, включая работу со специальными клавишами.


fireEvent и клавиатурные события

Основной метод для имитации событий клавиатуры — fireEvent. С его помощью можно отправлять события keydown, keypress и keyup. Для специальных клавиш важно использовать правильные свойства события:

  • key — строковое представление клавиши, например "Enter", "Escape", "ArrowDown".
  • code — физическая клавиша на клавиатуре, например "Enter", "Escape", "ArrowDown".
  • keyCode — числовое значение клавиши (устаревшее, но иногда используется в старых обработчиках).

Пример отправки события нажатия Enter:

import { render, fireEvent } from '@testing-library/react';
import MyInput from './MyInput';

test('нажатие Enter вызывает submit', () => {
  const handleSubmit = jest.fn();
  const { getByRole } = render(<MyInput onSub mit={handleSubmit} />);
  const input = getByRole('textbox');

  fireEvent.keyDown(input, { key: 'Enter', code: 'Enter', keyCode: 13 });
  expect(handleSubmit).toHaveBeenCalledTimes(1);
});

Здесь ключевой момент — использование именно keyDown для имитации нажатия. В большинстве случаев обработчики форм реагируют именно на keydown.


Эмуляция специальных клавиш

Клавиши-модификаторы и навигации требуют особого внимания.

Модификаторы: Shift, Control, Alt, Meta

fireEvent.keyDown(input, { key: 'A', code: 'KeyA', shiftKey: true });
  • shiftKey, ctrlKey, altKey, metaKey — булевы значения, указывающие, удерживается ли клавиша-модификатор.
  • Можно комбинировать с другими клавишами для тестирования сложных комбинаций.

Навигационные клавиши: ArrowUp, ArrowDown, ArrowLeft, ArrowRight, Tab, Escape

fireEvent.keyDown(menuItem, { key: 'ArrowDown', code: 'ArrowDown' });
fireEvent.keyDown(menuItem, { key: 'Escape', code: 'Escape' });

Эти события полезны при тестировании выпадающих списков, меню и кастомных каруселей.


userEvent для более реалистичной имитации

React Testing Library рекомендует использовать библиотеку @testing-library/user-event для более реалистичного поведения. В отличие от fireEvent, userEvent учитывает последовательность событий (keydownkeypresskeyup) и фокус элемента.

Пример имитации нажатия Enter:

import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MyInput from './MyInput';

test('Enter вызывает submit с userEvent', async () => {
  const handleSubmit = jest.fn();
  const { getByRole } = render(<MyInput onSub mit={handleSubmit} />);
  const input = getByRole('textbox');

  await userEvent.type(input, 'Тест{Enter}');
  expect(handleSubmit).toHaveBeenCalledTimes(1);
});

Здесь {Enter} — специальная запись для клавиш в userEvent.type. Аналогично можно использовать {Tab}, {Escape}, {ArrowDown} и другие.

Особенность userEvent — автоматически ставит фокус на элемент, что упрощает тестирование клавиатурной навигации.


Тестирование комбинаций клавиш

Иногда поведение компонента зависит от сочетаний клавиш, например Ctrl + S для сохранения:

fireEvent.keyDown(input, { key: 's', code: 'KeyS', ctrlKey: true });

Или с использованием userEvent:

await userEvent.keyboard('{Control>}{s}{/Control}');
  • {Control>} — удержание клавиши Control
  • {s} — нажатие клавиши S
  • {/Control} — отпускание Control

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


Проверка фокуса и реакций на Tab

Навигация с клавиатуры часто связана с перемещением фокуса. В тестах можно проверять, какой элемент активен после нажатия Tab:

import { screen } from '@testing-library/react';

await userEvent.tab(); // перемещает фокус на следующий элемент
expect(screen.getByRole('button')).toHaveFocus();

await userEvent.tab({ shift: true }); // перемещает фокус назад
expect(screen.getByRole('textbox')).toHaveFocus();

userEvent.tab() учитывает порядок табуляции, что делает тестирование более приближенным к реальному поведению пользователя.


Особенности тестирования на мобильных и нестандартных клавиатурах

  • Значение keyCode может различаться в старых браузерах.
  • code всегда соответствует физической клавише и удобен для эмуляции одинаковых комбинаций.
  • Для кастомных компонентов важно, чтобы обработчики клавиатуры проверяли event.key, а не только keyCode.

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

  1. Разделять тесты по типу клавиш: отдельные тесты для Enter, Escape, стрелок и модификаторов.
  2. Использовать userEvent там, где важно поведение фокуса.
  3. Стараться избегать keyCode, опираясь на key и code.
  4. Проверять реакцию компонента на последовательность событий, а не только на одно событие.

Такой подход обеспечивает стабильность тестов и их соответствие реальному поведению пользователей.