Навигация табуляцией: tab взаимодействие

Навигация с клавиатуры является важным аспектом доступности веб-приложений. Клавиша Tab позволяет пользователю перемещаться между интерактивными элементами страницы, такими как кнопки, ссылки, поля ввода и другие элементы формы. В React Testing Library моделирование такого поведения позволяет проверить не только корректность работы компонентов, но и соблюдение принципов accessibility (a11y).

Основные принципы Tab-навигации

  1. Порядок фокуса Элементы получают фокус в порядке их появления в DOM, если явно не указано поведение через tabIndex.

    • tabIndex=0 — элемент включается в естественный порядок табуляции.
    • tabIndex>0 — элемент получает приоритет перед элементами с tabIndex=0.
    • tabIndex<0 — элемент исключен из последовательности табуляции, но может быть сфокусирован программно.
  2. Интерактивные элементы По умолчанию фокусируются:

    • <button>
    • <input> (включая textarea и select)
    • <a> с атрибутом href
    • Элементы с tabIndex >= 0

Моделирование Tab-навигации в тестах

React Testing Library предоставляет утилиту userEvent.tab() для имитации нажатия клавиши Tab. Это позволяет проверять, как фокус перемещается между элементами.

Пример базового теста:

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

test('фокус перемещается по полям формы при Tab', async () => {
  render(<Form />);
  const user = userEvent.setup();

  const inputName = screen.getByLabelText(/имя/i);
  const inputEmail = screen.getByLabelText(/email/i);
  const submitButton = screen.getByRole('button', { name: /отправить/i });

  await user.tab();
  expect(inputName).toHaveFocus();

  await user.tab();
  expect(inputEmail).toHaveFocus();

  await user.tab();
  expect(submitButton).toHaveFocus();
});

Ключевые моменты:

  • Использование userEvent.setup() позволяет более реалистично симулировать действия пользователя.
  • Проверка toHaveFocus() подтверждает правильность последовательности табуляции.

Обход нестандартного порядка фокуса

Иногда порядок элементов в DOM не совпадает с ожидаемой последовательностью. Для тестирования используется tabIndex:

<div tabIndex={2}>Элемент A</div>
<div tabIndex={1}>Элемент B</div>

В тесте:

await user.tab();
expect(screen.getByText('Элемент B')).toHaveFocus();

await user.tab();
expect(screen.getByText('Элемент A')).toHaveFocus();

Это гарантирует, что фокус следует заданной последовательности, а не естественному порядку DOM.

Работа с модальными окнами и диалогами

При открытии модального окна фокус должен автоматически перемещаться внутрь модалки и быть замкнутым. Проверка Tab-навигации в модальных компонентах включает:

  1. Инициализация модального окна.
  2. Проверка, что первый интерактивный элемент получает фокус.
  3. Последовательное нажатие Tab и проверка циклического обхода элементов внутри модалки.

Пример:

const firstInput = screen.getByLabelText(/логин/i);
const lastButton = screen.getByRole('button', { name: /отмена/i });

await user.tab();
expect(firstInput).toHaveFocus();

await user.tab();
expect(lastButton).toHaveFocus();

await user.tab();
expect(firstInput).toHaveFocus(); // фокус замкнулся

Сочетание с клавишами Shift+Tab

Для проверки обратного обхода используется user.tab({ shift: true }):

await user.tab({ shift: true });
expect(lastButton).toHaveFocus();

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

Тонкости тестирования фокусируемых элементов

  • display: none и visibility: hidden исключают элемент из последовательности Tab.
  • Элементы с disabled не получают фокус.
  • Скрытые элементы можно проверять программно через user.tab() только если они доступны в DOM и имеют tabIndex.

Проверка сложных компонентов

Для компонентов типа таблиц, списков с динамическим содержимым или кастомных кнопок важно:

  1. Проверять наличие всех интерактивных элементов.
  2. Убедиться, что фокус не теряется при динамическом изменении DOM.
  3. Имитация Tab должна учитывать состояние компонента (открытые/закрытые вкладки, модальные окна).

Пример для кастомного компонента кнопок:

const buttons = screen.getAllByRole('button');
await user.tab();
expect(buttons[0]).toHaveFocus();

await user.tab();
expect(buttons[1]).toHaveFocus();

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

  • Использовать beforeEach для рендеринга компонента и инициализации userEvent.
  • Делить тесты на логические блоки: последовательность фокуса, модальные окна, Shift+Tab.
  • Проверять не только фокус, но и состояние компонентов после Tab (активация, отображение подсказок и т. д.).

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