How to assert file downloads in Playwright

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.

Steps to assert Playwright file downloads:

  1. Open the existing Playwright project.
    $ cd ~/projects/playwright-demo

    Install Playwright first when the project does not already have @playwright/test.
    Related: How to install Playwright with npm

  2. Create tests/download.spec.ts with a deterministic CSV response and file assertions.
    tests/download.spec.ts
    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

  3. Run the download spec.
    $ 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.

  4. Confirm the saved artifact appears in the test output directory.
    $ 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.