In Playwright, how does locator.setInputFiles() attach a file without the OS picker?
answer
- The native picker is unreachable
- Set the input's file list directly
- One path, many paths, or a buffer
- Empty array clears the selection
- Relative paths use the working directory
basics
~20 sPlaywright sets the file input's file list directly through the browser protocol instead of driving the native dialog. Pass one path, several paths, an in-memory buffer object, or an empty array to clear the selection.
solid answer
~40 s`locator.setInputFiles()` targets an `<input type=file>` (or a `<label>` whose associated control is one) and assigns its file list over the browser's remote-debugging connection, so the operating system's picker never opens -- which is what makes uploads driveable at all. It accepts a single path string, an array of paths for an input marked `multiple`, one or more `{ name, mimeType, buffer }` objects that synthesise a file never written to disk, and `[]` to clear a previous selection. Relative paths resolve against the test process's current working directory, not the spec file's folder. For an input carrying `webkitdirectory`, pass a single directory path. Setting files fires the `input` and `change` events, so the issue tracker's attachment list re-renders exactly as it would for a real user.
code
typescript · 19 linesimport { test, expect } from '@playwright/test';
test('attach files to an issue', async ({ page }) => {
await page.goto('/issues/ISS-482');
const input = page.locator('input[type=file]');
await input.setInputFiles('fixtures/crash-log.txt');
await expect(page.getByText('crash-log.txt')).toBeVisible();
await input.setInputFiles({
name: 'triage.csv',
mimeType: 'text/csv',
buffer: Buffer.from('id,title,status'),
});
await expect(page.getByText('triage.csv')).toBeVisible();
await input.setInputFiles([]);
await expect(page.getByText('triage.csv')).toBeHidden();
});go deeper
Recall the call itself: locator.setInputFiles with a path string, and that no native picker ever opens. Attaching a fixture file and asserting the filename appears is the whole junior bar here.
Explain the four argument shapes -- one path, an array, a name/mimeType/buffer object, and an empty array -- and that relative paths resolve against the test process's working directory rather than the spec file.
Show judgment about how the input is reached in a real application, and be ready to say what setInputFiles cannot do when the page never renders a file input you can target.
Own the convention: whether the suite ships binary fixtures or synthesises files in memory, and how much upload coverage belongs in end-to-end tests versus cheaper layers.
## The picker is not part of the page When a user clicks **Attach file** on an issue, the browser hands control to the operating system. The window that opens is an OS dialog: it has no DOM, no accessible tree the page can reach, and no protocol surface a browser automation tool can drive. Any strategy that begins "click the button, then choose the file" is dead before it starts. `locator.setInputFiles()` sidesteps that dialog entirely. Rather than simulating the user's trip through the file system, it assigns the input element's file list directly over the browser's remote-debugging connection and then lets the `input` and `change` events fire. From the application's point of view a file was chosen; from the test's point of view nothing ever blocked. ## What it can be pointed at The target must be an `<input type=file>`, or a `<label>` whose associated control is one -- Playwright retargets to the control for you, which is why a locator built from the visible label text usually works. Point it at anything else and the call raises an error rather than quietly doing nothing, so a mis-aimed locator shows up as a failure instead of an upload that never happened. Real applications frequently hide the input behind a styled button. That does not change the approach: you target the input element, not the thing the user clicks. ## The four argument shapes | argument | what it does | | --- | --- | | `'fixtures/crash-log.txt'` | attaches one file read from disk | | `['before.png', 'after.png']` | attaches several; the input needs the `multiple` attribute | | `{ name, mimeType, buffer }` | synthesises a file in memory, nothing on disk | | `[]` | clears the current selection | Consequences worth holding onto: - Relative paths resolve against the **current working directory of the test process**, not the folder holding the spec file. Invoking the runner from a subdirectory changes what a relative path means, so anchoring on a path built at runtime removes the surprise. - Passing several files to an input that lacks `multiple` fails loudly instead of attaching only the first one. - The buffer form produces a complete file: `name` is what the application reads as the file's name, `mimeType` is its type, and `buffer` is the bytes. Nothing touches disk, so there is no binary fixture in the repository and nothing to clean up. - An empty array is a real operation, not a no-op. It resets the file list and fires the same events, so the issue tracker's attachment strip re-renders empty. - For an input carrying the `webkitdirectory` attribute, pass a single directory path and the browser expands it into the files underneath. ## A worked shape 1. Locate the input itself, even when it is visually hidden behind a styled control. 2. Call `setInputFiles()` with a path, an array, or a buffer object. 3. Assert on what the application did with it -- the filename in the attachment list, an upload progress row, a submit button that disables while the request is in flight. Step 3 is the step people skip. `setInputFiles()` resolves once the file list is set and the events are dispatched; it does not wait for the application's own upload request to finish. If the very next line submits the comment, the test is racing the network. Assert on a user-visible consequence before moving on. ## When there is nothing to point at Some upload controls never render a reachable input: script creates one, clicks it, and throws it away. Those pages ask the browser for a file picker instead, and Playwright surfaces that request as a page event you can wait for and answer with the same argument shapes. Reach for it only when there is genuinely no input to target -- a plain `setInputFiles()` call has no event ordering to get wrong. ## Failure modes you will actually meet - **Wrong element.** A locator matching the styled button rather than the input produces an error about the element not being a file input. - **Path drift.** Fixture paths that work under one invocation and break under another are almost always working-directory assumptions in disguise. - **Nothing uploaded.** The file list is set correctly, but the application only uploads on a separate submit action the test never performs. - **Stale assertion.** The test asserts the filename before the application has re-rendered, which is a timing bug in the assertion, not in the upload.
- What happens if you call setInputFiles on an element that is not a file input?It throws. The method requires an `input[type=file]`, or a `<label>` whose associated control is one -- Playwright retargets to that control automatically. Anything else fails with an error naming the element, so a mis-aimed locator surfaces as a real failure rather than an upload that silently never happened.
- How do you attach two files at once to the same input?Pass an array of paths, or an array of buffer objects. The input must carry the `multiple` attribute; without it Playwright raises an error rather than quietly attaching only the first file, which is a useful signal that the markup does not support what the test assumes.
saying these in an interview costs you the question
- Claims Playwright can click buttons in the native OS file dialog
- Assumes relative fixture paths resolve from the spec file's folder
- Thinks setInputFiles types a filename into the input like text
- Believes clearing a selection requires recreating the input
- Assumes a buffer upload needs a temporary file on disk first