Popper.js предоставляет мощный механизм для позиционирования всплывающих элементов, таких как тултипы, дропдауны и поповеры. Центральным элементом этой системы являются модификаторы, которые позволяют изменять поведение и внешний вид всплывающего элемента. Unit-тестирование модификаторов помогает гарантировать их корректную работу при различных конфигурациях.
Для unit-тестирования используется сочетание инструментов, таких как Jest или Mocha. Основные задачи включают:
Пример подключения необходимых инструментов:
import { createPopper } from '@popperjs/core';
Каждый тест состоит из нескольких этапов:
Пример шаблона:
test('custom modifier correctly updates popper offsets', () => {
const reference = document.createElement('div');
const popper = document.createElement('div');
document.body.appendChild(reference);
document.body.appendChild(popper);
const customModifier = {
name: 'customOffset',
enabled: true,
phase: 'main',
fn({ state }) {
state.styles.popper.top = `${parseInt(state.styles.popper.top || 0) + 10}px`;
}
};
const popperInstance = createPopper(reference, popper, {
modifiers: [customModifier]
});
popperInstance.update();
expect(parseInt(popper.style.top)).toBe(10);
});
Popper.js содержит стандартные модификаторы, такие как
offset, preventOverflow и flip.
Тестирование встроенных модификаторов требует проверки их
конфигурационных параметров и эффекта на
позиции popper-а.
offsettest('offset modifier shifts popper position', () => {
const reference = document.createElement('button');
const popper = document.createElement('div');
document.body.appendChild(reference);
document.body.appendChild(popper);
const popperInstance = createPopper(reference, popper, {
modifiers: [
{ name: 'offset', options: { offset: [0, 20] } }
]
});
popperInstance.update();
const computedTop = parseFloat(popper.style.top);
expect(computedTop).toBeGreaterThan(0);
});
preventOverflowpreventOverflow гарантирует, что popper не выйдет за
пределы указанных границ:
test('preventOverflow keeps popper within boundaries', () => {
const reference = document.createElement('div');
const popper = document.createElement('div');
const boundary = document.createElement('div');
boundary.style.position = 'absolute';
boundary.style.top = '0';
boundary.style.left = '0';
boundary.style.width = '100px';
boundary.style.height = '100px';
document.body.appendChild(boundary);
document.body.appendChild(reference);
document.body.appendChild(popper);
const popperInstance = createPopper(reference, popper, {
modifiers: [
{ name: 'preventOverflow', options: { boundary } }
]
});
popperInstance.update();
const popperRect = popper.getBoundingClientRect();
const boundaryRect = boundary.getBoundingClientRect();
expect(popperRect.top).toBeGreaterThanOrEqual(boundaryRect.top);
expect(popperRect.left).toBeGreaterThanOrEqual(boundaryRect.left);
});
Для unit-тестов важно мокировать поведение браузера, если тест запускается в среде без полноценного DOM (например, Node.js):
getBoundingClientRect для reference и
popper.Пример мокирования getBoundingClientRect:
reference.getBoundingClientRect = jest.fn(() => ({
width: 100,
height: 50,
top: 100,
left: 100,
right: 200,
bottom: 150
}));
popper.getBoundingClientRect = jest.fn(() => ({
width: 80,
height: 40,
top: 0,
left: 0,
right: 80,
bottom: 40
}));
Каждый модификатор в Popper.js выполняется на определённой фазе:
read, main, write. Unit-тест
должен проверять поведение на соответствующей фазе:
read — вычисление размеров и позиции элементов.main — изменение state и стилей.write — запись изменений в DOM.Пример проверки вызова модификатора на фазе main:
const mockFn = jest.fn(({ state }) => {
state.attributes.popper['data-test'] = 'applied';
});
createPopper(reference, popper, {
modifiers: [{ name: 'mockModifier', phase: 'main', fn: mockFn }]
}).update();
expect(popper.getAttribute('data-test')).toBe('applied');
Иногда модификатор зависит от других модификаторов, указанных в
requires или requiresIfExists. Unit-тест
должен:
const baseModifier = {
name: 'base',
enabled: true,
phase: 'read',
fn({ state }) { state.modifierData.baseValue = 42; }
};
const dependentModifier = {
name: 'dependent',
enabled: true,
phase: 'main',
requires: ['base'],
fn({ state }) { state.styles.popper.top = `${state.modifierData.baseValue}px`; }
};
createPopper(reference, popper, { modifiers: [baseModifier, dependentModifier] }).update();
expect(popper.style.top).toBe('42px');
Unit-тестирование модификаторов Popper.js обеспечивает высокую надёжность работы всплывающих элементов, выявляет ошибки взаимодействия между модификаторами и гарантирует стабильное поведение интерфейса при любых изменениях.