Implementation:MarketSquare Robotframework browser Js Module Exports Pattern
Document Type
Pattern Doc -- Describes the canonical CommonJS module structure for JavaScript extension files that provide custom Robot Framework keywords to the Browser library.
Pattern Summary
A JavaScript extension module must use CommonJS exports with exports.__esModule = true and individually exported functions. Each exported function becomes a Robot Framework keyword. Functions may be async or synchronous, may use reserved parameter names for Playwright object injection, and may attach a .rfdoc property for documentation.
Source References
| File | Lines | Description |
|---|---|---|
atest/test/05_JS_Tests/funky.js |
L1-74 | Comprehensive example with multiple function styles |
atest/test/05_JS_Tests/another.js |
L1-5 | Minimal example with arrow function export |
Comprehensive Example
The following is the complete funky.js extension module from the test suite, demonstrating all supported patterns:
async function myFunkyKeyword(page, args, logger) {
const h = await page.locator(args[0]);
logger("Logging something funky");
return await h.evaluate((e) => e.textContent = "Funk yeah!");
}
async function myNewStyleFunkyKeyword(selector, page, logger) {
const h = await page.$(selector);
logger("Logging something funky here");
return await h.evaluate((e) => e.textContent = "Funk yeah again!");
}
let browserServer;
async function createRemoteBrowser(logger, playwright) {
logger("Launching chromium server");
browserServer = await playwright.chromium.launchServer({headless: false});
logger("Returning server address");
return browserServer.wsEndpoint();
}
async function withDefaultValue(a = "default") {
return a.toUpperCase();
}
withDefaultValue.rfdoc = "This function returns the default value if no argument is passed";
async function closeRemoteBrowser() {
return browserServer.kill();
}
function crashKeyword() {
throw Error("Crash");
}
async function moreDefaults(bTrue = true,
bFalse = false,
integer = 123,
floater = 1.3,
text = "hello",
nothing = null,
undefineder = undefined) {
return {
bTrue,
bFalse,
integer,
floater,
text,
nothing,
"undefineder": undefineder || null
};
}
async function contextAndBrowserDemo(message, context, browser, logger) {
logger(`Message: ${message}`);
logger(`Browser: ${browser}`);
logger(`Context: ${context}`)
const pages = context.pages();
logger(`Number of pages in context: ${pages.length}`);
return {
message,
browserType: browser?.browserType().name() ?? "NO BROWSER",
pageCount: pages.length
};
}
exports.__esModule = true;
exports.myFunkyKeyword = myFunkyKeyword;
exports.createRemoteBrowser = createRemoteBrowser;
exports.closeRemoteBrowser = closeRemoteBrowser;
exports.crashKeyword = crashKeyword;
exports.withDefaultValue = withDefaultValue;
exports.moreDefaults = moreDefaults;
exports.myNewStyleFunkyKeyword = myNewStyleFunkyKeyword;
exports.contextAndBrowserDemo = contextAndBrowserDemo;
Minimal Example
The another.js file shows the smallest viable extension module using an arrow function:
exports.__esModule = true;
exports.myOtherKeyword = (arg, logger) => {
logger("Logging something else");
return arg;
}
This produces a single keyword My Other Keyword that accepts one user argument (arg) and one reserved parameter (logger).
Pattern Breakdown
1. Module Header: __esModule Flag
exports.__esModule = true;
This flag is required. It signals to the Browser library's module loader that this is a properly structured extension module following ES module interop conventions.
2. Legacy Style: args Array
async function myFunkyKeyword(page, args, logger) {
const h = await page.locator(args[0]);
// ...
}
The args parameter maps to Python's *args. The caller passes positional arguments which arrive as an array. Reserved parameters page and logger are injected automatically.
3. New Style: Named Parameters
async function myNewStyleFunkyKeyword(selector, page, logger) {
const h = await page.$(selector);
// ...
}
The selector parameter is a named user-supplied argument. This style is preferred for clarity and type safety.
4. Default Values
async function moreDefaults(bTrue = true,
bFalse = false,
integer = 123,
floater = 1.3,
text = "hello",
nothing = null,
undefineder = undefined) {
Default values are introspected by the Node.js side and reported to Python. The Python wrapper converts them using the mapping defined in _js_value_to_python_value():
| JS Default | Python Default |
|---|---|
true |
True
|
false |
False
|
null |
None
|
undefined |
None
|
123 |
123
|
1.3 |
1.3
|
"hello" |
"hello"
|
5. Custom Documentation with .rfdoc
withDefaultValue.rfdoc = "This function returns the default value if no argument is passed";
The .rfdoc property is read during InitializeExtension and passed to the Python side as the keyword's docstring.
6. Error Throwing
function crashKeyword() {
throw Error("Crash");
}
Synchronous functions that throw are supported. The error propagates through gRPC and becomes a Robot Framework failure.
7. Context and Browser Access
async function contextAndBrowserDemo(message, context, browser, logger) {
const pages = context.pages();
return {
message,
browserType: browser?.browserType().name() ?? "NO BROWSER",
pageCount: pages.length
};
}
Reserved parameters context and browser provide access to the active BrowserContext and Browser objects, enabling advanced multi-page and multi-browser operations.
8. Export Block
exports.__esModule = true;
exports.myFunkyKeyword = myFunkyKeyword;
exports.createRemoteBrowser = createRemoteBrowser;
// ... additional exports
Every function that should be exposed as a keyword must be explicitly assigned to exports. Functions defined in the module but not exported are private and will not become keywords.
Keywords Generated
From funky.js, the following Robot Framework keywords are registered:
| Function Name | Keyword Name | User Arguments | Reserved Parameters |
|---|---|---|---|
myFunkyKeyword |
My Funky Keyword | *args |
page, logger |
myNewStyleFunkyKeyword |
My New Style Funky Keyword | selector | page, logger |
createRemoteBrowser |
Create Remote Browser | (none) | logger, playwright |
closeRemoteBrowser |
Close Remote Browser | (none) | (none) |
crashKeyword |
Crash Keyword | (none) | (none) |
withDefaultValue |
With Default Value | a="default" | (none) |
moreDefaults |
More Defaults | bTrue=True, bFalse=False, integer=123, floater=1.3, text="hello", nothing=None, undefineder=None | (none) |
contextAndBrowserDemo |
Context And Browser Demo | message | context, browser, logger |
Related
- MarketSquare_Robotframework_browser_JavaScript_Module_Authoring -- Principle document for JS module authoring rules
- MarketSquare_Robotframework_browser_Babel_Transpile -- Transpiling ES2015+ to this CommonJS pattern
- MarketSquare_Robotframework_browser_Create_Lib_Component_From_Jsextension -- How these exports are loaded and registered