File download is one of the most common scenarios in UI automation. Playwright provides built-in support for handling downloads without relying on browser-specific configurations.
This guide explains everything from basic file downloads to advanced scenarios with practical examples.
Prerequisites
Install Playwright:
npm init playwright@latest
Import Playwright:
import { test, expect } from '@playwright/test';
How Playwright Handles Downloads
Whenever a download starts, Playwright creates a Download object.
You can:
- Wait for the download event
- Get download information
- Save the file
- Verify file name
- Verify file content
- Delete downloaded files
The download object provides methods like:
| Method | Description |
|---|---|
| download.path() | Returns downloaded file path |
| download.saveAs() | Save file to custom location |
| download.suggestedFilename() | Returns original filename |
| download.failure() | Returns download failure reason |
| download.delete() | Deletes downloaded file |
| download.createReadStream() | Reads downloaded file |
Example Website
Suppose clicking this button downloads a PDF.
<button>Download Report</button>
Example 1: Basic File Download
import { test, expect } from '@playwright/test';
test('Download File', async ({ page }) => {
await page.goto('https://example.com');
const downloadPromise = page.waitForEvent('download');
await page.getByText('Download Report').click();
const download = await downloadPromise;
console.log(download.suggestedFilename());
});
What happens?
Click Button
│
▼
Browser Starts Download
│
▼
Playwright Creates Download Object
│
▼
You Can Save or Verify File
Example 2: Save Download to Custom Folder
import path from 'path';
test('Save Download', async ({ page }) => {
await page.goto('https://example.com');
const downloadPromise = page.waitForEvent('download');
await page.locator('#download').click();
const download = await downloadPromise;
await download.saveAs(
path.join('downloads', download.suggestedFilename())
);
});
Downloaded folder:
Project
│
├── downloads
│ report.pdf
│
├── tests
Example 3: Get File Name
const fileName = download.suggestedFilename();
console.log(fileName);
Output
report.pdf
Example 4: Get Download Path
const path = await download.path();
console.log(path);
Example Output
C:\Users\Deepesh\AppData\Local\Temp\playwright-downloads\12345.pdf
Example 5: Verify Downloaded File Exists
import fs from 'fs';
const filePath = await download.path();
expect(fs.existsSync(filePath!)).toBeTruthy();
Example 6: Verify File Extension
expect(download.suggestedFilename()).toContain('.pdf');
or
expect(download.suggestedFilename()).toMatch(/\.pdf$/);
Example 7: Verify File Size
import fs from 'fs';
const filePath = await download.path();
const stats = fs.statSync(filePath!);
console.log(stats.size);
Assertion
expect(stats.size).toBeGreaterThan(1000);
Example 8: Verify Downloaded CSV Content
import fs from 'fs';
const filePath = await download.path();
const content = fs.readFileSync(filePath!, 'utf-8');
expect(content).toContain('Employee Name');
Example 9: Verify Downloaded Text File
const filePath = await download.path();
const text = fs.readFileSync(filePath!, 'utf-8');
expect(text).toContain('Welcome');
Example 10: Download Multiple Files
const download1 = page.waitForEvent('download');
await page.click('#download1');
const file1 = await download1;
const download2 = page.waitForEvent('download');
await page.click('#download2');
const file2 = await download2;
console.log(file1.suggestedFilename());
console.log(file2.suggestedFilename());
Example 11: Using Promise.all()
This is the recommended approach because it avoids missing the download event.
const [download] = await Promise.all([
page.waitForEvent('download'),
page.locator('#download').click()
]);
await download.saveAs(
`downloads/${download.suggestedFilename()}`
);
Example 12: Verify Download Failure
const failure = await download.failure();
expect(failure).toBeNull();
If download fails
Network Error
or
Cancelled
Example 13: Delete Downloaded File
await download.delete();
Example 14: Read Download Stream
const stream = await download.createReadStream();
stream?.on('data', chunk => {
console.log(chunk.toString());
});
Example 15: Download After Login
test('Download Invoice', async ({ page }) => {
await page.goto('https://example.com/login');
await page.fill('#username', 'admin');
await page.fill('#password', 'admin123');
await page.click('#login');
const [download] = await Promise.all([
page.waitForEvent('download'),
page.click('text=Download Invoice')
]);
await download.saveAs(
`downloads/${download.suggestedFilename()}`
);
});
Download PDF Example
const [download] = await Promise.all([
page.waitForEvent('download'),
page.getByText('Download PDF').click()
]);
expect(download.suggestedFilename()).toContain('.pdf');
Download Excel Example
const [download] = await Promise.all([
page.waitForEvent('download'),
page.getByRole('button', { name: 'Export Excel' }).click()
]);
expect(download.suggestedFilename()).toContain('.xlsx');
Download ZIP Example
const [download] = await Promise.all([
page.waitForEvent('download'),
page.click('#zip')
]);
await download.saveAs(
`downloads/${download.suggestedFilename()}`
);
Browser Download Behavior
| Browser | Supported |
|---|---|
| Chromium | ✅ |
| Firefox | ✅ |
| WebKit | ✅ |
No browser-specific download configuration is required in Playwright.
Best Practices
- Always use
Promise.all()to wait for the download event and trigger the download simultaneously. - Save downloads to a dedicated folder (for example,
downloads/) to keep test artifacts organized. - Verify both the filename and file extension.
- Validate the file contents whenever possible instead of checking only that a file exists.
- Clean up downloaded files after the test to avoid consuming unnecessary disk space.
- Use
download.failure()to detect download issues early. - Avoid hard-coded delays such as
waitForTimeout()when handling downloads. - Use
download.suggestedFilename()instead of assuming a fixed filename.
Common Interview Questions
1. How do you handle file downloads in Playwright?
Use page.waitForEvent('download') together with the action that triggers the download, preferably inside Promise.all(). Then use the Download object to save or verify the file.
2. Why should you use Promise.all() for downloads?
It ensures Playwright starts listening for the download event before the click occurs, preventing race conditions where the download starts before the listener is attached.
3. How do you save a downloaded file to a custom location?
Use:
await download.saveAs('downloads/report.pdf');
4. How do you verify a file was downloaded successfully?
You can:
- Check that
await download.failure()returnsnull. - Verify the file exists using
fs.existsSync(). - Validate the filename with
download.suggestedFilename(). - Check the file size or inspect its contents.
5. Can Playwright verify the contents of a downloaded file?
Yes. After obtaining the file path with download.path(), use Node.js modules like fs (or libraries such as xlsx for Excel or pdf-parse for PDFs) to read and validate the file contents.
Summary
Playwright’s download API is simple yet powerful. By using the Download object and the Promise.all() pattern, you can reliably automate downloading PDFs, Excel files, ZIP archives, CSVs, and other file types while verifying filenames, sizes, and contents. This approach works consistently across Chromium, Firefox, and WebKit, making it suitable for both functional and end-to-end automation tests.