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

Интерфейсы, построенные с использованием Interact.js, часто включают сложные сценарии пользовательского взаимодействия: перетаскивание элементов, изменение размеров, жесты, взаимодействие между объектами, drag-and-drop зоны, ограничения перемещения и динамическую реакцию DOM.

Модульные и интеграционные тесты проверяют отдельные функции или изолированные компоненты, однако поведение интерфейса в реальном браузере определяется множеством факторов:

  • обработкой событий указателя (pointer, mouse, touch);
  • вычислением координат;
  • обновлением стилей и трансформаций;
  • взаимодействием элементов между собой;
  • работой CSS и layout-движка браузера.

End-to-End тестирование (E2E) проверяет полный пользовательский сценарий: от действия пользователя до итогового состояния интерфейса.

Для библиотек вроде Interact.js это особенно важно, поскольку большая часть логики работает через реальные события браузера, которые невозможно полноценно смоделировать в unit-тестах.


Особенности тестирования drag-and-drop интерфейсов

Сценарии взаимодействия, создаваемые Interact.js, обладают рядом особенностей:

Непрерывное движение указателя

Перетаскивание — это не одно событие, а последовательность событий:

  1. pointerdown / mousedown
  2. pointermove (множество событий)
  3. pointerup

Каждый шаг может изменять координаты и состояние DOM.

Зависимость от координат

Interact.js активно использует:

  • clientX
  • clientY
  • bounding rectangles элементов
  • CSS transforms

Следовательно, тест должен проверять фактическое положение элемента, а не только факт вызова функции.

Асинхронность обновления интерфейса

В процессе взаимодействия могут участвовать:

  • requestAnimationFrame
  • transitions
  • реактивные фреймворки (React, Vue)
  • debounce/timeout

E2E-тест должен учитывать задержки обновления DOM.


Популярные инструменты для E2E тестирования

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

Playwright

Современный инструмент автоматизации браузеров.

Преимущества:

  • поддержка Chromium, Firefox, WebKit
  • встроенная работа с координатами мыши
  • мощный API ожиданий
  • высокая скорость выполнения

Cypress

Популярный инструмент тестирования веб-интерфейсов.

Особенности:

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

Однако Cypress имеет ограничения при сложных drag-and-drop сценариях, поскольку не всегда точно воспроизводит нативные события указателя.

WebdriverIO / Selenium

Классические инструменты браузерной автоматизации.

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


Подготовка среды тестирования

Типичная структура проекта:

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

Пример интерфейса с Interact.js

Простейший пример перетаскиваемого элемента.

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-тест должен проверить:

  • возможность захвата элемента
  • перемещение
  • изменение координат

Базовый 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')
})

Этот тест:

  1. открывает страницу
  2. находит элемент
  3. имитирует захват мышью
  4. выполняет перемещение
  5. проверяет изменение CSS-трансформации

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

Более точный тест проверяет конкретные координаты.

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

Тестирование drop-зон

Interact.js поддерживает dropzones — области, принимающие перетаскиваемые элементы.

Пример:

interact('.dropzone').dropzone({
  accept: '.draggable',

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

Проверка попадания в drop-зону

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/)
})

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

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

Пример:

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

E2E тест изменения размера

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

Тестирование жестов (gestures)

Interact.js может обрабатывать:

  • pinch
  • rotate
  • multi-touch

Однако E2E-тестирование жестов сложнее, поскольку требуется мультисенсорный ввод.

Playwright поддерживает эмуляцию touch-событий:

await page.touchscreen.tap(x, y)

Для сложных сценариев иногда используется:

  • мобильная эмуляция браузера
  • реальные устройства
  • BrowserStack / SauceLabs

Стабилизация E2E тестов

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

Это ускоряет тестирование сложных интерфейсов с большим количеством сценариев.


Тестирование сложных drag-and-drop систем

В реальных приложениях Interact.js используется для:

  • канбан-досок
  • редакторов интерфейсов
  • конструкторов форм
  • графических редакторов

Тесты могут включать сценарии:

Перемещение карточки между колонками

Backlog → In Progress

Изменение размера виджета

resize dashboard widget

Перестановка элементов списка

drag item #3 above item #1

Такие тесты проверяют не только координаты, но и изменение структуры данных.


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

После drag-операции обычно изменяется:

  • состояние хранилища
  • DOM-структура
  • порядок элементов

Пример проверки:

const items = await page.locator('.item').allTextContents()

expect(items[0]).toBe('Task 3')

CI интеграция E2E тестов

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

Это полезно для интерфейсов с:

  • сложным layout
  • drag-редакторами
  • динамическими позициями элементов

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

В сложных интерфейсах важна не только корректность, но и плавность drag-операций.

Playwright позволяет собирать метрики:

  • FPS
  • время отклика
  • производительность рендеринга

С помощью:

page.metrics()

или интеграции с Lighthouse.


Стратегия тестирования интерфейсов Interact.js

Эффективная структура тестирования включает несколько уровней.

Unit-тесты

Проверяют:

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

Integration-тесты

Проверяют:

  • работу компонентов
  • взаимодействие с DOM

E2E-тесты

Проверяют:

  • реальные пользовательские сценарии
  • drag-and-drop
  • resize
  • drop-зоны
  • работу в браузере

Такой подход позволяет обеспечить надежность сложных интерфейсов, активно использующих возможности Interact.js.