Browser library is a browser automation library for Robot Framework.
This is the keyword documentation for Browser library. For information about installation, support, and more please visit the project pages. For more information about Robot Framework itself, see robotframework.org.
Browser library uses Playwright Node module to automate Chromium, Firefox and WebKit with a single library.
Table of contents
Browser, Context and Page
Browser library works with three different layers that build on each other: Browser, Context and Page.
Browsers
A browser can be started with one of the three different engines Chromium, Firefox or Webkit.
Supported Browsers
| Browser |
Browser with this engine |
chromium |
Google Chrome, Microsoft Edge (since 2020), Opera |
firefox |
Mozilla Firefox |
webkit |
Apple Safari, Mail, AppStore on MacOS and iOS |
Since Playwright comes with a pack of builtin binaries for all browsers, no additional drivers e.g. geckodriver are needed.
All these browsers that cover more than 85% of the world wide used browsers, can be tested on Windows, Linux and MacOS. There is no need for dedicated machines anymore.
A browser process is started headless (without a GUI) by default. Run New Browser with specified arguments if a browser with a GUI is requested or if a proxy has to be configured. A browser process can contain several contexts.
Contexts
A context corresponds to a set of independent incognito pages in a browser that share cookies, sessions or profile settings. Pages in two separate contexts do not share cookies, sessions or profile settings. Compared to Selenium, these do not require their own browser process. To get a clean environment a test can just open a new context. Due to this new independent browser sessions can be opened with Robot Framework Browser about 10 times faster than with Selenium by just opening a New Context within the opened browser.
To make pages in the same suite share state, use the same context by opening the context with New Context on suite setup.
The context layer is useful e.g. for testing different user sessions on the same webpage without opening a whole new browser context. Contexts can also have detailed configurations, such as geo-location, language settings, the viewport size or color scheme. Contexts do also support http credentials to be set, so that basic authentication can also be tested. To be able to download files within the test, the acceptDownloads argument must be set to True in New Context keyword. A context can contain different pages.
Pages
A page does contain the content of the loaded web site and has a browsing history. Pages and browser tabs are the same.
Typical usage could be:
* Test Cases *Starting a browser with a page New Browser chromium headless=false New Context viewport={'width': 1920, 'height': 1080} New Page https://marketsquare.github.io/robotframework-browser/Browser.html Get Title == Browser
The Open Browser keyword opens a new browser, a new context and a new page. This keyword is useful for quick experiments or debugging sessions.
When a New Page is called without an open browser, New Browser and New Context are executed with default values first.
Each Browser, Context and Page has a unique ID with which they can be addressed. A full catalog of what is open can be received by Get Browser Catalog as a dictionary.
Automatic page and context closing
Controls when contexts and pages are closed during the test execution.
If automatic closing level is TEST, contexts and pages that are created during a single test are automatically closed when the test ends. Contexts and pages that are created during suite setup are closed when the suite teardown ends.
If automatic closing level is SUITE, all contexts and pages that are created during the test suite are closed when the suite teardown ends.
If automatic closing level is MANUAL, nothing is closed automatically while the test execution is ongoing. All browsers, context and pages are automatically closed when test execution ends.
If automatic closing level is KEEP, nothing is closed automatically while the test execution is ongoing. Also, nothing is closed when test execution ends, including the node process. Therefore, it is users responsibility to close all browsers, context and pages and ensure that all process that are left running after the test execution end are closed. This level is only intended for test case development and must not be used when running tests in CI or similar environments.
Automatic closing can be configured or switched off with the auto_closing_level library import parameter.
See: Importing
Finding elements
All keywords in the library that need to interact with an element on a web page take an argument typically named selector that specifies how to find the element. Keywords can find elements with strict mode. If strict mode is true and locator finds multiple elements from the page, keyword will fail. If keyword finds one element, keyword does not fail because of strict mode. If strict mode is false, keyword does not fail if selector points many elements. Strict mode is enabled by default, but can be changed in library importing or Set Strict Mode keyword. Keyword documentation states if keyword uses strict mode. If keyword does not state that strict mode is used, then strict mode is not applied for the keyword. For more details, see Playwright strict documentation.
Selector strategies that are supported by default are listed in the table below.
| Strategy |
Match based on |
Example |
css |
CSS selector. |
css=.class > \#login_btn |
xpath |
XPath expression. |
xpath=//input[@id="login_btn"] |
text |
Browser text engine. |
text=Login |
id |
Element ID Attribute. |
id=login_btn |
CSS Selectors can also be recorded with Record selector keyword.
Explicit Selector Strategy
The explicit selector strategy is specified with a prefix using syntax strategy=value. Spaces around the separator are ignored, so css=foo, css= foo and css = foo are all equivalent.
Implicit Selector Strategy
The default selector strategy is css.
If selector does not contain one of the know explicit selector strategies, it is assumed to contain css selector.
Selectors that are starting with // or .. are considered as xpath selectors.
Selectors that are in quotes are considered as text selectors.
Examples:
# CSS selectors are default.Click span > button.some_class # This is equivalentClick css=span > button.some_class # to this.# // or .. leads to xpath selector strategyClick //span/button[@class="some_class"]Click xpath=//span/button[@class="some_class"]# "text" in quotes leads to exact text selector strategyClick "Login"Click text="Login"
CSS
As written before, the default selector strategy is css. See css selector for more information.
Any malformed selector not starting with // or .. nor starting and ending with a quote is assumed to be a css selector.
Note that # is a comment character in Robot Framework syntax and needs to be escaped like \# to work as a css ID selector.
Examples:
Click span > button.some_classGet Text \#username_field == George
XPath
XPath engine is equivalent to Document.evaluate. Example: xpath=//html/body//span[text()="Hello World"].
Malformed selector starting with // or .. is assumed to be an xpath selector. For example, //html/body is converted to xpath=//html/body. More examples are displayed in Examples.
Note that xpath does not pierce shadow_roots.
Text
Text engine finds an element that contains a text node with the passed text. For example, Click text=Login clicks on a login button, and Wait For Elements State text="lazy loaded text" waits for the "lazy loaded text" to appear in the page.
Text engine finds fields based on their labels in text inserting keywords.
Malformed selector starting and ending with a quote (either " or ') is assumed to be a text selector. For example, Click "Login" is converted to Click text="Login". Be aware that these leads to exact matches only! More examples are displayed in Examples.
Insensitive match
By default, the match is case-insensitive, ignores leading/trailing whitespace and searches for a substring. This means text= Login matches <button>Button loGIN (click me)</button>.
Exact match
Text body can be escaped with single or double quotes for precise matching, insisting on exact match, including specified whitespace and case. This means text="Login " will only match <button>Login </button> with exactly one space after "Login". Quoted text follows the usual escaping rules, e.g. use \" to escape double quote in a double-quoted string: text="foo\"bar".
RegEx
Text body can also be a JavaScript-like regex wrapped in / symbols. This means text=/^hello .*!$/i or text=/^Hello .*!$/ will match <span>Hello Peter Parker!</span> with any name after Hello, ending with !. The first one flagged with i for case-insensitive. See https://regex101.com for more information about RegEx.
Button and Submit Values
Input elements of the type button and submit are rendered with their value as text, and text engine finds them. For example, text=Login matches <input type=button value="Login">.
Cascaded selector syntax
Browser library supports the same selector strategies as the underlying Playwright node module: xpath, css, id and text. The strategy can either be explicitly specified with a prefix or the strategy can be implicit.
A major advantage of Browser is that multiple selector engines can be used within one selector. It is possible to mix XPath, CSS and Text selectors while selecting a single element.
Selectors are strings that consists of one or more clauses separated by >> token, e.g. clause1 >> clause2 >> clause3. When multiple clauses are present, next one is queried relative to the previous one's result. Browser library supports concatenation of different selectors separated by >>.
For example:
Highlight Elements "Hello" >> ../.. >> .select_buttonHighlight Elements text=Hello >> xpath=../.. >> css=.select_button
Each clause contains a selector engine name and selector body, e.g. engine=body. Here engine is one of the supported engines (e.g. css or a custom one). Selector body follows the format of the particular engine, e.g. for css engine it should be a css selector. Body format is assumed to ignore leading and trailing white spaces, so that extra whitespace can be added for readability. If the selector engine needs to include >> in the body, it should be escaped inside a string to not be confused with clause separator, e.g. text="some >> text".
Selector engine name can be prefixed with * to capture an element that matches the particular clause instead of the last one. For example, css=article >> text=Hello captures the element with the text Hello, and *css=article >> text=Hello (note the *) captures the article element that contains some element with the text Hello.
For convenience, selectors in the wrong format are heuristically converted to the right format. See Implicit Selector Strategy
Examples
# queries 'div' css selectorGet Element css=div# queries '//html/body/div' xpath selectorGet Element //html/body/div# queries '"foo"' text selectorGet Element text=foo# queries 'span' css selector inside the result of '//html/body/div' xpath selectorGet Element xpath=//html/body/div >> css=span# converted to 'css=div'Get Element div# converted to 'xpath=//html/body/div'Get Element //html/body/div# converted to 'text="foo"'Get Element "foo"# queries the div element of every 2nd span element inside an element with the id fooGet Element \#foo >> css=span:nth-child(2n+1) >> divGet Element id=foo >> css=span:nth-child(2n+1) >> div
Be aware that using # as a starting character in Robot Framework would be interpreted as comment. Due to that fact a #id must be escaped as \#id.
iFrames
By default, selector chains do not cross frame boundaries. It means that a simple CSS selector is not able to select an element located inside an iframe or a frameset. For this use case, there is a special selector >>> which can be used to combine a selector for the frame and a selector for an element inside a frame.
Given this simple pseudo html snippet:
<iframe id="iframe" src="src.html"> #document <!DOCTYPE html> <html> <head></head> <body> <button id="btn">Click Me</button> </body> </html></iframe>
Here's a keyword call that clicks the button inside the frame.
Click id=iframe >>> id=btn
The selectors on the left and right side of >>> can be any valid selectors. The selector clause directly before the frame opener >>> must select the frame element itself. Frame selection is the only place where Browser Library modifies the selector, as explained in above. In all cases, the library does not alter the selector in any way, instead it is passed as is to the Playwright side.
If multiple keyword shall be performed inside a frame, it is possible to define a selector prefix with Set Selector Prefix. If this prefix is set to a frame/iframe it has similar behavior as SeleniumLibrary keyword Select Frame.
WebComponents and Shadow DOM
Playwright and so also Browser are able to do automatic piercing of Shadow DOMs and therefore are the best automation technology when working with WebComponents.
Also other technologies claim that they can handle Shadow DOM and Web Components. However, none of them do pierce shadow roots automatically, which may be inconvenient when working with Shadow DOM and Web Components.
For that reason, the css engine pierces shadow roots. More specifically, every Descendant combinator pierces an arbitrary number of open shadow roots, including the implicit descendant combinator at the start of the selector.
That means, it is not necessary to select each shadow host, open its shadow root and select the next shadow host until you reach the element that should be controlled.
CSS:light
css:light engine is equivalent to Document.querySelector and behaves according to the CSS spec. However, it does not pierce shadow roots.
css engine first searches for elements in the light dom in the iteration order, and then recursively inside open shadow roots in the iteration order. It does not search inside closed shadow roots or iframes.
Examples:
<article> <div>In the light dom</div> <div slot='myslot'>In the light dom, but goes into the shadow slot</div> <open mode shadow root> <div class='in-the-shadow'> <span class='content'> In the shadow dom <open mode shadow root> <li id='target'>Deep in the shadow</li> </open mode shadow root> </span> </div> <slot name='myslot'></slot> </open mode shadow root></article>
Note that <open mode shadow root> is not an html element, but rather a shadow root created with element.attachShadow({mode: 'open'}).
- Both
"css=article div" and "css:light=article div" match the first <div>In the light dom</div>.
- Both
"css=article > div" and "css:light=article > div" match two div elements that are direct children of the article.
"css=article .in-the-shadow" matches the <div class='in-the-shadow'>, piercing the shadow root, while "css:light=article .in-the-shadow" does not match anything.
"css:light=article div > span" does not match anything, because both light-dom div elements do not contain a span.
"css=article div > span" matches the <span class='content'>, piercing the shadow root.
"css=article > .in-the-shadow" does not match anything, because <div class='in-the-shadow'> is not a direct child of article
"css:light=article > .in-the-shadow" does not match anything.
"css=article li#target" matches the <li id='target'>Deep in the shadow</li>, piercing two shadow roots.
text:light
text engine open pierces shadow roots similarly to css, while text:light does not. Text engine first searches for elements in the light dom in the iteration order, and then recursively inside open shadow roots in the iteration order. It does not search inside closed shadow roots or iframes.
id, data-testid, data-test-id, data-test and their :light counterparts
Attribute engines are selecting based on the corresponding attribute value. For example: data-test-id=foo is equivalent to css=[data-test-id="foo"], and id:light=foo is equivalent to css:light=[id="foo"].
Element reference syntax
It is possible to get a reference to a Locator by using Get Element and Get Elements keywords. Keywords do not save reference to an element in the HTML document, instead it saves reference to a Playwright Locator. In nutshell Locator captures the logic of how to retrieve that element from the page. Each time an action is performed, the locator re-searches the elements in the page. This reference can be used as a first part of a selector by using a special selector syntax element=. like this:
${ref}= Get Element .some_class Click ${ref} >> .some_child # Locator searches an element from the page. Click ${ref} >> .other_child # Locator searches again an element from the page.
The .some_child and .other_child selectors in the example are relative to the element referenced by ${ref}. Please note that frame piercing is not possible with element reference.
Assertions
Keywords that accept arguments assertion_operator <AssertionOperator> and assertion_expected can optionally assert that a specified condition holds. Keywords will return the value even when the assertion is performed by the keyword.
Assert will retry and fail only after a specified timeout. See Importing and retry_assertions_for (default is 1 second) for configuring this timeout.
Currently supported assertion operators are:
| Operator |
Alternative Operators |
Description |
Validate Equivalent |
== |
equal, equals, should be |
Checks if returned value is equal to expected value. |
value == expected |
!= |
inequal, should not be |
Checks if returned value is not equal to expected value. |
value != expected |
> |
greater than |
Checks if returned value is greater than expected value. |
value > expected |
>= |
|
Checks if returned value is greater than or equal to expected value. |
value >= expected |
< |
less than |
Checks if returned value is less than expected value. |
value < expected |
<= |
|
Checks if returned value is less than or equal to expected value. |
value <= expected |
*= |
contains |
Checks if returned value contains expected value as substring. |
expected in value |
|
not contains |
Checks if returned value does not contain expected value as substring. |
expected in value |
^= |
should start with, starts |
Checks if returned value starts with expected value. |
re.search(f"^{expected}", value) |
$= |
should end with, ends |
Checks if returned value ends with expected value. |
re.search(f"{expected}$", value) |
matches |
|
Checks if given RegEx matches minimum once in returned value. |
re.search(expected, value) |
validate |
|
Checks if given Python expression evaluates to True. |
|
evaluate |
then |
When using this operator, the keyword does return the evaluated Python expression. |
|
There are three different possibilities what keyword returns when matches operator is used: string, tuple or dictionary. What keyword returns depends on how the RegEx is formed. If RegEx does not contain group(s), then keyword will return the string without modifications. If RegEx contains groups, meaning (...), then keyword will return a tuple. Each tuple item contains the text which is matched by the group. If there is group and group has a name, (?P<name>...) syntax, then keyword returns a dictionary. In this case dictionary key is the group name and value contains the matched text. If there mix of groups and groups with names, then tuple is returned.
Currently supported formatters for assertions are:
| Formatter |
Description |
normalize spaces |
Substitutes multiple spaces to single space from the value |
strip |
Removes spaces from the beginning and end of the value |
case insensitive |
Converts value to lower case before comparing |
apply to expected |
Applies rules also for the expected value |
Formatters are applied to the value before assertion is performed and keywords returns a value where rule is applied. Formatter is only applied to the value which keyword returns and not all rules are valid for all assertion operators. If apply to expected formatter is defined, then formatters are then formatter are also applied to expected value.
By default, keywords will provide an error message if an assertion fails. Default error messages can be overwritten with a message argument. The message argument accepts {value}, {value_type}, {expected} and {expected_type} format options. The {value} is the value returned by the keyword and the {expected} is the expected value defined by the user, usually the value in the assertion_expected argument. The {value_type} and {expected_type} are the type definitions from {value} and {expected} arguments. In similar fashion as Python type returns type definition. Assertions will retry until timeout has expired if they do not pass.
The assertion assertion_expected value is not converted by the library and is used as is. Therefore when assertion is made, the assertion_expected argument value and value returned the keyword must have the same type. If types are not the same, assertion will fail. Example Get Text always returns a string and has to be compared with a string, even the returned value might look like a number.
Other Keywords have other specific types they return. Get Element Count always returns an integer. Get Bounding Box and Get Viewport Size can be filtered. They return a dictionary without a filter and a number when filtered. These Keywords do automatic conversion for the expected value if a number is returned.
* < less or greater > With Strings* Comparisons of strings with greater than or less than compares each character, starting from 0 regarding where it stands in the code page. Example: A < Z, Z < a, ac < dc It does never compare the length of elements. Neither lists nor strings. The comparison stops at the first character that is different. Examples: `'abcde' < 'abd', '100.000' < '2' In Python 3 and therefore also in Browser it is not possible to compare numbers with strings with a greater or less operator. On keywords that return numbers, the given expected value is automatically converted to a number before comparison.
The getters Get Page State and Get Browser Catalog return a dictionary. Values of the dictionary can directly asserted. Pay attention of possible types because they are evaluated in Python. For example:
Get Page State validate 2020 >= value['year'] # Comparison of numbersGet Page State validate "IMPORTANT MESSAGE!" == value['message'] # Comparison of strings
The 'then' or 'evaluate' closure
Keywords that accept arguments assertion_operator and assertion_expected can optionally also use then or evaluate closure to modify the returned value with BuiltIn Evaluate. Actual value can be accessed with value.
For example Get Title then 'TITLE: '+value. See Builtin Evaluating expressions for more info on the syntax.
Examples
# Keyword Selector Key Assertion Operator Assertion ExpectedGet Title equal Page TitleGet Title ^= PageGet Style //*[@id="div-element"] width > 100Get Title matches \\w+\\s\\w+Get Title validate value == "Login Page"Get Title evaluate value if value == "some value" else "something else"
Implicit waiting
Browser library and Playwright have many mechanisms to help in waiting for elements. Playwright will auto-wait before performing actions on elements. Please see Auto-waiting on Playwright documentation for more information.
On top of Playwright auto-waiting Browser assertions will wait and retry for specified time before failing any Assertions. Time is specified in Browser library initialization with retry_assertions_for.
Browser library also includes explicit waiting keywords such as Wait for Elements State if more control for waiting is needed.
Experimental: Re-using same node process
Browser library integrated nodejs and python. The NodeJS side can be also executed as a standalone process. Browser libraries running on the same machine can talk to that instead of starting new node processes. This can speed execution when running tests parallel. To start node side run on the directory when the Browser package is PLAYWRIGHT_BROWSERS_PATH=0 node Browser/wrapper/index.js PORT.
PORT is the port you want to use for the node process. To execute tests then with pabot for example do ROBOT_FRAMEWORK_BROWSER_NODE_PORT=PORT pabot ...
Scope Setting
Some keywords which manipulates library settings have a scope argument. With that scope argument one can set the "live time" of that setting. Available Scopes are: Global, Suite and Test/Task See Scope. Is a scope finished, this scoped setting, like timeout, will no longer be used.
Live Times:
- A Global scope will live forever until it is overwritten by another Global scope. Or locally temporarily overridden by a more narrow scope.
- A Suite scope will locally override the Global scope and live until the end of the Suite within it is set, or if it is overwritten by a later setting with Global or same scope. Children suite does inherit the setting from the parent suite but also may have its own local Suite setting that then will be inherited to its children suites.
- A Test or Task scope will be inherited from its parent suite but when set, lives until the end of that particular test or task.
A new set higher order scope will always remove the lower order scope which may be in charge. So the setting of a Suite scope from a test, will set that scope to the robot file suite where that test is and removes the Test scope that may have been in place.
Extending Browser library with a JavaScript module
Browser library can be extended with JavaScript. The module must be in CommonJS format that Node.js uses. You can translate your ES6 module to Node.js CommonJS style with Babel. Many other languages can be also translated to modules that can be used from Node.js. For example TypeScript, PureScript and ClojureScript just to mention few.
async function myGoToKeyword(url, args, page, logger, playwright) { logger(args.toString()) playwright.coolNewFeature() return await page.goto(url);}
Functions can contain any number of arguments and arguments may have default values.
There are some reserved arguments that are not accessible from Robot Framework side. They are injected to the function if they are in the arguments:
page: the playwright Page object.
context: the playwright BrowserContext object.
browser: the playwright Browser object.
args: the rest of values from Robot Framework keyword call *args.
logger: callback function that takes strings as arguments and writes them to robot log. Can be called multiple times.
playwright: playwright module (* from 'playwright'). Useful for integrating with Playwright features that Browser library doesn't support with it's own keywords. API docs
also argument name self can not be used.
Example module.js
async function myGoToKeyword(pageUrl, page) { await page.goto(pageUrl); return await page.title();}exports.__esModule = true;exports.myGoToKeyword = myGoToKeyword;
Example Robot Framework side
* Settings *Library Browser jsextension=${CURDIR}/module.js* Test Cases *Hello New Page ${title}= myGoToKeyword https://playwright.dev Should be equal ${title} Playwright
Also selector syntax can be extended with a custom selector using a js module
Example module keyword for custom selector registering
async function registerMySelector(playwright) {playwright.selectors.register("myselector", () => ({ // Returns the first element matching given selector in the root's subtree. query(root, selector) { return root.querySelector(a[data-title="${selector}"]); }, // Returns all elements matching given selector in the root's subtree. queryAll(root, selector) { return Array.from(root.querySelectorAll(a[data-title="${selector}"])); }}));return 1;}exports.__esModule = true;exports.registerMySelector = registerMySelector;
Plugins
Browser library offers plugins as a way to modify and add library keywords and modify some of the internal functionality without creating a new library or hacking the source code. See plugin API documentation for further details.
Language
Browser library offers possibility to translate keyword names and documentation to new language. If language is defined, Browser library will search from module search path Python packages starting with robotframework_browser_translation by using Python pluging API. Library is using naming convention to find Python plugins.
The package must implement single API call, get_language without any arguments. Method must return a dictionary containing two keys: language and path. The language key value defines which language the package contains. Also value should match (case insensitive) the library language import parameter. The path parameter value should be full path to the translation file.
Translation file
The file name or extension is not important, but data must be in json format. The keys of json are the methods names, not the keyword names, which implements keywords. Value of key is json object which contains two keys: name and doc. The name key contains the keyword translated name and doc contains translated documentation. Providing doc and name are optional, example translation json file can only provide translations to keyword names or only to documentation. But it is always recommended to provide translation to both name and doc. Special key __intro__ is for class level documentation and __init__ is for init level documentation. These special values name can not be translated, instead name should be kept the same.
Generating template translation file
Template translation file, with English language can be created by running: rfbrowser translation /path/to/translation.json command. Command does not provide translations to other languages, it only provides easy way to create full list keywords and their documentation in correct format. It is also possible to add keywords from library plugins and js extensions by providing --plugings and --jsextension arguments to command. Example: rfbrowser translation --plugings myplugin.SomePlugin --jsextension /path/ot/jsplugin.js /path/to/translation.json
Example project for translation can be found from robotframework-browser-translation-fi repository.
ENVIRONMENT VARIABLES
There are some environment variables that can be used to modify the behavior of the library. Some of these environment variables are for experimental/development features and must not be used in production environments. They are listed here to prevent users using them by accident without knowing the possible consequences.
These are:
| Environment variable |
Description |
ROBOT_FRAMEWORK_BROWSER_NODE_PORT |
Port number for connecting to an existing node process. This is an alternative to playwright_process_port import argument. |
ROBOT_FRAMEWORK_BROWSER_NODE_COVERAGE |
If set to 1, will collect code coverage for the node process. This must not be used in production environments and is not supported on Windows. |
ROBOT_FRAMEWORK_BROWSER_NODE_DEBUG_OPTIONS |
Debug options for the node process. This is a comma-separated list of arguments, for example --inspect. This must not be used in production environments. |
Experimental: Provide parameters to node process
Browser library is integrated with Node.js and Python. Browser library starts a node process, to communicate Playwright API on the Node.js side. It is possible to provide parameters for the started node process by defining ROBOT_FRAMEWORK_BROWSER_NODE_DEBUG_OPTIONS environment variable, before starting the test execution. Example: ROBOT_FRAMEWORK_BROWSER_NODE_DEBUG_OPTIONS=--inspect,--trace-warnings robot path/to/tests. There can be multiple arguments defined in the environment variable and arguments must be separated with comma.