MarketSquare / MarketSquare/robotframework-browser

Extend `HTTP` keyword with `multipart=` file uploads, `form=`, `params=`, `timeout=` and `fail_on_status_code=`

Open
#5,109 0 comments 0 reactions 1 assignee View on GitHub

@Snooz82 is already working on this.

Since Aug 6, 2026.

enhancement
Dominant language
Python
Stars
655
Forks
147
Avg merge
5h 27m
Merged PRs (30d)
59

Description

## Use case

The `HTTP` keyword is the library's way to make API calls that automatically share cookies and session state with the open browser page — the main reason to use it over RequestsLibrary. But today it only supports `url`, `method`, `body` and `headers`, which leaves several very common API-testing needs uncovered:

- **File uploads (the headline of this issue):** there is currently *no way at all* to POST a file (`multipart/form-data`) via the `HTTP` keyword. Avatar upload, document upload and import endpoints are extremely common in real suites, and users are forced to fall back to RequestsLibrary — losing exactly the automatic browser-session cookie sharing they came for.
- **Classic form posts:** endpoints backed by HTML forms (login forms, legacy APIs) expect `application/x-www-form-urlencoded` bodies. Users must hand-build the encoded string and set the `Content-Type` header themselves.
- **Query parameters:** special characters must be URL-encoded manually when concatenating query strings.
- **Timeouts:** every other Browser keyword exposes a `timeout` argument; `HTTP` is an outlier where a hung endpoint can stall a test indefinitely with no keyword-level control.
- **Fail-fast on error status:** asserting `${res.status}` works, but a fail-fast option matching Playwright's `failOnStatusCode` saves a line in every API test and produces a clearer error at the point of failure.

## Proposed keyword / arguments

All new arguments are named-only with `None`/behavior-preserving defaults:

```
HTTP url method=GET body=None headers=None
... *, multipart=None form=None params=None timeout=None fail_on_status_code=False
```

- `multipart` — dict of form fields; each value is either a string or a file spec `{name, mimeType, path}` (or `buffer` instead of `path`), mirroring how `Upload File By Selector` already handles files. Sends the request as `multipart/form-data`.
- `form` — dict, URL-encoded by the library and sent with `Content-Type: application/x-www-form-urlencoded`.
- `params` — dict of query parameters, URL-encoded and merged into the request URL.
- `timeout` — Robot Framework time format (e.g. `10 s`), per-request timeout.
- `fail_on_status_code` — when `True`, the keyword fails if the response status is not in the 200–399 range.

`multipart`, `form` and `body` are mutually exclusive.

```robotframework
*** Test Cases ***
Upload Avatar Via API
&{file}= Create Dictionary name=avatar.png mimeType=image/png path=${CURDIR}/testdata/avatar.png
&{fields}= Create Dictionary description=Profile picture file=${file}
&{res}= HTTP /api/users/me/avatar method=POST multipart=${fields} timeout=10 s
Should Be Equal ${res.status} ${201}

Legacy Form Login
&{creds}= Create Dictionary username=demo password=mode
&{res}= HTTP /login method=POST form=${creds} fail_on_status_code=True

Search With Query Params
&{query}= Create Dictionary q=robot framework page=1
&{res}= HTTP /api/search params=${query}
Should Be Equal ${res.body.results[0].name} robot framework
```

## Playwright API

Maps to the request options of `APIRequestContext.fetch`/`post`/etc.: [`multipart`](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-fetch-option-multipart), [`form`](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-fetch-option-form), [`params`](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-fetch-option-params), [`timeout`](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-fetch-option-timeout), [`failOnStatusCode`](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-fetch-option-fail-on-status-code).

Note: the library's `HTTP` keyword is implemented as `fetch()` inside the page (`page.evaluate` in `node/playwright-wrapper/network.ts`), not via a real `APIRequestContext`, so these options are implemented as equivalents, not direct pass-throughs.

## Implementation notes

- `protobuf/playwright.proto`: extend the `HttpRequest` message with the new fields (multipart spec, form dict, params dict, timeout, fail flag).
- `node/playwright-wrapper/network.ts` (`httpRequest`): the **multipart body must be built node-side** — file contents are read from disk in the Node process (large/binary payloads cannot reasonably cross the gRPC/JSON boundary or be constructed as a binary `FormData` inside `page.evaluate` from Python-supplied data), then assembled into the `multipart/form-data` body and handed to the in-page `fetch`. `form`/`params` are simple encoding steps; `timeout` via `AbortController` around the fetch.
- `Browser/keywords/network.py` (`http`): new named-only arguments, validation of mutually exclusive body arguments, docs.
- Stub regeneration (`inv build`), atest coverage against the test app including an upload endpoint.

## Backwards compatibility

Fully additive: all five arguments are new, named-only, and default to values that reproduce the current behavior exactly (`None`/`False`). Existing calls are unaffected.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.