Recommended Free Tools
To handle a download reliably in Playwright, start waiting for the download before triggering it, save the completed download to a path you control, and verify the saved file’s content against your application’s requirements. Use Node.js path utilities or Python’s pathlib for portable paths, and finish saving or checking the file before closing its browser context.
Use an awaited download event, not a late listener
A download event signals that a download has started; it does not by itself prove that the file has finished saving. Register the wait before clicking or performing the action that starts the download, then await the resulting download object. This prevents a fast download event from firing before the test begins listening.
As an Amazon Associate I earn from qualifying purchases.
JavaScript and TypeScript
Prepare a destination directory with Node.js filesystem and path utilities. The example saves under a test-controlled name; it does not assume the browser suggested that name.
import { mkdir } from 'node:fs/promises';
import path from 'node:path';
const outputDir = path.resolve('test-results', 'downloads');
await mkdir(outputDir, { recursive: true });
const downloadPromise = page.waitForEvent('download');
await page.getByRole('link', { name: 'Download report' }).click();
const download = await downloadPromise;
const destination = path.join(outputDir, 'report.csv');
await download.saveAs(destination);
Playwright’s JavaScript download guide demonstrates waiting before the action and then saving the result. Its Download API documents that saveAs waits for the download to finish if necessary.
#1 Best Overall
Python
Python’s context manager makes the ordering explicit: the download wait surrounds the action that triggers it. pathlib.Path handles path separators across supported operating systems.
from pathlib import Path
output_dir = Path("test-results") / "downloads"
output_dir.mkdir(parents=True, exist_ok=True)
with page.expect_download() as download_info:
page.get_by_role("link", name="Download report").click()
download = download_info.value
destination = output_dir / "report.csv"
download.save_as(destination)
Playwright documents equivalent Python download examples, including asynchronous usage. Java uses page.waitForDownload() with saveAs in its download guide; .NET uses WaitForDownloadAsync() followed by SaveAsAsync() in its download guide.
Rank #2
Save to a controlled path before the context closes
Playwright stores downloads in a temporary location associated with the browser context. Closing that context deletes its downloads, so save the file to a test-runner destination—or inspect it while the context is still open—before cleanup. The downloads documentation states: “Downloaded files are deleted when the browser context that produced them is closed.”
saveAs copies the download to the path you choose and is preferable to depending on the browser-managed temporary location. The Download API also exposes path(), but that method returns the temporary path only after completion and throws for failed or canceled downloads. It also throws when Playwright is connected remotely, where the browser’s filesystem may not be accessible to the test process. Use saveAs to put the artifact at a runner-accessible destination instead.
Rank #3
If you need Playwright to retain artifacts beyond context closure, configure an artifactsDir when launching the browser. Without that configuration, the artifacts directory is temporary and cleaned up. See the BrowserType API for the relevant browser launch options.
Verify the file’s contract, not just the download event
A robust assertion checks the output at the level the application promises. The event confirms that a download started; a successful save confirms that a destination was written. Neither alone proves that the file is valid or contains the expected data.
- Confirm the action produced a download. Await the event in the same flow as the intended click or other trigger.
- Save and check the destination. Use the language’s filesystem tools to verify that the destination exists as a regular file.
- Check size when meaningful. A nonzero length or expected range can catch empty or truncated output, but only assert a size requirement if the product’s contract supports it.
- Parse and validate content. Use the appropriate CSV, JSON, archive, PDF, or other format parser and assert expected schema, records, fixture values, or other stable content.
- Check metadata separately when required. Assert a filename or response-derived property only if it is part of the feature requirement.
The right content assertions depend on the file your application generates; Playwright’s download APIs do not prescribe a universal validity check. Avoid treating an extension or the presence of any file as proof that the download is correct.
Outdated 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 matchWindows 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 reinstallHandle filenames as browser behavior, not a universal invariant
download.suggestedFilename() reports a name computed by the browser, typically from the response’s Content-Disposition header or an HTML download attribute. Playwright notes that browsers may compute this name differently. For stable cross-browser tests, save to a test-controlled destination name and assert the saved file’s content.
If the product requirement is specifically about the filename users receive, test suggestedFilename() as a separate behavior assertion in the browser engines you support. Do not substitute the Download object’s temporary path for the suggested name: the temporary file uses an internal GUID-style name.
Adapt the pattern for multiple pages and remote runs
When the triggering action may occur on any of several pages in one context, use the context-level download event. A page-level event is more narrowly scoped and is a better fit when the responsible page is known. Playwright documents both options in its BrowserContext API.
In remote or containerized execution, keep the browser host’s temporary filesystem separate from the test runner’s output directory. Save the download to a location accessible to the runner rather than expecting path() to expose the browser host’s file. Use runtime path utilities and create output directories explicitly so the same test does not depend on Windows, macOS, or Linux path separators.
Run portable assertions across the environments that matter
Playwright supports Chromium, WebKit, and Firefox, and documents browser setup and platform-specific cache locations for Windows, macOS, and Linux in its browser documentation. Exercise the same download flow in the browser engines and operating systems your project supports, while asserting stable application semantics.
Do not assume generated files are byte-for-byte identical across environments if they contain timestamps, line endings, archive metadata, or other platform-dependent values. Normalize only fields that are intentionally variable; keep the assertions on the actual file format and content that the application promises.
Quick Recap
Common failure modes and their fixes
- The wait starts after the click: register
waitForEvent('download'),expect_download(), or the equivalent before triggering the action. - The test inspects the file as soon as the event fires: await
saveAs/save_asbefore checking the destination. - The browser context closes before the file is retained: save or validate before closing, or configure artifact retention when appropriate.
- The test calls
path()during a remote run: save to a runner-accessible path instead; the API documents thatpath()throws for remote connections. - The test assumes every browser suggests the same filename: use a controlled save destination unless filename computation itself is under test.
- A background event listener leaves the scenario unfinished: use an awaited event pattern for a known trigger so control flow waits for the download.
- The assertion checks only an extension or file presence: parse the file and verify the application-specific content or schema.
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.




