Q21 of 42 · Playwright
How do you handle file uploads in Playwright?
Short answer
Short answer: Use `locator.setInputFiles(path)` on the `<input type=file>` element. Pass a path, multiple paths, an in-memory `{ name, mimeType, buffer }`, or an empty array to clear. For drag-and-drop or programmatic uploads without a visible file input, intercept the `filechooser` event.
Detail
Playwright's first-class API for uploads is setInputFiles:
await page.getByLabel('Profile photo').setInputFiles('fixtures/avatar.png');
The file picker doesn't open; Playwright assigns the file directly to the input. This works whether the input is visible or hidden behind a styled <label>.
Variations:
// Multiple files
await fileInput.setInputFiles(['a.pdf', 'b.pdf']);
// In-memory file (no disk roundtrip)
await fileInput.setInputFiles({
name: 'report.csv',
mimeType: 'text/csv',
buffer: Buffer.from('id,name\n1,Alice'),
});
// Clear the selection
await fileInput.setInputFiles([]);
For UIs that hide the input behind a custom button or drag-and-drop zone, listen for the filechooser event:
const [fileChooser] = await Promise.all([
page.waitForEvent('filechooser'),
page.getByRole('button', { name: 'Upload photo' }).click(),
]);
await fileChooser.setFiles('fixtures/avatar.png');
For drag-and-drop uploads, you typically need to dispatch a synthetic drop event with a constructed DataTransfer containing the file. This is fiddlier; setInputFiles should be your first choice if the input exists in the DOM at all.
Common asserts after upload:
await fileInput.setInputFiles('fixtures/avatar.png');
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByTestId('avatar')).toHaveAttribute('src', /avatar\.png/);
Watch-outs:
- File paths are relative to the test's working directory (project root, typically). Use absolute paths or
path.join(__dirname, ...)for clarity. - Browsers limit upload sizes; very large files in tests may need splitting or stubbing.
// EXAMPLE
upload.spec.ts
import { test, expect } from '@playwright/test';
import path from 'node:path';
test('uploads a profile photo', async ({ page }) => {
await page.goto('/profile');
const file = path.join(__dirname, '../fixtures/avatar.png');
await page.getByLabel('Profile photo').setInputFiles(file);
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByTestId('avatar-img'))
.toHaveAttribute('src', /avatar\.png$/);
});
test('uploads from in-memory buffer', async ({ page }) => {
await page.goto('/import');
await page.getByLabel('CSV file').setInputFiles({
name: 'orders.csv',
mimeType: 'text/csv',
buffer: Buffer.from('id,name\n1,Alice\n2,Bob'),
});
await page.getByRole('button', { name: 'Import' }).click();
await expect(page.getByTestId('import-status')).toHaveText('2 rows imported');
});