VALIDATION LABDRAG & DROP

Mouse

Drag & Drop

Reorder a Kanban card across columns using mouse events and verify the destination column owns it.

Advanced

Scenario

Kanban, file managers, image gallery reorder — drag is everywhere and notoriously hard to test. Native `dragTo()` works for HTML5 drag, but custom React DnD libs need pointer events + `force: true`.

Todo

Design test pyramid

Doing

QA review
Perf budget

Done

Lint config

Live widget · interact freely

Manual test checklist

  • 1Drag QA card from Doing → Done — card moves and counts update
  • 2Drag back to Doing — card returns; no duplicates
  • 3Try dragging past the right edge — column should auto-scroll, not crash
  • 4On touch device, long-press to start drag — same UX?
  • 5Tab into a card — does it expose keyboard reorder (Space + arrows)?

Expected result

Dragging the 'QA review' card from Doing → Done updates the source/dest counts.

Automation challenge

Try `card.dragTo(target)` first. If your real app uses React DnD, fall back to `dispatchEvent('mousedown')` + intermediate `mousemove` + `mouseup` with `{ force: true }`. Assert via the column count, not the visual position.

Stable selectors

  • Card to drag[data-testid="dnd-card-qa"]
  • Source column[data-testid="dnd-col-doing"]
  • Target column[data-testid="dnd-col-done"]

Locator strategy

Three levels from simple IDs to scoped Playwright locators. IDs and names are easy to learn but are not always the best long-term choice when labels change or components repeat.

Use simple IDs and names to understand how locating elements works.

1
2
3
await page.goto('https://lab.hakdogan.com/practice/drag-drop');

await page.locator('#dnd-card-qa').dragTo(page.locator('#dnd-col-done'));

Avoid as primary strategies

XPath (unless there is no alternative), long CSS chains, Tailwind-style utility class selectors, generated or unstable IDs, and volatile framework internals break when layout, styling, or DOM structure shifts.

1
2
await page.locator('.w-full.rounded-xl.border.bg-blue-500').fill('demo@example.com');
await page.locator('//div[2]/form/div[1]/input').fill('demo@example.com');

Reference Playwright spec

drag-drop.spec.ts
ts
1
2
3
4
5
6
7
8
9
10
11
import { test, expect } from '@playwright/test';

test('kanban drag and drop', async ({ page }) => {
  await page.goto('https://lab.hakdogan.com/practice/drag-drop');

  const card = page.getByTestId('dnd-card-qa');
  const target = page.getByTestId('dnd-col-done');
  await card.dragTo(target);

  await expect(page.getByTestId('dnd-col-done')).toContainText('QA review');
});

Headed Test Playback

Simulated headed-browser flow — no real browser is launched.

Automation-style playback (Playwright-shaped logs). No real browser; no commands run on your machine.

Idledrag-drop.spec.ts
Failure demos
https://lab.hakdogan.com/practice/drag-drop
playwright · headed · chromium0.00s
# awaiting Run Test · terminal scrolls automatically
Steps
  1. Locating QA card
  2. Dragging to Done column
  3. Asserting card moved
StatusIdle
BrowserChromium
FrameworkPlaywright + TypeScript
Elapsed (live)--
Specdrag-drop.spec.ts