Тестирование взаимодействий

Библиотека Muuri используется для создания динамических сеток элементов с возможностью сортировки, фильтрации, перетаскивания и анимации. Поведение таких интерфейсов определяется не только логикой кода, но и действиями пользователя.

Тестирование взаимодействий направлено на проверку того, что:

  • элементы корректно реагируют на перетаскивание;
  • сортировка выполняется без визуальных артефактов;
  • фильтрация не нарушает структуру сетки;
  • события Muuri вызываются в правильной последовательности;
  • анимации не конфликтуют друг с другом;
  • состояние сетки остаётся консистентным после серии операций.

Особенность тестирования интерактивных интерфейсов заключается в необходимости воспроизводить пользовательские действия и проверять изменения DOM, состояния элементов и внутренних данных Muuri.


Уровни тестирования Muuri-интерфейсов

Модульное тестирование

Модульное тестирование проверяет отдельные функции, которые управляют сеткой.

К таким функциям относятся:

  • логика сортировки элементов;
  • фильтрация данных;
  • обработчики событий 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);
});

E2E-тестирование

End-to-End тесты воспроизводят реальные пользовательские сценарии.

Проверяются:

  • drag-and-drop;
  • сортировка;
  • фильтрация;
  • изменение позиции элементов;
  • обновление интерфейса.

Для таких тестов используются инструменты:

  • Playwright
  • Cypress
  • Puppeteer

Проверка drag-and-drop

Перетаскивание является ключевой функцией Muuri. При тестировании необходимо убедиться, что:

  • элемент можно захватить;
  • элемент перемещается;
  • позиция обновляется;
  • событие сортировки происходит корректно.

Тест с Playwright

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

Muuri генерирует множество событий:

  • dragStart
  • dragEnd
  • move
  • layoutStart
  • layoutEnd
  • add
  • remove

События позволяют отслеживать изменения состояния сетки.

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

test("dragStart event fires", done => {
  grid.on("dragStart", () => {
    done();
  });

  const item = grid.getItems()[0];

  item._drag.start();
});

Проверяется факт вызова обработчика.


Проверка анимаций

Muuri активно использует анимации при:

  • сортировке;
  • фильтрации;
  • изменении размеров элементов;
  • перемещении элементов.

Проблемы могут проявляться в виде:

  • пересечения элементов;
  • скачков интерфейса;
  • зависших анимаций.

Проверка выполняется через:

  • ожидание завершения layout;
  • проверку координат элементов.

Пример:

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);

Тестирование должно проверить:

  • добавление элемента в DOM;
  • корректную позицию;
  • запуск layout.

Пример теста:

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);

Тест должен удостовериться, что:

  • элемент удалён из сетки;
  • DOM обновлён;
  • layout пересчитан.

Пример:

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 при изменении размеров контейнера.

Проверяется:

  • реакция на resize;
  • корректность позиционирования элементов.

Пример теста:

test("grid reacts to resize", () => {
  window.innerWidth = 800;

  window.dispatchEvent(new Event("resize"));

  grid.layout();

  const items = grid.getItems();

  expect(items.length).toBeGreaterThan(0);
});

Проверка производительности взаимодействий

При большом количестве элементов важно убедиться, что интерфейс остаётся отзывчивым.

Проверяются:

  • скорость layout;
  • задержка drag-and-drop;
  • частота перерисовок.

Пример простого теста:

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();

Моки позволяют:

  • контролировать поведение функций;
  • изолировать тестируемую часть;
  • проверять количество вызовов методов.

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

Полноценная проверка взаимодействий включает реальные последовательности действий.

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

  1. загрузка сетки;
  2. добавление элемента;
  3. фильтрация;
  4. сортировка;
  5. перетаскивание элемента;
  6. удаление элемента.

Тест такого сценария:

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 корректно работают совместно и не приводят к нарушению структуры сетки.