|
CustomJQuerySelectorInTellurium
Custom jQuery selectors and Plugins in Tellurium.
Phase-Support IntroductionTellurium leverages jQuery to add jQuery selector as a new locator to improve the test speed in IE and add other new functionalities to Selenium core, for example, fetching bulk data in one method call and the diagonse utility. With the adoption of jQuery, we also need some custom jQuery selectors and plugins to meet our needs. To design jQuery custom selectors, we need to understand the jQuery selector syntax: $.expr[':'].selector_name = function(obj, index, meta, stack){
......
}where
The above function returns true to include current element and returns false to exclude current element. A more detailed explanation could be found from jQuery Custom Selectors with Parameters. To avoid conflicts with user's jQuery library, we yield the "$" symbol and rename jQuery to teJQuery in Tellurium. Custom jQuery Selectors:te_textThe :te_text selector is created to select a UI element whose text attribute is a given string. The implementation is simple, teJQuery.extend(teJQuery.expr[':'], {
te_text: function(a, i, m) {
return teJQuery.trim(teJQuery(a).text()) === teJQuery.trim(m[3]);
}
});You may wonder why we use m[3] here, the variable m includes the following parameters
As a result, the selector picks up the elements whose text attribute, obtained by text(), is equal to the passed in parameter m[3]. :groupThe :group selector is used to implement the group locating in Tellurium. For example, we want to select a "div" whose children include one "input", one "img", and one "span" tags. How to express this using jQuery? One way is to use the following selector, teJQuery.expr[':'].group = function(obj){
var $this = teJQuery(obj);
return ($this.find("input").length > 0) && ($this.find("img").length > 0) && ($this.find("span").length > 0);
};That is to say, only a DOM node satisfying all the three conditions, i.e, whose children include "input", "img", and "span", is selected because the AND conditions. Remember, only the node that returns true for the above function is selected. However, in real world, we may have many conditions and we cannot use this hard-coded style selector and we need to use the custom selector with parameters instead. Here is our implementation, teJQuery.expr[':'].group = function(obj, index, m){
var $this = teJQuery(obj);
var splitted = m[3].split(",");
var result = true;
for(var i=0; i<splitted.length; i++){
result = result && ($this.find(splitted[i]).length > 0);
}
return result;
};If we use firebug to debug the code by running the following jQuery selector teJQuery("div:group(input, img, span)")We can see the variable m includes the following parameters
:stylesOne user provided us the following UI module, ui.Container(uid: "Program", clocator: [tag: "div"], group: "true") {
Div(uid: "label", clocator: [tag: "a", text: "Program"])
Container(uid: "triggerBox", clocator: [tag: "div"], group: "true") {
InputBox(uid: "inputBox", clocator: [tag: "input", type: "text", readonly: "true", style: "width: 343px;"], respond: ["click"])
Image(uid: "trigger", clocator: [tag: "img", style: "overflow: auto; width: 356px; height: 100px;"], respond: ["click"])
}
}Unfortunately, the following generated jQuery selector does not work. $('div:has(input[type=text][readonly=true][style="width: 343px;"], img[style="overflow: auto; width: 356px;height: 100px;"]) img[style="overflow: auto; width: 356px; height: 100px;"]')We have to use a custom jQuery selector to handle the style attribute as follows, teJQuery.expr[':'].styles = function(obj, index, m){
var $this = teJQuery(obj);
var splitted = new Array();
var fs = m[3].split(/:|;/);
for(var i=0; i<fs.length; i++){
var trimed = teJQuery.trim(fs[i]);
if(trimed.length > 0){
splitted.push(trimed);
}
}
var result = true;
var l=0;
while(l < splitted.length){
result = result && (teJQuery.trim($this.css(splitted[l])) == splitted[l+1]);
l=l+2;
}
return result;
};The main idea is to split the content of the style attribute into multiple single-css classes, then try to match each css class one by one. This approach may not be the optimal one, but it works. Then, the new runtime jQuery selector becomes, div:group(a:te_text(Program), div) div:group(input:styles(width: 343px;)[type=text][readonly=true], img:styles(overflow: auto; width: 356px; height: 100px;)) img:styles(overflow: auto; width: 356px; height: 100px;) :nextToLastOne implemented suggested by Kevin is shown as follows, teJQuery.expr[':'].nextToLast = function(obj, index, m){
var $this = teJQuery(obj);
if ($this.index() == $this.siblings().length - 1) {
return true;
} else {
return false;
}
};and he also suggested a more efficient implementation. // this is a selector called nextToLast. its sole purpose is to return the next to last
// element of the array of elements supplied to it.
// the parameters in the function below are as follows;
// obj => the current node being checked
// ind => the index of obj in the array of objects being checked
// prop => the properties passed in with the expression
// node => the array of nodes being checked
teJQuery.expr[':'].nextToLast = function(obj, ind, prop, node){
// if ind is 2 less than the length of the array of nodes, keep it
if (ind == node.length-2) {
return true;
} else {
// else, remove the node
return false;
}
};Custom jQuery Plugins
outerHTMLWhen we worked on the diagnose utility, we were frustrated because we need to get the HTML source of a DOM node, but the html() method in jQuery only returns innerHTML. We posted a question to jQuery group and got the answer, $('<div>').append( $(jQuery_Selector).clone() ).html() and as suggested by another person, we went further to implement this as a simple jQuery plugin, teJQuery.fn.outerHTML = function() {
return teJQuery("<div/>").append( teJQuery(this[0]).clone() ).html();
};We made two changes here.
SummaryWith the evolution of Tellurium and the heavy use of jQuery in Tellurium, the list of custom jQuery selectors and plugins in Tellurium will keep growing. We will keep updating this wiki page, please do check back. If you are a Javascript or jQuery guru and like to contribute to Tellurium, please contact us. AcknowledgementsSpecial thanks to folks in jQuery forum for providing us helps on custom jQuery selectors and functions. Resources | |
Hi, Are you thinking of adding support for jQuery selecters that can directly cope with style: "overflow: auto; width: 356px; height: 100px;"
or
class: aaa bbb ccc
Sorry for replying late. It is hard for us to check comments on wiki because we never got any notification. You are welcome to post your comments to tellurium user group at http://groups.google.com/group/tellurium-users.
Yes, we need to handle the individual class or style in our project.