2.1 Python Plugin API
Python plugin API allows extending the Browser library directly by adding new keywords or replacing existing keywords. The plugin API is provided by PythonLibCore and is similar to what is found in SeleniumLibrary.
Python plugin API differs from JavaScript Plugin. The main difference is that the JavaScript plugin allows you to write code only on the NodeJS side. The Python plugin API allows using both Python and JavaScript when creating keywords. For example, with the Python plugin API it is possible to use AssertionEngine.
2.1.1 Python only plugin
The simplest way to use the Python plugin API is to create new keywords only on the Python side. For example, given this Python class:
import json
from robot.api import logger
from robot.api.deco import keyword
from Browser.base.librarycomponent import LibraryComponent
from Browser.generated.playwright_pb2 import Request
class SimplePythonPlugin(LibraryComponent):
@keyword
def new_plugin_cookie_keyword_with_grpc(self) -> dict:
"""Uses grpc to directly call node side function."""
with self.playwright.grpc_channel() as stub:
response = stub.GetCookies(Request().Empty())
cookies = json.loads(response.json)
logger.debug(json.dumps(cookies, indent=4))
assert len(cookies) == 1, "Too many cookies."
return {"name": cookies[0]["name"], "value": cookies[0]["value"]}
@keyword
def other_plugin_cookie_keyword_with_public_api(self) -> dict:
"""Use Browser public API to create new keyword"""
cookies = self.library.get_cookies()
logger.debug(json.dumps(cookies, indent=4))
assert len(cookies) == 1, "Too many cookies."
return {"name": cookies[0]["name"], "value": cookies[0]["value"]}
and this test:
*** Settings ***
Library Browser
... enable_playwright_debug=${True}
... plugins=${CURDIR}/SimplePythonPlugin.py
... auto_closing_level=SUITE
*** Test Cases ***
Simple Plugin Example With GRPC
[Setup] New Page https://github.com/MarketSquare/robotframework-browser
${URL} = Get Url
Set Suite Variable ${URL}
Add Cookie
... Foo22
... Bar22
... url=${URL}
... expires=3 155 760 000,195223
${cookies} = New Plugin Cookie Keyword With Grpc
Should Be Equal ${cookies}[name] Foo22
Should Be Equal ${cookies}[value] Bar22
Simple Plugin Example With Public API
Add Cookie
... Foo22
... Bar22
... url=${URL}
... expires=3 155 760 000,195223
${cookies} = Other Plugin Cookie Keyword With Public Api
Should Be Equal ${cookies}[name] Foo22
Should Be Equal ${cookies}[value] Bar22
And run the test with command:
robot --outputdir output --loglevel debug examples/2/2.1/simple.robot
Then look at the log.html file in the output folder.
It is possible to use multiple plugins at the same time, but
it is the user's responsibility to make sure that they do not
collide.
2.1.2 Resolving Selectors / Presenter Mode
Selector prefix is something that has to be handled by the keyword itself. The first step should be to resolve the selector:
@keyword
def disable_element(self, selector):
"""Disables an element."""
selector = self.resolve_selector(selector) # prefixes the selector with the set prefix
self.library.evaluate_javascript(selector=selector, "e => e.disabled = true")
Presenter mode works similarly and also resolves the selector:
@keyword
def blur(self, selector):
"""Calls blur on the element."""
selector = self.presenter_mode(selector, self.strict_mode) #highlights the element and waits and resolves the selector
self.call_js_keyword("blur", selector=selector)
2.1.3 Python plugin with NodeJS implementation
Load JS plugin
class PythonPlugin(LibraryComponent):
def __init__(self, library: Browser):
super().__init__(library)
self.initialize_js_extension(Path(__file__).parent.resolve() / "JSPlugin.js")
### Use it with self.call_js_keyword
@keyword
def my_mouse_wheel(self, x: int, y: int):
"""This keyword calls a custom javascript keyword from the file JSPlugin.js."""
return self.call_js_keyword("myMouseWheel", x=x, y=y)
All arguments must be named, and they must be JSON serializable.