Skip to content
TargetQA
Playwright · TypeScriptFile Operations & NetworkIntermediate4 min read

How to Wait For and Verify File Downloads in Playwright

File downloads in Playwright are native event streams. Discover how to inspect filenames, calculate byte sizes, and assert file contents cleanly in headless cloud browsers.

By TargetQA Engineering Team•Updated 2026-03-10
Interactive Cloud Sandbox

Test this solution live in real cloud browsers

Preload the working test script directly into TargetQA's Playwright compiler. Zero local dependencies or browser drivers needed.

Test Live in Compiler
The Problem & Error Symptoms

Understanding the Breakdown

Testing file downloads in legacy tools like Selenium is notorious for requiring custom Chrome/Firefox profile preferences, disabling download dialogs, and writing flaky polling scripts that check local folders for `.crdownload` temporary files.

Under the Hood

Why Traditional Selectors Fail

Browsers handle file downloads through the operating system rather than the web page DOM. In headless environments (like Linux CI runners), native OS download prompts can hang or trigger unpredictable file saving locations.

Field-Tested Patterns

Production-Grade Solutions

Choose the strategy that matches your test architecture. Pattern #1 is recommended for most production test suites.

1. The waitForEvent('download') Pattern (Recommended)

Recommended

Listen for the download event and trigger the click simultaneously using `Promise.all`.

1. The waitForEvent('download') Pattern (Recommended) (typescript)
// Start waiting for the download event before or alongside the trigger click:
const [download] = await Promise.all([
  page.waitForEvent('download'),
  page.getByRole('button', { name: 'Export CSV' }).click(),
]);

// Verify the suggested filename:
expect(download.suggestedFilename()).toBe('users-export.csv');

// Save the file to a permanent path or temp directory:
const destination = './downloads/' + download.suggestedFilename();
await download.saveAs(destination);
How it works: Playwright captures the browser's internal download stream directly. The file is saved in a managed temporary location until you save or delete it.

2. Verifying File Content in Memory Without Saving

Read the downloaded stream directly into memory to assert CSV or JSON content without creating disk artifacts.

2. Verifying File Content in Memory Without Saving (typescript)
const [download] = await Promise.all([
  page.waitForEvent('download'),
  page.locator('a.download-json').click(),
]);

// Stream the downloaded file directly:
const stream = await download.createReadStream();
const chunks: Buffer[] = [];
for await (const chunk of stream) {
  chunks.push(chunk as Buffer);
}
const content = Buffer.concat(chunks).toString('utf-8');

expect(content).toContain('"status": "ACTIVE"');
How it works: `createReadStream()` reads the payload directly from browser cache, avoiding disk writes and leaving zero residual files.

3. Handling Download Failures or Cancellations

Check for download cancellation or failures gracefully using `download.failure()`.

3. Handling Download Failures or Cancellations (typescript)
const [download] = await Promise.all([
  page.waitForEvent('download'),
  page.locator('#cancel-download-trigger').click(),
]);

const error = await download.failure();
if (error) {
  console.log(`Download failed as expected: ${error}`);
}
How it works: `download.failure()` returns an error string if the download was aborted by the server or browser.
Full Executable Cloud Test
Open in compiler

Complete Working Test Script

You can copy this exact test into your repository or run it immediately in our cloud sandbox against live demo targets.

test-recipe.spec.ts
import { test, expect } from '@playwright/test';

test('Verify file download event, suggested filename, and byte size', async ({ page }) => {
  // Navigate to herokuapp download sample page
  await page.goto('https://the-internet.herokuapp.com/download');

  // Locate the first downloadable file link
  const fileLink = page.locator('.example a').first();
  await expect(fileLink).toBeVisible();
  const expectedName = await fileLink.textContent();

  // Set up the download event listener and click concurrently
  const [download] = await Promise.all([
    page.waitForEvent('download'),
    fileLink.click(),
  ]);

  // Assert suggested filename matches link text
  const actualFilename = download.suggestedFilename();
  console.log('Downloaded file:', actualFilename);
  expect(actualFilename).toBeTruthy();

  // Retrieve temporary path created by Playwright
  const tempPath = await download.path();
  expect(tempPath).toBeTruthy();

  console.log('Download verified successfully in cloud browser!');
});
Target framework: Playwright · Language: TypeScript
Preload & Run in Compiler
Production Checklist

Best Practices & Golden Rules

  • Always trigger downloads inside `Promise.all([ page.waitForEvent('download'), trigger.click() ])`.
  • Check `download.suggestedFilename()` to ensure proper naming conventions.
  • Use `download.saveAs(path)` to persist files for downstream test stages or reporting.
  • Delete temporary downloads with `download.delete()` in long-running test suites to preserve disk space.
Avoid Test Flakiness

Common Pitfalls to Avoid

  • Polling the local OS `~/Downloads` directory: this breaks in Docker containers and remote browser grids.
  • Clicking download without awaiting the `waitForEvent('download')` promise: causes tests to terminate before download finishes.

Frequently Asked Questions

Where does Playwright save downloaded files by default?

Playwright saves downloads to a temporary folder managed by the browser process and deletes them when the browser context closes, unless you call `download.saveAs(path)`.

Can I inspect the size of the downloaded file?

Yes. Once `await download.path()` resolves, you can inspect the file via `fs.statSync(path).size` or stream it directly using `download.createReadStream()`.

Do I need special browser flags for headless downloads in Playwright?

No. Playwright automatically configures headless Chromium, Firefox, and WebKit to support file downloads without any extra flags or preferences.

Hands-On Practice

Practice This Skill in TargetQA Challenges

Explore More Automation Guides