Extending Browser
Just enough JavaScript
The JavaScript you need to write extensions and Evaluate JavaScript, for people who write Python.
You do not need to know JavaScript to use Browser. You need a little to extend it. This page is the little.
Two different JavaScripts
The most common confusion, and worth getting straight first:
| Where it runs | What exists there | Reached by |
|---|---|---|
Node-side code drives the browser. Page-side code runs inside it. document
does not exist in Node; Playwright does not exist in the page.
Variables
Use const by default and let when you must reassign.
Strings
'single' and "double" are the same. Backticks interpolate:
Equality
The one that catches Python developers:
Use ===. Always.
Functions and arrow functions
Arrow functions are what you will mostly write, because callbacks are everywhere:
Objects and arrays
A JS object is close to a Python dict, with unquoted keys:
Arrays have the methods you want — map, filter, forEach, find. But many
DOM results are iterable without being arrays, so convert first:
Forgetting Array.from is the single most common mistake here:
document.querySelectorAll(...) has no .map.
async and await
Almost every Playwright call is asynchronous:
An async function returns a promise. await waits for one.
Browser awaits whatever your keyword returns, so a promise returned directly
resolves fine. A promise tucked inside the returned value does not — it is
serialised as {}. If one field of your result comes back empty, a missing
await is usually why.
Evaluate JavaScript
You can run page-side JavaScript from Robot Framework directly, which is often enough without writing an extension at all.
Three things about its shape, because they are what people get wrong first:
- The first argument is a selector, and it cannot be omitted — pass
${None}when you do not want one. - With a selector, the function receives
(element, arg); withall_elements=Trueit receives(elements, arg)— that flag is what makes it an array. With no selector it receives just(arg). - The return value must be JSON-serialisable. Returning nothing gives you an
empty string, and a DOM node comes back as the useless string
ref: <Node>.
Compare that with doing it keyword by keyword:
Both are correct. The first is one round trip to the browser; the second is one
per element, plus one for Get Elements. On a page with a hundred links, that
difference is visible.
Returning an object works too, and arrives in Robot Framework as a dictionary: