record and assert Ext JS

ExtJS is a cross-browser JavaScript library for building rich internet applications.
Selenium IDE has trouble testing apps written in ExtJS.
There are two reasons.

First, the DOM of ExtJS widget is complex, many elements have a dynamic id

Let's look at the Button widget:

<table cellspacing="0" cellpadding="0" border="0" class="x-btn-wrap x-btn eos-widget x-btn-focus" id="button1" style="width: auto;">
<tbody>
<tr>
<td class="x-btn-left"><i> </i></td>
<td class="x-btn-center" id="ext-gen411">
<em unselectable="on">
<button type="button" class="x-btn-text" id="ext-gen78">button1</button>
</em>
</td>
<td class="x-btn-right"><i> </i></td>
</tr>
</tbody>
</table>

I have recorded the command click for button1 like this;

command click
target //button[@id='ext-gen78']

But next time the id of button element may be changed to ext-gen88, since ExtJS used dynamic code to create the widget.

To resolve the problem I write a ext locator to locate the ExtJS element.

For example, the command is

command click
target //TABLE[@id='button1']/tbody/tr/td[2]/em/button

The root element's id does not change, and the DOM structure is also stable.

My extension is :

var EXT_PREFIX = "ext-gen";
function findExtLocator(e) {
	function getElementIndex(el, p) {
		var childs = p.childNodes;
		for (var i = 0; i < childs.length; i++) {
			var curr = childs[i];
			if (curr == el) {
				return "[" + (i + 1) + "]";
			}
		}
	}
	if (e.id) {
		var elId = e.id;
		if (elId.indexOf(EXT_PREFIX) == 0) {
			var currNode = e;
			var locator = "";
			while (currNode && currNode.tagName.toLowerCase() != "body") {
				parentNode = currNode.parentNode;
				locator = this.relativeXPathFromParent(currNode) + locator;//
				if (parentNode.id && parentNode.id.length > 0
						&& parentNode.id.indexOf(EXT_PREFIX) != 0) {
					locator = "//" + parentNode.tagName + "[@id='"
							+ parentNode.id + "']" + locator;
					return locator;
				}
				currNode = currNode.parentNode;
			}
		}
	}
	return null;
}
LocatorBuilders.add('ext', findExtLocator);
// You can change the priority of builders by setting LocatorBuilders.order.
LocatorBuilders.order = ['ext', 'id', 'link', 'name', 'dom:name', 'xpath:link',
		'xpath:img', 'xpath:attributes', 'xpath:href', 'dom:index',
		'xpath:position'];

Then you can record ExtJs's page.

Second, ExtJS is based on JavaScript context

We can't assert JavaScript value of ExtJS.
To resolve this problem, I have extended selenium-core a little. You can include the following in your user-extensions.js file.

Selenium.prototype.assertExtEqual = function(expression, text) {
	/**
	 * the euqal assertion of ext
	 * @param expression ext expression , just like "button1.text" or "text1.getValue()"
	 * @param String target value
	 */
	var result = this.extEval(expression)
	if (result != text) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not equal with " + text);
	}
};

Selenium.prototype.assertExtGreaterThan = function(expression, text) {
	/**
	 * the greater than assertion of ext
	 * @param expression ext expression , just like "button1.text" or "text1.getValue()"
	 * @param String target value
	 */
	var result = this.extEval(expression)
	if (result <= text) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not greater than " + text);
	}
}

Selenium.prototype.assertExtGreaterEqualThan = function(expression, text) {
	/**
	 * the greater and equal than assertion of ext
	 * @param expression ext expression , just like "button1.text" or "text1.getValue()"
	 * @param String target value
	 */
	var result = this.extEval(expression)
	if (result < text) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not greater equal than " + text);
	}
}

Selenium.prototype.assertExtLessThan = function(expression, text) {
	/**
	 * the less than assertion of ext
	 * @param expression ext expression , just like "button1.text" or "text1.getValue()"
	 * @param String target value
	 */
	var result = this.extEval(expression)
	if (result >= text) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not less than " + text);
	}
}

Selenium.prototype.assertExtLessEqualThan = function(expression, text) {
	/**
	 * the less and equal than assertion of ext
	 * @param expression ext expression , just like "button1.text" or "text1.getValue()"
	 * @param String target value
	 */
	var result = this.extEval(expression)
	if (result > text) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not less equal than " + text);
	}
}

Selenium.prototype.doExecuteExtFunction = function(expression, text) {
	/**
	 * do ext function ,if the expression end with ")" ,the params is not useful
	 * @param expression ext expression return a ext function, just like "button1.getText" or "text1.getValue()"
	 * @param String params ,just like "a,b,c"
	 */
	if (expression.lastIndexOf(")") == expression.length - 1) {
		this.extEval(expression);
	} else {
		var scopeObj = this.extEval(expression.substring(0, expression
				.lastIndexOf(".")));
		var func = this.extEval(expression);
		if (typeof(func) != "function") {
			Assert.fail("the value of [" + func + "] " + expression
					+ " is not a function");
		}
		var params = [];
		if (text) {
			params = text.split(",");
		}
		try {
			func.apply(scopeObj, params);
		} catch (e) {
			Assert.fail("error execute function [" + func + "] " + expression);
		}
	}
}

Selenium.prototype.assertExtTrue = function(expression) {
	/**
	 * the true assertion of ext
	 * @param expression ext expression , just like "button1.hidden"
	 */
	var result = this.extEval(expression);
	if (result !== true) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not true");
	}
}

Selenium.prototype.assertExtFalse = function(expression) {
	/**
	 * the false assertion of ext
	 * @param expression ext expression , just like "button1.hidden"
	 */
	var result = this.extEval(expression);
	if (result !== true) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not false");
	}
}


Selenium.prototype.assertExtNull = function(expression, text) {
	/**
	 * the null assertion of ext
	 * @param expression ext expression , just like "button1.text"
	 */
	var result = this.extEval(expression);
	if (result !== null) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not null");
	}
}


Selenium.prototype.assertExtNotNull = function(expression, text) {
	/**
	 * the not null assertion of ext
	 * @param expression ext expression , just like "button1.text"
	 */
	var result = this.extEval(expression);
	if (result === null) {
		Assert.fail("the value of [" + result + "] " + expression + " is null");
	}
}


Selenium.prototype.assertExtUndefined = function(expression, text) {
	/**
	 * the undefined assertion of ext
	 * @param expression ext expression , just like "button1"
	 */
	var result = this.extEval(expression);
	if (result !== undefined) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not undefined");
	}
}


Selenium.prototype.assertExtNotUndefined = function(expression, text) {
	/**
	 * the not undefined assertion of ext
	 * @param expression ext expression , just like "button1"
	 */
	var result = this.extEval(expression);
	if (result === undefined) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is undefined");
	}
}


Selenium.prototype.assertExtPresent = function(expression, text) {
	/**
	 * the present assertion of ext
	 * @param expression ext expression , just like "button1"
	 */
	var result = this.extEval(expression);
	if (result == null || result == undefined) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not present");
	}
}

Selenium.prototype.assertExtNotPresent = function(expression, text) {
	/**
	 * the not present assertion of ext
	 * @param expression ext expression , just like "button1"
	 */
	var result = this.extEval(expression);
	if (result != null || result != undefined) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is present");
	}
}

Selenium.prototype.assertExtMatches = function(expression, text) {
	/**
	 * the matches assertion of ext
	 * @param expression ext expression , just like "button1.text" or "text1.getValue()"
	 * @param String target value
	 */
	var result = this.extEval(expression);
	var reg = new RegExp(text);
	if (!reg.test(result)) {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not match " + text);
	}
}

Selenium.prototype.assertExtContains = function(expression, text) {
	/**
	 * the contains assertion of ext
	 * @param expression ext expression , just like "button1.text" or "text1.getValue()"
	 * @param String target value
	 */
	var result = this.extEval(expression);
	if (typeof(result) == "undefined" || result == null) {
		Assert.fail("the value of " + expression + " dos not contains " + text);
	} else if (result.indexOf) {
		if (result.indexOf(text) < 0) {
			Assert.fail("the value of [" + result + "] " + expression
					+ " dos not contains " + text);
		}
	} else {
		Assert.fail("the value of [" + result + "] " + expression
				+ " is not a String or Array");
	}
}

Selenium.prototype.assertExtTypeof = function(expression, text) {
	/**
	 * the typeof assertion of ext
	 * @param expression ext expression , just like "button1.text" or "text1.getValue()"
	 * @param String target value
	 */
	var type = typeof(this.extEval(expression));
	if (type != text) {
		Assert.fail("the type of [" + type + "] " + expression + " is not "
				+ text);
	}
}

PageBot.prototype.getWrappedWindow = function(extpath) {
	var win = this.getCurrentWindow() || {};
	return win.wrappedJSObject;
}


Selenium.prototype.getWrappedWindow = function(extpath) {
	return this.browserbot.getWrappedWindow();
}


Selenium.prototype.extEval = function(expression) {
	var script = expression;
	if (expression) {
		var expArr = expression.split(".");
		expArr[0] = "(window.Ext.getCmp('" + expArr[0] + "')||window.Ext.get('"
				+ expArr[0] + "')||window.Ext.StoreMgr.lookup('" + expArr[0]
				+ "'))";
		expression = expArr.join(".");
	}
	try {
		return this.doEval(expression);
	} catch (e) {
		throw new SeleniumError("the expression " + script
				+ " is not a Ext expression !");
	}
};
// I have to rewrite the eval function to get the context of window
Selenium.prototype.doEval = function(expression) {
	/**
	 * execute js ecpression
	 *
	 * @param {Object}
	 *            expression js expression
	 */
	try {
		var win = this.getWrappedWindow();
		var result = eval(expression, win);
		return result;
	} catch (e) {
		throw new SeleniumError("the expression " + expression
				+ " is not a Ext expression !");
	}
}

There are now enough assertions to test ExtJS.

We can now write a assert command like this:

command assertExtEqual
target button1.getText()
value button1

In a word, we can record and assert ExtJS applications using the above extensions.

Labels

extjs extjs Delete
selenium_ide selenium_ide Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. May 17, 2010

    alex knol says:

    Super, is there a chance you can tell me where and how I put the first extension...

    Super, is there a chance you can tell me where and how I put the first extension.

    Maybe we can complete this page into a complete howto.

  2. Jul 20, 2010

    spoona says:

    Brilliant post... Keep on going..

    Brilliant post... Keep on going..

  3. Jan 07, 2011

    Joel Ransom says:

    I'm surprise there's not more activity here considering how widespread this prob...

    I'm surprise there's not more activity here considering how widespread this problem is. This (the first example) seems to work wonderfully. I would like to understand a little better exactly when it runs, are there any performance considerations, etc. I don't know much about extensions to Selenium. 

  4. Apr 28, 2011

    Ethan Thomason says:

    Alex, I found this blurb on "Contributed Extensions and Formats" main page: To ...

    Alex, I found this blurb on "Contributed Extensions and Formats" main page:

    To use the extensions:

    1. Download or copy the extension code into a new .js file. You can place it anywhere on your disk.
    2. Open Options - Options... in the menu bar.
    3. Choose the saved file in "Selenium IDE extensions" field and click OK.
    4. Restart Selenium IDE by closing the window and opening it again.
  5. Apr 29, 2011

    Jitendra Singh says:

    Hi Olle, thanks for such a wonderful post!! 

    Hi Olle, thanks for such a wonderful post!!