Tuesday, June 06, 2006

XPath and WATiR

WATiR provides very simple API's for functional testing an Web Application. With even limited scripting experience with Ruby you'll be able to create scripts to test your application. It provides API's for accessing almost all common elements on an HTML page. It allows you to access elements on the basis of some pre-defined attributes.

Now, there were few things that were lacking:
1. What if you have a element which you can't access using those pre-defined attributes?
2. What if there is no API for a particular HTML element?
3. What if you want to access some element depeding upon some other element? For e.g.: Accessing which has a image with some pre-defined "src" attribute.

The solution to all above problems is using XPATH to access these elements. XPATH query is a well defined and very powerful way for addressing elements in an XML document. If the HTML markup of the page is well-formed we can treat it as XML document and use XPATH query to select an element. So we introduced a new attribute to address such elements called ":xpath".

Will explain the usage of XPATH with examples for each of the above problem.

Problem1: Accessing elements using attributes that are not pre-defined:


Suppose you have an HTML "select" element like:

<select foo="bar"> <option value="1">1< /option> < /select>


Now "foo" is not standard attribute but browser will simply ignore it. Now if you want to access this element you need to use XPATH query. So to access element the statement will look like:

element = browser.select(:xpath, "//select[@foo='bar']")

The above statement will return you the desired element.

Problem2: Accessing elements for which there is no class in WATiR:

Suppose you have an HTML "map" element on your page. Now for "map" element there is no class in WATiR which you can use to access the elements. For such elements you can use the function "element_by_xpath" which takes in "XPATH query" and return you the desired element.

Example:

Suppose you have a "map" like this:

< map name="top_menu_map" id="top_menu_map">
< area shape="rect" coords="18,2,62,17" ref="http://engin.com.au/public/index.htm" target="_self" alt="engin home"> < /area >
< /map>

Now you want to access "area" element. You can do it using the function described above. So to access element the statement will look like:

browser.element_by_xpath("//area[contains(@href , 'signup.htm')]").click()

Problem3: Accessing elements with respect to other elements:

Suppose you need to access an element(which doesn't have any fix attribute) based on position of some other element(which has a fix attribute). WATiR doesn't provide any direct way to access element based on position of other element. So what you can do is:
1. Go to the element which is fixed using WATiR classes. Then, traverse the DOM tree using the underlying "ole_object" for that element.
OR
2. Supply an XPATH query that selects the element based on the position of other element.


Example:

Suppose you have an HTML like this:


<table>
<tr>
<td> <img src="1.jpg"> <input type="button"> < /td>
<td> <img src="2.jpg"> <input type="button"> < /td>
<td> <img src="3.jpg"> <input type="button"> < /td>
<td> <img src="4.jpg"> <input type="button"> < /td>
< /tr>
<tr>
<td> <img src="5.jpg"> <input type="button"> < /td>
<td> <img src="6.jpg"> <input type="button"> < /td>
<td> <img src="7.jpg"> <input type="button"> < /td>
<td> <img src="8.jpg"> <input type="button"> < /td>
< /tr>
< /table>

Now suppose you want to click on button that has image with src="7.jpg" in front of it. So you have two ways to do it:
1. First find out the table using :index attribute which is always dangerous because page structure may change. Then iterate over rows and then cells to find which cell contains that image. Then in that cell find the button and then click it.
OR
2. You can give an "XPATH " query to select the element. So your statement will look like this:

browser.button(:xpath, "//img[@src='7.jpg']/input").click()

Problems with XPATH:
The only problem with XPATH is that its a bit slower while selecting the elements on the page. We are working on improving the time it takes to select the element.

So to summarize, using XPATH query you get extreme powerful selection mechanism which will allow you select those elements also that are other wise difficult to select using WATiR native selection mechanism.

20 comments:

Santosh said...

Where can I get the extensions to Watir?

Angrez Singh said...

Hi Santosh,

These extensions are in the WATiR tool itself. There is no need to install anything besides WATiR

Regards,
Angrez

Santosh said...

Thanks!

I hope you will continue blogging on some of the tools!

Harmeet said...

Do I require something to use xpath in watir because normally it doesn't work in watir. I have tried on IRB also. It gives following
irb(main):005:0> ie.getLink(:xpath, "//td[@id='best_performing_FSF0497AU']/a").click
Watir::Exception::MissingWayOfFindingObjectException: :xpath is an unknown way of finding a link ( //td[@id='best_performing_FSF0497AU']/a )

Филипп said...

Can you please tell how to get XPATH of some element using it's attributes? F.e I need to get xpath of borwser.link(:id, "Link_id") using WATIR. How can I do it?

Angrez Singh said...

@Harmeet:

You have to use latest gem of watir to use xpath for addressing or locating element on web page. It will not work with watir 1.4.1

@Филипп:
See if you have id or any other attribute for addressing element on page its better to use that in Watir. XPath is only used in emergency situation's when you don't have any attribute to address element uniquely.
In your case it will be like
browser.link(:xpath, "//a[@id='id']")
But I advise not to use XPath until it is absolutely necessary

qa excellence said...

Very helpful post! Have you experience with Selenium and Watir?

-newsqa.com

Angrez Singh said...

@qa_excellence:

I have experience with Watir and Firewatir. I haven't used selenium while testing web applications. Anything in particular that you would like to ask?

al3kc said...

Hi,

Can I specify atribute TYPE for button from last exemple?

al3kc said...

Sorry for spam but i have one more question. Could I specify multiple attribute for one element?
Something like:
ie.table(:xpath,"//table[@class=>'test4',@index =>3]/tr/td/input/").text

I tried to run this but it doesn't work.

al3kc said...

Sorry for spam but i have one more question. Could I specify multiple attribute for one element?
Something like:
ie.table(:xpath,"//table[@class=>'test4',@index =>3]/tr/td/input/").text

I tried to run this but it doesn't work.

Guru said...

Angrez's can you please help me in working out with WATIR for the below HTML Code?

HTML Code:
class="formbody" INPUT VALUE ="" name="search_name" size="20" maxLength="80"

In the above scenario, I can't able to access the text box even with name='search_name". Can you please help me?

Anonymous said...

I want to collect all the links that have lets say "Details.aspx" somewhere in the href attribute, and click follow each link. I know how to select them using XPATH, //a[contains(., 'Details.aspx')]. Can I use XPATH with links()?

Nilesh Joshi said...

Dear Angrez,

I have got a button "like" object on my AUT screen. However,Watir does not
recognize it. When i viewed source for this page, i got it defined as per below:

LI class='viewTabBtnOff' tabIndex='2' id='Frame_1_TabList2'
onclick="tabObjList['Frame_1_TabList'].tabAreaShowHide('1','2','LATAM','Frame_1_TabArea','GENERIC');"
onmouseover="tabObjList['Frame_1_TabList'].tabMenuShowHide(this,null,true,event);"
onmouseout="tabObjList['Frame_1_TabList'].tabMenuShowHide(this,null,false,event);">LATAM</LI

So I am defining the xpath element as per below:
ie.element_by_xpath("//viewTabBtnOn[@id='Frame_1_TabList2']").click()

However, it is throwing error as below:
"Load_Empire.rb:23: undefined method `click' for nil:NilClass (NoMethodError)"

Am i doing anything wrong here?

Regards,
Nilesh

Nilesh Joshi said...

Dear Angrez,

I have got a button "like" object on my AUT screen. However,Watir does not
recognize it. When i viewed source for this page, i got it defined as per below:

LI class='viewTabBtnOff' tabIndex='2' id='Frame_1_TabList2'
onclick="tabObjList['Frame_1_TabList'].tabAreaShowHide('1','2','LATAM','Frame_1_TabArea','GENERIC');"
onmouseover="tabObjList['Frame_1_TabList'].tabMenuShowHide(this,null,true,event);"
onmouseout="tabObjList['Frame_1_TabList'].tabMenuShowHide(this,null,false,event);">LATAM</LI

So I am defining the xpath element as per below:
ie.element_by_xpath("//viewTabBtnOn[@id='Frame_1_TabList2']").click()

However, it is throwing error as below:
"Load_Empire.rb:23: undefined method `click' for nil:NilClass (NoMethodError)"

Am i doing anything wrong here?

Regards,
Nilesh

elizat8 said...

If the HTML were modified slightly to include a full path to the jpg file, ie src="http://server/images/7.jpg", how would that change the xpath?

Erna Tercero said...

How can I get the text "Duzul" from this element:

"td valign="bottom" align="center" style="width:100px;">Dzul</td"

the xpath is:

xpath = /html/body/div/div[2]/form/div[3]/div/div/table/tbody/tr[2]/td/div/table/tbody/tr[2]/td[2]

thanks in advance!!!!

Deepak Kumar said...

sorry sir if this is rude...Xpath never works...

Deepak Kumar said...

Sorry sir if this is rude.. Xpath never works..it alwayz shows a syntax error..

Jegan Raj Kumar said...

Hi Angrez,

I am using watir (4.0.2 x86-mingw32) version. Whenever I execute the commands like ie.cell/ ie.element_by_xpath. I am getting an undefined method error

----
undefined method `element_by_xpath' for # (NoMethodError)
------

Help me on this...