Export buttons and report links need browser-level tests because the page often hands the file to the browser instead of leaving visible text in the DOM. A Playwright download assertion captures that handoff and checks the file name plus saved contents.
Playwright raises a download event when a click starts a transfer. Start page.waitForEvent('download') before clicking the control, then read download.suggestedFilename() and save the stream to a test-owned output path.
A deterministic CSV response keeps the first version independent of a backend report service. For a live application, keep the same capture and file assertions, but replace the mocked route and inline link with the real page navigation and export control.
Related: How to run Playwright tests
Related: How to set Playwright output directory
$ cd ~/projects/playwright-demo
Install Playwright first when the project does not already have @playwright/test.
Related: How to install Playwright with npm
import { test, expect } from '@playwright/test';
import fs from 'node:fs/promises';
test('download report', async ({ page }, testInfo) => {
await page.route('**/reports/monthly.csv', async route => {
await route.fulfill({
status: 200,
headers: {
'Content-Type': 'text/csv',
'Content-Disposition': 'attachment; filename="report.csv"',
},
body: 'month,total\nJune,42\n',
});
});
await page.setContent(`
<a href="https://app.example.com/reports/monthly.csv">
Download report
</a>
`);
const downloadPromise = page.waitForEvent('download');
await page.getByRole('link', { name: 'Download report' }).click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe('report.csv');
const savedPath = testInfo.outputPath('downloads', download.suggestedFilename());
await download.saveAs(savedPath);
const contents = await fs.readFile(savedPath, 'utf8');
expect(contents).toContain('June,42');
});
The page.route() block supplies the CSV fixture for a self-contained test. Replace it with the real report page and export control when the application already serves the download.
Related: How to mock network responses in Playwright
$ npx playwright test tests/download.spec.ts --reporter=line Running 1 test using 1 worker [1/1] [chromium] › tests/download.spec.ts:4:5 › download report 1 passed (768ms)
Start the event waiter before the click so Playwright cannot miss a fast download event.
Related: How to run specific Playwright tests
$ find test-results -name report.csv -print test-results/tests-download-download-report-chromium/downloads/report.csv
testInfo.outputPath() keeps downloaded files inside the test run output directory, which prevents parallel tests from writing to the same path.