Tellurium Tutorial Series: the secret of the Tellurium runtime XPath

12 views
Skip to first unread message

John.Ji...@gmail.com

unread,
Oct 23, 2008, 10:18:41 PM10/23/08
to tellurium-users
As I repeated many times, the runtime xpath for a UI object in
Tellurium
is automatically generated by the framework itself, but how?

Tellurium UI module usually consists of a set of nested UI objects,
for instance,

A{
B{
D{
E
}
}
C {
}
}

For each UI object, it either uses the locator, i.e., relative XPath,
or clocator,
i.e., attributes, to define itself. The question is how tellurium
glues them
together to generate the runtime xpath. Take the UI object E as an
example,
Tellurium will walk all the way from A to E,

A -> B -> D -> E

When tellurium walks along the path, it either uses the given relative
XPath
for the UI object or generate a relative XPath from the UI object's
attributes,
i.e., clocator.

For clocator, The neighboring objects are glued by using the XPath
axis
"descendant-or-self", which means "Selects all descendants (children,
grandchildren, etc.) of the current node and the current node
itself".
In this way, even you skip some tags in between, you can still have
the
correct XPath. For example, if you have the original XPath

//table/tr/td/div/div/div/a

you might be able to re-write it as

//table/tr/td/descendant-or-self:a

and skip the three divs.

Sometimes, you cannot skip the tags in between otherwise you get
multiple elements with the same XPath. In such a case, you should
specify
the "direct" attribute in the clocator as true, in that way, tellurium
will
use the XPath axis "child" to link the two neighboring objects.

The clocator is defined in the class,

class CompositeLocator {
String header
String tag
String text
String trailer
def position
boolean direct
Map<String, String> attributes = [:]
}

The "tag", "text", "position", and other attributes are used to
generated the relative XPath.
The header and trailer are used to specify additional xpath related to
this UI object and
the relative xpath for this UI object is,

XPath = header + (derived xpath for object) + trailer

Take the google start page as an example with the following UI module,

ui.Container(uid: "google_start_page", clocator: [tag: "td"], group:
"true"){
InputBox(uid: "searchbox", clocator: [title: "Google Search"])
SubmitButton(uid: "googlesearch", clocator: [name: "btnG", value:
"Google Search"])
SubmitButton(uid: "Imfeelinglucky", clocator: [value: "I'm Feeling
Lucky"])
}

The relative XPath generated for the UI object "googlesearch"
SubmitButton is as follows,

input[@name="btnG" and @value="Google Search" and @type="submit"]

Obviously, the xpath expression for an attribute is normally

@attribute = value

Note, some UI object comes with default tag and default attributes,
which will all be
included in the generated xpath. For example, the "googlesearch"
object has the default
tag "input" and default type "submit" since it is a SubmitButton. Also
be aware that
Tellurium uses "%%" for partial text matching, i.e., uses the
following format

contains(text(), expected text)

otherwise, it uses the format

normalize-space(text())= normalize-space(expected text)

The function normalize-size() is used to eliminate space problem in
text comparison.

For Group Locating, i.e, with the attribute "group" to be true,
Tellurium only considers
its direct children at the current stage. For example, the generated
XPath for the Container
"google_start_page" is

td[descendant::input[@title="Google Search"] and
descendant::input[@name="btnG"
and @value="Google Search" and @type="submit"] and
descendant::input[@value="I'm Feeling Lucky" and @type="submit"]]

Apparently, Tellurium uses "and" to aggregate the current node's
children
attributes to do group locating. The above XPath means the UI object
comes
with a "td" tag and it has the following children with attributes,

input[@title="Google Search"]
input[@name="btnG" and @value="Google Search" and @type="submit"]
input[@value="I'm Feeling Lucky" and @type="submit"]

As I said above, the neighboring UI objects are glued with the XPath
axis
"descendant-or-self" for clocator in normal case, for the example,
the xpath for the InputBox "searchbox" is

//descendant-or-self::td[descendant::input[@title="Google Search"]
and
descendant::input[@name="btnG" and @value="Google Search" and
@type="submit"]
and descendant::input[@value="I'm Feeling Lucky" and
@type="submit"]]
/descendant-or-self::input[@title="Google Search"]

What if the UI objects are defined using relative xpaths, i.e.,
locators, not the clocators?
Take the google code hosting page as an example, the UI module could
be defined as,

ui.Container(uid: "googlecodehosting"){
Table(uid: "labels_table", locator: "//
table[descendant::div[contains(text(),\"Example project labels:\")]]")
{
TextBox(uid: "row: 1, column: 1", locator: "/div")
Table(uid: "row: 2, column: 1", locator: "/div[@id=\"popular\"]/
table"){
UrlLink(uid: "all", locator: "/a")
}
}
}

The generated XPath for the UrlLink is as follows,

//table[descendant::div[contains(text(),"Example project labels:")]]
/tbody/tr[2]/td[1]/div[@id="popular"]/table/tbody/tr[3]/td[2]/a

It shows that Tellurium just glues relative XPaths together by
concatenate them.

For special UI objects such as table, the additional xpath element
such as
tr and td are handled by the object itself. Please check the table
object for
more details.

The xpath for the List object is handled differently by a special
algorithm.
I will not cover the details in this tutorial.
Reply all
Reply to author
Forward
0 new messages