Тестирование взаимодействий

React Aria предоставляет набор хуков и утилит для создания доступных компонентов пользовательского интерфейса. Тестирование этих компонентов требует особого внимания к взаимодействиям, которые реализуются через хуки useButton, useCheckbox, useMenu, useSlider и другие. Основной целью тестирования является не только проверка визуального рендеринга, но и корректное поведение с точки зрения доступности (accessibility, a11y).


Важные аспекты тестирования

  1. Проверка событий клавиатуры Большинство компонентов React Aria должны корректно реагировать на стандартные клавиши:

    • Enter и Space для активации кнопок или элементов списка.
    • Стрелки ArrowUp/ArrowDown для перемещения по элементам меню или слайдера.
    • Tab и Shift+Tab для навигации между фокусируемыми элементами.

    При тестировании необходимо использовать утилиты вроде @testing-library/user-event для симуляции этих событий. Например:

    import { render, screen } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    import { Button } from './Button';
    
    test('кнопка активируется по Enter', async () => {
      render(<Button>Нажми меня</Button>);
      const btn = screen.getByRole('button');
      btn.focus();
      await userEvent.keyboard('{Enter}');
      expect(btn).toHaveAttribute('aria-pressed', 'true');
    });

    Этот подход гарантирует, что компонент правильно обрабатывает события клавиатуры, а атрибуты ARIA обновляются корректно.

  2. Тестирование состояния компонентов React Aria часто управляет состоянием через хуки вроде useToggleState или useSelectableCollection. Тесты должны проверять:

    • начальное состояние (например, checked для чекбокса),
    • переходы состояний при взаимодействиях пользователя,
    • синхронизацию состояния с атрибутами ARIA (aria-checked, aria-selected).

    Пример для чекбокса:

    import { Checkbox } from './Checkbox';
    
    test('чекбокс переключается по клику', async () => {
      render(<Checkbox>Согласен</Checkbox>);
      const checkbox = screen.getByRole('checkbox');
      expect(checkbox).not.toBeChecked();
      await userEvent.click(checkbox);
      expect(checkbox).toBeChecked();
    });

    Такой тест проверяет не только визуальное поведение, но и корректность ARIA-свойств.

  3. Фокус и порядок табуляции Для доступных интерфейсов критично, чтобы фокус перемещался по логическому порядку. Тестирование фокуса включает:

    • проверку, что первый элемент получает фокус при открытии меню или диалога,
    • проверку перемещения фокуса с помощью Tab и Shift+Tab,
    • симуляцию закрытия модальных окон с сохранением предыдущего фокуса.

    Пример теста модального диалога:

    import { Dialog } from './Dialog';
    
    test('фокус возвращается после закрытия диалога', async () => {
      render(<Dialog isOpen={true}><button>Закрыть</button></Dialog>);
      const closeBtn = screen.getByRole('button', { name: /закрыть/i });
      closeBtn.focus();
      await userEvent.keyboard('{Escape}');
      expect(document.activeElement).toBe(document.body);
    });

Тестирование взаимодействий коллекций

React Aria предоставляет хуки для управляемых коллекций (useListState, useSelectionManager). Основные сценарии тестирования:

  • Выбор элементов: проверка как одиночного (singleSelect), так и множественного выбора (multiSelect).
  • Навигация по элементам: перемещение фокуса с клавиатуры.
  • Обновление ARIA-атрибутов: aria-selected, aria-activedescendant.

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

import { ListBox } from './ListBox';

test('мультивыбор элементов через клавиатуру', async () => {
  render(<ListBox multiSelect options={['A', 'B', 'C']} />);
  const items = screen.getAllByRole('option');
  items[0].focus();
  await userEvent.keyboard('{Space}');
  items[1].focus();
  await userEvent.keyboard('{Space}');
  expect(items[0]).toHaveAttribute('aria-selected', 'true');
  expect(items[1]).toHaveAttribute('aria-selected', 'true');
});

Использование act для асинхронных эффектов

Многие хуки React Aria используют внутренние состояния и эффекты, которые могут обновляться асинхронно. Для корректного тестирования необходимо оборачивать события в act:

import { act } from 'react-dom/test-utils';

act(() => {
  userEvent.click(button);
});

Это предотвращает предупреждения о “необработанных обновлениях состояния” и обеспечивает точные проверки DOM.


Валидация доступности

Тестирование взаимодействий тесно связано с доступностью. Основные подходы:

  • Проверка правильности ARIA-атрибутов (aria-pressed, aria-expanded, aria-label).
  • Проверка роли элементов (role="button", role="menuitem").
  • Использование инструментов типа jest-axe для автоматической проверки a11y.

Пример интеграции jest-axe:

import { axe } from 'jest-axe';

test('кнопка соответствует требованиям доступности', async () => {
  const { container } = render(<Button>Нажми</Button>);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

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

  • Разделение на юнит и интеграцию: юнит-тесты проверяют отдельные хуки, интеграционные — поведение целых компонентов.
  • Тесты сценариев пользователя: моделируют реальные действия: клики, клавиатурную навигацию, открытие и закрытие модальных окон.
  • Фокус на ARIA: каждый тест должен проверять не только визуальный результат, но и соответствие стандартам доступности.

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