Implementation:MarketSquare Robotframework browser Initialize Js Extension
Initialize JavaScript Extension API
Type
API Doc
Source
Browser/base/librarycomponent.py, lines 218-224
APIs
initialize_js_extension
def initialize_js_extension(
self, js_extension_path: Path | str
) -> Response.Keywords:
return self.library.init_js_extension(js_extension_path=js_extension_path)
Parameters:
| Parameter | Type | Description |
|---|---|---|
js_extension_path |
str | Absolute or relative path to a JavaScript file containing exported async functions. The path is resolved to an absolute path before being sent to the Node.js process. |
Returns: Response.Keywords
The return value is a protobuf message containing three repeated fields:
| Field | Type | Description |
|---|---|---|
.keywords |
repeated string |
Names of the exported JavaScript functions discovered in the file. |
.keywordArguments |
repeated string |
Comma-separated argument names (including defaults) for each function. |
.keywordDocumentations |
repeated string |
Documentation strings for each function (if provided in the JS source). |
Underlying Implementation:
The method delegates to Browser.init_js_extension() at Browser/browser.py lines 1011-1017:
def init_js_extension(self, js_extension_path: Path | str) -> Response.Keywords:
with self.playwright.grpc_channel() as stub:
return stub.InitializeExtension(
Request().FilePath(
path=str(Path(js_extension_path).resolve().absolute())
)
)
This sends an InitializeExtension gRPC request with the resolved absolute file path. The Node.js process loads the JavaScript module, inspects its exports, and returns metadata about the discovered functions.
call_js_keyword
def call_js_keyword(self, keyword_name: str, **args) -> Any:
return self.library.call_js_keyword(keyword_name, **args)
Parameters:
| Parameter | Type | Description |
|---|---|---|
keyword_name |
str |
The name of the JavaScript function to call. Must match an exported function name from a previously loaded extension. |
**args |
Any |
Keyword arguments to pass to the JavaScript function. Argument names matching reserved names (page, logger, playwright, context, browser) are automatically replaced with "RESERVED" and injected on the Node.js side.
|
Returns: Any
The return value is the deserialized JSON response from the JavaScript function. Returns None if the JavaScript function returns undefined or produces an empty response.
Underlying Implementation:
The method delegates to Browser.call_js_keyword() at Browser/browser.py lines 1086-1109:
def call_js_keyword(self, keyword_name: str, **args) -> Any:
reserved = {
"logger": "RESERVED",
"playwright": "RESERVED",
"page": "RESERVED",
"context": "RESERVED",
"browser": "RESERVED",
}
_args_browser_internal = {
"arguments": [
(arg_name, reserved.get(arg_name, value))
for arg_name, value in args.items()
]
}
with self.playwright.grpc_channel() as stub:
responses = stub.CallExtensionKeyword(
Request().KeywordCall(
name=keyword_name,
arguments=json.dumps(_args_browser_internal),
)
)
for response in responses:
logger.info(response.log)
if response.json == "":
return None
The method:
- Builds an argument list, replacing reserved parameter names with
"RESERVED" - Serializes the arguments as JSON
- Sends a
CallExtensionKeywordgRPC request - Iterates over the streaming response, logging each message
- Parses and returns the final JSON response
Usage: Called from Plugin __init__
The typical usage pattern is to call initialize_js_extension during plugin construction and then use call_js_keyword in keyword methods:
from pathlib import Path
from robot.api.deco import keyword
from Browser import Browser
from Browser.base.librarycomponent import LibraryComponent
class MyPlugin(LibraryComponent):
def __init__(self, library: Browser) -> None:
super().__init__(library)
# Load the JavaScript extension during initialization
response = self.initialize_js_extension(
Path(__file__).parent.resolve() / "my_extension.js"
)
# Optionally inspect what was loaded
for kw_name in response.keywords:
print(f"Loaded JS keyword: {kw_name}")
@keyword
def my_custom_scroll(self, x: int, y: int):
"""Scrolls the page using a custom JavaScript function."""
return self.call_js_keyword("customScroll", x=x, y=y)
JavaScript Extension File Format
The JavaScript file must export functions using CommonJS or ES module syntax:
// my_extension.js
async function customScroll(x, y, logger, page) {
logger(`Scrolling to ${x}, ${y}`);
await page.mouse.wheel(x, y);
return await page.evaluate("document.scrollingElement.scrollTop");
}
exports.__esModule = true;
exports.customScroll = customScroll;
Key rules for JavaScript extensions:
- Functions should be
async(they run in an async context) - Reserved parameter names (
page,logger,playwright,context,browser) receive live Playwright objects - Non-reserved parameters receive values passed from the Python
call_js_keywordcall - Return values are serialized to JSON and sent back to Python
- Use
logger()to emit log messages visible in Robot Framework reports
Example from the Test Suite
From atest/test/09_Plugins/jsplugin.js:
async function mouseWheel(x, y, logger, page) {
logger(`Mouse wheel at ${x}, ${y}`);
await page.mouse.wheel(x, y);
logger("Returning a funny string");
return await page.evaluate("document.scrollingElement.scrollTop");
}
exports.__esModule = true;
exports.mouseWheel = mouseWheel;
Called from atest/test/09_Plugins/ExamplePlugin.py:
@keyword
def mouse_wheel(self, x: int, y: int):
"""This keyword calls a custom javascript keyword from the file jsplugin.js."""
return self.call_js_keyword("mouseWheel", y=y, x=x)
Error Handling
- If the JavaScript file path does not exist or cannot be loaded, the gRPC call raises an error during
initialize_js_extension - If a called keyword name does not match any loaded function,
CallExtensionKeywordraises a gRPC error - JavaScript runtime exceptions are captured and propagated back as gRPC errors
Related
- MarketSquare_Robotframework_browser_JavaScript_Extension_Integration -- Principle: how JavaScript extensions bridge Python and Node.js
- MarketSquare_Robotframework_browser_LibraryComponent_Init -- The base class providing these methods
- MarketSquare_Robotframework_browser_Keyword_Decorator_Pattern -- How Python keyword methods wrap JS extension calls