October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
Browser Testing

How to Upload Files With Puppeteer in Jest (Inputs, Choosers, CI, and Assertions)

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The reliable way to test an upload in Puppeteer with Jest is to select the page’s real input[type="file"], resolve a fixture to an absolute path, call uploadFile(), and then assert the application’s actual upload result. If a button launches a native chooser instead, register waitForFileChooser() before clicking and pass the file to accept(). Selecting a file changes browser state; it does not prove that your server received or stored it.

Choose the upload path your page actually exposes

Start by inspecting the DOM, not the visual design. A styled drop zone often wraps a genuine file input. If that input is present and addressable, use ElementHandle.uploadFile(); this is Puppeteer’s normal upload API. Use the chooser API only when the action that starts selection is indirect or the input is not practical to access.

Page behavior Puppeteer approach What you must verify
A usable input[type="file"] Find the element and call uploadFile(path) The application submits or starts its upload request
A button opens a file chooser Start waitForFileChooser() and the click in one Promise.all, then call accept() The chooser is accepted or cancelled and the UI reports the result
Several files Pass an array of paths (or the documented variadic form for your installed version) The input has the application’s multiple behavior and all files are handled

Do not add or alter the multiple attribute merely to make a test pass. Your test should represent the production control.

Set up Jest, Puppeteer, and a deterministic fixture

Keep browser creation and cleanup in Jest hooks. Store a small fixture in the test project (for example, test/fixtures/report.pdf) or create one in a temporary directory during setup. The file must exist on the machine running Puppeteer, including a CI runner or remote test host.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import puppeteer from 'puppeteer';
import path from 'node:path';

describe('file upload', () => {
  let browser;
  let page;

  beforeAll(async () => {
    browser = await puppeteer.launch({ headless: true });
    page = await browser.newPage();
  });

  afterAll(async () => {
    await browser.close();
  });

  test('uploads the fixture', async () => {
    await page.goto('http://localhost:3000/upload');
    const fixture = path.resolve(process.cwd(), 'test/fixtures/report.pdf');
    const input = await page.waitForSelector('input[type="file"]');
    await input.uploadFile(fixture);
    await page.click('button[type="submit"]');
    await page.waitForSelector('[data-testid="upload-success"]');
  });
});

Use selectors that are stable in your application, such as data-testid values. Replace the success selector with the signal your product actually provides: a success message, a completed row, a redirect, or another state change.

Upload through a real file input

The direct method has four distinct stages: locate the input, provide an existing path, trigger the page’s upload action, and assert the result. uploadFile() selects the file for the page; it does not click Submit and does not wait for an API response.

  1. Resolve the fixture. Use path.resolve() so the path does not depend on the directory from which Jest was invoked.
  2. Wait for the actual input. A visible label or drop-zone container is not a file input. Target input[type="file"] or an application-specific selector on that element.
  3. Select the file. Call await input.uploadFile(absolutePath).
  4. Perform the application action. Click the submit button or invoke the UI operation that starts an asynchronous upload.
  5. Assert an application-level outcome. Wait for a success state, inspect a response, or verify the resulting navigation. A selected filename alone is only a client-side assertion.

If your page uploads immediately when the input changes, omit the submit click and wait for the UI or network-backed state that indicates completion. Avoid arbitrary sleeps when a selector or response can express readiness.

Testing the request as well as the UI

For a stronger test, wait for the request your application sends and assert its response status, then check the visible result. The exact endpoint and payload are application-specific, so keep those assertions next to the form’s contract rather than assuming a universal URL or success element.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handle a button-launched file chooser

When a button launches a chooser, the order of operations is critical. Puppeteer’s chooser reference requires waitForFileChooser() to be called before the chooser is launched. Register the wait and perform the click concurrently:

import path from 'node:path';

const filePath = path.resolve(process.cwd(), 'test/fixtures/report.pdf');

const [fileChooser] = await Promise.all([
  page.waitForFileChooser(),
  page.click('#upload-file-button'),
]);

await fileChooser.accept([filePath]);
await page.waitForSelector('[data-testid="upload-success"]');

The Promise.all prevents a race in which the click opens the chooser before Puppeteer starts listening. Always finish the chooser interaction: call accept() with the intended files or cancel() when testing cancellation. An unresolved chooser can block later interactions.

Chooser cancellation

const [chooser] = await Promise.all([
  page.waitForFileChooser(),
  page.click('#upload-file-button'),
]);
await chooser.cancel();
await page.waitForSelector('[data-testid="upload-cancelled"]');

This tests the page’s cancel path without selecting a file.

Upload multiple files

For an input that supports multiple selection, provide every fixture path. Puppeteer versions document both an array form and, in some versions, a variadic form; use the form documented for the version installed in your project.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const first = path.resolve(process.cwd(), 'test/fixtures/one.txt');
const second = path.resolve(process.cwd(), 'test/fixtures/two.txt');

const input = await page.waitForSelector('input[type="file"][multiple]');
await input.uploadFile([first, second]);

The chooser equivalent is:

const [chooser] = await Promise.all([
  page.waitForFileChooser(),
  page.click('#upload-files-button'),
]);
await chooser.accept([first, second]);

After selection, assert that the application displays both names or, preferably, that the server reports both uploads completed. If the production input does not permit multiple files, a test that supplies several paths is testing behavior users cannot perform.

Paths, fixtures, and CI environments

Relative paths resolve from the Node process’s current working directory, which can differ between a local shell, an IDE, and a CI job. Absolute paths are safer, especially when Puppeteer connects to remote Chrome: the file must be readable where the Puppeteer process runs, not merely on your workstation.

  • Resolve with path.resolve(process.cwd(), ...) or an equivalent path based on the test module.
  • Commit small, non-sensitive fixtures, or create them in Jest setup and remove them afterward.
  • Confirm the fixture is included in the CI checkout and is not excluded by ignore rules or artifact packaging.
  • Log the resolved path when diagnosing failures, but avoid logging confidential fixture contents.
  • If the browser and test runner are separated, place the file on the runner that calls Puppeteer and follow your remote-browser setup’s file-transfer requirements.

Assertions that prove a real upload

Separate three facts that are often conflated:

  1. Selection: the input or chooser accepted a path.
  2. Submission: the page made the intended form submission or upload request.
  3. Completion: the server accepted and processed the file, and the UI reflects that state.

A robust test waits for the page’s completion signal after submission. Depending on the product, that may be a success element, a status transition from “Uploading” to “Complete,” a navigation to a result page, or a request whose response you inspect. Do not invent a universal upload-success selector; define one in the application or adapt the assertion to the existing contract.

Common failures and precise fixes

Chooser timeout

Symptom: waitForFileChooser() times out. Cause: the click happened before the wait was registered, or the clicked control does not launch a native chooser. Fix: use the Promise.all ordering shown above and verify the control’s event actually opens a chooser. If a real input exists, use uploadFile() instead.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

No file selected

Symptom: the test finds an element but the form remains empty. Cause: the selector matched a styled wrapper, label, or drop-zone rather than the file input. Fix: inspect the DOM and target the actual input[type="file"], or use chooser handling for the button that owns the interaction.

Selection passes, server upload is absent

Symptom: the filename appears, but no record is stored. Cause: the test asserted selection only and never submitted, or the upload request failed. Fix: perform the page’s submit/change action and assert the response or completed UI state separately.

CI cannot find the file

Symptom: a local run passes but CI reports a missing path. Cause: a relative path resolved from another working directory, or the fixture was not checked out. Fix: resolve an absolute path, print it during diagnosis, and ensure the fixture is part of the CI workspace.

Unsupported picker API

Symptom: a page using window.showOpenFilePicker does not respond to the documented chooser interception. Cause: that API is outside the documented waitForFileChooser() support described for this workflow. Fix: test the application through a real file input where possible, or provide a testable input path rather than assuming chooser interception covers the picker API.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make the test maintainable and reliable

  • Use one fixture per behavior. Keep files small and choose types that exercise validation (for example, an allowed PDF and a rejected extension).
  • Wait on conditions, not time. Prefer selectors, navigation completion, or request promises to fixed delays.
  • Keep lifecycle hooks isolated. Launch once per suite when state can be reset safely; use a fresh page or browser when tests would otherwise share upload state.
  • Exercise negative paths. Add cases for cancellation, size/type rejection, duplicate names, and server errors using the same selection mechanism.
  • Preserve diagnostics. On failure, capture the resolved path, current URL, visible error text, and (where appropriate) the failed response status.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a rendered screenshot of an upload result or another page state rather than an end-to-end file-selection test, ScreenshotNeo can return a screenshot or PDF from one request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo documentation for all options. A direct call looks like this:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The same request in Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

And Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Sign up for ScreenshotNeo if that fits your capture workflow.

FAQ

Can Puppeteer upload a file that exists only on my laptop?

No. The path must exist where the Puppeteer process runs. Copy or generate the fixture in the runner’s workspace, especially in CI or remote-browser arrangements.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does calling uploadFile() submit the form?

No. It selects the file for the page. Trigger the application’s submit or change-driven upload and assert its resulting response or UI state.

Should I intercept window.showOpenFilePicker with waitForFileChooser()?

Not on the basis of the documented chooser API. That picker API is outside the documented interception support, so use a real file input or a dedicated test seam.

Frequently Asked Questions

Can Puppeteer upload a file that exists only on my laptop?

No. The path must exist where the Puppeteer process runs, including a CI runner or remote test host.

Does calling uploadFile() submit the form?

No. It selects the file; your test must trigger the application upload and verify the resulting state.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should waitForFileChooser() intercept window.showOpenFilePicker?

That picker API is outside the documented chooser interception support; use a real file input or a test-specific seam.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.