Wednesday, December 10, 2008

Selenium XPath Fu: Clicking the Right Dynamically Generated Button

Problem: I'm writing a Selenium test that needs to click a button in a table row. Said table row has a title cell. Said row may not always be in the same position in the table, so I can't just hit it via absolute positioning with something like 'clickAndWait('//input[@value='Edit' and @type='button' and @onclick="window.location = '/CS/controlpanel/forums/SectionEdit.aspx?SectionID=35'"]);'

NOTE: All images below are links to full-sized ones.

I need to be able to find that title text, then hit the appropriate button and all this needs to happen dynamically.

Answer: First off, you need a couple extra tools to help you out. Grab the Firefox extensions XPath Checker and DOM Inspector. (Tools | Add-ons | Get Add-ons and search for those titles)

My goal is to use XPath to find find the table cell with the title text I'm interested in, then in that same row find the Edit button so I can click it. This way I'm not locked on to an absolute position in the table which will likely be wrong as I do my tests.

Navigate Firefox to the page you're working on testing. Open the DOM inspector (Tools | DOM Inspector or use Ctrl-Shift-I). In DOM Inspector select Search | Select Element By Click , switch back to your page and click the text you're interested in. In my case, DOM Inspector will now show something like this:

The cell's selected and the text of it shows in the right pane, but the button I want is actually an INPUT element further down the DOM -- that's the lower box. First we need to use XPath to grab the table row we want, then we'll work our way over to the button.

Flip back to Firefox and right-click by the cell you want and select View XPath from the context menu.

The XPath Checker window will open.

Note the XPath (id('Listing')/tbody/tr[3]/td[1]) is the absolute position format, which we don't want! However, we can see the element ID of the table is 'Listing' and we need that! Change the XPath in the field to "//table[@id='Listing']" and you'll see we've gotten the entire table. A good start.

Now remember that our title was in the text field of an <A> element (see the DOM Inspector above).  Edit the existing XPath to search for any <A> element in the table which has "My Test Table" as its text content: "//table[@id='Listing']//a[text()='My Test Forum']". Note how the display changes.

Important note: This assumes (bad word!) that only one <A> element has the text "My Test Forum."

Now we need to navigate over to the <INPUT> element holding the Edit button. We are in an <A> element in a <TD> of a <TR>. We need to go up a couple levels, then look for the Edit button. The XPath "parent" will help us. Edit the XPath to go to the parent <TD> element ("//table[@id='Listing']//a[text()='My Test Forum']/parent::td") and you'll see the display change.

Finish by going up another parent to the row and then looking for the INPUT element with a "value" of "Edit" ("//table[@id='Listing']//a[text()='My Test Forum']/parent::td/parent::tr//input [@value='Edit']") which will result in the following:

Voila! We've got our XPath correct!

Now paste that entire XPath into your Selenium IDE's test case's Target field. The command type should be "clickAndWait." Click the Find button and you'll see the Edit button on your target page flash green, signifying you've gotten it right! (If it doesn't flash green, then go back and fool around with your XPath in XPath Checker.)

One last step. Execute that command of the test case with right-click | Execute this command. You should see the command line shaded green, and the Firefox window in the back nav to the proper page.

 

Phew!

10 comments:

Anonymous said...

What about simplifying your XPath, for instance to something like

"//table[@id='Listing']//tr[td/a/text()='My Test Forum']//input [@value='Edit']"

Jim Holmes said...

Simpler's better!

That's a good suggestion.

Anonymous said...

Good stuff, thank you both.

Liam said...

Thanks this is a terrific post...really helped me after several hours of frustration.

So well explained and the tools you recommend performed as described.

Thanks Liam.

Anonymous said...

Easiest way to find xpath is Firebug(addon to firefox) just select the control and right click on it and copy xpath from it.

Jim Holmes said...

@Anonymous: Using Firebug's copy XPath feature will get you the XPath with absolute declarations which may or may not work for your environment. It won't in ours, which is why I show the longer route.

Kevin said...

Beautifully explained! Helped me solve exactly the same problem quickly and easily. Thank you so much!

Anonymous said...

Thanks for this tutorial! As a beginner here it helped me a lot to solve a problem that I had to define an ancestor of my span's parent!

Anonymous said...

thank you this was exactly what i was struggling with!!

Anonymous said...

not working for me

the dom inspector does not show me the text in the right hand side

and in the xpath checker as soon as I reach "//a..." it find nothing and I can go no further

Subscribe (RSS)

The Leadership Journey