## TL;DR

Downloads work headless; the usual miss is clicking before listening. Start page.waitForEvent('download') before the click using Promise.all, and make sure the browser context was created with acceptDownloads set to true.

## The query

```text
playwright download event never fired in headless: how to fix
```

## Use this when

- download event never fires in headless mode
- download.path() hangs forever
- export-button tests fail only in CI

## Not for

- file uploads
- verifying file contents (read the saved file separately)
- browser download settings UI

## Steps

1. Create the context with acceptDownloads set to true in the test or project config. Expected output: the context options include acceptDownloads true.
2. Restructure so the listener starts before the click: await Promise.all with page.waitForEvent('download') and the click together. Expected output: the waitForEvent promise is created before the click promise.
3. Await download.path() to confirm the file landed, and assert on the suggested filename. Expected output: a real file path on disk and the expected filename.
4. If the download opens in a new tab, wait for the new page on the context and listen for the download there. Expected output: the download is captured from the correct page.
5. Run headless and headed to confirm both modes capture the file. Expected output: passing runs in both modes with the file saved.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_4NloQitH8N2NW6AU6oyJKA
