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

Popper.js — это библиотека для управления позиционированием элементов относительно других элементов на странице, часто используется для создания всплывающих подсказок, тултипов, дропдаунов и модальных окон. Основной принцип работы Popper.js заключается в том, чтобы динамически рассчитывать положение элемента (поппера) относительно целевого элемента (reference) с учётом ограничений пространства и видимой области браузера.

Создание экземпляра Popper осуществляется через конструктор createPopper(reference, popper, options), где:

  • reference — DOM-элемент, относительно которого будет позиционироваться поппер.
  • popper — сам элемент поппера, который нужно разместить.
  • options — объект конфигурации, включающий стратегию позиционирования, модификаторы и прочие настройки.

Пример базовой инициализации:

import { createPopper } from '@popperjs/core';

const button = document.querySelector('#button');
const tooltip = document.querySelector('#tooltip');

const popperInstance = createPopper(button, tooltip, {
  placement: 'top',
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8],
      },
    },
  ],
});

В данном примере поппер будет располагаться сверху относительно кнопки с отступом 8 пикселей.


Позиционирование и модификаторы

Popper.js использует модификаторы для управления поведением поппера. Основные модификаторы:

  • offset — задаёт смещение поппера относительно reference.
  • preventOverflow — не позволяет попперу выходить за пределы видимой области.
  • flip — автоматически изменяет сторону поппера, если выбранная сторона не помещается на экране.
  • arrow — управляет стрелкой поппера, чтобы она указывала на reference.
  • computeStyles — управляет CSS-свойствами transform и position для оптимального рендеринга.

Пример использования нескольких модификаторов:

createPopper(button, tooltip, {
  placement: 'right',
  modifiers: [
    { name: 'offset', options: { offset: [10, 20] } },
    { name: 'preventOverflow', options: { padding: 5 } },
    { name: 'flip', options: { fallbackPlacements: ['top', 'bottom'] } },
  ],
});

Здесь поппер будет смещён на 10px по горизонтали и 20px по вертикали, а при нехватке места автоматически перевернётся на верх или низ.


Динамическое обновление положения

Popper.js предоставляет метод update(), который пересчитывает позиции поппера в реальном времени. Он возвращает промис, что позволяет использовать асинхронные операции:

popperInstance.update().then(() => {
  console.log('Позиция поппера обновлена');
});

Если требуется часто пересчитывать позицию, можно использовать метод forceUpdate(), который выполняет пересчёт синхронно, без промиса.


Интеграция с React и Vue

Popper.js активно используется вместе с фреймворками. Для React существует официальный пакет @popperjs/react, который позволяет управлять позиционированием через хуки и компоненты. Пример использования хука usePopper:

import { usePopper } from 'react-popper';

const [referenceElement, setReferenceElement] = useState(null);
const [popperElement, setPopperElement] = useState(null);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
  placement: 'bottom',
});

return (
  <>
    <button ref={setReferenceElement}>Кнопка</button>
    <div ref={setPopperElement} style={styles.popper} {...attributes.popper}>
      Тултип
    </div>
  </>
);

Для Vue используется директива v-popper или компонентный подход с привязкой элементов через refs.


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

При интеграционных тестах Popper.js проверяется не только логика позиционирования, но и визуальная корректность расположения элементов относительно DOM. Основные подходы:

  • Симуляция пользовательских действий — клики, наведение, прокрутка страницы.
  • Проверка координат — сравнение фактического getBoundingClientRect() поппера с ожидаемыми значениями относительно reference.
  • Тестирование модификаторов — проверка поведения offset, flip, preventOverflow в различных сценариях (мелкие окна, скролл, изменение размера окна).
  • Асинхронные события — тестирование обновлений после анимаций или динамической подгрузки контента.

Пример интеграционного теста на Jest с jsdom:

import { createPopper } from '@popperjs/core';

test('Поппер позиционируется сверху кнопки', () => {
  document.body.innerHTML = `
    <button id="btn">Кнопка</button>
    <div id="popper">Тултип</div>
  `;

  const button = document.querySelector('#btn');
  const tooltip = document.querySelector('#popper');
  const popperInstance = createPopper(button, tooltip, { placement: 'top' });

  const btnRect = button.getBoundingClientRect();
  const popperRect = tooltip.getBoundingClientRect();

  expect(popperRect.bottom).toBeLessThanOrEqual(btnRect.top);
});

Тест проверяет, что тултип появляется сверху кнопки. Аналогично можно тестировать flip, offset и другие модификаторы.


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

  • Всегда использовать реальные размеры элементов, чтобы избежать расхождений в вычислении координат.
  • Проверять поведение в разных разрешениях и на мобильных устройствах.
  • При тестировании динамических попперов учитывать асинхронное обновление позиции.
  • Для сложных взаимодействий применять визуальное сравнение через snapshot testing или инструменты типа Cypress с cy.get().should('have.css', ...).

Popper.js позволяет гибко управлять положением элементов, а интеграционные тесты обеспечивают стабильность интерфейса при сложной логике пользовательских взаимодействий. Использование модификаторов и методов обновления создаёт надёжную и предсказуемую систему позиционирования, пригодную для любых интерфейсных компонентов.