Locate the xpath by its position

Hi There. I am adding one item in my test. once save it, its potion is set in alphabetic order. Now I need to delete this I added. For My delete object, While recording with spy object ,I use xpath:position so that by changing row position , I can find its related delete button.
image

The delete button has below HTML
<input type="image" name="ctl00$cphMain$RadGridRRID$ctl00$ctl72$gbccolumn" id="ctl00_cphMain_RadGridRRID_ctl00_ctl72_gbccolumn" title="Delete" src="/QA/Validator5330/WebResource.axd?d=9IDadMdFOLEmGD4gtcR66X-QTb62gn45t2rd0mkmwOORSSP7mpRbvyuD3cqz0BKfc6snM6BYOle6CCCGhCx3I6DnvW5b9QhOq9H-b5G4br4pVempOoQqGypUnT_6QcmIQkMyWkASksahEW1jmuiXIRjOj-OmySnT5r17Ej2K2pE1&amp;t=637387549600000000" alt="Delete" onclick="if(!$find('ctl00_cphMain_RadGridRRID').confirm('Delete this Risk Rating ID?', event, 'ctl00_cphMain_RadGridRRID', 'Delete'))return false;" style="border-width:0px;">

This is the position after added my items and its delete button.

now when I add new item, its position can’t be identified. Can anyone help me what can I keep in the xpath for delete action to delete exact added item.

As you can tell, using position is not that stable. The attribute, id, is supposed to be unique, so you should use it–xpath:attributes in your image above.

If you want to use some other means to identify your element initially, then maybe you can change the xpath property in code to use the id when you want to be specific to deleting that item.

The <input> element has an attribute title='Delete'. It also has an attribute alt='Delete'. I suppose this attribute identifies the element uniquely amongst all elements in the HTML. If so, an XPath expression

//input[contains(@title,'Delete')]

would work. You can write this expression manually.

See https://www.guru99.com/xpath-selenium.html#6 for the technical background.

Unfortunately Spy & Recorder tool can not generate this expression, as the tools can not be aware that the title attribute can identify your <input> element.

1 Like

@kazurayam I use your suggestion but it locate all delete icons. I want to loacte specific delete icon
@grylion54 The delete object is coded from Telerik. Thus, There is no control to identify the code which makes id uniquely. The number ct172 makes it unique in id=“ctl00_cphMain_RadGridRRID_ctl00_ctl72_gbccolumn”. This number always dynamic. Is there any way/document to resolve this issue if we use telerik framework?

Sorry, is the ctl72 dynamic as well or is it stable within the dynamic parts? I’m not sure if it will be enough so that it will be an unique identification, however, you can try:
//input[contains(@id, 'ctl172')]

From your screenshots it appears that this “button” is embedded within a row of a table. In scenarios like this, you do have to provide a position in your xpath, but I would recommend finding the correct row/column, then find the element from that context. So something like:

//table//tbody//tr[i]//td[j]//input[@title='Delete']

where i/j are the indices of the row/column for your button, respectively. This is basically what your current locator is doing, you just need to parameterize this xpath so that you can pass in the appropriate indices at runtime:

You’d then call the click method like this:

WebUI.click(findTestObject("path/to/test/object", ['i' : '1', 'j' : '7']), 30)

(or whichever coordinates correspond to the target button at runtime).

If the row in which the target button exists changes between any 2 test runs, there are a couple of approaches:

1.) If your table has a search function, enter a search value which narrows down the results to the exact row you are looking for, then click the button in row 1.
2.) If your table does not have a search function, you’ll have to write one into your script, where you iterate through the rows, searching for some text which corresponds to the target row, then use the looping index once it finds a match.

If you use the filter, then you know the row will be one (@Brandon_Hein used variable “i”) and the column will be 6 (variable “j”) (but @Brandon_Hein says 7 so you may have to play with it).

If you don’t want to use the filter, then you can treat it like a web table:

What I meant by the search function:

Today, your target row might be, for example the second or third row. Tomorrow, it may be the 10th or the 100th. The point of entering a search value in your table first is to ensure that the table will ALWAYS be filtered down to 1 row, regardless of whether new rows are added/removed in the future. If you’re confident that your target row will ALWAYS exist at the same index (unlikely, but possible) you can skip this step. It’s just a future-proofing suggestion.

1 Like

The 7 came from OP’s original screenshot, which shows the xpath contains //td[7]//input. I assume this came from the Object Spy or some other auto-location tool.

There is, but you have a problem. Your table is paginated. What if the row with that text lives on another page? It may not now, but what if it does in the future? The searching approach solves all potential problems, so long as your search narrows the table down to the exact row you’re looking for.

Yes. The following XPath expression identifies the <input> element you want.

//table[@class='rgMasterTable']/tbody/tr[contains(td[4],'RiskID3')]/td[8]/input

Please note that this XPath expression does NOT locate the tr by its position.

The row of RiskID3 may be in the 1st <tr> at a time, it may be in the 4th <tr> next time, it may be in the 7th <tr> … whichever n-th position the <tr> of RiskID3 is in the table, doesn’t matter, the expression will find the tr by the content text of its descendent td[4] element.


Possibly you would want to process other rows with “RiskID1”, “VCAT4”, “VCAT3” etc. Then you would want to parameterise the expression by the method @Brandon_Hein showed you above.

@Brandon_Hein reminded that the table is paginated. I suppose you should take his point as well.

1 Like

Thank you @kazurayam and @Brandon_Hein. Your Xpath expression works. Yes I have to work on parameterization. Katalon is quite new for me and hard to digest initially. Can you please share something more on parameterise elements?