Free tools Windows power users keep installed
One-click scans. No signup required.
Use the event model your component actually consumes. Cypress has no universal built-in cy.drag() command. For a mouse-driven sortable widget, use trigger() with the button and coordinates the widget expects. For a file upload area, use selectFile() with action: 'drag-drop'. For native HTML5 drag-and-drop that ignores synthetic events, use a CDP-based helper such as cypress-real-dnd. In every case, assert the resulting application state with a fresh query.
Identify the kind of drag and drop you are testing
“Drag and drop” describes several different browser interactions. Selecting the wrong technique is the main reason a test fires events without moving anything.
Mouse-driven sortable widgets
jQuery UI Sortable, many custom list components, and older drag libraries listen for mouse events such as mousedown, mousemove, and mouseup. These handlers often inspect properties that a generic event does not provide automatically.
Native HTML5 draggable elements
An element with draggable="true" uses the browser’s drag event pipeline: dragstart, repeated dragover events, and drop. A synthetic mouse sequence may not create the native DataTransfer state that the application expects.
#1 Best Overall
File upload drop zones
A file drop zone is not the same as dragging one DOM element onto another. Cypress’s built-in selectFile() command simulates files entering the browser and can perform the drop action directly.
Prepare selectors and assertions before writing the drag
Use stable, user-facing selectors rather than CSS classes that describe presentation. Add attributes such as:
<li data-cy="card" data-id="alpha">Alpha</li>
<ul data-cy="done-list"></ul>
- Give the source and destination distinct
data-cy(or equivalent) attributes. - Expose a business-state assertion, such as the destination list containing the moved item, instead of asserting only that an event handler ran.
- Ensure both regions are visible and large enough for coordinates used by the test.
Implement a mouse drag for a sortable widget
Cypress documents this minimal sequence for a jQuery UI Sortable-style component:
it('moves the card to the destination', () => {
cy.get('[data-cy=draggable]')
.trigger('mousedown', {
which: 1,
pageX: 600,
pageY: 100,
})
cy.get('[data-cy=draggable]')
.trigger('mousemove', {
which: 1,
pageX: 600,
pageY: 600,
})
cy.get('[data-cy=draggable]').trigger('mouseup')
cy.get('[data-cy=destination]')
.find('[data-id="alpha"]')
.should('be.visible')
})
Why the properties matter
cy.trigger() is a low-level utility. Cypress deliberately makes few assumptions because any event type can be dispatched and the receiving handler decides which properties are required. In the documented sortable example, which: 1 identifies the primary mouse button, while pageX and pageY let the widget calculate movement. A different library may require client coordinates, a particular event target, or several intermediate moves.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Adapt coordinates to the real elements
The example coordinates are illustrative. Keep the pointer inside the visible source first, then inside the target. You can derive coordinates from element geometry:
cy.get('[data-cy=draggable]').then(($source) => {
const source = $source[0].getBoundingClientRect()
const startX = source.left + source.width / 2
const startY = source.top + source.height / 2
cy.get('[data-cy=destination]').then(($target) => {
const target = $target[0].getBoundingClientRect()
const endX = target.left + target.width / 2
const endY = target.top + target.height / 2
cy.wrap($source).trigger('mousedown', {
which: 1,
clientX: startX,
clientY: startY,
pageX: startX + window.scrollX,
pageY: startY + window.scrollY,
})
cy.wrap($source).trigger('mousemove', {
which: 1,
clientX: endX,
clientY: endY,
pageX: endX + window.scrollX,
pageY: endY + window.scrollY,
})
cy.wrap($source).trigger('mouseup', { which: 1 })
})
})
Use the coordinate names your widget reads. If it listens on the document rather than the source element, trigger the move or release on the appropriate document-level subject instead of assuming the source receives every event.
Rank #2
Test a file upload drop zone with selectFile()
For an upload area, let Cypress simulate an operating-system file being dragged into the browser:
it('uploads a JSON file through the drop zone', () => {
cy.get('[data-cy=drop-zone]').selectFile(
'cypress/fixtures/file.json',
{ action: 'drag-drop' }
)
cy.get('[data-cy=upload-status]')
.should('contain', 'file.json')
})
action: 'drag-drop' mimics dragging files from the operating system into the selected element. The subject may be any DOM element. If the application installs its listener on the document, use cy.get('body') (or another element that bubbles to the listener):
cy.get('body').selectFile('cypress/fixtures/image.png', {
action: 'drag-drop',
})
Use aliases, buffers, and file metadata
The command accepts a fixture path, an alias, a buffer, or a file object. Supplying metadata is useful when validation depends on the name or MIME type:
cy.fixture('avatar.png', 'base64').then((base64) => {
const contents = Cypress.Buffer.from(base64, 'base64')
cy.get('[data-cy=drop-zone]').selectFile({
contents,
fileName: 'avatar.png',
mimeType: 'image/png',
lastModified: Date.now(),
}, { action: 'drag-drop' })
})
Do not continue chaining commands that rely on the old subject after selectFile(). Re-query the interface before asserting:
cy.get('[data-cy=drop-zone]').selectFile(
'cypress/fixtures/file.json',
{ action: 'drag-drop' }
)
cy.get('[data-cy=file-list]').find('[data-file="file.json"]')
.should('exist')
When synthetic events do not activate HTML5 drag and drop
If your component depends on the browser-native pipeline, a mouse trigger() sequence can leave the application unchanged. A community helper listed by Cypress, cypress-real-dnd, uses the Chrome DevTools Protocol and triggers native dragstart, dragover, and drop events. It is intended for plain draggable="true" elements and libraries including react-dnd, Sortable.js, and dnd-kit.
Install and configure the helper according to its current package instructions, then keep the test focused on the business result. Pin the version in your project and verify it in the same browser and CI image used by your suite; CDP-based tooling is inherently tied to compatible Chromium tooling.
Recommended Free Tools
Rank #3
Community command alternatives
@4tw/cypress-drag-drop
This package adds drag and move commands. Install it as a development dependency:
npm install --save-dev @4tw/cypress-drag-drop
Import it from your Cypress commands or support file, then call:
cy.get('.sourceitem').drag('.targetitem')
Its options can be split into source and target, and the command yields true or false for the attempt. Treat that value as a diagnostic; still assert the resulting UI state.
cypress-dragndrop-kit
Cypress’s directory also lists this option for sortable lists, draggable elements, and custom components. Check its current maintenance, Cypress compatibility, and license before adopting it. Community packages change independently of Cypress, so lock and periodically review the version you use.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →A workflow that stays reliable in CI
- Classify the interaction. Decide whether it is a mouse widget, native HTML5 drag, or file drop.
- Choose stable selectors. Identify source, target, and the post-drop state with durable attributes.
- Start with the smallest mechanism. Use the documented event sequence or
selectFile()before adding a plugin. - Keep elements actionable. Scroll them into view, remove accidental overlays, and use visible coordinates. Let Cypress actionability checks run unless testing a deliberately hidden control.
- Assert after the interaction. Query the destination, order, count, status message, or uploaded file again after the command completes.
- Escalate only when necessary. If the app requires native events, evaluate a CDP helper and pin its version.
Common failures and precise fixes
“The event fired, but nothing moved”
The handler probably requires properties or a different event target. Inspect the component or library implementation, then add the required button, coordinate, data-transfer, or modifier properties. A generic trigger('mousemove') is not a universal drag.
“It works locally but fails in headless CI”
Coordinates may land outside the element at a different viewport, the element may be covered, or the browser may differ. Use deterministic viewport settings, calculate coordinates from getBoundingClientRect(), and preserve Cypress actionability checks. For native HTML5 interactions, confirm the CI browser supports the helper’s CDP version.
Rank #4
“The drop zone sees no file”
Use selectFile() with action: 'drag-drop', verify the fixture path, and target the element that actually receives the listener. Re-query after the command rather than chaining against the previous subject.
“The test is flaky around list order”
Wait on a meaningful application signal, such as the destination item appearing or a network-backed save completing, then assert the final order. Avoid arbitrary delays unless the widget has no observable readiness signal.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitches“A plugin command is undefined”
Confirm the package is installed, imported from the Cypress support/commands file loaded by your configuration, and compatible with your Cypress version. Restart the Cypress process after changing support files.
Performance, maintenance, and scope
Synthetic event tests are usually quick and deterministic when they match the widget’s contract. Native helpers add browser and CI coupling but can validate behavior closer to a real user gesture. File simulation avoids operating-system automation while still exercising the application’s drop handling. Keep one focused test for each interaction model, and avoid re-testing the same drag through several helpers unless browser support is a product requirement.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is to capture a page after automating a workflow—not to test the drag itself—ScreenshotNeo returns a screenshot or PDF from one request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response reports the result through X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
Use the API documentation at https://screenshotneo.com/docs/ for authentication and options. A basic call is:
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchcurl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://stripe.com
-o shot.webp
The same request in Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
And in Node.js:
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', bytes);
Every plan includes the full feature set, including full-page lazy-image capture, CSS-selector element shots, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper and page controls, custom CSS/JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, an OpenAPI specification, and familiar parameter names for easier migration. Pricing is 1,000 free shots per month without a card; paid plans start at $5 for 3,000 shots, with yearly billing providing two months free.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
Frequently Asked Questions
Does Cypress provide a built-in cy.drag() command?
No. Cypress provides low-level event utilities such as cy.trigger(), plus selectFile() for file simulation. Native drag behavior may require a community helper.
Should I assert that dragstart or mousemove fired?
Use event assertions only for diagnosing a handler. The durable test should assert the business result: new list order, destination membership, upload status, or another visible state.
Can selectFile() drop files on the document instead of one element?
Yes. Select a subject such as body when the application listens at document level, and pass action: ‘drag-drop’.
Why do coordinates pass locally but fail on another machine?
Viewport size, scrolling, overlays, and responsive layout can change pointer locations. Use deterministic viewports and calculate points from the current element rectangles.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

