Resource library

QA How-To

How to Test drag and drop in Cypress (2026)

Learn cypress how to test drag and drop for HTML5 DataTransfer flows, pointer sortables, file dropzones with selectFile, order assertions, and flake control.

22 min read | 2,408 words

TL;DR

Match events to the library: HTML5 uses trigger plus DataTransfer; pointer DnD uses mouse/pointer sequences; files use selectFile drag-drop. Always assert settled order or location and any persistence request after the drop.

Key Takeaways

  • Classify HTML5 versus pointer/mouse DnD before writing helpers.
  • HTML5 flows typically trigger dragstart, dragover, and drop with one DataTransfer instance.
  • Pointer sortables need mousedown/mousemove/mouseup with realistic coordinates.
  • File dropzones should use selectFile with action drag-drop when possible.
  • Assert final order and PATCH/POST persistence, not only that events fired.
  • Prefer keyboard reordering when the widget supports it for stability and a11y.
  • Keep exotic DnD cases in unit or component tests; E2E proves critical paths.

The workable answer to cypress how to test drag and drop is to identify the library and event model first. HTML5 drag and drop uses dragstart, dragover, drop, and a DataTransfer payload. Many modern UIs use pointer or mouse move sequences instead (react-dnd, dnd-kit, beautiful-dnd variants, sortable lists). Cypress can drive both, but the commands differ, and a single generic helper rarely fits every library.

This guide covers HTML5 DnD with trigger, DataTransfer basics, pointer-based sortables, asserting order and API updates, accessibility keyboard reordering when supported, flake control, and when to push coverage down to component tests.

TL;DR

Implementation Cypress approach Assert
HTML5 draggable trigger dragstart/dragover/drop + dataTransfer order / dropzone state
Mouse/pointer DnD mousedown, mousemove, mouseup (or pointer events) order / positions
File drop selectFile with drag-drop mode or drop fixture uploaded file handling
Keyboard reorder space/arrows/escape per widget contract order without pointer

Always verify the application state (list order, request body), not only that events fired.

1. Cypress How to Test Drag and Drop: Identify the Event Model

Inspect listeners in DevTools or the component source.

<!-- HTML5 -->
<li draggable="true" data-cy="task-1">Write tests</li>
<div data-cy="done-column" ondrop="...">Done</div>

If draggable="true" and the code reads event.dataTransfer, start with HTML5 triggers. If the library documents pointer sensors, use mouse or pointer sequences. If the product supports keyboard reordering, add that path because it is often more stable.

Spike test idea: log events.

cy.visit('/board')
cy.window().then((win) => {
  ;['dragstart', 'dragover', 'drop', 'dragend'].forEach((type) => {
    win.document.addEventListener(type, (e) => cy.log(type), true)
  })
})

Do not ship event spam logs; use them only while classifying the control.

2. HTML5 Drag and Drop With trigger and DataTransfer

A widely used pattern constructs a DataTransfer and fires the sequence on source and target.

function html5Drag(source: string, target: string) {
  const dataTransfer = new DataTransfer()

  cy.get(source).trigger('dragstart', { dataTransfer })
  cy.get(target).trigger('dragover', { dataTransfer })
  cy.get(target).trigger('drop', { dataTransfer })
  cy.get(source).trigger('dragend', { dataTransfer })
}

it('moves a card to the Done column', () => {
  cy.visit('/board/proj_1')
  html5Drag('[data-cy=task-42]', '[data-cy=column-done]')
  cy.get('[data-cy=column-done]').find('[data-cy=task-42]').should('exist')
})

Notes:

  1. The same dataTransfer instance should flow through the sequence when the app relies on it.
  2. Some apps need dragenter as well as dragover.
  3. drop may require preventDefault behavior in the app; if the app listens correctly, Cypress still needs the event to reach it.
  4. Coordinates sometimes matter for nested drop zones. You can pass clientX and clientY in the event options when required.
cy.get(target).trigger('drop', {
  dataTransfer,
  clientX: 200,
  clientY: 40,
})

If HTML5 events do nothing, the library may not use HTML5 DnD at all. Switch models instead of adding more random events.

3. Pointer and Mouse Based Sortable Lists

Libraries that track pointer movement often need a down-move-up sequence with intermediate coordinates.

function pointerDragByOffset(
  selector: string,
  deltaX: number,
  deltaY: number,
) {
  cy.get(selector)
    .trigger('mousedown', { which: 1, button: 0 })
    .trigger('mousemove', { clientX: 0, clientY: 0 })
    .trigger('mousemove', {
      clientX: deltaX,
      clientY: deltaY,
      force: true,
    })
    .trigger('mouseup', { force: true })
}

it('reorders tasks in a list', () => {
  cy.visit('/tasks')
  cy.get('[data-cy=task-row]').then(($rows) => {
    const order = [...$rows].map((el) => el.getAttribute('data-id'))
    expect(order[0]).to.eq('t1')
  })

  // drag first item downward; offsets are app-specific
  cy.get('[data-cy=task-row][data-id=t1]').then(($el) => {
    const rect = $el[0].getBoundingClientRect()
    cy.wrap($el)
      .trigger('mousedown', {
        button: 0,
        clientX: rect.x + 10,
        clientY: rect.y + 10,
      })
      .trigger('mousemove', {
        clientX: rect.x + 10,
        clientY: rect.y + 80,
        force: true,
      })
      .trigger('mouseup', { force: true })
  })

  cy.get('[data-cy=task-row]').eq(1).should('have.attr', 'data-id', 't1')
})

Offsets are brittle across themes and densities. Prefer dropping onto a target element with explicit coordinates derived from that target's getBoundingClientRect() rather than magic numbers when possible.

Some ecosystems recommend the cypress-real-events plugin for realMouseDown style interactions closer to OS events. Only use it if your project installs and standardizes it. Core Cypress trigger remains the baseline documented approach.

4. Assert Order, Drop Zones, and Server Persistence

UI movement without persistence is only half the feature.

it('persists column moves', () => {
  cy.intercept('PATCH', '/api/tasks/42').as('moveTask')
  cy.visit('/board/proj_1')

  html5Drag('[data-cy=task-42]', '[data-cy=column-done]')

  cy.wait('@moveTask').its('request.body').should('deep.include', {
    columnId: 'done',
  })
  cy.get('[data-cy=column-done] [data-cy=task-42]').should('exist')

  cy.reload()
  cy.get('[data-cy=column-done] [data-cy=task-42]').should('exist')
})

Order assertion helper:

const expectTaskOrder = (ids: string[]) => {
  cy.get('[data-cy=task-row]').should(($rows) => {
    const actual = [...$rows].map((el) => el.getAttribute('data-id'))
    expect(actual).to.deep.eq(ids)
  })
}

Use retryable .should callbacks so the assertion waits for React state commits after drop.

5. File Drag and Drop Onto Upload Zones

File drop zones are a special case. Prefer Cypress selectFile with drag-drop behavior when attaching files, which is more reliable than synthesizing full OS file drag for many apps.

it('uploads a CSV via the dropzone', () => {
  cy.intercept('POST', '/api/imports').as('importCsv')
  cy.visit('/imports')

  cy.get('[data-cy=import-dropzone]').selectFile(
    'cypress/fixtures/customers.csv',
    { action: 'drag-drop' },
  )

  cy.wait('@importCsv').its('response.statusCode').should('eq', 201)
  cy.get('[data-cy=import-status]').should('contain', 'Upload complete')
})

This uses a supported Cypress file selection API rather than reinventing DataTransfer file lists manually. For broader file workflows, see Cypress file upload.

If you must construct a drop with files manually, keep it in one documented helper and test it thoroughly; browser security around file inputs is strict.

6. Cypress How to Test Drag and Drop Cancellations and Invalid Targets

Negative paths matter:

  1. Drop outside any zone: item returns.
  2. Drop on a disabled column: rejected.
  3. Drag ends with Escape if the library supports cancel.
  4. Unauthorized move shows an error toast.
it('rejects moves into a locked column', () => {
  cy.intercept('PATCH', '/api/tasks/42', {
    statusCode: 409,
    body: { message: 'Column locked' },
  }).as('moveTask')

  cy.visit('/board/proj_1')
  html5Drag('[data-cy=task-42]', '[data-cy=column-locked]')
  cy.wait('@moveTask')
  cy.get('[data-cy=board-error]').should('contain', 'Column locked')
  cy.get('[data-cy=column-todo] [data-cy=task-42]').should('exist')
})

Optimistic UI bugs show as brief correct placement then snap-back. Assert final settled state with retryable queries, not a single immediate check without retry.

7. Keyboard Reordering and Accessibility

Many design systems support keyboard reordering: focus handle, space to lift, arrows to move, space to drop.

it('reorders with the keyboard when supported', () => {
  cy.visit('/tasks')
  cy.get('[data-cy=drag-handle][data-id=t1]').focus().type(' ')
  cy.get('[data-cy=drag-handle][data-id=t1]').type('{downarrow}{downarrow}')
  cy.get('[data-cy=drag-handle][data-id=t1]').type(' ')
  expectTaskOrder(['t2', 't3', 't1', 't4'])
})

Exact keys depend on the component. Read the library accessibility docs. Keyboard paths are often more deterministic than pixel drags and better match inclusive UX requirements.

If keyboard reordering is unsupported, document that limitation and cover pointer paths plus unit tests on reorder reducers.

8. Nested Boards, Auto-Scroll Containers, and Iframes

Complex boards auto-scroll while dragging near edges. Cypress synthetic events may not trigger the same auto-scroll timers. Strategies:

  1. Ensure source and target are both visible before drag without needing auto-scroll.
  2. Pre-scroll the container so the target is in view.
  3. Limit E2E to non-scrolling happy paths; unit test auto-scroll math separately.
cy.get('[data-cy=board-scroller]').scrollTo('right')
cy.get('[data-cy=column-later]').should('be.visible')
html5Drag('[data-cy=task-42]', '[data-cy=column-later]')

Iframes require cy.iframe patterns or cy.get within the frame context your project uses. Do not assume board DnD helpers work cross-frame without an iframe strategy. See Cypress iframe handling if the board is embedded.

9. Flake Control for Drag and Drop Tests

DnD tests are flake magnets. Reduce risk:

  1. Deterministic data: seed exact card IDs and column IDs.
  2. Disable animations in test builds when the app supports a reduced-motion or test flag.
  3. Wait for board ready markers before dragging.
  4. Avoid parallel DOM mutations (live websockets reshuffling cards). Intercept or stub realtime updates.
  5. One drag per test when debugging; multi-drag scenarios come later.
  6. Retryable order assertions after drop.
  7. Prefer keyboard if equivalent.
cy.intercept('GET', '/api/boards/proj_1', { fixture: 'board.json' }).as(
  'board',
)
cy.visit('/board/proj_1')
cy.wait('@board')
cy.get('[data-cy=board-ready]').should('exist')

If a DnD test fails only in CI, compare viewports and animation timing. Record a video and open the failure screenshot (screenshot on failure).

10. Component Tests Versus End-to-End for DnD

Not every drag interaction belongs in full E2E.

Layer Good DnD coverage
Unit reorder pure functions, boundary rules
Component (Cypress CT) item moves within a mocked board
E2E critical path move + API + reload

Cypress component testing can mount a board with fixtures and assert reorder without authentication and network noise. See Cypress component testing. Keep one or two E2E proofs for the real page shell and persistence.

11. Library-Specific Tips Without Inventing APIs

Guidance that stays honest across versions:

  1. Read the library test docs for recommended event sequences.
  2. Prefer public test IDs on handles and droppables.
  3. Do not rely on private class names from virtual lists.
  4. Match sensors: if the library uses Pointer events only, triggering drag HTML5 events will not work.
  5. Check touch-action and handle elements: many UIs require dragging from a handle, not the whole card.
// drag from handle, not the whole row, when required
html5Drag('[data-cy=task-42] [data-cy=drag-handle]', '[data-cy=column-done]')

If a popular plugin is standard in your org, document it in the repo rather than each article inventing install steps that may not match your package versions.

12. Full Example: Board Move Spec Skeleton

describe('kanban drag and drop', () => {
  const dataTransfer = () => new DataTransfer()

  const drag = (from: string, to: string) => {
    const dt = dataTransfer()
    cy.get(from).trigger('dragstart', { dataTransfer: dt })
    cy.get(to).trigger('dragenter', { dataTransfer: dt })
    cy.get(to).trigger('dragover', { dataTransfer: dt })
    cy.get(to).trigger('drop', { dataTransfer: dt })
    cy.get(from).trigger('dragend', { dataTransfer: dt })
  }

  beforeEach(() => {
    cy.intercept('GET', '/api/boards/b1', { fixture: 'board-b1.json' }).as(
      'board',
    )
    cy.intercept('PATCH', '/api/tasks/*').as('patchTask')
    cy.visit('/boards/b1')
    cy.wait('@board')
  })

  it('moves task 42 from todo to done and persists', () => {
    cy.get('[data-cy=column-todo] [data-cy=task-42]').should('exist')
    drag('[data-cy=task-42]', '[data-cy=column-done]')
    cy.wait('@patchTask')
      .its('request.body.columnId')
      .should('eq', 'done')
    cy.get('[data-cy=column-done] [data-cy=task-42]').should('exist')
  })
})

Adapt event names if your spike shows pointer sensors instead of HTML5.

13. Multi-Select Drag, Lasso Selection, and Bulk Moves

Boards sometimes allow selecting multiple cards and dragging them together. E2E coverage should stay thin but real.

it('moves two selected cards to Review', () => {
  cy.intercept('POST', '/api/tasks/bulk-move').as('bulkMove')
  cy.visit('/board/b1')

  cy.get('[data-cy=task-1]').click()
  cy.get('[data-cy=task-2]').click({ shiftKey: true })
  // use the project's multi-drag gesture; HTML5 or pointer based
  cy.get('[data-cy=task-1]').trigger('dragstart', {
    dataTransfer: new DataTransfer(),
  })
  cy.get('[data-cy=column-review]').trigger('drop', {
    dataTransfer: new DataTransfer(),
  })

  cy.wait('@bulkMove').its('request.body.taskIds').should('deep.equal', [
    '1',
    '2',
  ])
})

Note that multi-drag often needs the same DataTransfer instance across events; adapt the helper accordingly. If multi-select uses a checkbox mode rather than shift-click, drive the checkboxes explicitly. Do not invent shift behavior the app lacks.

Lasso selection (marquee) is highly pixel dependent and usually a poor E2E investment. Prefer unit tests for selection geometry and one smoke if product leadership insists.

14. Touch Emulation and Mobile Boards

Mobile browsers fire touch events. Cypress can send touch sequences in some configurations, but support and fidelity vary by browser. Practical strategy:

  1. Cover desktop pointer/HTML5 paths thoroughly.
  2. Smoke the mobile board layout with viewport + button alternatives ("Move to..." menus).
  3. If the product only offers drag on mobile, spike touch events early and standardize one helper.
cy.viewport('iphone-x')
cy.visit('/board/b1')
cy.get('[data-cy=task-1] [data-cy=move-menu]').click()
cy.get('[data-cy=move-to-done]').click()
cy.get('[data-cy=column-done] [data-cy=task-1]').should('exist')

Many teams intentionally expose a non-drag move affordance on small screens because drag is awkward. Test the affordance users actually get.

15. Permission Boundaries and Realtime Collaboration

Drag and drop interacts poorly with authorization bugs. A viewer role should not be able to reorder; the UI may hide handles or the API may reject moves.

it('does not expose drag handles to viewers', () => {
  cy.intercept('GET', '/api/boards/b1', { fixture: 'board-viewer.json' })
  cy.visit('/board/b1')
  cy.get('[data-cy=drag-handle]').should('not.exist')
})

Realtime collaboration can move cards under the cursor mid-test. Stub websocket channels or use a single-user fixture mode in test environments. Otherwise DnD tests will fail with mysterious order mismatches.

When conflicts return 409, assert the board re-syncs to server order:

cy.intercept('PATCH', '/api/tasks/1', {
  statusCode: 409,
  body: { message: 'Conflict' },
}).as('patch')
// perform drag
cy.wait('@patch')
cy.get('[data-cy=board-error]').should('contain', 'Conflict')
// board refetches
cy.get('[data-cy=task-1]').parent().should('have.attr', 'data-column', 'todo')

16. Building a Maintainable DnD Toolkit in the Repo

Centralize helpers per event family, not one mythical dragAndDrop that guesses.

// cypress/support/dnd.ts
export const html5DragDrop = (source: string, target: string) => {
  const dt = new DataTransfer()
  cy.get(source).trigger('dragstart', { dataTransfer: dt })
  cy.get(target).trigger('dragenter', { dataTransfer: dt })
  cy.get(target).trigger('dragover', { dataTransfer: dt })
  cy.get(target).trigger('drop', { dataTransfer: dt })
  cy.get(source).trigger('dragend', { dataTransfer: dt })
}

export const expectOrder = (rowSelector: string, ids: string[]) => {
  cy.get(rowSelector).should(($rows) => {
    expect([...$rows].map((r) => r.getAttribute('data-id'))).to.deep.eq(ids)
  })
}

Document which product surfaces use which helper. When a library upgrade changes sensors, update one file. Add a short ADR note if you adopt cypress-real-events so future engineers know why both styles exist.

Code review standards for DnD tests:

  1. Fixture-based board data required.
  2. Network assertion on any move that hits an API.
  3. No unexplained magic pixel constants without a comment referencing layout.
  4. Prefer keyboard path when equivalent.
  5. Failures must be diagnosable from screenshot of the board after drop.

17. Migrating From Brittle Coordinate Scripts to Stable Contracts

Older suites often encode drag paths as long chains of absolute coordinates recorded once on a laptop. Those scripts rot when spacing, fonts, or sidebar width change.

Migration steps:

  1. Replace absolute page coordinates with rects from source and target elements.
  2. Introduce data-cy on handles and droppables.
  3. Assert order arrays rather than pixel positions after drop.
  4. Move pure reorder logic tests out of E2E.
  5. Delete redundant multi-step drag demos that never assert persistence.
// before: magic numbers
// .trigger('mousemove', { clientX: 812, clientY: 366 })

// after: target-relative
cy.get('[data-cy=column-done]').then(($to) => {
  const r = $to[0].getBoundingClientRect()
  cy.get('[data-cy=task-1]').trigger('mousemove', {
    clientX: r.left + r.width / 2,
    clientY: r.top + 24,
    force: true,
  })
})

Track flake rate before and after migration. The goal is fewer retries and clearer failures, not cleverer event synthesis.

18. Checklist Before Declaring DnD Coverage Done

Use this exit checklist for a board or sortable feature:

  1. Event model documented (HTML5, pointer, or keyboard).
  2. Happy-path move with network assertion and reload persistence.
  3. Invalid target or permission denial path.
  4. Keyboard reorder if supported.
  5. File drop covered with selectFile when uploads exist.
  6. Realtime/web socket noise disabled in test env.
  7. Helpers live in support/dnd and are reused.
  8. Component or unit tests own edge reorder rules.
  9. CI videos enabled long enough to debug one failure.
  10. Known limitations written next to the spec (auto-scroll, mobile touch).

When those boxes are checked, you have professional coverage for cypress how to test drag and drop without an unbounded sea of geometric edge cases. Ship the checklist with the feature so QA, SDET, and frontend share the same definition of done.

Production Readiness Notes for Drag and Drop Suites

Release managers should know which board flows are under automation. Publish a one-page matrix: card move, bulk move, keyboard reorder, file import dropzone, viewer permissions. Gaps are fine when explicit; silent gaps become assumed coverage.

Load tests and DnD E2E should not share the same board fixtures in parallel jobs. Reservation of a test project id or full network stubbing prevents confusing cross-talk. If a flake appears only on Friday night deploys, check whether demo data refresh jobs reorder cards under the test user.

When upgrading the DnD library major version, run the board specs on a dedicated branch before merging the upgrade. Sensor defaults change more often than public docs emphasize. Budget an hour for helper adjustments rather than discovering breakage on main.

Capture a short Loom or internal note once showing a green drag test in headed mode. New hires copy patterns they can see. Written helpers help, but a thirty-second visual of the card moving and the assertion passing anchors the approach for cypress how to test drag and drop far better than abstract event lists alone.

Interview Questions and Answers

Q: How do you automate drag and drop in Cypress?

I identify whether the UI uses HTML5 DnD or pointer/mouse sensors. For HTML5 I trigger dragstart, dragover, and drop with a shared DataTransfer. For pointer-based lists I dispatch mousedown/mousemove/mouseup with coordinates from element rects. I always assert final order or location plus any persistence request.

Q: Why did my draganddrop helper work in one app but not another?

Because the event models differed. HTML5 helpers will not drive a pointer-sensor library. I reclassify the control and match events to the library.

Q: How do you test file dropzones?

I prefer cy.get(dropzone).selectFile(path, { action: 'drag-drop' }), then assert the upload request and UI status. It is more reliable than hand-built file DataTransfer sequences for many applications.

Q: How do you reduce flakiness in DnD tests?

Deterministic fixtures, wait for ready state, disable animations when possible, pre-scroll targets into view, use retryable order assertions, and avoid live websocket reshuffles during the test.

Q: Should drag and drop be tested only in E2E?

No. Reorder rules belong in unit tests, interaction mechanics fit component tests, and E2E should prove a critical path with API persistence. Overloading E2E with every drag edge case creates slow flakes.

Q: How do you assert list order after a drag?

I map data-id attributes of the rows inside a retryable .should callback and compare to the expected array. That waits for the UI to settle after asynchronous state updates.

Q: What role does keyboard reordering play?

When supported, it is both an accessibility requirement and often a more stable automation path than pixel-perfect pointer moves. I cover it for design-system sortables that document keyboard interaction.

Common Mistakes

  • Using HTML5 events against a pointer-only DnD library.
  • Asserting only that dragstart fired, not the final business state.
  • Magic pixel offsets that break at different viewports.
  • Dragging before the board finished loading fixtures.
  • Forgetting dragover or preventDefault requirements for HTML5 drop targets.
  • Ignoring persistence and reload assertions.
  • Parallel websocket updates reshuffling cards mid-test.
  • Trying to drag from the card body when only the handle is draggable.
  • Testing every micro-interaction in full E2E instead of component tests.
  • Hand-rolling fragile file DataTransfer code when selectFile drag-drop works.

Conclusion

For cypress how to test drag and drop, match your automation to the real event model, drive a minimal deterministic scenario, and assert order plus persistence. Use selectFile for file dropzones, prefer keyboard reordering when the widget supports it, and keep exotic DnD edge cases closer to unit and component layers.

Next step: spike one real drag in your app with event logging, implement a single helper for that event model, and lock a critical path test that waits on the PATCH and verifies the item after reload.

Interview Questions and Answers

How do you implement drag and drop automation in Cypress?

I identify the event model, then either fire HTML5 drag events with DataTransfer or pointer/mouse sequences with coordinates. I assert final placement and any API persistence, and I seed deterministic board data.

HTML5 versus mouse events: how do you choose?

If the app uses draggable and dataTransfer, I use HTML5 triggers. If the library documents pointer sensors, I use mouse or pointer events. Using the wrong family is the top reason helpers silently fail.

How do you test a kanban move end to end?

I fixture the board, drag a known card to another column, wait for the PATCH, assert the request body column id, assert DOM location, and reload to confirm persistence.

How do you automate file dropzones?

I call selectFile on the dropzone with action drag-drop and a fixture path, then assert the upload network call and success UI. I avoid OS-level file dialog automation.

What assertions matter after a drop?

Settled order or parent column, visible feedback, network payload, and often reload persistence. Event-fired-only checks are too weak.

How can keyboard interaction help DnD testing?

When the widget supports lift-and-move keys, keyboard reorder is more stable and validates accessibility. I still keep one pointer path if pointer is the primary UX.

How do you debug a flaky drag test in CI only?

I compare viewports, watch videos and failure screenshots, freeze realtime updates, pre-scroll targets into view, and simplify to a single drag with fixture data to isolate timing issues.

Frequently Asked Questions

How do I do drag and drop in Cypress?

For HTML5, trigger dragstart on the source and dragover/drop on the target with a shared DataTransfer. For pointer-based libraries, dispatch mousedown, mousemove, and mouseup using element coordinates. Assert the final UI state.

Why does my HTML5 drag helper not move items?

The UI may use pointer sensors instead of HTML5 DnD, the drop target may need dragenter, or dragging must start from a handle. Reclassify the event model and adjust the sequence.

How do I test file drag and drop uploads?

Use cy.get(dropzone).selectFile('path', { action: 'drag-drop' }), then assert the upload request and status UI. This is usually more reliable than manual file DataTransfer construction.

How do I assert list order after reordering?

Collect data-id attributes inside a retryable should callback and compare to the expected array so Cypress waits for asynchronous state updates after drop.

Are drag and drop tests usually flaky?

They can be. Reduce flake with fixtures, ready markers, reduced motion, visible targets, and fewer magic offsets. Prefer keyboard reorder when available.

Should I test every drag edge case in E2E?

No. Cover critical moves with persistence in E2E and push boundary rules and micro-interactions to unit or Cypress component tests.

Can Cypress drag across iframes easily?

Cross-frame DnD needs an explicit iframe strategy and is more complex. Prefer testing the board inside its primary frame or isolating the component.

Related Guides