Библиотека Shopify Draggable реализует сложную пользовательскую механику: перемещение элементов, сортировку списков, работу с контейнерами, drag-and-drop между областями интерфейса. Такие сценарии трудно покрыть только модульными или интеграционными тестами.
E2E-тестирование (End-to-End) проверяет систему на уровне реального пользовательского поведения. Тесты взаимодействуют с браузером так же, как это делает пользователь:
Для drag-and-drop интерфейсов это особенно важно, потому что:
E2E-тесты позволяют проверить полный цикл работы Draggable: от пользовательского жеста до финального состояния интерфейса.
Для браузерного тестирования JavaScript-приложений чаще всего используются следующие инструменты:
| Инструмент | Особенности |
|---|---|
| Cypress | быстрые тесты, простой API, встроенные ожидания |
| Playwright | кроссбраузерное тестирование, высокая точность эмуляции |
| Selenium | классический инструмент автоматизации браузеров |
| Puppeteer | управление Chromium через DevTools |
Наиболее удобными для drag-and-drop считаются Playwright и Cypress, так как они умеют корректно эмулировать пользовательские действия мышью.
При тестировании интерфейсов на основе Shopify Draggable важно проверять несколько уровней поведения.
Проверяется:
После перемещения элемент должен:
Возможны дополнительные проверки:
<ul class="list">
<li class="item">A</li>
<li class="item">B</li>
<li class="item">C</li>
</ul>
import {Sortable} from '@shopify/draggable';
const sortable = new Sortable(document.querySelectorAll('.list'), {
draggable: '.item'
});
После инициализации элементы можно менять местами. Именно это поведение и будет тестироваться.
Сценарий пользовательского поведения:
Тестовый файл:
import { test, expect } from '@playwright/test';
test('items can be reordered', async ({ page }) => {
await page.goto('http://localhost:3000');
const first = page.locator('.item').nth(0);
const third = page.locator('.item').nth(2);
const firstBox = await first.boundingBox();
const thirdBox = await third.boundingBox();
await page.mouse.move(
firstBox.x + firstBox.width / 2,
firstBox.y + firstBox.height / 2
);
await page.mouse.down();
await page.mouse.move(
thirdBox.x + thirdBox.width / 2,
thirdBox.y + thirdBox.height / 2
);
await page.mouse.up();
const items = await page.locator('.item').allTextContents();
expect(items).toEqual(['B', 'C', 'A']);
});
mousedownmouseupТакой тест максимально близок к реальному пользовательскому взаимодействию.
Перетаскивание формирует цепочку событий:
mousedown
drag:start
drag:move
drag:over
drag:stop
mouseup
Библиотека Shopify Draggable генерирует собственные события поверх стандартных DOM-событий.
Большинство drag-and-drop тестов используют:
element.boundingBox()
Это позволяет получить:
Координаты используются для корректного перемещения мыши.
После перемещения элемента DOM может обновляться асинхронно.
Поэтому тест должен учитывать:
В Playwright это реализуется автоматически через
expect.
Интерфейсы часто позволяют переносить элементы между списками.
<div class="container">
<ul class="list list-a">
<li class="item">A</li>
</ul>
<ul class="list list-b">
<li class="item">B</li>
</ul>
</div>
test('item can move between lists', async ({ page }) => {
await page.goto('http://localhost:3000');
const item = page.locator('.list-a .item');
const target = page.locator('.list-b');
const itemBox = await item.boundingBox();
const targetBox = await target.boundingBox();
await page.mouse.move(
itemBox.x + 10,
itemBox.y + 10
);
await page.mouse.down();
await page.mouse.move(
targetBox.x + 20,
targetBox.y + 20
);
await page.mouse.up();
await expect(page.locator('.list-b .item')).toHaveCount(2);
});
Проверяется факт появления элемента во втором контейнере.
Shopify Draggable генерирует собственные события:
drag:startdrag:movedrag:overdrag:stopsortable:sortsortable:sortedИногда необходимо убедиться, что события действительно происходят.
window.events = [];
sortable.on('sortable:sorted', event => {
window.events.push(event);
});
const events = await page.evaluate(() => window.events.length);
expect(events).toBeGreaterThan(0);
Во время перетаскивания библиотека может создавать placeholder — временную позицию элемента.
Проверки:
Пример проверки:
await expect(page.locator('.draggable--placeholder')).toBeVisible();
После завершения операции:
await expect(page.locator('.draggable--placeholder')).toHaveCount(0);
Drag-and-drop интерфейсы должны поддерживать клавиатуру.
В Shopify Draggable это реализуется через модуль:
KeyboardSensor
E2E тест может проверять:
Пример теста:
await page.keyboard.press('Tab');
await page.keyboard.press('Space');
await page.keyboard.press('ArrowDown');
await page.keyboard.press('Space');
После этого проверяется порядок элементов.
Draggable часто использует CSS-анимации при перестановке элементов.
E2E тесты должны учитывать:
Playwright позволяет ожидать стабилизацию DOM:
await page.waitForTimeout(300);
Однако предпочтительно проверять конечное состояние интерфейса, а не промежуточные состояния.
Каждый E2E тест должен запускаться в чистой среде.
Основные методы:
await page.goto('/');
localStorage.clear();
Часто используется отдельный тестовый билд приложения.
Библиотека Shopify Draggable использует сенсоры для различных устройств:
| Сенсор | Назначение |
|---|---|
| MouseSensor | мышь |
| TouchSensor | сенсорные устройства |
| ForceTouchSensor | pressure touch |
| KeyboardSensor | клавиатура |
E2E тесты могут проверять разные сценарии.
Например, сенсорный ввод.
Playwright:
await page.touchscreen.tap(100, 200);
Для мобильных интерфейсов это особенно важно.
Современные инструменты позволяют запускать E2E тесты параллельно.
Например в Playwright:
npx playwright test --workers=4
Это ускоряет проверку интерфейсов с большим количеством drag-and-drop сценариев.
E2E тесты обычно запускаются в системе непрерывной интеграции.
Наиболее распространённые системы:
Типичный pipeline:
install dependencies
build project
start test server
run e2e tests
collect reports
Большинство фреймворков генерируют отчёты:
В Playwright включается запись:
trace: 'on'
video: 'retain-on-failure'
Это значительно упрощает диагностику ошибок drag-and-drop интерфейсов.
Решение:
boundingBoxТесты могут выполняться быстрее браузера.
Решение:
Некоторые элементы могут перекрывать draggable-элементы.
Решение:
pointer-eventsХорошая практика
data-testid)Плохая практика
Для полноценного покрытия интерфейса рекомендуется проверять:
Такой набор E2E тестов обеспечивает проверку всей функциональности интерфейсов, построенных на основе Shopify Draggable.