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

Интеграционное тестирование Flatpickr в JavaScript ориентировано на проверку взаимодействия компонента выбора даты с DOM, событиями пользовательского интерфейса, внешними библиотеками и состоянием приложения. В отличие от юнит-тестов, где изолируется логика, здесь проверяется целостное поведение календаря в реальной среде браузера или его эмуляции, включая рендеринг, реакцию на ввод, синхронизацию состояния и корректную работу API-интерфейсов.

Flatpickr, будучи UI-компонентом, тесно связан с DOM-структурой и пользовательскими событиями, поэтому интеграционные тесты должны учитывать асинхронность обновлений, особенности событийной модели и возможные побочные эффекты при повторной инициализации экземпляра.

Основная цель интеграционного тестирования заключается в проверке того, что:

  • календарь корректно инициализируется на реальном DOM-элементе;
  • выбранная дата синхронизируется с input-полем;
  • события change, open, close, onChange вызываются в ожидаемой последовательности;
  • повторная инициализация не приводит к утечкам состояния;
  • взаимодействие с внешним кодом (валидация, формы, state-менеджеры) работает стабильно.

Flatpickr имеет внутреннее состояние экземпляра, доступное через объект, возвращаемый функцией инициализации. Это состояние используется в тестах как точка контроля поведения.

Подготовка тестового окружения

Интеграционные тесты Flatpickr обычно выполняются в одном из следующих окружений:

  • jsdom (быстрое выполнение, ограниченная эмуляция браузера);
  • happy-dom (более полная модель DOM);
  • реальные браузеры через Playwright или Cypress.

Выбор зависит от глубины тестирования. Для проверки пользовательских сценариев предпочтительнее браузерное окружение.

Базовая подготовка DOM-элемента:

beforeEach(() => {
  document.body.innerHTML = `
    <input id="datepicker" type="text" />
  `;
});

Инициализация Flatpickr:

import flatpickr from "flatpickr";

let instance;

beforeEach(() => {
  instance = flatpickr("#datepicker", {
    enableTime: true,
    dateFormat: "Y-m-d H:i",
  });
});

afterEach(() => {
  instance.destroy();
});

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

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

Инициализация является первым критическим сценарием. Проверяется создание DOM-структуры и привязка к input.

test("инициализация Flatpickr создает экземпляр", () => {
  expect(instance).toBeDefined();
  expect(instance.input).toBe(document.querySelector("#datepicker"));
});

Дополнительно проверяется создание pop-up контейнера:

test("создается календарный контейнер", () => {
  const calendar = document.querySelector(".flatpickr-calendar");
  expect(calendar).not.toBeNull();
});

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

Проверка взаимодействия с DOM

Flatpickr активно модифицирует DOM: добавляет классы, атрибуты, inline-стили. Это требует проверки состояния элементов после действий пользователя.

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

test("календарь открывается по фокусу", () => {
  instance.open();

  const calendar = document.querySelector(".flatpickr-calendar");
  expect(calendar.classList.contains("open")).toBe(true);
});

Закрытие:

test("календарь закрывается", () => {
  instance.open();
  instance.close();

  const calendar = document.querySelector(".flatpickr-calendar");
  expect(calendar.classList.contains("open")).toBe(false);
});

Важно учитывать, что Flatpickr может использовать CSS-классы для управления видимостью, а не удалять DOM-элементы.

Тестирование выбора даты и синхронизации input

Одним из ключевых сценариев является проверка связи между UI и значением input.

test("выбор даты обновляет значение input", () => {
  instance.setDate("2026-01-01");

  const input = document.querySelector("#datepicker");
  expect(input.value).toBe("2026-01-01 00:00");
});

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

Также важно тестировать обратный сценарий:

test("изменение input обновляет состояние Flatpickr", () => {
  const input = document.querySelector("#datepicker");

  input.value = "2026-02-10";
  input.dispatchEvent(new Event("input", { bubbles: true }));

  expect(instance.selectedDates.length).toBeGreaterThan(0);
});

Тестирование событий Flatpickr

Flatpickr предоставляет набор callback-событий, которые являются важной частью интеграции с приложением.

Пример тестирования onChange:

test("onChange вызывается при выборе даты", () => {
  const mock = jest.fn();

  instance.set("onChange", mock);
  instance.setDate("2026-03-01");

  expect(mock).toHaveBeenCalled();
});

Проверка порядка событий:

test("события вызываются в правильном порядке", () => {
  const order = [];

  instance.set("onOpen", () => order.push("open"));
  instance.set("onClose", () => order.push("close"));

  instance.open();
  instance.close();

  expect(order).toEqual(["open", "close"]);
});

При тестировании событий важно учитывать асинхронность UI-обновлений и использовать requestAnimationFrame или setTimeout при необходимости.

Тестирование повторной инициализации

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

test("повторная инициализация не создает дубликатов", () => {
  instance.destroy();

  instance = flatpickr("#datepicker");

  instance.setDate("2026-04-01");

  const input = document.querySelector("#datepicker");
  expect(input.value).toContain("2026-04-01");
});

Критически важно проверять отсутствие утечек DOM-элементов:

test("после destroy DOM очищается", () => {
  instance.destroy();

  const calendar = document.querySelector(".flatpickr-calendar");
  expect(calendar).toBeNull();
});

Интеграция с формами

Flatpickr часто используется внутри HTML-форм, поэтому тестирование поведения при submit является обязательным.

test("значение Flatpickr отправляется в форме", () => {
  document.body.innerHTML = `
    <form>
      <input id="datepicker" name="date" />
      <button type="submit">Send</button>
    </form>
  `;

  const fp = flatpickr("#datepicker");
  fp.setDate("2026-05-01");

  const form = document.querySelector("form");

  const formData = new FormData(form);
  expect(formData.get("date")).toBe("2026-05-01");
});

Также проверяется корректность работы с HTML5 validation и required-полями.

Тестирование с фреймворками (React, Vue, Angular)

Flatpickr часто используется как внешний контрол внутри UI-фреймворков. Интеграционные тесты должны учитывать жизненный цикл компонентов.

Пример для React-подобной логики:

test("Flatpickr инициализируется при mount компонента", () => {
  render(<DateInput />);

  const input = screen.getByRole("textbox");
  expect(input).toBeInTheDocument();

  const calendar = document.querySelector(".flatpickr-calendar");
  expect(calendar).not.toBeNull();
});

Особое внимание уделяется:

  • unmount (вызывает ли destroy);
  • повторному mount;
  • синхронизации state и Flatpickr.

Моки и стабилизация поведения

Для стабилизации тестов часто мокируются:

  • таймеры (jest.useFakeTimers);
  • requestAnimationFrame;
  • Date.now.
beforeEach(() => {
  jest.useFakeTimers();
});

afterEach(() => {
  jest.useRealTimers();
});

Это позволяет детерминированно тестировать анимации и задержки открытия/закрытия календаря.

Тестирование edge cases

Интеграционные тесты должны включать нестандартные сценарии:

  • ввод некорректной даты;
  • переключение месяцев при быстром клике;
  • смена locale;
  • работа с minDate и maxDate.

Пример:

test("не позволяет выбрать дату раньше minDate", () => {
  instance.set("minDate", "2026-01-10");

  instance.setDate("2026-01-01");

  expect(instance.selectedDates[0]).not.toBeDefined();
});

Поведение при динамическом изменении конфигурации

Flatpickr поддерживает изменение настроек на лету:

test("изменение конфигурации влияет на поведение", () => {
  instance.set("enableTime", true);
  instance.setDate("2026-06-01 10:00");

  expect(document.querySelector("#datepicker").value).toContain(":");
});

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

Интеграция с внешними состояниями

При использовании Redux, Zustand или аналогичных систем важно проверять синхронизацию состояния:

  • обновление store при выборе даты;
  • восстановление значения из store при инициализации;
  • отсутствие циклических обновлений.

Типичный сценарий:

test("синхронизация с внешним state", () => {
  store.setState({ date: "2026-07-01" });

  render(<DatePicker />);

  expect(document.querySelector("#datepicker").value).toContain("2026-07-01");
});