How to upload files in Playwright

File inputs in browser tests need a different approach from ordinary clicks because the native file picker lives outside the page DOM. Playwright handles that boundary by attaching a local fixture file directly to an <input type="file"> control and letting the page react as if a user selected the file.

The locator-based setInputFiles() method targets the file input through the same selectors used for other actions. Relative file paths are resolved from the current working directory, so upload fixtures should live in predictable project paths such as tests/fixtures/.

Keep upload samples small and safe to commit with the test suite. A browser-side upload check proves that the input received the file and that client-side validation or preview code ran; server-side upload handling still needs a submit action and a response, database, or artifact assertion.

Steps to upload files in Playwright tests:

  1. Open the existing Playwright project.
    $ cd ~/projects/playwright-demo

    Install Playwright first when the project does not already have @playwright/test.
    Related: How to install Playwright with npm

  2. Create a directory for upload fixtures.
    $ mkdir -p tests/fixtures
  3. Create a small fixture file.
    $ printf "invoice upload sample\n" > tests/fixtures/invoice.txt
  4. Create tests/file-upload.spec.ts with a file input and upload assertion.
    import { test, expect } from "@playwright/test";
    import path from "path";
    
    test("uploads invoice fixture", async ({ page }) => {
      const uploadPath = path.join(process.cwd(), "tests/fixtures/invoice.txt");
    
      await page.setContent(`
        <label>
          Upload file
          <input type="file" name="attachment">
        </label>
        <p id="chosen">No file selected</p>
        <script>
          document.querySelector("input").addEventListener("change", event => {
            const file = event.target.files[0];
            document.querySelector("#chosen").textContent =
              file ? file.name + " (" + file.size + " bytes)" : "No file selected";
          });
        </script>
      `);
    
      await page.getByLabel("Upload file").setInputFiles(uploadPath);
    
      await expect(page.locator("#chosen")).toHaveText("invoice.txt (22 bytes)");
    
      const fileName = await page.getByLabel("Upload file").evaluate(
        (input: HTMLInputElement) => input.files?.[0]?.name
      );
      expect(fileName).toBe("invoice.txt");
    });

    Replace page.setContent() with page.goto() and the application's upload control when validating a real page. Pass an array to setInputFiles() for a multiple file input, or pass an empty array when a reset test needs to clear the selection.

  5. Run the upload spec.
    $ npx playwright test tests/file-upload.spec.ts
    
    Running 1 test using 1 worker
    
      ✓  1 tests/file-upload.spec.ts:4:5 › uploads invoice fixture (482ms)
    
      1 passed (1.4s)

    The passing spec confirms that Playwright attached the fixture to the input and that browser-side change handling read the selected file. Add a submit and response assertion when the application must prove server-side storage.
    Related: How to run specific Playwright tests