Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Locate the file input inside the correct frame, then call Playwright’s locator-based setInputFiles. For a stable iframe and input, the essential TypeScript is:
await page
.frameLocator('iframe[name="upload-frame"]')
.locator('input[type="file"]')
.setInputFiles('/absolute/path/to/file.pdf');
If the control has an accessible label, prefer getByLabel. Stagehand v3 can traverse iframes for natural-language actions, but its documented act API does not define a file-payload upload method. Use Stagehand for discovery or clicks and Playwright’s explicit locator API to assign the file.
What you need before writing the test
- A Playwright project with the browser you intend to run in.
- A fixture file available on the test runner’s filesystem, or an in-memory buffer.
- A reliable iframe selector such as an
id,name, or distinctive data attribute. - A user-visible completion condition, for example a filename, progress state, success message, or enabled submit button.
Playwright’s FrameLocator API scopes locators into an iframe, while Locator.setInputFiles assigns one or more files to a file input. The assignment itself does not prove that the application uploaded, validated, or persisted the file; your test must assert the application’s resulting state.
Upload to an input that already exists
Use a labeled control when possible
An accessible label is less brittle than a CSS class generated by a component library:
#1 Best Overall
import { test, expect } from '@playwright/test';
test('uploads a PDF in the embedded form', async ({ page }) => {
await page.goto('https://example.test/profile');
const uploadFrame = page.frameLocator('iframe[name="upload-frame"]');
await uploadFrame.getByLabel('Upload file').setInputFiles('/absolute/path/to/file.pdf');
await expect(uploadFrame.getByText('Upload complete')).toBeVisible();
});
setInputFiles also accepts a locator pointing at a label associated with the input. If no accessible name is exposed, target the actual input:
const uploadFrame = page.frameLocator('#document-uploader');
await uploadFrame.locator('input[type="file"][name="document"]').setInputFiles('/absolute/path/to/file.pdf');
Do not target the styled “Choose file” button or a drag-and-drop surface unless that interaction creates the input dynamically. The input is the element that owns the browser’s file selection.
Select several files
await uploadFrame
.locator('input[type="file"][multiple]')
.setInputFiles([
'/absolute/path/to/photo-1.jpg',
'/absolute/path/to/photo-2.jpg'
]);
For an input with webkitdirectory, Playwright documents support for supplying one directory path. Use the exact behavior of the binding and Playwright version installed in your project.
Use an in-memory file instead of a path
Buffers make tests independent of fixture files and are useful in containers where the expected host path does not exist:
Free tools Windows power users keep installed
One-click scans. No signup required.
import { Buffer } from 'node:buffer';
await uploadFrame.locator('input[type="file"]').setInputFiles({
name: 'notes.txt',
mimeType: 'text/plain',
buffer: Buffer.from('Generated test contentn')
});
To clear a previous selection, pass an empty array:
Rank #2
await uploadFrame.locator('input[type="file"]').setInputFiles([]);
Relative paths are resolved from the process’s current working directory. Resolve paths explicitly when the test can run from different directories:
import path from 'node:path';
const fixture = path.resolve('tests/fixtures/file.pdf');
await uploadFrame.locator('input[type="file"]').setInputFiles(fixture);
Handle a file input created after a click
Some widgets do not insert an input[type="file"] until the user presses an upload button. In that case, wait for the chooser before triggering the action. The official Playwright input guide demonstrates this ordering:
const chooserPromise = page.waitForEvent('filechooser');
await page
.frameLocator('iframe[name="upload-frame"]')
.getByRole('button', { name: 'Upload file' })
.click();
const chooser = await chooserPromise;
await chooser.setFiles('/absolute/path/to/file.pdf');
Create the wait promise first. If you click and only then begin waiting, a fast chooser event can already have been emitted and the test can hang. The event belongs to the page that owns the interaction, so validate this pattern with the target application when the control is inside an iframe.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Deal with nested and changing frames
Nested iframe
When the upload control is inside a second iframe, chain frame locators:
const nestedUpload = page
.frameLocator('iframe#outer-frame')
.frameLocator('iframe[name="inner-uploader"]');
await nestedUpload
.getByLabel('Upload file')
.setInputFiles('/absolute/path/to/file.pdf');
Inspect the page when a locator times out. The visible upload panel may be in a child frame, while the outer frame contains only the application shell.
Rank #3
Frame reloads and late rendering
Do not cache an ElementHandle across a frame navigation. Keep a locator and perform the action after the frame has rendered. Add a narrowly scoped wait for a meaningful element rather than an arbitrary long sleep:
const frame = page.frameLocator('iframe[name="upload-frame"]');
await frame.locator('input[type="file"]').waitFor({ state: 'attached' });
await frame.locator('input[type="file"]').setInputFiles('/absolute/path/to/file.pdf');
If the input is replaced during rendering, locate it again immediately before assignment and assert the post-upload state afterward.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsVerify that the application accepted the file
Playwright reports that the input received a file, not that the server accepted it. Choose an assertion that represents your application’s contract:
- an uploaded filename appears in the frame;
- a progress indicator reaches a completed state;
- a success or error message is visible;
- a submit or “Continue” control becomes enabled;
- an API-backed record or preview is rendered.
await uploadFrame.locator('input[type="file"]').setInputFiles('/absolute/path/to/file.pdf');
await expect(uploadFrame.getByRole('status')).toHaveText(/complete|uploaded/i);
await expect(uploadFrame.getByRole('button', { name: 'Continue' })).toBeEnabled();
For asynchronous uploads, wait on the visible completion condition or a request that your application owns, and set an explicit test timeout appropriate to the file size and environment. Avoid declaring success merely because setInputFiles returned.
Combining Stagehand with Playwright
Stagehand v3’s act reference documents iframe traversal for browser interactions. That is useful when the frame or control must be found from a natural-language instruction. The cited Stagehand documentation does not specify a dedicated API for passing a local file payload.
Rank #4
A dependable division of work is:
- Use Stagehand to navigate, identify the embedded uploader, or click a control whose wording changes.
- Use Playwright’s
page.frameLocator(...)to obtain the exact frame context. - Use
getByLabelor a precise input locator and callsetInputFiles. - Use Playwright assertions for the application’s completion state.
If you run Stagehand in a hosted browser, its documentation names Browserbase as an initialization environment. Hosting changes where the browser runs; it does not change the requirement that the file must be accessible to the runner or supplied as an in-memory payload.
Python equivalent
The same frame-first approach is available in Playwright’s Python binding:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.test/profile")
upload = page.frame_locator('iframe[name="upload-frame"]')
upload.get_by_label("Upload file").set_input_files("/absolute/path/to/file.pdf")
upload.get_by_text("Upload complete").wait_for()
browser.close()
Use the binding’s current method names and signatures if your project uses asynchronous Python, Java, or .NET; the frame scoping and post-upload assertion remain the same.
Common failures and fixes
“Locator resolved to nothing” or a timeout
Check that the selector identifies the iframe currently on the page, then inspect its contents. If there are nested frames, chain another frameLocator. A selector copied from the outer document cannot find an element inside the frame.
Several file inputs match
Constrain the locator with an accessible label, name, accept, a multiple attribute, or a frame-specific container. Avoid selecting the first matching input unless the page guarantees there is only one.
Best Value
The chooser wait never resolves
Start page.waitForEvent('filechooser') before clicking. Also ensure the click is performed on the page that emits the event and that the control truly opens a native chooser rather than revealing an already present input.
A path works locally but fails in CI
The browser process reads the runner’s filesystem, not your workstation’s. Copy fixtures into the CI workspace, use path.resolve, or pass an in-memory buffer. Log the resolved path and verify it exists before the test starts.
The file is selected but no upload occurs
Inspect the application’s own validation and network flow. It may reject the MIME type, size, extension, authentication, or server-side response. Assert the visible error state so the test explains the rejection.
An old example calls frame.setInputFiles
Prefer locator-based setInputFiles. The current Frame API marks the older frame method as discouraged and directs users toward a locator.
Performance, reliability, and security considerations
- Use small deterministic fixtures for ordinary tests and reserve large files for dedicated size and timeout coverage.
- Reuse a browser context where safe, but isolate tests that can alter account data or server-side uploads.
- Do not commit secrets or sensitive documents as fixtures. Generate disposable content in memory when practical.
- When uploads trigger background processing, wait for the application’s completion signal instead of polling the DOM at a tight interval.
- Record the frame selector and the final visible state in failure output; this is more useful than a screenshot of the outer page alone.
- Cross-origin framing does not remove Playwright’s frame-scoping requirement. You still must locate the frame element and then the input inside it.
Or skip the browser setup
If your goal is to capture the page that contains an upload workflow rather than submit a local file through it, ScreenshotNeo returns a screenshot or PDF from one GET request. It is not a replacement for uploading a file into an application, but it can remove the browser-installation work when you need a clean visual record of the result.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for all request options. The service removes cookie and consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it without a card.
FAQ
Can I upload a directory?
For an input using webkitdirectory, the locator API documents one directory path. Confirm the target application’s expected directory behavior before relying on it in cross-platform CI.
Does Stagehand replace Playwright for file uploads?
Stagehand can traverse frames for actions, but the cited v3 documentation does not define a file-payload upload call. Keep Playwright for deterministic file assignment.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWhy does a successful test still show a server-side error?
Input assignment and server acceptance are separate operations. The application may reject the file after validation or processing, so expose that state in an assertion and diagnostic output.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

