Recommended Free Tools
Use locator.setInputFiles() to upload files in Playwright. If the file input appears only after a click, wait for the filechooser event before clicking and then call fileChooser.setFiles(). For downloads, register page.waitForEvent('download') before the action that starts the transfer, then call download.saveAs() before the browser context closes.
This guide shows reliable JavaScript/Node.js patterns, Python equivalents, multi-file and in-memory uploads, download assertions, persistence, failure handling, and the edge cases that commonly make tests flaky.
Upload a file with a visible file input
When the page contains an <input type="file">, address it with a locator and set the files directly. Playwright does not require you to open the operating-system file dialog.
import { test, expect } from '@playwright/test';
test('uploads a PDF', async ({ page }) => {
await page.goto('https://example.test/upload');
await page.locator('input[type="file"]').setInputFiles('fixtures/report.pdf');
await page.getByRole('button', { name: 'Upload' }).click();
await expect(page.getByText('report.pdf')).toBeVisible();
});
A relative path is resolved from the process current working directory. In a Playwright Test project, keeping fixtures in a known directory (such as fixtures/) avoids differences between local runs and CI working directories. You can also pass an absolute path.
#1 Best Overall
- Easily store and access 2TB to content on the go with the Seagate Portable Drive, a USB external hard drive
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
Target the input by a stable locator
Prefer an accessible label, test id, or a specific input over a broad CSS selector when the page has more than one file field:
await page.getByLabel('Profile picture').setInputFiles('fixtures/avatar.png');
// or
await page.getByTestId('attachment-input').setInputFiles('fixtures/spec.pdf');
Upload several files
Pass an array of paths when the input permits multiple selection. The page must use the multiple attribute for a native multi-file input.
await page.locator('input[type="file"]').setInputFiles([
'fixtures/one.txt',
'fixtures/two.txt',
'fixtures/three.txt'
]);
Upload a directory
The locator API also accepts a directory path. The browser receives the files contained in that directory according to the page’s file-input behavior.
await page.locator('input[type="file"]').setInputFiles('fixtures/evidence');
Upload data without creating a fixture file
For generated content, provide an in-memory object with a file name, MIME type, and byte buffer. This is useful for API payloads, small text files, and deterministic test data.
const csv = Buffer.from('id,namen1,Adan2,Linusn', 'utf8');
await page.locator('input[type="file"]').setInputFiles({
name: 'people.csv',
mimeType: 'text/csv',
buffer: csv
});
Clear a file input
Set an empty array to remove the currently selected files.
await page.locator('input[type="file"]').setInputFiles([]);
Handle a picker that appears after a click
Some applications create the file input only when a user clicks “Choose file,” or keep it hidden behind a custom control. In that case, start waiting for the page’s filechooser event and perform the click in the same operation. Waiting after the click can miss a fast event.
Rank #2
- Easily store and access 5TB of content on the go with the Seagate portable drive, a USB external hard Drive
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
test('uploads through a dynamic chooser', async ({ page }) => {
await page.goto('https://example.test/profile');
const chooserPromise = page.waitForEvent('filechooser');
await page.getByRole('button', { name: 'Choose file' }).click();
const chooser = await chooserPromise;
await chooser.setFiles('fixtures/avatar.png');
await page.getByRole('button', { name: 'Save' }).click();
});
For multiple files, pass an array to chooser.setFiles(). For generated data, pass the same { name, mimeType, buffer } object used with a locator.
const chooserPromise = page.waitForEvent('filechooser');
await page.locator('.dropzone-button').click();
const chooser = await chooserPromise;
await chooser.setFiles([
'fixtures/a.txt',
'fixtures/b.txt'
]);
The locator method is the preferred API when the input is directly addressable. The older page-level page.setInputFiles() method is discouraged; migrate selectors to locator.setInputFiles() where possible.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Choose the upload technique by page behavior
| Page behavior | Use | Important detail |
|---|---|---|
| File input is present in the DOM | locator.setInputFiles() |
No operating-system dialog is needed. |
| Input is created or exposed by a button | page.waitForEvent('filechooser'), then fileChooser.setFiles() |
Create the wait before clicking. |
| Several files are accepted | Pass an array of paths or file objects | The input must support multiple selection. |
| Test data is generated at runtime | Pass an in-memory object | Include name, mimeType, and buffer. |
| Remove an existing selection | Pass [] |
This clears the input. |
Download a file and save it reliably
Register the download wait before clicking the link or button that triggers the transfer. The event and the action can be coordinated with Promise.all:
import { test, expect } from '@playwright/test';
import fs from 'node:fs/promises';
test('downloads an invoice', async ({ page }) => {
await page.goto('https://example.test/invoices');
const [download] = await Promise.all([
page.waitForEvent('download'),
page.getByRole('link', { name: 'Download invoice' }).click()
]);
await download.saveAs('artifacts/invoice.pdf');
await expect.poll(() => fs.stat('artifacts/invoice.pdf'))
.toBeTruthy();
});
saveAs() copies the temporary browser download to a path you control. Create the destination directory in your test setup or use a fixture directory that already exists. Saving before teardown is essential: Playwright removes context downloads when the browser context closes.
Use a test-specific destination
Parallel tests should not write to the same filename. Include a test identifier, worker number, or unique suffix in the destination.
const destination = `artifacts/invoice-${Date.now()}.pdf`;
await download.saveAs(destination);
For stronger assertions, read the saved bytes or parse the file with a format-specific library after saveAs() completes. The Playwright download event only confirms that a download was created; application-level validation still belongs in your test.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteRank #3
- Easily store and access 1TB to content on the go with the Seagate Portable Drive, a USB external hard drive.Specific uses: Personal
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop. Reformatting may be required for Mac
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
Inspect the Download object
The Download object exposes more than the file copy operation. Use the method that matches the assertion you need.
| Need | Method | Use it for |
|---|---|---|
| Persist a file at a known location | download.saveAs(path) |
CI artifacts, later parsing, and stable assertions. |
| Read the server or page URL | download.url() |
Checking that the expected endpoint was requested. |
| See the browser’s suggested name | download.suggestedFilename() |
Displaying or asserting a filename, with browser differences allowed. |
| Obtain a temporary path | download.path() |
Local connections where a temporary path is useful; it throws when connected remotely. |
| Read as a stream | download.createReadStream() |
Streaming processing instead of copying first. |
| Find out why it failed | download.failure() |
Returning the browser-reported failure status. |
| Stop an in-progress transfer | download.cancel() |
Tests that intentionally cancel downloads. |
The suggested filename commonly comes from the response’s Content-Disposition header or the HTML download attribute. Browsers can calculate it differently, so use your own destination with saveAs() when the test needs a stable name.
Configure download acceptance
Playwright Test’s acceptDownloads option controls whether attachments are accepted automatically and is documented as true by default. Set it explicitly when your project configuration needs to communicate the requirement:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
acceptDownloads: true
}
});
Regardless of that setting, retain the event-first pattern and call saveAs() for any artifact that must survive context teardown.
Python Playwright equivalents
The same sequencing applies in Python’s synchronous API. Wait for the chooser before the click:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.test/upload")
with page.expect_file_chooser() as chooser_info:
page.get_by_role("button", name="Choose file").click()
chooser = chooser_info.value
chooser.set_files("fixtures/report.pdf")
browser.close()
For a direct input, call set_input_files() on the locator. For a download, use expect_download() around the triggering action and save the result:
Rank #4
- 【Versatile Storage Expansion – For Gaming, Work & Everyday Use】 Running out of space on your PS5 or Xbox Series X/S? This external hard drive lets you store and play PS4 / Xbox One games directly, instantly freeing up your console’s internal storage for next‑gen titles. At the same time, it handles work file backups, media libraries, and cross‑device data transfers with ease. One drive, all your needs. *(Note: PS5 / Xbox Series X|S games cannot be run or stored directly from the external hard drive. However, by offloading your PS4 / Xbox One games, you can free up valuable space for newer titles.)*
- 【Patented Silicone Sleeve – Data Protection You Can Count On】 Worried about drops? We’ve got you covered. The patented built‑in silicone sleeve acts like a shock‑absorbing armor, cushioning your drive against bumps and falls. Whether it’s important work documents, precious family photos, or hard‑earned game saves, your data deserves this level of protection.
- 【Plug & Play, Compatible with Computers & Consoles】 No complicated setup—just plug in and go. Works seamlessly with Windows, Mac, and Linux computers, as well as PS4, PS5, Xbox One, and Xbox Series X/S. Process files at the office, back up data at home, or enjoy gaming in your downtime—one drive handles all your devices, simply and hassle‑free.
- 【USB 3.0 Ultra‑Fast Transfer – No More Waiting】 Tired of watching progress bars crawl? With USB 3.0 speeds up to 5Gbps, large files transfer in seconds. Whether you’re moving work documents, transferring hundreds of gigs of games, or backing up a year’s worth of photos, you get more done in less time.
- 【Sleek, Lightweight, and Ready to Go】 Weighing just 0.16 kg—lighter than a can of soda—this compact drive features a stylish mirror‑and‑frosted finish. Toss it in your bag and go, whether you’re heading to the office, visiting a friend for a gaming session, or giving a presentation on the road.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(accept_downloads=True)
page.goto("https://example.test/invoices")
with page.expect_download() as download_info:
page.get_by_role("link", name="Download invoice").click()
download = download_info.value
download.save_as("artifacts/invoice.pdf")
browser.close()
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Common failures and fixes
“No file chooser event was received”
The click may not open a chooser, the event wait may have started too late, or the wrong control was clicked. Start waitForEvent('filechooser') before the click, target the button that actually invokes the picker, and verify that the application creates a native file input.
“File not found” during upload
Relative paths use the process working directory, not necessarily the directory containing the test file. Log the resolved path, use an absolute path temporarily, or standardize the working directory in your test command and CI job.
The upload appears selected but the application rejects it
Check the file extension, MIME type, size limits, and whether the input allows multiple files. For generated content, set an appropriate mimeType and a filename the server accepts.
The test hangs waiting for a download
The action may open a new page, navigate instead of downloading, or be blocked by validation. Confirm the click reaches the intended element and inspect page messages. Keep the event wait attached to the exact action that should produce the download.
The download disappears after the test
Context-managed downloads are temporary. Call saveAs() before the context or test fixture closes and write to a directory retained by your CI system.
download.path() throws
That method is not available for remote browser connections. Use saveAs(), which copies the file to a path you choose.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Best Value
- High capacity in a small enclosure – The small, lightweight design offers up to 6TB* capacity, making WD Elements portable hard drives the ideal companion for consumers on the go.
- Plug-and-play expandability
- Vast capacities up to 6TB[1] to store your photos, videos, music, important documents and more
- SuperSpeed USB 3.2 Gen 1 (5Gbps)
Filename assertions fail in one browser
Suggested names can differ because browsers derive them from headers and markup differently. Assert the content or use a deterministic destination filename rather than requiring the suggested name to match everywhere.
The event is missed intermittently
Do not await the click first and then start waiting. Use Promise.all (or Python’s expect_download()/expect_file_chooser() context) so the listener is installed before the trigger.
Or skip the browser setup
If your goal is to document a page rather than exercise its upload/download controls, ScreenshotNeo can return a screenshot or PDF from one HTTP request. It is separate from Playwright’s file-transfer APIs, but useful for capturing the final page state without maintaining browser code.
Use the API examples in the ScreenshotNeo documentation to choose PNG, JPEG, WebP, or PDF output and configure waits, selectors, headers, cookies, or other capture options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const data = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', data));
Before capture, ScreenshotNeo accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. 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; higher plans are Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000), and Business ($249 for 1,000,000). Yearly billing provides two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to start without a card.
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.

