Интерфейсы, построенные с использованием Interact.js, часто включают сложные сценарии пользовательского взаимодействия: перетаскивание элементов, изменение размеров, жесты, взаимодействие между объектами, drag-and-drop зоны, ограничения перемещения и динамическую реакцию DOM.
Модульные и интеграционные тесты проверяют отдельные функции или изолированные компоненты, однако поведение интерфейса в реальном браузере определяется множеством факторов:
End-to-End тестирование (E2E) проверяет полный пользовательский сценарий: от действия пользователя до итогового состояния интерфейса.
Для библиотек вроде Interact.js это особенно важно, поскольку большая часть логики работает через реальные события браузера, которые невозможно полноценно смоделировать в unit-тестах.
Сценарии взаимодействия, создаваемые Interact.js, обладают рядом особенностей:
Перетаскивание — это не одно событие, а последовательность событий:
Каждый шаг может изменять координаты и состояние DOM.
Interact.js активно использует:
clientXclientYСледовательно, тест должен проверять фактическое положение элемента, а не только факт вызова функции.
В процессе взаимодействия могут участвовать:
E2E-тест должен учитывать задержки обновления DOM.
Для тестирования интерфейсов на основе Interact.js чаще всего используются следующие инструменты.
Современный инструмент автоматизации браузеров.
Преимущества:
Популярный инструмент тестирования веб-интерфейсов.
Особенности:
Однако Cypress имеет ограничения при сложных drag-and-drop сценариях, поскольку не всегда точно воспроизводит нативные события указателя.
Классические инструменты браузерной автоматизации.
Используются в крупных проектах, где необходима интеграция с существующей инфраструктурой тестирования.
Типичная структура проекта:
project
├─ src
│ ├─ app.js
│ └─ drag.js
│
├─ tests
│ └─ e2e
│ └─ drag.spec.js
│
├─ playwright.config.js
└─ package.json
Установка Playwright:
npm install -D @playwright/test
Инициализация:
npx playwright install
Простейший пример перетаскиваемого элемента.
import interact from 'interactjs'
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)
}
}
})
HTML:
<div class="draggable">Drag</div>
E2E-тест должен проверить:
Пример на Playwright.
import { test, expect } from '@playwright/test'
test('element can be dragged', async ({ page }) => {
await page.goto('http://localhost:3000')
const element = page.locator('.draggable')
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 + 200,
box.y + 100
)
await page.mouse.up()
const transform = await element.evaluate(el => el.style.transform)
expect(transform).toContain('translate')
})
Этот тест:
Более точный тест проверяет конкретные координаты.
test('element moves to expected position', async ({ page }) => {
await page.goto('/')
const el = page.locator('.draggable')
const box = await el.boundingBox()
await page.mouse.move(box.x + 10, box.y + 10)
await page.mouse.down()
await page.mouse.move(box.x + 150, box.y + 80)
await page.mouse.up()
const x = await el.getAttribute('data-x')
const y = await el.getAttribute('data-y')
expect(Number(x)).toBeGreaterThan(100)
expect(Number(y)).toBeGreaterThan(50)
})
Interact.js поддерживает restrictions, ограничивающие перемещение элемента.
Пример конфигурации:
interact('.draggable')
.draggable({
modifiers: [
interact.modifiers.restrictRect({
restriction: 'parent'
})
]
})
Тест должен убедиться, что элемент не выходит за пределы контейнера.
test('element stays inside container', async ({ page }) => {
await page.goto('/')
const el = page.locator('.draggable')
const container = page.locator('.container')
const cBox = await container.boundingBox()
await page.mouse.move(cBox.x + 20, cBox.y + 20)
await page.mouse.down()
await page.mouse.move(cBox.x + 1000, cBox.y + 1000)
await page.mouse.up()
const elBox = await el.boundingBox()
expect(elBox.x + elBox.width)
.toBeLessThanOrEqual(cBox.x + cBox.width)
})
Interact.js поддерживает dropzones — области, принимающие перетаскиваемые элементы.
Пример:
interact('.dropzone').dropzone({
accept: '.draggable',
ondrop(event) {
event.target.classList.add('active')
}
})
test('element can be dropped into zone', async ({ page }) => {
await page.goto('/')
const draggable = page.locator('.draggable')
const dropzone = page.locator('.dropzone')
const dBox = await draggable.boundingBox()
const zBox = await dropzone.boundingBox()
await page.mouse.move(dBox.x + 5, dBox.y + 5)
await page.mouse.down()
await page.mouse.move(
zBox.x + zBox.width / 2,
zBox.y + zBox.height / 2
)
await page.mouse.up()
await expect(dropzone).toHaveClass(/active/)
})
Interact.js поддерживает изменение размеров элементов.
Пример:
interact('.resizable')
.resizable({
edges: { right: true, bottom: true }
})
test('element can be resized', async ({ page }) => {
await page.goto('/')
const el = page.locator('.resizable')
const box = await el.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 + 100,
box.y + box.height + 50
)
await page.mouse.up()
const newBox = await el.boundingBox()
expect(newBox.width).toBeGreaterThan(box.width)
expect(newBox.height).toBeGreaterThan(box.height)
})
Interact.js может обрабатывать:
Однако E2E-тестирование жестов сложнее, поскольку требуется мультисенсорный ввод.
Playwright поддерживает эмуляцию touch-событий:
await page.touchscreen.tap(x, y)
Для сложных сценариев иногда используется:
Drag-and-drop тесты склонны к нестабильности. Причины:
1. Отключение анимаций
* {
transition: none !important;
}
2. Явные ожидания
await page.waitForTimeout(50)
или
await expect(locator).toBeVisible()
3. Использование стабильных селекторов
Лучше использовать:
data-testid
Пример:
<div data-testid="card"></div>
4. Проверка состояния вместо пикселей
Лучше проверять:
а не точные координаты.
Playwright поддерживает параллельное выполнение.
В конфигурации:
workers: 4
Это ускоряет тестирование сложных интерфейсов с большим количеством сценариев.
В реальных приложениях Interact.js используется для:
Тесты могут включать сценарии:
Перемещение карточки между колонками
Backlog → In Progress
Изменение размера виджета
resize dashboard widget
Перестановка элементов списка
drag item #3 above item #1
Такие тесты проверяют не только координаты, но и изменение структуры данных.
После drag-операции обычно изменяется:
Пример проверки:
const items = await page.locator('.item').allTextContents()
expect(items[0]).toBe('Task 3')
E2E-тесты выполняются автоматически в CI.
Пример GitHub Actions:
name: e2e
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm install
- run: npx playwright install
- run: npm run test:e2e
Playwright поддерживает визуальные тесты.
await expect(page).toHaveScreenshot()
Это полезно для интерфейсов с:
В сложных интерфейсах важна не только корректность, но и плавность drag-операций.
Playwright позволяет собирать метрики:
С помощью:
page.metrics()
или интеграции с Lighthouse.
Эффективная структура тестирования включает несколько уровней.
Проверяют:
Проверяют:
Проверяют:
Такой подход позволяет обеспечить надежность сложных интерфейсов, активно использующих возможности Interact.js.