Cypress - click hyperlink on row based on value of two cells - cypress

Trying to get a Cypress script to click a hyperlink based on two values - the text of the hyperlink in column 1 and the value of the cell in the second column:
<tbody>
<tr>
<td>Anything</td>
<td>Casualty</td>
</tr>
<tr>
<td>Declined Prospect</td>
<td>Casualty</td>
</tr>
<tr>
<td>Declined Prospect</td>
<td>Package</td>
</tr>
<tr>
<td>Declined Prospect</td>
<td>Casualty</td>
</tr>
<tr>
<td>Irrelevant</td>
<td>Package</td>
</tr>
</tbody>
cy.get('a').contains('Declined Prospect').click()
fails because there's more than one hyperlink with that value. The id is not useful because it's dynamic.
In the example above, I want to click Declined Prospect when the second column is Casualty (but the order of the rows may vary and values in the first and second column are repeated - but only once for the combination).
Any thoughts?

The trick is to target <td>Casualty</td> then click the preceding <td><a>.
There are quite a few ways to get to sibling elements, the simplest in this case is prev().
cy.contains('td', 'Casualty') // target the 'marker' element
.prev() // move to the previous sibling
.click()
Approach from row and move inwards
To target a row with a specific combination of text in some of it's cells, just concatenate the text and use contains().
cy.contains('tr', 'Declined Prospect Casualty') // target unique text within children
This even works when there are other cells with text that's not relevent to the search, e.g
<tr>
<td>Declined Prospect</td>
<td>Casualty</td>
<td>Irrelevent text here</td>
</tr>
Then you can walk down the HTML tree,
cy.contains('tr', 'Declined Prospect Casualty') // target unique text within children
.find('td a') // descendant of previous subject
.click()

I think this can be useful: https://github.com/cypress-io/cypress-xpath
You can create selectors in xpath instead of css and in xpath you can search tree by text. e.g:
cy.xpath("//text() = 'Declined Prospect'")
==================================Edited====================
You can merge couples of xpatch selectors: it will looks like this //tr[td='Casualty']/td/a
Playgroud

Related

How do you select two different tags via xpath, both at different levels, when one of them is optional

I have a situation where the data is a mix of these format on the same page. In other words, some rows will show up as:
some lengthy XPATH_X uptill here:
<td/>
<td>
I Need this element td
</td>
<td/>
<td/>
<td/>
<td/>
and a few other rows will show in this format:
the same lengthy XPATH_X uptill here:
<td/>
<td>
<span>
I Need this element span
</span>
</td>
<td/>
<td/>
<td/>
<td/>
Please note that there are no differentiating attributes for each of the td tags. I need to select the second row (td) in both the cases.
I'm trying to catch both of the elements using the following xpath:
XPATH_X/*[self::td[position()=2] or self::td[position()=2]/span]
I tried this out on the page but for some reason it doesn't select anything.
Can someone please help me out with this? I've spent more than 2 hours on this already.
You should try XPATH_X/td[2]//text() to retrieve the text whether it's at the root of the td or in a child tag
You can test it here ; in this test I retrieve three results :
the text inside a span inside a td
the text at the root of a td
both the texts at the root of a td and inside the enclosed span (if this doesn't work for you and the text of the td should be retrieved only if there's no span, use XPATH_X/td[position()=2 and not(./span)]/text() | XPATH_X/td[2]/span/text() instead)
To retrieve the elements containing text nodes rather than the text node themselves, you can use the following :
XPATH_X/td[2]//self::node()[text()]

XPath of the edit button is //*[#id="edit_1088"]

My query is I need to click on the first edit link on the table. The table has 7 columns and rows will be incremented dynamically.
HTML Code for first row:
<tr role="row" class="odd">
<td>
<form method="GET" target="_blank" action>...</form>
<td> ALLOCATION CHANGE</td>
<td class="right"></td>
<td> SATTER, KRAIG</td>
<td> CAFEMANAGER1</td>
<td class="sorting_1">03/08/2016 17:00</td>
<td class="edit_icon" id="edit_1088" onclick="on EditClick(1088)">
<span class="view_icon" style="margin-left: 40%;"></span>
</td>
</tr>
Note: the ID of the edit button keep on changing as the row increments.
Mycode in cucumber -ruby -capybara
And /^I click on the Expresso image$/ do
find(:xpath, '//*[#id="l1row"]/span').click
find('tr:odd > td:edit_icon [id="edit_"] match: first').click
sleep 10
end
Error Message: invalid selector: An invalid or illegal selector was specified
Update based on the posted HTML -
Note: Your first <td> isn't closed, I'm assuming thats just an error when you were adding the HTML to the question.
So from the HTML posted you don't actually have an edit link you just have a td you need to click on - the one in the first row with an id beginning with "edit_" so
find('tr:first-child > td[id^="edit_"]').click
The attempt you posted in your question won't work because there is no such CSS selector as odd or edit_icon and find needs valid CSS selectors (or XPath if you specify XPath or set it as your default)
Previous Answer Based on the wording of the question:
If this is a table then you can do what you want with CSS and not worry about XPath.
find('tr:first-child > td:last-child [id^="edit_"]')
will find the element whose id starts with "edit_" in the last td in the first row. If your rows and columns are not actually a table you'll need to provide some example HTML of what you're talking about.

XPath get only first Parent of nested HTML

I am newbie in XPath. Can someone explain how to resolve this problem:
<table>
<tr>
<td>
<table>
<tr>
<td>
<table>
<tr>
<td>Label</td>
<td>value</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
I try to get <tr> which contains Label value, but it does not work for me,
Here is my code :
//td[contains(.,'Label')]/ancestor::tr[1]
Desired result:
<tr>
<td>Label</td>
<td>value</td>
</tr>
Can someone help me ?
This expression matches the tr that you want:
//tr[contains(td/text(), 'Label')]
Like yours, this starts by scanning all tr elements in the document, but this version uses just a single predicate. The td/text() limits the test to actual text nodes which are grandchildren of the row. If you just used td, then all of the td's descendant text nodes would be collected and concatenated, and the outer tr would match.
UPDATE: Also, for what it's worth, the reason your expression isn't working is that the ancestor axis returns elements in document order, not "outward" from the point of the context node. This is something I've run into myself, as it is somewhat unintuitive. To make your approach work, you would need to say
//td[contains(.,'Label')]/ancestor::tr[last()]
instead of
//td[contains(.,'Label')]/ancestor::tr[1]
I had the same issue, except that the text 'Label' was sometimes in a nested span, or even further nested in the td. For example:
<td><span>Label</span></td>
The previous answer only finds 'Label' if it is in a text element that is a direct child of the td. This issue is a bit harder because we need to search for a td that contains the text 'Label' in any of its children. Since the tds are nested, all tds qualify as having a descendant that contains the text 'Label'. So, the only way I found to overcome this is to add a check that makes sure that the td we select does not contain a td with the search text.
//td[contains(., 'Label') and not(.//td[contains(., 'Label')])]/ancestor::tr[1]
This says give me all of the tds that have a decedent text containing 'Label', but exclude all tds that contain a td that has a decedent text containing 'Label' (nesting ancestors). This returns the child most td that contains the text. Then you can go back to the tr that contains this td using ancestor.
Also, if you just want the lowest table that contains text use this:
//table[contains(., 'Label') and not(.//table[contains(., 'Label')])]
or you can select the tr directly:
//tr[contains(., 'Label') and not(.//tr[contains(., 'Label')])]
This seems like a common problem, but I didn't see a solution anywhere. So, I decided to post to this old unanswered question in hopes that it helps somebody.

How do I get the value inside a <td> tag with xpath/htmlwebunit

I am trying to create a Java Application that retrieves information from a webpage. This is part of the code I am trying to access the value in the 1st td tag in the 2nd tr tag:
<TABLE CLASS="datadisplaytable" width = "100%">
<TR>
<TD CLASS="dddead"> </TD>
<TH CLASS="ddheader" scope="col" ><SPAN class="fieldlabeltext">Capacity</SPAN></TH>
<TH CLASS="ddheader" scope="col" ><SPAN class="fieldlabeltext">Actual</SPAN></TH>
<TH CLASS="ddheader" scope="col" ><SPAN class="fieldlabeltext">Remaining</SPAN></TH>
</TR>
<TR>
<TH CLASS="ddlabel" scope="row" ><SPAN class="fieldlabeltext">Seats</SPAN></TH>
**<TD CLASS="dddefault">46</TD>**
<TD CLASS="dddefault">46</TD>
<TD CLASS="dddefault">0</TD>
</TR>
This is what i have right now but this only returns the class of the td tag and not the value inside it:
List<?> table = page.getByXPath("//table[#class='datadisplaytable'][1]//tr[2]/td");
How would I go about getting the value of the td tag and not its properties?
edit: The code above returns this:
HtmlTableDataCell[<td class="dddefault">]
I am trying to create a Java Application that retrieves information from a webpage. This is part of the code I am trying to access the value in the 1st td tag in the 2nd tr tag:
Assuming that the document is as shown in the question (TABLE is the top element),
Use:
/TABLE/TR[2]/TD[1]/text()
This selects any text-node child of the first TD child of the second TR child of the top element TABLE.
In case the table is buried in the XML document, but can be uniquely identified by its CLASS attribute, use:
//TABLE[#CLASS='datadisplaytable']/TR[2]/TD[1]/text()
This selects any text-node child of the first TD child of the second TR child of any (we know thre is only one such) element TABLE in the XML document, such that the string value of its CLASS attribute is the string 'datadisplaytable'.
Finally, if even worse, there could be many TABLE elements whose CLASS attribute's value is 'datadisplaytable', and we want to select in the first such table, use:
(//TABLE[#CLASS='datadisplaytable'])[1]/TR[2]/TD[1]/text()
for getting the text content from an element there is an xpath function called "text()" which you can use.
Element containing text 't' exactly //*[.='t']
Element <E> containing text 't' //*[.='t']
<a> containing text 't' //a[contains(text(),'t')]
<a> with target link 'url' //a[#href='url']
Link URL labeled with text 't' exactly //a[.='t']/#href
If you are also using JwebUnit, there is a method "getElementTextByXPath" which can also be used to get the text.
net.sourceforge.jwebunit.junit.WebTestCase
getElementTextByXPath
public String getElementTextByXPath(String xpath)
Deprecated.
Get text of the given element.
Parameters:
xpath - xpath of the element.
for (int i = 1; i != 6; i++) {
String result = getElementTextByXPath("//td["+i+"][text()]");
System.out.println("The Content of TD is " +result);
}

xpath nearest element to a given element

I am having trouble returning an element using xpath.
I need to get the text from the 2nd TD from a large table.
<tr>
<td>
<label for="PropertyA">Some text here </label>
</td>
<td> TEXT!! </td>
</tr>
I'm able to find the label element, but then I'm having trouble selecting the sibling TD to return the text.
This is how I select the label:
"//label[#for='PropertyA']"
thanks
You are looking for the axes following-sibling. It searches in the siblings in the same parent - there it is tr. If the tds aren't in the same tr then they aren't found. If you want to it then you can use axes following.
//td[label[#for='PropertyA']]/following-sibling::td[1]
From the label element, it should be:
//label[#for='PropertyA']/following::td[1]
And then use the DOM method from the hosting language to get the string value.
Or select the text node (something I do not recommend) with:
//label[#for='PropertyA']/following::td[1]/text()
Or if there's going to be just this one only node, then you could use the string() function:
string(//label[#for='PropertyA']/following::td[1])
You can also select from the common ancestor tr like:
//tr[td/label/#for='PropertyA']/td[2]
Getting ANY following element:
//td[label[#for='PropertyA']]/following-sibling::*

Resources