Implementing re-usable custom locators for RF SeleniumLibrary
By: Toni Kangas
Tags: Testing, Selenium, Test Automation, Python, Robot Framework

As a full stack developer, I am often involved in multiple different testing phases of an application. Recently, the front-end applications I have been working with have been implemented with React and unit-tested with Jest and react-testing-library. In addition to these unit-tests, the projects have usually had end-to-end tests run in a browser and implemented with Robot Framework and SeleniumLibrary.

These two testing tools, react-testing-libary and SeleniumLibrary, have very different approaches to locating HTML elements. react-testing-library, and all other testing libraries based on the dom-testing-library, provides methods for finding elements in a similar way that the end-user would use, such as findByLabelText() . SeleniumLibrarys locators are more developer focused and use, for example, elements id attribute or a XPath expression to locate the element. This difference in locating elements is one of the things that makes transitions between development and testing tasks more challenging for me.

In order to reduce the cost of context switches between development and testing task, I have often implemented some sort of hack to the end-to-end tests in order to produce XPath expressions to match the logic of the finder functions provided by the dom-testing-library. While these hacks have greatly helped to reduce the confusion when switching between these tools, they have not been very clean or re-usable solutions. For example:

*** Keywords ***

Title locator strategy

[Arguments] ${browser} ${locator} ${tag} ${constraints}

${element}= Get WebElements //*[@title='${locator}']

[Return] ${element} Add custom locator strategies

FOR ${name} ${keyword} IN

... title Title locator strategy

# Other custom locators

Run Keyword And Ignore Error

... Add Location Strategy ${name} ${keyword}

END

After implementing a very similar hacky solution to many different projects, I decided to look into implementing these dom-testing-library inspired locators as custom locators that could be installed as a Python package. According to the most recent recommendations on how to extend SeleniumLibrary, it seemed like the way to go would be to implement the custom locators in a SeleniumLibrary plugin.

The plugin interface of the SeleniumLibrary provides an easy way to extend the default element finder. The element finder can be modified through the element_finder property of LibraryComponent , which is the base class that the plugin class must inherit. The default element finder can be created as an instance of ElementFinder by providing the constructor with the same context as the plugin class instance is initialized with. This created ElementFinder instance can then be used to register the custom locator strategies.

My solution replaces the default element finder with a new element finder that registers custom locators to implement the dom-testing-library style locators when the plugin is loaded. This allows easy re-usability and simplifies the setup process a lot. See import and usage example below.

*** Settings ***

Library SeleniumLibrary plugins=TestingLibrarySelectorsPlugin *** Test cases ***

Click search bar

Click element placeholder:Search

The actual locator functions are mostly just wrappers for the xpath:… locator strategy of the SeleniumLibrary. At the moment, the only more complex locator is the find by label strategy, which will first find all the label elements with matching for or id attributes. This will allow locating input fields with matching id or aria-labelledby attributes. See the _plugin.py file in GitHub for the implementation.

At this point I had pretty much all of the features implemented that I have been usually using and could have been happy with what I had implemented. However, as the dom-testing-library supports also regexps and functions to match elements, I wanted to try something similar. Parsing the regexps from the locator criteria and passing it to matches() function in XPath expression seemed simple enough. And it was, but any of the browsers available didn’t seem to support this approach. Nevertheless, I still wanted to know if these XPath expressions are valid.