Contents
SeleniumLibrary offers three main ways to creating new functionality for SeleniumLibrary: Plugin API, EventFiringWebDriver and building new libraries on top the SeleniumLibrary (later referred as extending SeleniumLibrary.) Plugin API and extending SeleniumLibrary allows similar access to the SeleniumLibrary public API and offers their own pros and cons for building custom functionality on top the SeleniumLibrary. The EventFiringWebDriver offers listener-like interface to the Selenium API. The plugin API and EventFiringWebDriver are new in SeleniumLibrary 4.0.
SeleniumLibrary offers plugins as a way to modify, add library keywords and modify some of the internal functionality without creating new library or hacking the source code. Plugins can be only loaded in the library import, with the plugins argument and SeleniumLibrary does not offer way to unload the plugins from the SeleniumLibrary. Creating new plugins is more strict than creating new libraries, but plugins allows more access to the methods that are used to implement the keywords.
The EventFiringWebDriver is an listener type of API offered by the Selenium. The EventFiringWebDriver allows to listen Selenium API calls and allows users to fire events before and after Selenium API methods. Refer the to the Selenium EventFiringWebDriver documentation what Selenium API methods are supported and how to EventFiringWebDriver works.
As with any Robot Framework Python library, new libraries can be build on top of the SeleniumLibrary by inheriting the SeleniumLibrary, getting active library instance from Robot Framework or by other means which are available from Python or from Robot Framework. Building new libraries allows library creator freedom to choose which keywords new library offers and more flexibility how new libraries are created than what the plugin API offers.
Before making your own plugin or library on top of the SeleniumLibrary, private or public, please consider if the plugin or extension be generally useful in the SeleniumLibrary. If the plugin or extension would be useful for others, then please create an issue and perhaps a pull request. More details on how the SeleniumLibrary project handles the enhancement requests can be found in the CONTRIBUTING.rst Enhancement requests chapter.
The plugin API and extending SeleniumLibrary have same access to the SeleniumLibrary public API.
All the methods, which are exposed as keywords, are available in the SeleniumLibrary public API.
Generally keywords are converted to lower case and spaces are converted to underscores. For
example, the Open Browser keyword is available as open_browser
method. The method name can
be overwritten with the @keyword
decorator, but the SeleniumLibrary 4.0.0 release does not
contain keywords where the keyword name would differ from the method name (other than the keyword
case.) Please note that keywords created by the plugins may not follow these rules and it is good
to verify the method name from the plugin API source.
The SeleniumLibrary also contains methods and attributes which are not keywords, but are useful when creating plugin or extending the SeleniumLibrary. The available methods are:
Method | Description |
---|---|
find_element | Finds first element matching locator . |
find_elements | Find all elements matching locator . |
get_keyword_tags | Responsible for returning keywords tags for Robot Framework dynamic library API. |
register_driver | Add's a Selenium driver to the library WebDriverCache. |
run_keyword | Responsible for executing keywords by Robot Framework dynamic library API. |
failure_occurred | Method that is executed when a SeleniumLibrary keyword fails. |
Also there are the following public attributes available:
Attribute | Description |
---|---|
driver | Current active driver. |
event_firing_webdriver | Reference to a class implementing event firing selenium support. |
timeout | Default value for timeouts used with Wait ... keywords. |
page_load_timeout | Default value to wait for page load to complete until error is raised. |
implicit_wait | Default value for implicit wait used when locating elements. |
run_on_failure_keyword | Default action for the run-on-failure functionality. |
screenshot_root_directory | Location where possible screenshots are created |
For more details about the methods, please read the individual method documentation and many of the attributes are explained in the library keyword documentation. please note that plugins may alter the functionality of the method or attributes and documentation applies only for the core SeleniumLibrary.
When instance is created from the SeleniumLibrary, example when library is imported in the test data, there is an order in the initialisation. At first all classes defining SeleniumLibrary keywords are discovered. As a second event, discovery for the EventFiringWebDriver is done. At third event, plugins are discovered. As a last event, keywords are found from SeleniumLibrary classes and plugins. Because plugins are discovered last, they may example alter the EventFiringWebDriver. Consult the plugin's documentation for more details.
SeleniumLibrary offers plugins as a way to modify, add library keywords and modify some of the internal functionality without creating new library or hacking the source code. See plugin example how plugins can be implemented.
Importing plugins is similar when importing Robot Framework libraries. It is possible import plugin with using physical path or with plugin name exactly in same way as importing libraries in Robot Framework. SeleniumLibrary plugins are searched from the same module search path as Robot Framework searches libraries. It is only possible to import plugins written in Python, other programming languages or Robot Framework test data is not supported. Like with Robot Framework library imports, plugin names are case sensitive and spaces are not supported in the plugin name. It is possible to import multiple plugins at the same time by separating plugins with comma. It is possible to have space before and after the comma. Plugins are imported in the order they defined in the plugins argument. If two or more plugins declare the same keyword or modify the same method/attribute in the SeleniumLibrary, the last plugin to perform the changes will overwrite the changes made by other plugins. Example of plugin imports:
| Library | SeleniumLibrary | plugins=${CURDIR}/MyPlugin.py | # Imports plugin with physical path | | Library | SeleniumLibrary | plugins=plugins.MyPlugin, plugins.MyOtherPlugin | # Import two plugins with name |
When SeleniumLibrary creates instances from the plugin classes, it will by default initiate the class
with a single argument, called ctx
(context). ctx
is the instance of the SeleniummLibrary and
it provides access to the SeleniumLibrary Public API.
It is also possible to provide optional arguments to the plugins. Arguments must be separated with a semicolon from the plugin. SeleniumLibrary will not convert arguments to any specific type and everything is by default unicode. Plugin is responsible for converting the argument to proper types. Example of importing plugin with arguments:
| Library | SeleniumLibrary | plugins=plugins.Plugin;ArgOne;ArgTwo | # Import two plugins with two arguments: ArgOne and ArgTwo |
It is also possible to provide variable number of arguments and keywords arguments. Named arguments must be defined first, variable number of arguments as second and keywords arguments as last. All arguments must be separated with semicolon. Example if plugin __init__ is defined like this:
class Plugin(LibraryComponent): def __init__(self, ctx, arg, *varargs, **kwargs): # Code to implement the plugin.
Then, for example, it is possible to plugin with these arguments:
| Library | SeleniumLibrary | plugins=plugins.Plugin;argument1;varg1;varg2;kw1=kwarg1;kw2=kwarg2 |
Then the argument1
is given the arg
in the __init__
. The varg1
and varg2
variable
number arguments are given to the *varargs
argument in the __init__
. Finally, the kw1=kwarg1
and kw2=kwarg2
keyword arguments are given to the **kwargs
in the __init__
. As in Python,
there can be zero or more variable number and keyword arguments.
Generally speaking, plugins are not any different from the classes that are used to implement keyword
in the SeleniumLibrary. Example like with BrowserManagementKeywords class inherits the LibraryComponent
and uses @keyword
decorator to mark which methods are exposed as keywords.
Plugins must be implemented as Python classes and plugins must inherit the SeleniumLibrary LibraryComponent
class. Plugin __init__ must support at least one argument: ctx
. Also optional arguments are supported, see
Plugin arguments for more details how to provide optional arguments to plugins.
SeleniumLibrary uses Robot Framework dynamic library API. The main difference, when compared to libraries
using dynamic library API, is that plugins are not responsible for implementing the dynamic library API.
SeleniumLibrary is handling the dynamic library API requirements towards Robot Framework. For plugins
this means that methods that implements keywords, must be decorated with @keyword
decorator. The @keyword
decorator can be imported from Robot Framework and used in the following way:
from robot.api.deco import keyword class Plugin(LibraryComponent): @keyword def keyword(self): self.driver.... # More code here to implement the keyword
SeleniumLibrary does not suppress exception raised during plugin import or during keywords discovery from the plugins. In this case the whole SeleniumLibrary import will fail and SeleniumLibrary keywords can not be used from that import.
By default when exceptions raised by SeleniumLibrary keywords will trigger the run on failure functionality, this also applies keywords created or modified by the plugins. But it must be noted that plugins can alter the SeleniumLibrary run on failure functionality and refer to the plugin documentation for further details.
Although ctx
provides access to the SeleniumLibrary Public API, the LibraryComponent provides more
methods and attributes and also an IDE friendly access to the plugin API, Example currently active
browser can be found from self.ctx.driver
, the LibraryComponent
exposes the browser as:
self.driver
and most IDE can discover the completion automatically. Plugin classes must inherit
the LibraryComponent
.
The following methods are available from the LibraryComponent
class:
Method | Description |
---|---|
find_element | Finds first element matching locator . |
find_elements | Find all elements matching locator . |
is_text_present | Returns True if text is present in the page. |
is_element_enabled | Returns True if element is enabled. |
is_visible | Returns True if element is visible. |
log_source | Calls method defining the Log Source keyword. |
assert_page_contains | Raises AssertionError if element is not found from the page. |
assert_page_not_contains | Raises AssertionError if element is found from the page. |
get_timeout | By default returns SeleniumLibrary timeout argument value. With argument converts string with Robot Framework timestr_to_secs to seconds. |
info | Wrapper to robot.api.logger.info method. |
debug | Wrapper to robot.api.logger.debug method. |
warn | Wrapper to robot.api.logger.warn method. |
log | Wrapper to robot.api.logger.write method. |
Also following attributes are available from the LibraryComponent
class:
Attribute | Description |
---|---|
driver | Currently active browser/WebDriver instance in the SeleniumLibrary. |
drivers | Cache for the opened browsers/WebDriver instances. |
element_finder | Read/write attribute for the ElementFinder instance. |
ctx | Instance of the SeleniumLibrary. |
log_dir | Folder where output files are written. |
event_firing_webdriver | Read/write attribute for the SeleniumLibrary EventFiringWebDriver instance. |
See the SeleniumLibrary init, the LibraryComponent and the ContextAware classes for further implementation details.
To separate keywords which are added or modified by plugins, SeleniumLibrary will add plugin
keyword tag
to all keywords added or modified from plugins. When SeleniumLibrary keyword documentation, with plugins,
is generated by libdoc it is easy to separate keywords which are added or modified by plugins. Keyword
documentation can be example generated by following command:
python -m robot.libdoc SeleniumLibrary::plugins=/path/to/Plugin.py ./SeleniumLibraryWithPlugin.html
The EventFiringWebDriver is an listener type of API offered by the Selenium. In practice EventFiringWebDriver
offers way to intercept Selenium API call, made by SeleniumLibrary or by other library keywords and fire
separate Selenium events. Events can be fired before and after Selenium API call.
SeleniumLibrary offers support for Selenium EventFiringWebDriver
listener class by providing possibility
to import the listener class by event_firing_webdriver argument. Importing EventFiringWebDriver
is similar when importing Robot Framework libraries. It is possible import EventFiringWebDriver
with using physical path or with name exactly in same way as importing libraries in
Robot Framework. EventFiringWebDriver
class is searched from the same module search path as
Robot Framework searches libraries. It is only possible to import EventFiringWebDriver
class
written in Python, other programming languages or Robot Framework test data is not supported. Like with
Robot Framework library imports, EventFiringWebDriver
class name is case sensitive and spaces
are not supported in the class name. It is possible to import only one EventFiringWebDriver
class.
Example of EventFiringWebDriver
imports:
| Library | SeleniumLibrary | event_firing_webdriver=${CURDIR}/MyListener.py | # Imports EventFiringWebDriver with physical path |
Refer the to the Selenium EventFiringWebDriver documentation what Selenium API methods are supported and how to EventFiringWebDriver works. Also there is simple EventFiringWebDriver example for more details.
Starting from SeleniumLibrary 3.0, the library has moved to use Robot Framework dynamic library API. To ease the usage of the dynamic library API, the SeleniumLibrary uses a PythonLibCore project to handle the most the dynamic library API requirements, except running the keyword and providing keywords tags. For more details please about the dynamic library API, read the Robot Framework dynamic library API documentation.
The principles described in the Robot Framework User Guide, Extending existing test libraries chapter also apply when extending SeleniumLibrary. There are two different ways to extend the SeleniumLibrary.
- Create a library which also contains the existing SeleniumLibrary keywords, example by using inheritance.
- Create library which contains only new keywords.
When creating a library, which also includes the existing SeleniumLibrary keywords, there are
extra steps which needs to be taken account, because SeleniumLibrary uses PythonLibCore
and the dynamic library API. All methods which should be published as keywords must be
decorated with the @keyword
decorator. The @keyword
decorator can be imported in
the following way:
from robot.api.deco import keyword
Keywords should be inside of a class
and the add_library_components
method
must be called to add keywords. The add_library_components
method is inherited from the
PythonLibCore project and the method must contain list of classes which contain the
new keywords.
Perhaps the easiest way to extend the SeleniumLibrary is to inherit the SeleniumLibrary and add
new keywords methods to a new library. The inheritance example shows how to declare a new
keyword Get Browser Desired Capabilities
and how to overwrite the existing Open Browser
keyword.
Because the InheritSeleniumLibrary
class does not overwrite the SeleniumLibrary init
method, the add_library_components
is called automatically. Then the InheritSeleniumLibrary
class methods which are decorated with @keyword
decorator are added to the
InheritSeleniumLibrary
library keywords. Also existing keywords from SeleniumLibrary are added as library keywords.
Because the methods are no longer directly available in the SeleniumLibrary class, it's not possible to call the original method example like this:
super(ClassName, self).open_browser(url, browser, alias, remote_url, desired_capabilities, ff_profile_dir)
Instead user must call the method from the class instance which implements the keyword, example:
browser_management = BrowserManagementKeywords(self) browser_management.open_browser(url, 'chrome')
Decomposition is a good way to split library into smaller namespaces and it usually eases the
testing of the library. The decomposition example shows how the Get Browser Desired Capabilities
and Open Browser
keywords can be divided into their own classes.
The example also shows the usage of the ctx
(context) object and the LibraryComponent class.
The ctx
object is an instance of the SeleniumLibrary which provides access to the
SeleniumLibrary Public API for the BrowserKeywords
and DesiredCapabilitiesKeywords
classes.
The LibraryComponent
is a wrapper class, which provides easier shortcuts to the ctx
object
methods and the example provides general logging methods. Example the Selenium WebDriver instance
in the context: self.ctx.driver
, but the LibraryComponent
provides a shortcut and it can be
accessed with: self.driver
Getting the active library instance provides a way to create a new library that does not
automatically contain keywords from the SeleniumLibrary. This eases the name space
handling and if only new keywords are created, the user does not have to prefix the keywords
with the library name. This way also allows users to freely choose the Robot Framework library API.
The instance example shows a way to get the active SeleniumLibrary from the Robot Framework.
The example shows how to declare Get Browser Desired Capabilities
and Open Browser
keywords
in the new library and the instance example uses the static keyword API to declare new
keywords.