BROWSER

Running it

The Node process

Browser starts a Node process to talk to Playwright. What that costs, how to share one across parallel runs, and how to pass it Node flags.

Browser is two halves. The Python half is the Robot Framework library you import; the Node half drives Playwright. They talk over gRPC on a local port, and the Python half starts the Node half for you the first time you call a Browser keyword.

Most of the time you never think about this. It matters in two situations: when you run many suites in parallel, and when you need to debug the Node side itself.

What the startup costs

Starting the Node process is the slowest single step in a run: a process launch plus a gRPC health handshake, once per Robot Framework execution. It is paid at the first Browser keyword rather than at import — the library starts the process lazily — and on a single suite it disappears into the noise.

Under Pabot it stops being noise. Pabot runs each suite in its own Robot Framework process, so each one starts its own Node process. Twelve parallel suites means twelve Node processes, twelve startups, and twelve copies of the Playwright runtime resident at once.

Sharing one Node process

You can start the Node side yourself, once, and point every run at it. Start it from the directory where the Browser package is installed:

PLAYWRIGHT_BROWSERS_PATH=0 node Browser/wrapper/index.js 127.0.0.1 12345

The two arguments are the host and the port, in that order — both are required, and with only one the script exits with No port defined. Then tell the runs where to find it:

ROBOT_FRAMEWORK_BROWSER_NODE_PORT=12345 pabot --processes 12 tests/

The same thing can be said at import time instead, which is better when only some suites should share and the rest should stay independent:

*** Settings ***Library    Browser    playwright_process_port=12345

There is a matching playwright_process_host, which defaults to 127.0.0.1. On its own it only changes the address the spawned process binds to; combined with a port it is the address the library connects to. If both port settings are set the import parameter wins: the port from playwright_process_port is checked first and the variable is only a fallback. There is no environment variable for the host — that is playwright_process_host only.

The environment variable is the blunt instrument — it applies to everything in that shell — and the import parameter is the precise one.

What you give up

Sharing genuinely removes that startup from every run but the first. It also means:

  • One failure domain. If the shared process dies, every run pointed at it fails, not just the one that killed it.
  • Teardown is per connection, not global. A finishing run closes the browsers it opened. The Node side keeps separate state per client connection, so other runs on the same process are untouched — runs only share state if they deliberately call Set Peer Id with the same value.
  • You own its lifecycle. Nothing starts it for you and nothing cleans it up. In CI that means a start step, a wait, and a teardown that runs even when the suite fails.
  • Debugging gets harder. Logs from twelve suites interleave in one process.

Measure before you adopt it. If your suite takes four minutes and startup takes two seconds, this is not your bottleneck — and a shared process is a new thing that can break in exchange for nothing.

Passing Node flags

ROBOT_FRAMEWORK_BROWSER_NODE_DEBUG_OPTIONS is passed through to the Node process as command-line arguments. Multiple arguments are separated by commas — not spaces, which is the mistake to expect:

ROBOT_FRAMEWORK_BROWSER_NODE_DEBUG_OPTIONS=--inspect,--trace-warnings robot tests/

--inspect opens the Node inspector, so you can attach Chrome DevTools or an IDE debugger to the Playwright side. That is occasionally the only way to understand a failure that looks impossible from the Robot Framework log.

Keep it out of production runs. An open inspector port is a debugging affordance, not something you want on a CI runner.

Where to look next