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

Интеграционные тесты проверяют взаимодействие нескольких частей системы одновременно. В контексте интерфейсов, построенных на Interact.js, такие тесты охватывают:

  • обработчики событий библиотеки;
  • DOM-элементы и их состояние;
  • пользовательские сценарии (drag, resize, drop, gestures);
  • бизнес-логику приложения;
  • взаимодействие с другими библиотеками.

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

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

Типичные сценарии для проверки:

  • перетаскивание элементов в контейнеры;
  • изменение размеров интерфейсных блоков;
  • корректность drop-зон;
  • синхронизация состояния DOM и JavaScript-логики;
  • реакция интерфейса на сложные цепочки событий.

Архитектура тестирования интерактивных интерфейсов

Интерактивные системы с Interact.js обычно имеют несколько уровней:

1. DOM-структура

HTML-разметка, содержащая интерактивные элементы.

2. Конфигурация Interact.js

Определяет поведение элементов:

  • draggable
  • resizable
  • dropzone
  • gesture

3. Бизнес-логика

Обработчики событий, обновляющие состояние интерфейса.

4. Сторонние зависимости

Например:

  • state-менеджеры
  • фреймворки (React, Vue)
  • сетевые запросы

Интеграционные тесты должны проверять совместную работу всех этих уровней.


Среда для интеграционного тестирования

Для тестирования интерфейсов чаще всего используются инструменты:

  • Jest
  • Vitest
  • Playwright
  • Cypress
  • Testing Library

Интерактивные события требуют среды, поддерживающей:

  • эмуляцию мыши
  • эмуляцию touch-событий
  • реальный DOM
  • выполнение JavaScript

Основные подходы

Headless браузеры

Playwright или Cypress запускают браузер и выполняют сценарии пользователя.

DOM-эмуляция

JSDOM позволяет тестировать логику, но не всегда корректно обрабатывает сложные pointer-события.

Для Interact.js наиболее надёжны браузерные интеграционные тесты.


Подготовка тестовой среды

Установка инструментов:

npm install --save-dev playwright

Инициализация проекта тестирования:

npx playwright init

Создаётся структура:

tests/
playwright.config.js

Минимальный тестовый интерфейс

HTML:

<div id="draggable" class="box"></div>
<div id="dropzone" class="zone"></div>

Jav * aScript:

interact('#draggable').draggable({
  listeners: {
    move(event) {
      const target = event.target
      const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx
      const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy

      target.style.transform = `translate(${x}px, ${y}px)`
      target.setAttribute('data-x', x)
      target.setAttribute('data-y', y)
    }
  }
})

interact('#dropzone').dropzone({
  ondrop(event) {
    event.target.classList.add('active')
  }
})

Структура интеграционного теста

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

Основные этапы:

  1. загрузка страницы
  2. получение элементов
  3. симуляция взаимодействия
  4. проверка DOM-результата

Пример теста drag-and-drop

import { test, expect } from '@playwright/test'

test('перетаскивание элемента в dropzone', async ({ page }) => {

  await page.goto('http://localhost:3000')

  const draggable = page.locator('#draggable')
  const dropzone = page.locator('#dropzone')

  const start = await draggable.boundingBox()
  const end = await dropzone.boundingBox()

  await page.mouse.move(start.x + 10, start.y + 10)
  await page.mouse.down()

  await page.mouse.move(end.x + 50, end.y + 50)

  await page.mouse.up()

  await expect(dropzone).toHaveClass(/active/)
})

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

  • успешное завершение drag-операции
  • корректное срабатывание drop-обработчика

Проверка координат после перемещения

Иногда требуется убедиться, что элемент действительно переместился.

test('изменение позиции элемента', async ({ page }) => {

  await page.goto('/')

  const element = page.locator('#draggable')

  const before = await element.boundingBox()

  await page.mouse.move(before.x + 5, before.y + 5)
  await page.mouse.down()

  await page.mouse.move(before.x + 200, before.y + 200)
  await page.mouse.up()

  const after = await element.boundingBox()

  expect(after.x).toBeGreaterThan(before.x)
  expect(after.y).toBeGreaterThan(before.y)
})

Проверяется изменение координат элемента в DOM.


Тестирование resize-поведения

Interact.js поддерживает изменение размеров элементов.

Конфигурация:

interact('.resizable').resizable({
  edges: { right: true, bottom: true }
})

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

test('изменение размера элемента', async ({ page }) => {

  await page.goto('/')

  const element = page.locator('.resizable')

  const box = await element.boundingBox()

  await page.mouse.move(box.x + box.width - 2, box.y + box.height - 2)

  await page.mouse.down()

  await page.mouse.move(box.x + box.width + 100, box.y + box.height + 100)

  await page.mouse.up()

  const resized = await element.boundingBox()

  expect(resized.width).toBeGreaterThan(box.width)
  expect(resized.height).toBeGreaterThan(box.height)
})

Проверка ограничений перемещения

Interact.js поддерживает ограничения (restrict).

Конфигурация:

interact('.item').draggable({
  modifiers: [
    interact.modifiers.restrict({
      restriction: 'parent'
    })
  ]
})

Интеграционный тест проверяет, что элемент не выходит за границы.

test('ограничение перемещения родителем', async ({ page }) => {

  await page.goto('/')

  const element = page.locator('.item')
  const parent = page.locator('.container')

  const parentBox = await parent.boundingBox()

  await element.dragTo(parent)

  const after = await element.boundingBox()

  expect(after.x).toBeGreaterThanOrEqual(parentBox.x)
})

Тестирование dropzone-логики

Dropzone может иметь сложные правила.

Пример конфигурации:

interact('.dropzone').dropzone({
  accept: '.draggable',
  overlap: 0.5
})

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

  • допустимость drop
  • реакцию интерфейса
test('dropzone принимает допустимый элемент', async ({ page }) => {

  await page.goto('/')

  const item = page.locator('.draggable')
  const zone = page.locator('.dropzone')

  await item.dragTo(zone)

  await expect(zone).toHaveClass(/active/)
})

Тестирование сложных сценариев взаимодействия

Иногда требуется проверка цепочки действий пользователя.

Пример:

  1. перемещение элемента
  2. изменение размера
  3. drop в контейнер
test('комбинированный сценарий', async ({ page }) => {

  await page.goto('/')

  const item = page.locator('.widget')
  const zone = page.locator('.dashboard')

  await item.dragTo(zone)

  const box = await item.boundingBox()

  await page.mouse.move(box.x + box.width, box.y + box.height)

  await page.mouse.down()
  await page.mouse.move(box.x + box.width + 50, box.y + box.height + 50)
  await page.mouse.up()

  await expect(item).toBeVisible()
})

Такие тесты выявляют ошибки интеграции между компонентами.


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

Interact.js активно используется на мобильных устройствах.

Playwright поддерживает эмуляцию тач-взаимодействий.

await page.touchscreen.tap(200, 200)

Drag-сценарий:

await page.touchscreen.tap(startX, startY)

await page.touchscreen.down()

await page.touchscreen.move(endX, endY)

await page.touchscreen.up()

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

  • корректность жестов
  • отсутствие конфликтов pointer-событий.

Работа с таймингами и анимациями

Interact.js может использовать:

  • CSS transitions
  • inertia
  • snapping

Тесты должны учитывать задержки.

Пример:

await page.waitForTimeout(300)

Или ожидание состояния:

await expect(element).toHaveCSS('transform', /translate/)

Проверка событий Interact.js

Некоторые сценарии требуют проверки срабатывания событий.

Например:

  • dragstart
  • dragmove
  • dragend
  • drop

Можно добавить логирование:

window.events = []

interact('.item').draggable({
  listeners: {
    start() {
      window.events.push('start')
    },
    end() {
      window.events.push('end')
    }
  }
})

Тест:

const events = await page.evaluate(() => window.events)

expect(events).toContain('start')
expect(events).toContain('end')

Изоляция тестовых сценариев

Каждый интеграционный тест должен:

  • запускаться независимо
  • иметь чистое состояние DOM
  • не зависеть от предыдущих тестов

Playwright автоматически создаёт новый контекст браузера для каждого теста.


Тестирование ошибок

Необходимо проверять поведение при некорректных условиях:

  • drop в недопустимую зону
  • попытка resize запрещённого элемента
  • drag отключённого компонента

Пример:

test('dropzone отклоняет недопустимый элемент', async ({ page }) => {

  await page.goto('/')

  const item = page.locator('.invalid')
  const zone = page.locator('.dropzone')

  await item.dragTo(zone)

  await expect(zone).not.toHaveClass(/active/)
})

Производительность интеграционных тестов

Интерактивные тесты могут быть медленными.

Методы оптимизации:

1. Минимальные страницы

Тестировать только нужный компонент.

2. Параллельное выполнение

Playwright поддерживает parallel-режим.

3. Стабильные селекторы

Использование:

data-testid

пример:

<div data-testid="drag-item"></div>

Проверка состояния приложения

Иногда необходимо проверять не только DOM, но и состояние JavaScript.

Пример:

const state = await page.evaluate(() => window.appState)

expect(state.items.length).toBe(1)

Это подтверждает, что взаимодействие пользователя обновило внутреннюю логику приложения.


Стратегии покрытия тестами

Интерактивные интерфейсы требуют особого подхода к покрытию.

Ключевые сценарии:

  • успешный drag
  • отменённый drag
  • drag за пределы контейнера
  • resize минимального размера
  • resize максимального размера
  • drop в допустимую и недопустимую зону
  • быстрые последовательности действий пользователя

Подход “пользовательских сценариев”

Интеграционные тесты лучше строить вокруг поведения пользователя, а не внутренней реализации.

Примеры сценариев:

  • пользователь перетаскивает карточку на доске
  • пользователь изменяет размер панели
  • пользователь перемещает виджет в новый контейнер

Такой подход делает тесты:

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

Частые проблемы при тестировании Interact.js

1. Неправильная симуляция pointer-событий

Некоторые тестовые среды генерируют только mouse-события.

Interact.js использует:

  • pointer events
  • touch events

2. Нестабильные координаты

DOM-элементы могут смещаться из-за:

  • CSS
  • анимаций
  • scroll

3. Асинхронные обновления

Иногда обработчики обновляют состояние через:

  • requestAnimationFrame
  • setTimeout

Тесты должны учитывать такие задержки.


Поддержание тестов

Интеграционные тесты требуют регулярного обновления.

Основные практики:

  • хранение тестовых утилит
  • единые функции drag-операций
  • повторно используемые селекторы

Пример вспомогательной функции:

async function drag(page, start, end) {
  await page.mouse.move(start.x, start.y)
  await page.mouse.down()
  await page.mouse.move(end.x, end.y)
  await page.mouse.up()
}

Это уменьшает дублирование кода и упрощает поддержку тестового набора.