To capture a download, wrap the action that triggers it in `page.waitForEvent('download')`: `const [download] = await Promise.all([page.waitForEvent('download'), page.getByText('Export').click()])` — this resolves to a `Download` object.
Save the file with `await download.saveAs('/tmp/export.csv')` or read its stream with `download.createReadStream()` for in-memory assertions.
For file uploads with an `<input type='file'>` element, obtain the locator and call `await fileInput.setInputFiles('/path/to/file.pdf')` — Playwright handles the file-chooser dialog internally.
For drag-and-drop upload zones or cases where clicking a button opens a system file chooser, use `page.waitForEvent('filechooser')` before the click, then call `fileChooser.setFiles([...paths])` on the resolved chooser object.
Assert upload success by waiting for a confirmation element, e.g., `await expect(page.getByText('Upload complete')).toBeVisible()`.
Known gotchas
`waitForEvent('download')` must be registered before the click that triggers the download. Registering it after the click introduces a race condition where the event fires before the listener is attached.
By default Playwright stores downloads in a temporary directory that is deleted when the browser context closes. If you need to inspect the file after the test, call `saveAs` before the context closes or before the test ends.
When uploading multiple files to a single input, pass an array to `setInputFiles`. Calling `setInputFiles` multiple times on the same input replaces the previous selection rather than appending to it.
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp