Principle:MarketSquare Robotframework browser Plugin Architecture
Plugin Architecture
Overview
The robotframework-browser library implements a plugin architecture that allows developers to extend the library's keyword set without modifying its core source code. Plugins are Python classes that inherit from the LibraryComponent base class, gaining access to the parent library's internal APIs including gRPC communication channels, configuration settings, context caches, and utility methods.
This architecture follows the Open/Closed Principle: the library is open for extension through plugins but closed for modification of its core behavior.
Core Concept: Extending Through Inheritance
The plugin architecture rests on a single design decision: every functional component of the Browser library -- whether built-in or user-provided -- inherits from the same base class, LibraryComponent. This base class acts as a facade that exposes a curated subset of the main Browser library's internals.
When a plugin class inherits from LibraryComponent, it receives:
- The library instance itself (
self.library) -- the fully initializedBrowserobject - The Playwright gRPC channel (
self.playwright) -- for direct communication with the Node.js Playwright process - Configuration stacks (
self.timeout,self.strict_mode,self.selector_prefix) -- scope-aware settings - Output paths (
self.outputdir,self.browser_output) -- for file generation - Context cache (
self.context_cache) -- for accessing browser/page state - Utility methods (
self.resolve_selector(),self.get_timeout()) -- shared logic
Dependency Injection
Plugin classes use constructor-based dependency injection. The plugin constructor receives the Browser library instance as its sole required argument:
class MyPlugin(LibraryComponent):
def __init__(self, library: Browser) -> None:
super().__init__(library)
# Plugin-specific initialization here
The PluginParser from robotlibcore handles the instantiation, automatically passing the library instance to each plugin constructor. This means plugin authors never need to construct their plugins manually -- the library's import mechanism does it.
Separation of Concerns
The architecture enforces a clean separation:
| Concern | Location | Responsibility |
|---|---|---|
| Core keywords | Browser/keywords/ modules |
Standard browser automation operations (click, type, navigate, etc.) |
| Plugin keywords | External plugin classes | Domain-specific or project-specific keywords |
| Base infrastructure | LibraryComponent |
Shared access to library internals |
| Plugin loading | PluginParser + DynamicCore |
Discovery, instantiation, and keyword registration |
Built-in keyword modules (like Interaction, Getters, Network) and plugins both inherit from LibraryComponent. From the Robot Framework runtime's perspective, there is no distinction between a built-in keyword and a plugin keyword -- they are all methods on components registered with the DynamicCore.
Plugin Capabilities
A plugin can exercise three distinct interaction patterns:
1. Direct gRPC Communication
Plugins can open a gRPC channel to the Node.js process and call Playwright operations directly:
with self.playwright.grpc_channel() as stub:
response = stub.GetCookies(Request().Empty())
2. Calling Existing Library Keywords
Plugins can invoke any keyword already defined on the Browser library by calling methods on self.library:
location = self.library.evaluate_javascript(None, "window.location")
3. JavaScript Extension Integration
Plugins can load custom JavaScript files that run in the Node.js context and call them as keywords:
self.initialize_js_extension(path_to_js_file)
result = self.call_js_keyword("myJsFunction", arg1=value1)
Automatic Tagging
All keywords originating from plugins are automatically tagged with the "Plugin" tag. This happens in the Browser.get_keyword_tags() method, which checks whether a keyword name appears in the _plugin_keywords list. This tagging allows test suites to:
- Filter test execution by plugin keywords
- Identify in documentation which keywords come from plugins
- Apply tag-based listeners or hooks
Architecture Diagram
+---------------------------+
| Robot Framework |
| Test Suite |
+------------+--------------+
|
v
+---------------------------+
| Browser (DynamicCore) |
| - Built-in keyword modules|
| - Plugin keyword modules |
+-----+----------+----------+
| |
v v
+----------+ +------------+
|LibraryComponent|LibraryComponent|
| (Built-in)| (Plugin) |
+-----+-----+------+------+
| |
v v
+---------------------------+
| Playwright gRPC Channel |
| (Node.js Process) |
+---------------------------+
Design Rationale
This architecture was chosen because:
- Composability: Multiple plugins can be loaded simultaneously, each contributing keywords without conflict
- Encapsulation: Plugins access library internals through a controlled interface (
LibraryComponent), not by reaching into private implementation details - Familiarity: Plugin authors use the same patterns as the library's own keyword modules, reducing the learning curve
- Runtime flexibility: Plugins are specified at library import time, allowing different test suites to load different plugin combinations
Domains
Implemented By
Related
- MarketSquare_Robotframework_browser_LibraryComponent_Init -- API reference for the
LibraryComponentbase class constructor - MarketSquare_Robotframework_browser_Plugin_Loading_Mechanism -- How plugins are discovered and loaded
- MarketSquare_Robotframework_browser_Keyword_Method_Implementation -- How keyword methods are defined in plugins