Библиотека Muuri используется для создания динамических сеток элементов с возможностью сортировки, фильтрации, перетаскивания и анимации. Поведение таких интерфейсов определяется не только логикой кода, но и действиями пользователя.
Тестирование взаимодействий направлено на проверку того, что:
Особенность тестирования интерактивных интерфейсов заключается в необходимости воспроизводить пользовательские действия и проверять изменения DOM, состояния элементов и внутренних данных Muuri.
Модульное тестирование проверяет отдельные функции, которые управляют сеткой.
К таким функциям относятся:
Пример функции сортировки:
function sortByPrice(items) {
return items.sort((a, b) => a.price - b.price);
}
Тест:
test("items sorted by price", () => {
const items = [
{ price: 20 },
{ price: 10 },
{ price: 30 }
];
const result = sortByPrice(items);
expect(result[0].price).toBe(10);
expect(result[2].price).toBe(30);
});
Такие тесты проверяют корректность алгоритмов независимо от Muuri.
Интеграционные тесты проверяют взаимодействие Muuri с DOM и другими частями приложения.
Проверяются:
Пример проверки добавления элемента:
test("item added to grid", () => {
document.body.innerHTML = `
<div class="grid">
<div class="item"></div>
</div>
`;
const grid = new Muuri('.grid');
const element = document.createElement("div");
element.className = "item";
grid.add(element);
expect(grid.getItems().length).toBe(2);
});
End-to-End тесты воспроизводят реальные пользовательские сценарии.
Проверяются:
Для таких тестов используются инструменты:
Перетаскивание является ключевой функцией Muuri. При тестировании необходимо убедиться, что:
test('drag item changes order', async ({ page }) => {
await page.goto('http://localhost:3000');
const firstItem = page.locator('.item').nth(0);
const secondItem = page.locator('.item').nth(1);
await firstItem.dragTo(secondItem);
const newFirst = await page.locator('.item').nth(0).textContent();
expect(newFirst).not.toBeNull();
});
Тест проверяет изменение порядка элементов после перетаскивания.
Muuri позволяет сортировать элементы программно.
Пример сортировки:
grid.sort((a, b) => {
return a.getElement().dataset.value - b.getElement().dataset.value;
});
Тестирование должно проверять:
Пример теста:
test("grid sorting works", () => {
const values = grid.getItems().map(item =>
parseInt(item.getElement().dataset.value)
);
const sorted = [...values].sort((a, b) => a - b);
expect(values).toEqual(sorted);
});
Фильтрация скрывает элементы, не удовлетворяющие условию.
Пример фильтра:
grid.filter(item => {
return item.getElement().dataset.category === "books";
});
Тестирование должно убедиться, что:
Пример теста:
test("filter hides non matching items", () => {
grid.filter(item => {
return item.getElement().dataset.category === "books";
});
const visibleItems = grid.getItems().filter(item => item.isVisible());
visibleItems.forEach(item => {
expect(item.getElement().dataset.category).toBe("books");
});
});
Muuri генерирует множество событий:
dragStartdragEndmovelayoutStartlayoutEndaddremoveСобытия позволяют отслеживать изменения состояния сетки.
test("dragStart event fires", done => {
grid.on("dragStart", () => {
done();
});
const item = grid.getItems()[0];
item._drag.start();
});
Проверяется факт вызова обработчика.
Muuri активно использует анимации при:
Проблемы могут проявляться в виде:
Проверка выполняется через:
Пример:
test("layout updates positions", done => {
grid.on("layoutEnd", () => {
const items = grid.getItems();
const positions = items.map(item => item.getPosition());
expect(positions.length).toBeGreaterThan(0);
done();
});
grid.layout();
});
Muuri поддерживает динамическое добавление элементов.
grid.add(newElement);
Тестирование должно проверить:
Пример теста:
test("element added to DOM", () => {
const element = document.createElement("div");
element.className = "item";
grid.add(element);
const items = document.querySelectorAll(".item");
expect(items.length).toBe(grid.getItems().length);
});
Удаление элементов может происходить программно или через пользовательские действия.
grid.remove(item);
Тест должен удостовериться, что:
Пример:
test("item removed from grid", () => {
const item = grid.getItems()[0];
grid.remove(item);
expect(grid.getItems().includes(item)).toBe(false);
});
Muuri хранит внутреннее состояние элементов:
Проверка состояния помогает выявлять ошибки синхронизации.
Пример:
test("grid state consistent", () => {
const items = grid.getItems();
items.forEach((item, index) => {
expect(item.getGrid()).toBe(grid);
expect(item.getIndex()).toBe(index);
});
});
Muuri автоматически перестраивает layout при изменении размеров контейнера.
Проверяется:
Пример теста:
test("grid reacts to resize", () => {
window.innerWidth = 800;
window.dispatchEvent(new Event("resize"));
grid.layout();
const items = grid.getItems();
expect(items.length).toBeGreaterThan(0);
});
При большом количестве элементов важно убедиться, что интерфейс остаётся отзывчивым.
Проверяются:
Пример простого теста:
test("layout performance", () => {
const start = performance.now();
grid.layout();
const end = performance.now();
expect(end - start).toBeLessThan(50);
});
Некоторые взаимодействия сложно воспроизвести напрямую. В таких случаях используются моки.
Пример мокирования метода:
jest.spyOn(grid, "layout");
Проверка вызова:
expect(grid.layout).toHaveBeenCalled();
Моки позволяют:
Полноценная проверка взаимодействий включает реальные последовательности действий.
Типичный сценарий:
Тест такого сценария:
test("full interaction scenario", () => {
const element = document.createElement("div");
element.className = "item";
grid.add(element);
grid.filter(() => true);
grid.sort((a, b) => a.getIndex() - b.getIndex());
const itemsBefore = grid.getItems().length;
grid.remove(grid.getItems()[0]);
const itemsAfter = grid.getItems().length;
expect(itemsAfter).toBe(itemsBefore - 1);
});
Такие тесты позволяют убедиться, что все функции Muuri корректно работают совместно и не приводят к нарушению структуры сетки.