Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use the Playwright API that matches what the page actually expects: locator.dragTo() for moving one DOM element to another, locator.drop() for an external file or clipboard-style drop zone, and locator.setInputFiles() for a native <input type="file">. If clicking the upload control creates the input dynamically, wait for page.waitForEvent('filechooser') before clicking, then call fileChooser.setFiles().

Choose the right Playwright workflow

“Drag and drop a file” can describe three different browser behaviors. Identifying the behavior first prevents brittle pointer choreography and makes the test express the user-visible contract.

Page behavior Recommended API What it does
Reordering or moving an element already rendered in the page locator.dragTo(target) Performs a pointer drag from one DOM element to another.
A drop zone receives files from outside the page locator.drop({ files }) Dispatches drag events with a synthetic DataTransfer containing files.
A standard HTML file input exists locator.setInputFiles() Sets the input’s files directly, without simulating pointer movement.
The input is created only after a click page.waitForEvent('filechooser') plus fileChooser.setFiles() Catches the chooser event before the click triggers it.

The current Microsoft Playwright API documents locator.dragTo() from v1.18 and locator.drop() from v1.60. Use a current Playwright release when relying on drop().

Move an existing page element with dragTo()

Use this when both the source and destination are elements in the page—for example, moving a card between columns or reordering a list. Playwright hovers the source, presses the left mouse button, moves to the target, and releases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
Logitech M185 Compact Ambidextrous 2.4 GHz Wireless Mouse - Swift Grey
  • Compact Mouse: With a comfortable and contoured shape, this Logitech ambidextrous wireless mouse feels great in either right or left hand and is far superior to a touchpad
  • Durable and Reliable: This USB wireless mouse features a line-by-line scroll wheel, up to 1 year of battery life (2) thanks to a smart sleep mode function, and comes with the included AA battery
  • Universal Compatibility: Your Logitech mouse works with your Windows PC, Mac, or laptop, so no matter what type of computer you own today or buy tomorrow your mouse will be compatible
  • Plug and Play Simplicity: Just plug in the tiny nano USB receiver and start working in seconds with a strong, reliable connection to your wireless computer mouse up to 33 feet / 10 m (5)
  • Better than touchpad: Get more done by adding M185 to your laptop; according to a recent study, laptop users who chose this mouse over a touchpad were 50% more productive (3) and worked 30% faster (4)
import { test, expect } from '@playwright/test';

test('moves a task to Done', async ({ page }) => {
  await page.goto('https://example.test/board');

  const task = page.locator('#item-to-be-dragged');
  const doneColumn = page.locator('#item-to-be-dropped-at');

  await task.dragTo(doneColumn);
  await expect(doneColumn).toContainText('Release notes');
});

Prefer accessible roles, labels, or stable test identifiers over fragile CSS generated by a framework. For example:

await page.getByRole('listitem', { name: 'Release notes' })
  .dragTo(page.getByRole('region', { name: 'Done' }));

Choose precise drag coordinates when needed

By default, Playwright chooses points inside each element. If a handle, padding area, or nested control changes where the gesture must begin or end, provide sourcePosition and targetPosition:

await page.locator('.card').dragTo(page.locator('.lane'), {
  sourcePosition: { x: 12, y: 12 },
  targetPosition: { x: 80, y: 40 },
});

Coordinates are relative to the corresponding element. Use this only when the application requires a particular drag handle or drop area; otherwise the default points are less coupled to layout.

Drop an external file on a drop zone

Use locator.drop() when the application listens for native drag events on a drop area such as “Drag files here.” It accepts a file path or an in-memory file object. The in-memory form keeps the test self-contained:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { test, expect } from '@playwright/test';

test('uploads a text file through the drop zone', async ({ page }) => {
  await page.goto('https://example.test/uploads');

  await page.locator('#dropzone').drop({
    files: {
      name: 'note.txt',
      mimeType: 'text/plain',
      buffer: Buffer.from('hello'),
    },
  });

  await expect(page.getByText('note.txt')).toBeVisible();
});

For a real fixture on disk, pass a path:

await page.locator('#dropzone').drop({
  files: 'tests/fixtures/photo.jpg',
});

Relative paths are resolved from the process working directory. Keep fixtures in a predictable project directory or use an absolute path produced by your test setup.

Rank #2
Sale
Logitech G305 Lightspeed Wireless Gaming Mouse - Black
  • The next-generation optical HERO sensor delivers incredible performance and up to 10x the power efficiency over previous generations, with 400 IPS precision and up to 12,000 DPI sensitivity
  • Ultra-fast LIGHTSPEED wireless technology gives you a lag-free gaming experience, delivering incredible responsiveness and reliability with 1 ms report rate for competition-level performance
  • G305 wireless mouse boasts an incredible 250 hours of continuous gameplay on just 1 AA battery; switch to Endurance mode via Logitech G HUB software and extend battery life up to 9 months
  • Wireless does not have to mean heavy, G305 lightweight mouse provides high maneuverability coming in at only 3.4 oz thanks to efficient lightweight mechanical design and ultra-efficient battery usage
  • The durable, compact design with built-in nano receiver storage makes G305 not just a great portable desktop mouse, but also a great laptop travel companion, use with a gaming laptop and play anywhere

Send clipboard-like data

A drop target can receive data entries instead of files. Pass a MIME-type map:

await page.locator('#dropzone').drop({
  data: {
    'text/plain': 'hello world',
    'text/uri-list': 'https://example.com',
  },
});

You can combine files and data when the application expects both:

await page.locator('#dropzone').drop({
  files: {
    name: 'report.txt',
    mimeType: 'text/plain',
    buffer: Buffer.from('status: ready'),
  },
  data: {
    'text/plain': 'report.txt',
  },
});

Why a drop can be rejected

Playwright dispatches native dragenter, dragover, and drop events at the center of the target with a synthetic DataTransfer. If the target’s dragover handler does not call preventDefault(), Playwright considers the target to have rejected the operation, dispatches dragleave, and throws. Inspect the application’s event handler before replacing a valid test with a lower-level workaround.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Set a native file input directly

If the page contains <input type="file">, direct assignment is usually the most reliable test. Locate the control by its accessible label:

import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { test, expect } from '@playwright/test';

const here = path.dirname(fileURLToPath(import.meta.url));

test('selects a PDF in a native input', async ({ page }) => {
  await page.goto('https://example.test/uploads');
  await page.getByLabel('Upload file').setInputFiles(
    path.join(here, 'fixtures', 'myfile.pdf')
  );
  await expect(page.getByText('myfile.pdf')).toBeVisible();
});

The method accepts one path, an array for multiple files, a directory for an input using webkitdirectory, an empty array to clear the selection, or an in-memory payload:

Rank #3
Sale
Logitech M185 Compact Ambidextrous Wireless Mouse with Rubber Grips - Blue
  • Compact Mouse: With a comfortable and contoured shape, this Logitech ambidextrous wireless mouse feels great in either right or left hand and is far superior to a touchpad
  • Durable and Reliable: This USB wireless mouse features a line-by-line scroll wheel, up to 1 year of battery life (2) thanks to a smart sleep mode function, and comes with the included AA battery
  • Universal Compatibility: Your Logitech mouse works with your Windows PC, Mac, or laptop, so no matter what type of computer you own today or buy tomorrow your mouse will be compatible
  • Plug and Play Simplicity: Just plug in the tiny nano USB receiver and start working in seconds with a strong, reliable connection to your wireless computer mouse up to 33 feet / 10 m (5)
  • Better than touchpad: Get more done by adding M185 to your laptop; according to a recent study, laptop users who chose this mouse over a touchpad were 50% more productive (3) and worked 30% faster (4)
await page.getByLabel('Upload files').setInputFiles([
  'tests/fixtures/one.txt',
  'tests/fixtures/two.txt',
]);

await page.getByLabel('Upload file').setInputFiles({
  name: 'file.txt',
  mimeType: 'text/plain',
  buffer: Buffer.from('this is a test'),
});

await page.getByLabel('Upload file').setInputFiles([]);

When the input is inside a label, Playwright can target the associated control through that label. Use locator-based setInputFiles(); the selector-based Page API is discouraged in the current documentation.

Handle a dynamically created file chooser

Some upload buttons create an invisible file input only after the user clicks. Start waiting for the event before triggering the click; otherwise the chooser can open and disappear before your test begins listening.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import path from 'node:path';
import { test, expect } from '@playwright/test';

test('uploads through a dynamic chooser', async ({ page }) => {
  await page.goto('https://example.test/uploads');

  const fileChooserPromise = page.waitForEvent('filechooser');
  await page.getByRole('button', { name: 'Upload file' }).click();
  const fileChooser = await fileChooserPromise;

  await fileChooser.setFiles(path.join(process.cwd(), 'fixtures', 'myfile.pdf'));
  await expect(page.getByText('myfile.pdf')).toBeVisible();
});

The same chooser accepts an array, an in-memory object, or an empty array to clear the selection:

await fileChooser.setFiles({
  name: 'avatar.png',
  mimeType: 'image/png',
  buffer: Buffer.from(imageBytes),
});

Why the order matters

The file chooser event may occur during click(). Creating the promise first registers the listener before the event can fire, while await page.waitForEvent('filechooser')
await button.click()
waits forever because the click has not happened yet.

Assert the upload, not just the gesture

A successful API call does not prove that the application accepted the file. After the interaction, assert the observable result that matters: a filename, preview, progress completion, validation message, request, or server-side state.

Rank #4
Amazon Basics 3-Button USB Wired Mouse with Responsive Tracking, Plug & Play, Compatible with Windows and Mac, Black
  • Computer mouse for easily navigating a computer interface; click, scroll, and more
  • USB-A wired connection; if existing device only supports USB-C, an additional adapter will be required
  • High-definition (1000 dpi) optical tracking ensures responsive cursor control for precise tracking and easy text selection
  • 3 buttons offer effortless fingertip control
  • Plug-and-go ready for instant use
  • Use a locator assertion for an uploaded filename or preview.
  • Assert an expected validation error for an unsupported type or size.
  • For asynchronous uploads, wait for the status to change from “Uploading” to “Complete.”
  • When the UI is ambiguous, observe the expected network response and then verify the rendered state.

Keep the test independent of implementation details: the same assertion should pass whether the product uses a visible input, a hidden input, or a drop-zone library.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting drag-and-drop uploads

drop() throws that the target rejected the drop

Check the target’s dragover listener. It must call preventDefault() for the browser to allow a drop. Confirm that the locator resolves to the actual drop zone rather than a child overlay, then retry against the element that owns the handler.

The chooser event times out

The clicked control may not open a native chooser, may be disabled, or may require a different locator. Verify the click works in a headed run and register the event promise before the click. If a real input is present, bypass the chooser with locator.setInputFiles().

The file name appears but the application does nothing

Setting the input only changes the selected files. The application still needs its normal change handling and upload request. Wait for the UI’s completion state or expected response, and ensure the test file meets the page’s accepted type and size rules.

dragTo() moves the wrong thing

Use a locator for the draggable element or its documented handle, not a container that contains several draggable children. Supply sourcePosition when the app starts a drag only from a handle, and targetPosition when the destination has nested drop regions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Sale
Acer Wireless Mouse for Laptop, 2.4GHz Computer Mouse 3 Adjustable 1600 DPI
  • 【Plug and Play for Home/Office/School】The wireless computer mouse features 2.4GHz connectivity, delivering a stable, interference-free connection up to 32ft. Designed for 𝐦𝐞𝐝𝐢𝐮𝐦 𝐭𝐨 𝐥𝐚𝐫𝐠𝐞 𝐬𝐢𝐳𝐞𝐝 𝐡𝐚𝐧𝐝𝐬, it ensures comfortable use all day. Simply plug in the USB-A receiver for instant pairing—no drivers needed. 📌📌 If the mouse isn’t suitable, place the USB receiver in the battery compartment and return both.
  • 【3 Levels Adjustable DPI】This travel USB mouse offers 3 adjustable DPI settings (800, 1200, 1600), allowing you to customize sensitivity for precise design work. Effortlessly switch to match your task and elevate your productivity. 📌 Please remove the film at the bottom of the mouse before use.
  • 【Effortless Browsing】Equipped with forward and backward buttons, this computer mice streamlines your workflow, making it easy to navigate through web pages and files with a simple click. 📌Side button does not work on Mac.
  • 【Visible Indicator Light】 The pc mouse features a visual indicator for DPI levels and low battery alerts. The red light flashes once for 800 DPI, twice for 1200 DPI, and three times for 1600 DPI. When the battery level is below 10%, the light flashes red until the mouse is completely out of power.
  • 【Click to Wake】With smart sleep mode, it saves power by standby after 10 inactive minutes, just 2-3 clicks to wake. This efficient design delivers 3x longer battery life than motion-wake mice. Engineered for durability, its buttons and scroll wheel are tested for 10 million clicks, ensuring long-term reliability and consistent performance.

Tests are flaky in responsive layouts

Give the source and target stable, visible geometry before dragging. Wait for the relevant locator to be visible and enabled, avoid changing viewport size mid-gesture, and prefer direct file assignment for native inputs. Do not add arbitrary delays when an assertion or network wait can express readiness.

The path cannot be found

Relative paths use the current working directory, which can differ between local and CI runs. Resolve fixtures from the test file or project root, and log the resolved path when diagnosing CI failures.

Performance, reliability, and test design

  • Prefer the least simulated mechanism. Use setInputFiles() for native inputs, drop() for actual external-drop behavior, and dragTo() for element movement.
  • Use in-memory files for small deterministic cases. They avoid fixture-path and encoding problems; use disk files when testing large assets or directory uploads.
  • Test negative paths deliberately. Include unsupported MIME types, empty selections, multiple files, and rejected drops when those rules are part of the product.
  • Keep locators accessible. Labels and roles survive visual redesigns better than generated class names.
  • Pin the behavior to your Playwright version. drop() requires v1.60 or later according to the current API reference; dragTo() was added in v1.18.

Or skip the browser setup

If your goal is a clean image or PDF of a page rather than testing an upload interaction, ScreenshotNeo makes one request to capture it. 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. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.

For a screenshot, use the documented API parameters:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

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 body = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', body));

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)

See the ScreenshotNeo documentation for output formats and options. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to begin.

Frequently asked questions

Frequently Asked Questions

Can I use dragTo() to upload a local file?

No. dragTo() moves one page element to another. Use drop({ files }) for a file drop zone or setInputFiles() for a native file input.

Does drop() open the operating-system file picker?

No. It supplies a synthetic DataTransfer to the page. Use a file chooser event only when the application opens a native chooser after a click.

How do I clear a selected file?

Call locator.setInputFiles([]) or fileChooser.setFiles([]).

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Quick Recap

SaleBestseller No. 1
Logitech M185 Compact Ambidextrous 2.4 GHz Wireless Mouse - Swift Grey
Logitech M185 Compact Ambidextrous 2.4 GHz Wireless Mouse - Swift Grey
Product carbon footprint: 3.97 kg CO2e; Contoured shape: Gives you more comfort and control
$14.90
SaleBestseller No. 3
Bestseller No. 4
Amazon Basics 3-Button USB Wired Mouse with Responsive Tracking, Plug & Play, Compatible with Windows and Mac, Black
Amazon Basics 3-Button USB Wired Mouse with Responsive Tracking, Plug & Play, Compatible with Windows and Mac, Black
Computer mouse for easily navigating a computer interface; click, scroll, and more; 3 buttons offer effortless fingertip control
$9.70

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.