Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Principle:MarketSquare Robotframework browser JavaScript Extension Integration

From Leeroopedia
Revision as of 18:23, 16 February 2026 by Admin (talk | contribs) (Auto-imported from principles/MarketSquare_Robotframework_browser_JavaScript_Extension_Integration.md)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

JavaScript Extension Integration

Overview

The robotframework-browser library enables a powerful cross-language integration pattern: Python keyword methods can load and call JavaScript functions that run in the Node.js process alongside Playwright. This bridges the Python test layer with the JavaScript browser automation layer, allowing plugins to perform operations that require direct access to Playwright's JavaScript API, DOM manipulation, or custom browser-side logic.

Core Concept: Python-to-JavaScript Bridge

The Browser library operates as a two-process system:

  • A Python process running Robot Framework and the Browser library
  • A Node.js process running Playwright and handling browser automation via gRPC

JavaScript extensions allow plugin authors to write functions in JavaScript that execute in the Node.js process. These functions have direct access to Playwright objects and can perform operations that would be difficult or impossible to express purely through the gRPC protocol.

+------------------+       gRPC       +------------------+
|  Python Process  | <--------------> |  Node.js Process |
|  (Robot Framework|                  |  (Playwright)    |
|   + Plugins)     |                  |  + JS Extensions |
+------------------+                  +------------------+

How JavaScript Extensions Work

1. Extension Registration

A plugin calls self.initialize_js_extension(path) during its __init__ method. This sends the JavaScript file path to the Node.js process via gRPC, which loads the module and registers its exported functions as callable keywords.

class MyPlugin(LibraryComponent):
    def __init__(self, library: Browser) -> None:
        super().__init__(library)
        self.initialize_js_extension(
            Path(__file__).parent.resolve() / "my_extension.js"
        )

2. JavaScript Function Definition

The JavaScript file exports async functions. Each exported function becomes a callable keyword:

async function mouseWheel(x, y, logger, page) {
    logger(`Mouse wheel at ${x}, ${y}`);
    await page.mouse.wheel(x, y);
    return await page.evaluate("document.scrollingElement.scrollTop");
}

exports.__esModule = true;
exports.mouseWheel = mouseWheel;

3. Keyword Invocation

From a Python keyword method, the plugin calls the JavaScript function by name:

@keyword
def mouse_wheel(self, x: int, y: int):
    return self.call_js_keyword("mouseWheel", y=y, x=x)

Reserved Parameter Names

JavaScript extension functions use reserved parameter names to receive Playwright objects automatically. When the Node.js process invokes the function, parameters with these names are injected with live Playwright instances rather than passed from the Python side:

Parameter Name Injected Object Description
page Playwright Page The currently active page object. Provides access to all page-level Playwright APIs.
context Playwright BrowserContext The current browser context. Used for context-level operations like cookies, permissions, etc.
browser Playwright Browser The browser instance. Used for browser-level operations.
logger Logger function A logging function that sends log messages back to the Robot Framework log.
playwright Playwright module The Playwright module itself. Provides access to selectors, devices, and other module-level APIs.

These reserved parameters are identified on the Python side and replaced with the sentinel value "RESERVED" before transmission. The Node.js process detects this sentinel and substitutes the actual Playwright objects:

# From Browser/browser.py - call_js_keyword method
reserved = {
    "logger": "RESERVED",
    "playwright": "RESERVED",
    "page": "RESERVED",
    "context": "RESERVED",
    "browser": "RESERVED",
}

Argument Passing

Non-reserved arguments are serialized as JSON and transmitted to the Node.js process. The JavaScript function receives them as native JavaScript values. This means:

  • Python str, int, float, bool, None map to their JavaScript equivalents
  • Python dict maps to JavaScript objects
  • Python list maps to JavaScript arrays
  • Complex Python objects must be serialized before passing

Return Values

The JavaScript function's return value is serialized to JSON and sent back to Python. If the function returns undefined or no value, the Python side receives None. Otherwise, the JSON response is parsed back into Python objects.

Logging from JavaScript

The logger parameter in JavaScript extensions provides a way to send log messages back to the Robot Framework log output. Each call to logger("message") creates a log entry that appears in the Robot Framework report:

async function myFunction(logger, page) {
    logger("Starting custom operation");
    // ... do work ...
    logger("Operation completed successfully");
}

These log messages appear at the INFO level in Robot Framework's log output.

Use Cases

JavaScript extensions are particularly useful for:

  • Custom mouse/keyboard interactions not exposed through standard keywords (e.g., mouse wheel scrolling)
  • Direct DOM queries that bypass Playwright's selector engine
  • Browser API access such as Web Storage, Service Workers, or IndexedDB
  • Performance measurements using the Navigation Timing API or Performance Observer
  • Custom wait conditions expressed as JavaScript predicates
  • Integration with page-specific JavaScript APIs in single-page applications

Two Loading Paths

There are two ways JavaScript extensions can be loaded:

1. Via the jsextension library argument:

*** Settings ***
Library    Browser    jsextension=${CURDIR}/my_extension.js

Keywords from this path are created automatically as methods on a LibraryComponent instance using dynamic code generation.

2. Via a plugin's __init__ method:

class MyPlugin(LibraryComponent):
    def __init__(self, library):
        super().__init__(library)
        self.initialize_js_extension(Path(__file__).parent / "extension.js")

In this path, the plugin author calls initialize_js_extension to register the functions, then creates Python keyword methods that call self.call_js_keyword(). This gives the plugin author control over argument handling, return value processing, and keyword naming.

Domains

Implemented By

Related

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment