The purpose of this thread is to find out how we are dealing with ExtJS 4.x (4.2 in particular) when it comes to testing with Selenium WebDriver.
So, tips, tricks, code samples, something practical, tangible that can be tested and used.
Now, I've gotta a handle on a few things as you will see from the code examples below, but obviously, the more input the merrier and who is to say what I have is the best way? I'm not, so, if you have a practical and improved alternative, please pass it along! :)
What I'm trying to do is make this thread a place of reference.
As I keep figuring things out myself, I will keep adding to the mix.
Below I will explain for those who don't know but show an interest, why it's so hard to test ExtJS 4.2 and finally, the code samples I've been able to figure out which allowed me to manipulate ExtJS 4.2 using Selenium WebDriver with code written in Java.
1. ExtJS 4.x uses a splitDOM engine to generate HTML code to the browser. The generated code is
optimized based on the target browser, so, the actual HTML code generated for each browser can be
different. In a nutshell, you cannot expect your HTML to be the same between IE and Firefox for
example.
This means that you can’t rely on XPath with 100% certainty.
2. Sencha’s SMVC does NOT rely on ExtJS components having unique IDs and as such, this means that
the generated code will not have any predictable unique IDs to work with. So, the best way to deal
with this is with the use of WebDriver’s JavascriptExecutor. But even that, as you will see below, can
be a tricky thing, especially when it comes to handling events. At best, you will try and use the
JavascriptExecutor to run command such as Component Queries.
CODE EXAMPLES
1) setting the value of a textfield
StringBuilder s = new StringBuilder();
s.append("with (Ext.ComponentQuery.query(\"textfield[name='username']\")[0]) {
setValue(arguments[0]); fireEvent('blur'); }");
((JavascriptExecutor) driver).executeScript(s.toString(), username);
I found out that unless I blur out of the field, the setValue() method doesn't take effect.
2) clicking an ExtJS button
In the case of buttons, you will find that events do NOT fire as expect.
First, let me show you how we are able to use a ComponentQuery to find a button and change its text.
s = new StringBuilder();
s.append("with (Ext.ComponentQuery.query(\"button[itemId='signInButton']\")[0]) { focus(); setText('new Signing Text'); }"); // this works
((JavascriptExecutor) driver).executeScript(s.toString());
Using a similar code structure, we now try to fire the click event on that captured button.
s = new StringBuilder();
s.append("with (Ext.ComponentQuery.query(\"button[itemId='signInButton']\")[0]) { focus(); fireEvent('click'); }"); // this doesn't work
((JavascriptExecutor) driver).executeScript(s.toString());
The above will not work, and causes the test to crash.
The alternative is to use findElements and/or findElement on a page to old fashion way; Inspecting the code.
But be careful. ExtJS HTML/Dom delivery isn’t identical between browsers. At first, try CSS selectors. The
reason is simple. 1) Performance. CSS Selectors are faster than XPaths. 2) IE and Chrome are very slow on
XPaths. Avoid it if you can.
The following snippet is how we can get a button to fire under ExtJS, it’s not pretty.
ArrayList<WebElement> buttons = (ArrayList) driver.findElements(By.cssSelector("a[class^='x-btn']"));
System.out.println(String.format("*********** Found %d buttons *********** ", buttons.size()));
String bText = "";
for(WebElement button : buttons) {
bText = button.getText();
if(bText.equals("Sign In")) { // "Sign In" would have to be part of a dictionary
button.click();
break;
}
}
The reason this was used is that when you inspect the HTML code, you will find that there was nothing
directly identifying the ExtJS component.
Here’s the code which creates the button in ExtJS
{
xtype: 'button',
text : "Sign In",
action : 'submit',
itemId : 'signInButton',
margin : '0 33 0 0',
formBind : true,
disabled : true
}
The generated HTML in Chrome
<a id="button-1015" unselectable="on" hidefocus="on" role="button" style="margin: 0px; right: auto; left: 1076px; top: 0px; width: 75px;" class="x-btn x-unselectable x-box-item x-toolbar-item x-btn-default-small x-item-disabled x-disabled x-btn-disabled x-btn-default-small-disabled x-noicon x-btnnoicon x-btn-default-small-noicon">
<span unselectable="on" class="x-btn-wrap" id="button-1015-btnWrap">
<span class="x-btn-button" id="button-1015-btnEl">
<span unselectable="on" class="x-btninner x-btn-inner-center" id="button-1015-btnInnerEl">Sign In</span>
<span style="" unselectable="on" class="x-btn-icon-el " id="button-1015-btnIconEl" role="img"></span>
</span>
</span>
</a>
When you compare the 2 of them, there is NOTHING that could be used to determine EXACTLY the button we want to use.
Of course, there is something that can be done, but it requires modifying your ExtJS code to add a CSS attribute which would be unique to this element as displayed below.
{
xtype: 'button',
text : "Sign In",
action : 'submit',
cls: ‘signInButtonCls’,
itemId : 'signInButton',
margin : '0 33 0 0',
formBind : true,
disabled : true
}