Unit-тестирование headroom-компонентов

Headroom.js — библиотека для управления поведением шапки страницы при скролле. Unit-тестирование компонентов, использующих Headroom, позволяет проверять корректность работы скрытия и появления элементов в ответ на события прокрутки. Основной задачей является тестирование поведения без фактической прокрутки страницы в браузере.

Настройка окружения для тестирования

Для unit-тестирования компонентов, которые используют Headroom.js, обычно применяются следующие инструменты:

  • Jest — тестовый раннер с поддержкой моков.
  • Testing Library / React Testing Library — для рендеринга и взаимодействия с DOM.
  • jsdom — эмуляция DOM в Node.js.

Пример базовой установки в проекте:

npm install --save-dev jest @testing-library/react @testing-library/jest-dom

Для Headroom важно обеспечить корректное поведение scroll-объекта в тестовой среде. В jsdom отсутствует полноценный скролл, поэтому события scroll приходится эмулировать вручную.

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

Headroom.js реагирует на window.scrollY, window.addEventListener('scroll', ...) и внутренние методы для вычисления направления прокрутки. Для unit-тестов необходимо замокировать эти элементы:

beforeEach(() => {
  Object.defineProperty(window, 'scrollY', { writable: true, value: 0 });
  window.addEventListener = jest.fn((event, cb) => {
    if (event === 'scroll') window._scrollCallback = cb;
  });
});

function triggerScroll(newScrollY) {
  window.scrollY = newScrollY;
  if (window._scrollCallback) window._scrollCallback();
}

Этот подход позволяет контролировать события скролла без реального перемещения страницы.

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

Headroom управляет классами элемента: по умолчанию headroom--pinned и headroom--unpinned. Unit-тест должен проверять корректное применение этих классов при изменении scrollY.

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

import { render } from '@testing-library/react';
import Headroom from 'headroom.js';
import MyHeader from './MyHeader';

test('header toggles classes on scroll', () => {
  const { container } = render(<MyHeader />);
  const header = container.querySelector('header');
  const headroomInstance = new Headroom(header);
  headroomInstance.init();

  expect(header.classList.contains('headroom--pinned')).toBe(true);

  triggerScroll(100); // скролл вниз
  expect(header.classList.contains('headroom--unpinned')).toBe(true);

  triggerScroll(50); // скролл вверх
  expect(header.classList.contains('headroom--pinned')).toBe(true);

  headroomInstance.destroy();
});

Проверка коллбеков

Headroom.js поддерживает события через опции: onPin, onUnpin, onTop, onNotTop. Unit-тестирование этих коллбеков выполняется с помощью мок-функций:

const onPinM ock = jest.fn();
const onUnpinM ock = jest.fn();

const headroom = new Headroom(header, {
  onPin: onPinMock,
  onUnpin: onUnpinMock,
});

headroom.init();
triggerScroll(200); // скролл вниз
expect(onUnpinMock).toHaveBeenCalled();

triggerScroll(100); // скролл вверх
expect(onPinMock).toHaveBeenCalled();

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

Тестирование параметров конфигурации

Headroom.js имеет несколько ключевых опций, влияющих на поведение:

  • tolerance — минимальное количество пикселей для срабатывания изменения состояния.
  • offset — расстояние прокрутки перед активацией Headroom.
  • classes — объект с пользовательскими классами.

Unit-тестирование должно проверять корректное применение этих параметров:

const headroom = new Headroom(header, { tolerance: 50, offset: 100 });
headroom.init();

triggerScroll(90);
expect(header.classList.contains('headroom--unpinned')).toBe(false); // не превышен offset

triggerScroll(150);
expect(header.classList.contains('headroom--unpinned')).toBe(true);

Тестирование реактивных компонентов

В проектах на React или Vue Headroom часто оборачивается в компонент. Unit-тестирование таких компонентов включает проверку:

  • корректного рендеринга дочерних элементов;
  • передачи ref в Headroom;
  • вызова методов Headroom (init, destroy) при монтировании и размонтировании.

Пример для React:

import { render, unmountComponentAtNode } from 'react-dom';
import MyHeader from './MyHeader';

let container = null;
beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});
afterEach(() => {
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

test('Headroom init and destroy called', () => {
  const initSpy = jest.spyOn(Headroom.prototype, 'init');
  const destroySpy = jest.spyOn(Headroom.prototype, 'destroy');

  render(<MyHeader />, container);
  expect(initSpy).toHaveBeenCalled();

  unmountComponentAtNode(container);
  expect(destroySpy).toHaveBeenCalled();
});

Автоматизация и покрытие

Unit-тесты для headroom-компонентов должны охватывать все сценарии:

  1. Скролл вниз и вверх с разными скоростями.
  2. Коллбеки (onPin, onUnpin, onTop, onNotTop).
  3. Параметры конфигурации (tolerance, offset, кастомные классы).
  4. Инициализация и разрушение экземпляра при монтировании/размонтировании компонента.

При этом важна изоляция тестов: состояние scrollY и события должны полностью контролироваться тестовой средой. Это позволяет выявить баги в логике Headroom без зависимости от реального браузера.

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

  • Каждый тест должен проверять одно поведение (AAA — Arrange, Act, Assert).
  • Использовать мокирование событий и таймеров для асинхронных сценариев.
  • Разделять тесты по методам Headroom: состояние класса, коллбеки, конфигурация.
  • Проверять корректность очистки ресурсов после destroy, чтобы избежать утечек памяти в больших приложениях.

Такая организация unit-тестов обеспечивает стабильность и предсказуемость поведения компонентов с Headroom.js в любом приложении.