Skip to content

Test files and uploads

Many flows need a file: a CV for a job application, an avatar, a CSV import, a PDF invoice. Upload those files to the project once. Every UI test in the project can then attach them to upload fields, and you refer to them in plain English: “upload the resume”.

The same files serve API tests: an uploaded OpenAPI or Swagger spec (.json, .yaml or .yml) can be picked as the test’s spec.

  1. Open the project and go to the Files tab.

  2. Drop files onto the upload area, or click it to browse. Each file can be up to 25 MB.

  3. The file appears under Stored files with its size and its path, for example /test-files/<project-id>/resume.pdf. Use the copy button if you want the exact path.

The project’s Files tab with the upload area and the stored files list

You need the editor role to upload or delete files. Viewers can see the list.

File names are made safe on upload: everything except letters, digits, ., _ and - becomes _, so My CV (final).pdf is stored as My_CV__final_.pdf. Uploading a file with the same name replaces the old one.

Mention the file in the description by name or by what it is:

What should the test do?
Go to /careers/frontend-engineer and click "Apply".
Fill in the form with a realistic name and a unique email address.
Upload the resume (resume.pdf) in the "CV" field and submit.
Verify the page says the application was received.

How the agent finds it: when a UI test runs, Plainwright lists every file in the project at the end of the prompt, with its full path, and tells the agent to use one of those exact paths when it uploads. The agent clicks the upload field and calls browser_file_upload with the path. It never reads the file’s contents.

Uploads are confined to the run’s own project. A path must name a file directly inside /test-files/<project-id>/ of the project the test belongs to (a bare file name such as resume.pdf is looked up there too). Another project’s files, paths with .., files in the root of the folder and files that don’t exist are refused with “File upload refused: …”, and the agent sees that error.

Files are stored on disk under TEST_FILES_DIR (default /test-files), one folder per project. In the Compose setup that folder is ./test-files next to docker-compose.yml, mounted into the containers that need it:

  • Directorytest-files/
    • Directory2e52323e-1ec8-49d3-993b-61bb7c7e0ea9/ one folder per project id
      • resume.pdf
      • avatar-square.png
      • openapi.yaml
Container Mount Why
backend read-write saves uploads and deletes files
worker read-only lists the files into the test prompt
the three playwright-mcp services read-only the browser reads the file when it uploads it

The path the agent uses, /test-files/<project-id>/<name>, is the path inside the browser containers. That’s why the folder must be mounted at the same place in all of them.

Terminal window
# List
curl -s "$PW/api/projects/PROJECT_ID/files" -H "Authorization: Bearer $TOKEN"
# Upload (multipart field "file")
curl -s -X POST "$PW/api/projects/PROJECT_ID/files" \
-H "Authorization: Bearer $TOKEN" -F "file=@./resume.pdf"
# Delete
curl -s -X DELETE "$PW/api/projects/PROJECT_ID/files/resume.pdf" -H "Authorization: Bearer $TOKEN"

Each file is returned as {"name", "path", "size_bytes"}. A file over 25 MB returns 413.

  • Deleting a file breaks the tests that upload it. They fail at the upload step until you add it again.
  • Files are not secrets. They aren’t encrypted, and their names are sent to the model in every UI test prompt of the project. Don’t put credentials or personal data in files or file names; use the secrets vault for credentials.
  • Only paths are shared with the model. The exception is an OpenAPI spec chosen in an API test, whose endpoint summary is added to that test’s prompt.
  • The template “File upload” (in New test → From a template) gives you a ready-made upload test that asks for the upload page and the file name.