Uploads and downloads

Give file inputs an attachment, handle file pickers and check downloaded 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 Link to 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.

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.

TypeScript
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 Link to 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.

TypeScript
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 Link to 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.

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.

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 Link to 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.

PlatformUploads and pickersDownloads
Chrome and ChromiumSupported.Supported.
Firefox and WebKitunsupported.unsupported.
ElectronSupported in the first window.unsupported.
iOS simulator and macOS appsThese 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 Link to Next steps