Open UI Automation
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.