A browser download leaves the DOM and becomes a browser-managed artifact as soon as the transfer starts. Playwright exposes that transition through a Download event, so the event waiter must be active before the click that can emit it.
The browser derives a suggested filename from response metadata or the link's download attribute. testInfo.outputPath() supplies an isolated destination for each test, while saveAs() preserves the payload beyond the browser context's temporary download storage.
An inline CSV link keeps the filename and bytes deterministic without introducing a web server. Replace that controlled page fixture with the application's report screen and export control while retaining the same waiter, filename assertion, save, and byte-verification sequence.
Related: How to run Playwright tests
Related: How to set Playwright output directory
import { test, expect } from '@playwright/test';
import { readFile } from 'node:fs/promises';
test('download report', async ({ page }, testInfo) => {
const destination = testInfo.outputPath('report.csv');
An existing Playwright Test project with a browser installed is the required starting state.
Related: How to install Playwright with npm
await page.setContent(`
<a
download="report.csv"
href="data:text/csv,month%2Ctotal%0AJune%2C42%0A"
>
Download report
</a>
`);
The link's download attribute supplies the suggested filename, and its data URL supplies the exact CSV bytes used by the final assertion. An application test should navigate to the real report page instead.
const downloadPromise = page.waitForEvent('download');
Keep this statement before the click because an immediate response can emit the event before a later waiter exists.
await page.getByRole('link', { name: 'Download report' }).click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe('report.csv');
await download.saveAs(destination);
saveAs() waits for the transfer to finish before copying the payload.
expect(await readFile(destination, 'utf8')).toBe('month,total\nJune,42\n');
});
$ npx playwright test tests/download.spec.ts --project=chromium --reporter=line Running 1 test using 1 worker [1/1] [chromium] › tests/download.spec.ts:4:5 › download report 1 passed (943ms)
A missing download event, unexpected filename, failed save, or byte mismatch prevents the runner from printing 1 passed.
Related: How to run specific Playwright tests