# Uploads and downloads

> Give file inputs an attachment, handle file pickers and check downloaded files.

Source: https://rehearsal.dev/retest/docs/guides/files

Use file actions when a flow attaches a document or exports data. This guide shows how to supply an attachment, catch a file picker and check a download's contents.

## Upload a file

Use `setInputFiles()` to replace a file input's attachments. For a small generated file, supply its name and content directly so you need no separate fixture on disk.

```ts filename="tests/files.retest.ts"
import { expect, test } from '@rehearsal-labs/retest'

test('attaches release notes', async ({ page }) => {
  await page.goto('/files')
  await page.getByLabel('Attachment').setInputFiles({
    name: 'notes.txt',
    mimeType: 'text/plain',
    content: 'Release checklist',
  })
  await expect(page.getByTestId('heard')).toContainText('attachment change: notes.txt')
})
```

The input receives the file and fires its `input` and `change` events. The task app lists those events in `heard`; in your app, check the attachment preview or upload result that tells the user it worked.

Chrome chooses the file type from the name's extension. The optional `mimeType` checks that choice; a mismatch fails with `usage` after the input receives the file. Omit it when you do not need to check the type.

You can also pass a path, a list of files, or an empty list to clear the input. Upload paths and `saveAs()` destinations resolve against the folder of the file that makes the call, as imports do. If a helper module makes the call, use that helper's folder.

```ts
await page.getByLabel('Attachment').setInputFiles('./fixtures/report.pdf')
await page.getByLabel('Photos').setInputFiles(['./fixtures/a.png', './fixtures/b.png'])
await page.getByLabel('Attachment').setInputFiles([])
```

When this call is in `tests/files.retest.ts`, the first path names `tests/fixtures/report.pdf`. A missing file fails with `usage`; several files also fail with `usage` if the input has no `multiple` attribute.

The input must exist and be enabled, but it may be hidden. Use a test id or CSS for a hidden input, because `getByLabel()` leaves it out. A locator for a `<label>` also works when it names a file input; other elements fail with `not_actionable`.

## Catch a file picker

If the flow starts with an upload button, wrap its click in `page.fileChooser()`. The wait starts before the click and catches the picker, so no operating system window appears.

```ts
await page.goto('/files')
const chooser = await page.fileChooser(() =>
  page.getByRole('button', { name: 'Upload' }).click(),
)
await chooser.setFiles({ name: 'notes.txt', content: 'Release checklist' })
await expect(page.getByTestId('heard')).toContainText('hidden-upload change: notes.txt')
```

`chooser.setFiles()` accepts the same paths and payloads as `setInputFiles()`, and `chooser.multiple` tells you whether the input accepts several files. Check the resulting page after supplying them, because catching a picker does not prove an upload succeeded.

If no picker opens before the wait ends, `fileChooser()` fails with `timeout`. A picker that no test waited for is closed and fails the action with `not_actionable`. The separate `showOpenFilePicker()` browser API is `unsupported` because it has no input to receive files.

## Check a download

Wrap the action that begins an export in `page.download()`, then wait for the file with `path()`. The returned path is absolute, so you can read the saved bytes with Node's file API.

```ts filename="tests/export.retest.ts"
import { readFile } from 'node:fs/promises'
import { expect, test } from '@rehearsal-labs/retest'

test('exports the task list', async ({ page }) => {
  await page.goto('/files')
  const download = await page.download(() =>
    page.getByRole('link', { name: 'Export CSV' }).click(),
  )
  expect(download.suggestedFilename).toBe('tasks.csv')
  const csv = await readFile(await download.path(), 'utf8')
  expect(csv).toContain('Release checklist,false')
})
```

`download()` returns when the download has begun and the triggering action has finished. `path()` waits until it is saved; `saveAs('./out/tasks.csv')` waits too, then makes a copy at your chosen path.

Retest keeps its copy under `artifacts/<attempt>/<app>/downloads/` in the run folder, even if you also call `saveAs()`. The report lists and links saved downloads, including those from a failed test or ones no test waited for.

A download wait with no download fails with `timeout`. If the browser cancels the transfer, `path()` and `saveAs()` fail with `not_actionable` and say how many bytes arrived; a transfer still running when the test ends is recorded as `unfinished`.

> [!WARNING]
> **Downloads containing secrets**
>
> If Retest detects a known secret value in a download, it removes the file and records it as `withheld`. `path()` and `saveAs()` then fail with `unsupported`; the file is not a redacted copy.

## Platform support

Uploads, pickers and downloads were checked on Chrome on macOS. Chromium uses the same driver. A browser file API that an engine does not support fails with `unsupported`.

| Platform | Uploads and pickers | Downloads |
| --- | --- | --- |
| Chrome and Chromium | Supported. | Supported. |
| Firefox and WebKit | `unsupported`. | `unsupported`. |
| Electron | Supported in the first window. | `unsupported`. |
| iOS simulator and macOS apps | These browser file APIs are unavailable. | These browser file APIs are unavailable. |

Inside a browser frame, direct `setInputFiles()` is `unsupported`. On a supported browser, catch a picker opened there through `page.fileChooser()`; `page.download()` can also catch a download begun in a frame or a page the opener creates.

## Next steps

- [Frames and shadow DOM](https://rehearsal.dev/retest/docs/guides/frames-and-shadow-dom.md): Find the controls that open a picker inside a frame.
- [Recordings and evidence](https://rehearsal.dev/retest/docs/guides/recordings-and-evidence.md): Find the files and screenshots retained by a run.
- [Test API](https://rehearsal.dev/retest/docs/reference/test-api.md): Look up file payloads and download methods.
