Open UI Automation

September 26, 2020
 · 4 min read

I guess all of you who have worked on UI testing know the "locator" problem. In order to find an element in the DOM you need to provide a locator and usually it's an XPath string. Sometimes that xpath can be quite nontrivial and in one day developers change the code and your locator cannot find the required element. In order to avoid such situations as much as possible my colleagues Peter Savage, Ronny Pfannschmidt and Karel Hala have developed a specification for frontend developers Open Web UI Design Specification for Enabling Automation (OUIA). If your application complies with OUIA it will have predictable locators and the behavior. This significantly simplifies writing automated tests.

The specification

You can familiarize the specification here https://ouia.readthedocs.io/. Below I will describe a part that relates to components and component frameworks.

OUIA components

The smallest building block of a front-end application is a component. It can be a button, dropdown or even just a text block. If we want to interact with it we should find the root of the element in the DOM. As I wrote above an xpath string is the most versatile way to locate it. Consider the following example:

.//div[@class="pf-c-button"]

data-component-id

This xpath finds all elements with div tag that has "pf-c-button" in class attribute. If you need a specific element you can specify an index:

.//div[@class="pf-c-buton"][1]

Such queries have several problems. First, there are might be situations when elements on the page are shuffled on every reload and in the next test session your automation will fail. Another issue with indexed xpath queries that they don't tell us anything about the underneath object's id. The most common example would be a table. Usually tables represent data from databases. In your automation test suite you often manipulate objects that abstract your testing application objects. Thus we as testers would like to see some unique id in the root HTML tag. We could use id attribute but in some cases it can be reserved for other purposes. It's better to use our own attrbiute which we can control. data-ouia-component-id is such attribute. If it's one of many buttons we could assign a variant of the button:

<button data-ouia-component-id="Submit"></button>

If it's a row in a table that represents data from some database we might want to have the id of the row matches to the id from the database, e.g. uuid:

<tr data-ouia-component-id="28b9fe89-da52-4dcf-aafe-9f675d708d09">
  <td>...</td>
</tr>

data-component-type

Such components as dropdowns, navigations, paginators and others have complex behavior and their root elements in the most cases don't tell us what exact component we work with. Of course we can assume the component type from class attribute or other indicators but it always requires adding some logic in a test suite. OUIA introduces data-component-type attribute. Let's consider Patternfly's Dropdown component:

<div class="pf-c-dropdown pf-m-expanded" data-ouia-component-type="PF4/Dropdown"
    data-ouia-component-id="some_id"><button data-ouia-component-type="PF4/DropdownToggle"
        data-ouia-component-id="some_id" class="pf-c-dropdown__toggle" type="button"
        aria-expanded="true" aria-haspopup="true"><span class="pf-c-dropdown__toggle-text">Dropdown</span></button>
    <ul aria-labelledby="toggle-id" class="pf-c-dropdown__menu" role="menu">
        <li role="menuitem"><a tabindex="-1" data-ouia-component-type="PF4/DropdownItem" data-ouia-safe="true"
                data-ouia-component-id="some_id" aria-disabled="false"
                class="pf-c-dropdown__menu-item">Link</a></li>
    </ul>
</div>

As you can see it has div as a root element and in order to recongize that it's a Dropdown we could get value of class attribute which is pf-c-dropdown pf-m-expanded and extract the name of the component from it. data-ouia-component-type explicitely tells us that we deal with Dropdown. PF4 part specifies a namespace or name of component library. In case of Patternfly it's PF4. It might be possible that a page can have the same components from different libraries therefore it would be good to separate them.

data-ouia-safe

In Selenium please wait! I described a problem with animated elements. We should wait when the animation is finished and only after that we can continue to interact with the element. data-ouia-safe must be true only when a component doesn't play any animation. It seems it's the most tricky part because it requires some additional javascript code that will set the value for that attribute.

OUIA xpath

Combining all these pieces together we can have a very minimal versatile xpath for a given component with some id. Here is it:

.//*[@data-ouia-component-type="Some Component" and @data-ouia-component-id="some id"]

We can use this xpath in a test suites and frameworks. Moreover OUIA opens vast possibilities for code generation. We can create a sort of DOM scanner that will generate ready-to-use abstractions for testing frameworks.

OUIA in the wild

The first component library that started add OUIA attributes is Patternfly. We closely work together with Patternlfy developers to provide better experience with OUIA to library consumers and testers. Many thanks to @zallen and @jschuller. Besides there is ongoing work to adding OUIA compatibility layer in various test suites and frameworks such as widgetastic.core and widgetastic.patternfly4. I hope the specification will go beyond of Red Hat's projects and other people will see the benefits of using OUIA.

References