Saturday, April 15, 2017

Why ExtJS UI Testing is Hard

Consider this simple ExtJS modern toggle field declaration below.



The above ExtJS code results in the following UI rendering display in a browser.



Now, consider the HTML output from that simple ExtJS declaration:



Yeah, crazy. The reason for this HTML craziness is because ExtJS does dynamic rendering. You declare some simple JavaScript code, and it takes care of the rest, including making it look the same across browsers. In order to do that, the HTML it emits can be different from browser to browser. Essentially ExtJS is a web abstraction layer that takes care of rendering so you can focus on building your app.

Now, that is just a simple toggle field component. Enter an advanced data grid with lots of rows or a tree with lots of nodes and the HTML is going to explode way beyond what's shown above for the toggle field.

Trying To UI Test this Jazz with Selenium

Selenium is designed to interact with DOM elements. It has different mechanisms to locate elements. The most reliable way to select a web element is by something unique such as an element id. However, in the case of ExtJS, developers are not supposed to set the "id" property as a potential duplicate "id" could crash or at least mess up the app. We can set the "itemId" in the ExtJS code, however that doesn't come through to the DOM at all. As a side note, I would like to know why ExtJS doesn't emit the itemId in some way to the DOM? If Sencha would change ExtJS to emit the itemId somehow into the DOM, locating unique top level div elements would be so much easier!

However, even if we did hard code the "id" or create a way to resolve the ExtJS itemId to an actual id (which is possible via JavaScript by calling the ExtJS component query Api), and it came through to DOM, there is a new problem. Take the toggle field example above. The "id" would resolve to the top level div with an id of "ext-togglefield-3". That is just the top level wrapper div for the form field and doesn't give us the id of the actual slider component. It's a couple div's down and nested a bit, with an id of "ext-toggleslider-3". This is completely dynamic HTML and there isn't any way to hard code that "id" even if I tried. Essentially, it's "inside" and unreachable from my ExtJS JavaScript declaration above.

Knowing the top level div id does help some, because then you could use Selenium look it up by id and then use a CSS selector and look for an "div.x-toggleslider" class inside that top level element. But this process is going to be different for every ExtJS component and is subject to change from version to version. Here is some Selenium Java code that would click on the toggle field above:

//For purposes of illustration, assume the toggleFieldId below 

//was resolved in a reliable way and not hard coded!
//e.g. this toggleFieldId could be resolved via ExtJS itemId by
//invoking ExtJS JavaScript code
String toggleFieldId = "ext-togglefield-3";

//get top level field by the resolved id
WebElement toggleField = driver.findElement(By.id(toggleFieldId));

//now select actual clickable slider by CSS selector on
//the toggle field element
WebElement toggleSlider = toggleField.findElement(By.cssSelector("div.x-toggleslider"));

//Now click the toggle slider
action.moveToElement(toggleSlider).click().build().perform();


The code above will actually work. However, we have to dance down through a top level ExtJS component div and select the "real" clickable components inside it via CSS selectors to get to the real thing. This is going to vary greatly from component to component and could change from version to version.

Use a UI Test Tool Designed for ExtJS [like Sencha Test]?

It seems this dynamic rendering craziness is why tools are created to help UI test ExtJS. Sencha built their own tool called "Sencha Test" which basically takes away all the DOM selection grunt & guess work away by creating an Api to select and manipulate components. The Sencha Test equivalent of the above code would be this:

ST.field('#tablet-inventory-model-view-expendable')
.setValue(true);

The "#tablet-inventory-model-view-expendable" above is an itemId we set on the ExtJS side and then from Sencha Test you can just target that direct and reliable itemId and set the value to true! That's it! You don't have to worry about figuring out the correct top level id and then finding the "real" clickable component via a CSS selector like my Java code example earlier.

This is just a simple example and as you can imagine, with data grids, trees, etc, the complexity is going to explode. With growing complexity, Sencha Test is going to shine even more.

So What Do You Do?

My team is still trying to figure that out. We'd love to use tools that are agnostic of the underlying web framework, however it's just highly complicated and frustrating dealing with dynamic DOM output from ExtJS. We are giving Sencha Test a look over as it appears to make life much easier, but it's own animal and you'd need to learn it's Api and ways of doing things.

I'll try to write another blog post in the future once we figure out our approach. Stay tuned!

If anybody has any ideas they'd like to share on how to UI test ExtJS, please comment below! Cheers!

1 comment:

Michael Worthington said...

Tips from our team:
ExtJS has their own testing tools, which we stopped using due to their large flaws. These tools likely have gotten much better, but we were stuck using older versions that were compatible with the ExtJS version we ship with. Other than that we use Selenium+Geb+Spock for Functional testing. In place of the concrete IDs; you're forced to use CSS selectors to call out specific things on the page, which takes a bit of practice.