Тестирование доступности

Доступность компонентов календаря Pikaday проверяется через сочетание автоматизированных и ручных подходов, охватывающих семантику разметки, работу с клавиатурой, взаимодействие со скринридерами и корректность ARIA-атрибутов в различных сценариях использования.

Основой доступности календаря выступает корректная HTML-структура, на которую опираются вспомогательные технологии. В Pikaday важно, чтобы интерактивные элементы календарной сетки не представлялись как произвольные div без семантического контекста.

Ключевые аспекты:

  • использование кнопок (<button>) для дней месяца вместо кликабельных блоков
  • наличие логической структуры заголовков месяцев и дней недели
  • корректное использование списков или таблиц там, где это оправдано
  • отсутствие вложенных интерактивных элементов

Автоматизированная проверка часто начинается с анализа DOM через тестовые утилиты, которые проверяют наличие кнопок и отсутствие запрещённых паттернов:

import { render } from '@testing-library/dom';

test('days are rendered as buttons', () => {
  document.body.innerHTML = `
    <div class="pika-single">
      <button class="pika-day">1</button>
      <button class="pika-day">2</button>
    </div>
  `;

  const days = document.querySelectorAll('.pika-day');
  days.forEach(day => {
    expect(day.tagName.toLowerCase()).toBe('button');
  });
});

Контроль ARIA-атрибутов

ARIA-атрибуты в Pikaday обеспечивают связь между визуальной и семантической моделью календаря. Проверка доступности включает в себя контроль правильности ролей и состояний.

Основные элементы проверки:

  • aria-label у кнопок дней
  • aria-selected для выбранной даты
  • aria-disabled для недоступных дат
  • role="grid" или аналогичная структура для контейнера календаря

Типичная ошибка — отсутствие описания текущего состояния фокуса или выбранной даты, что приводит к потере контекста для скринридеров.

Пример теста на ARIA:

test('selected date has aria-selected true', () => {
  const selected = document.querySelector('.is-selected');

  expect(selected.getAttribute('aria-selected')).toBe('true');
});

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

Тестирование клавиатурной навигации

Календарь Pikaday должен полноценно управляться клавиатурой без использования мыши. Проверка включает сценарии перемещения по сетке дат и управление фокусом.

Основные клавиши:

  • стрелки для перемещения по дням
  • Enter и Space для выбора даты
  • Escape для закрытия календаря
  • Tab для выхода из компонента

Автоматизированное тестирование клавиатуры часто реализуется через имитацию событий:

test('arrow right moves focus to next day', () => {
  const firstDay = document.querySelector('.pika-day');

  firstDay.focus();

  firstDay.dispatchEvent(new KeyboardEvent('keydown', {
    key: 'ArrowRight',
    bubbles: true
  }));

  const focused = document.activeElement;
  expect(focused.textContent).toBe('2');
});

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

Проверка работы со скринридерами

Тестирование с помощью скринридеров выявляет проблемы, которые невозможно обнаружить только через DOM или автоматические проверки.

Ключевые сценарии:

  • чтение текущего месяца и года при открытии календаря
  • объявление выбранной даты
  • корректное озвучивание навигации по сетке
  • информирование о недоступных датах

Типичная ошибка — отсутствие контекстной информации при переходе между месяцами. Например, скринридер должен озвучивать «Март 2026», а не просто набор чисел.

Для ручной проверки используется последовательное прохождение календаря в режиме:

  • NVDA (Windows)
  • VoiceOver (macOS / iOS)
  • Orca (Linux)

Проблемные зоны фиксируются при потере контекста или повторяющихся неинформативных объявлениях.

Использование автоматических инструментов анализа доступности

Инструменты вроде axe-core позволяют выявлять базовые нарушения доступности без ручного анализа.

Типичные проверки:

  • контрастность текста дней
  • наличие label у интерактивных элементов
  • отсутствие пустых кнопок
  • корректность ARIA-ролей

Пример интеграции axe-core в тесты:

import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

test('no accessibility violations in calendar', async () => {
  document.body.innerHTML = `<div class="pika-single"></div>`;

  const results = await axe(document.body);
  expect(results).toHaveNoViolations();
});

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

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

Фокус в Pikaday является критическим элементом доступности. Проверка включает:

  • корректную установку фокуса при открытии календаря
  • возврат фокуса на исходный элемент после закрытия
  • предотвращение «потери фокуса» внутри календарной сетки

Типичная модель поведения:

test('focus returns to input after close', () => {
  const input = document.querySelector('input');
  const calendar = document.querySelector('.pika-single');

  input.focus();
  // открытие календаря
  calendar.classList.add('is-open');

  // закрытие календаря
  calendar.classList.remove('is-open');

  expect(document.activeElement).toBe(input);
});

Ошибки в управлении фокусом особенно критичны для пользователей скринридеров, так как приводят к «зависанию» навигации.

Проверка динамических обновлений интерфейса

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

  • корректное обновление списка дней
  • отсутствие «мертвых» элементов в DOM
  • сохранение состояния выбранной даты при смене месяца
  • обновление ARIA без перерендера всей структуры

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

Интеграционное тестирование в браузере

Интеграционные тесты через Cypress или Playwright позволяют проверить поведение календаря в реальной среде.

Пример сценария:

it('selects date via keyboard', () => {
  cy.get('input').focus();
  cy.get('.pika-single').should('be.visible');

  cy.get('.pika-day').first().focus();
  cy.focused().type('{rightarrow}{enter}');

  cy.get('input').should('have.value');
});

Такие тесты выявляют расхождения между эмуляцией DOM-событий и реальным поведением браузера.

Проверка контрастности и визуальной доступности

Хотя Pikaday является функциональным компонентом, визуальная доступность напрямую влияет на восприятие интерфейса.

Проверяются:

  • контраст текста дней и фона
  • выделение выбранной даты
  • видимость фокуса клавиатуры
  • различимость отключённых дат

Инструменты автоматизации (Lighthouse, axe) дают первичную оценку, но визуальные регрессии часто требуют ручной проверки или snapshot-тестов.

Регрессионное тестирование доступности

При изменениях в логике календаря важно отслеживать, не нарушается ли доступность.

Подходы:

  • snapshot тесты DOM-структуры календаря
  • проверка ARIA-атрибутов после обновлений
  • контроль стабильности клавиатурной навигации
  • повторное прогонание axe-core на всех состояниях компонента

Особенно чувствительны изменения, связанные с:

  • кастомизацией рендера дней
  • локализацией
  • изменением структуры сетки
  • добавлением ограничений дат

Типовые ошибки, выявляемые при тестировании

  • использование div вместо кнопок для дней
  • отсутствие aria-label у дат
  • потеря фокуса при смене месяца
  • невозможность навигации стрелками
  • несоответствие визуального и доступного состояния выбора
  • некорректное обновление скринридер-описаний

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

Стратегия построения тестового покрытия

Полноценное тестирование доступности Pikaday строится слоями:

  • юнит-тесты DOM и ARIA
  • тесты клавиатурного управления
  • axe-core проверки на уровне компонентов
  • интеграционные сценарии в браузере
  • ручное тестирование со скринридерами

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