Finding only required links on a webpage using xpath - xpath

I have a webpage that has more than hundred links on a webpage. I am trying to see if i can only store the required number of web links and store them in a array and run a loop for verifying data within each individual links.
The number of links are dynamic and change as per the user, so each time the page loads it has to find the specified links.
This is what i currently have under the for loop for each user:
List<WebElement> linksize = driver.findElements(By.xpath(obj.getProperty("totalbookings")));
int linksCount = linksize.size();
String[] linksENR = null;
linksENR = new String[linksCount];
System.out.println(linksCount);
if (linksCount > 0)
{
System.out.println("We have enrollment");
System.out.println(linksCount);
for (int i = 1; i <= linksCount; i++)
{
driver.navigate().to(linksENR[i]);
System.out.println("here");
}
}
else
{
System.out.println("No enrollment record found");
}
The xpath for the elements that i am trying to click for user 1 is and there is one element:
.//*[#id='006g0000007RH1V_00Nb0000009Yc1K_body']/table/tbody/tr[2]/th/a
The xpath for the elements that i am trying to click for user 2 is as follows and there are two elements:
.//*[#id='006g0000007RH1V_00Nb0000009Yc1K_body']/table/tbody/tr[2]/th/a
.//*[#id='006g0000007RH1V_00Nb0000009Yc1K_body']/table/tbody/tr[3]/th/a
This is what i am using to search with the xpath:
totalbookings = .//*[contains(#id, "7RH1V_00Nb0000009Yc1K_body") and contains(#tagName , "a")]
Output:
No enrollment record found
Is there way to uniquely identify only few chosen links with the xpath? Please assist.
Here is the HTML information for the element:
<table class="list" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr class="headerRow"></tr>
<!--
ListRow
-->
<tr class="dataRow even first" onmouseover="if (window.hiOn){hiOn(this);}" onmouseout="if (window.hiOff){hiOff(this);}" onfocus="if (window.hiOn){hiOn(this);}" onblur="if (window.hiOff){hiOff(this);}">
<td class="actionColumn"></td>
<th class=" dataCell " scope="row">
<a class=" firepath-matching-node" href="/a1Bg0000001ih6M"></a>
</th>
<td class=" dataCell CurrencyElement"></td>
<td class=" dataCell "></td>
<td class=" dataCell "></td>
<td class=" dataCell "></td>
<td class=" dataCell numericalColumn"></td>
<td class=" dataCell "></td>
</tr>
<!--
ListRow
-->
<tr class="dataRow odd last" onmouseover="if (window.hiOn){hiOn(this);}" onmouseout="if (window.hiOff){hiOff(this);}" onfocus="if (window.hiOn){hiOn(this);}" onblur="if (window.hiOff){hiOff(this);}"></tr>
</tbody>
</table>

I would do it more like this:
List<WebElement> pl = driver.findElements(By.xpath(".//a[contains(#id,'totalbookings')]"));
if (pl.size() > 0)
{
System.out.println("We have enrollment. Looping through links...");
for ( WebElement we : pl )
{
we.click();
System.out.println("Clicked link: " + we.toString() );
thisPageObject.verifyPageLoaded();
driver.executeScript("window.history.go(-1)");
org.openqa.selenium.browserlaunchers.Sleeper.sleepTight(1000);
}
} else {}
System.out.println("No enrollment record found");
}

I guess contains(#tagName , "a") is intended to mean that the current element has a descendent element a. But it actually means that the current element has an attribute named tagName, and this attribute's string value contains the letter "a".
This says to me that you are trying to do something moderately complex with a tool that you really know little about. There's no shame in not knowing XPath, but a few hours spent learning the fundamentals will spare you many hours of trial and error and trying to communicate your problem on web forums.
To do what I think you intended to do, you can use the following XPath expression:
.//*[contains(#id, "7RH1V_00Nb0000009Yc1K_body")]//a
That is if you're trying to select the links themselves. If you're trying to select the element whose id you're testing, as your totalbookings XPath expression implies, you could do
.//*[contains(#id, "7RH1V_00Nb0000009Yc1K_body") and .//a]
but that would not select the links, which was your stated goal.
Also, both of the above look for descendants of the current element (whatever that is). With driver.findElements() you don't seem to have a current node, so it would make more sense to remove the . from the beginning of the XPath and just use //*... to make it clear that you're looking for nodes anywhere in the web page, not just in some chosen subtree.

Related

Unable to click on Select link present in last column of web table

I want to search Seller and have to click on select link for selected one. When I type seller name, it shows only record for selected seller.
I tried with following code, its not working. Can anyone please help
cy.get('input[name="search"]',{ timeout: 10000 }).type(this.data1.vehicle1_seller1)
//cy.wait(6000)
Cypress.config('defaultCommandTimeout', 10000);
cy.get('td[class="span-3"] div').each(($el, index, $list) => {
if ($el.text().includes('STB002')) {
// cy.contains("Select").eq(index).click()
cy.get('.span-1-5 > div > a > span').contains('select').eq(index).click({force:true})
}
}
this is the DOM structure >
<table>
<tbody>
<tr class="even">
<td class="span-3">
<div title="06V001">06V001</div> == $0
</td>
<td>
<div title="06 Vauxhall Ormskirk">06 Vauxhall Ormskirk</div>
</td>
<td class="span-1-5">
<div>
<a id="link57" href="./wicket/page?7-1.-seller-table-body-rows-10-cells-3-cell-link">
<span>select</span>
</a>
</div>
</td>
</tr>
<tr class="odd">
</tr>
<tr class="even">
</tr>
</tbody>
</table>
The HTML table is set out in rows and cells, exactly as you see it on the screen.
Your test is searching for the cell containing the text, but really you want to search for the row containing the text, then get the select button of that row.
The basic test would be
cy.contains('tr', 'STB002')
.within(() => {
// now inside the row
cy.contains('span', 'select').click()
})
The next problem is the car STB002 isn't on the first page, so you won't find it straight after loading.
Maybe use the search box to load that row (as you have in one screen-shot). I can't say what that code is, because the DOM picture doesn't include the search box.

Fetch parent of a specific row in a table without iteration

Consider the below table structure contains many rows with multiple column values. I need to identify the parent of specific row, which has to be identified using the cell .
<table class = 'grid'>
<thead id = 'header'>
</thead>
<tbody>
<tr>
<td>
<span class="group">
<span class="group__link"><a class="disabledlink"">copy</a>
</span>
</span>
</td>
<td class="COLUMNNAME">ACE</td>
<td class="COLUMNLONGNAME">Adverse Childhood Experiences</td>
<li>Family Medicine</li>
<li>General Practice</li>
</td>
<td class="COLUMNSEXFILTER">Both</td>
<td class="COLUMNAGEFILTERMIN">Any</td>
<td class="COLUMNTYPE">Score Only</td>
</tr>
<tr>
<td class="nowrap" showactionitem="2">
<span class="group">
<span class="group__link"><a onclick="Check()" href="#">copy</a>
</span>
</span>
</td>
<td class="COLUMNNAME">AM-PAC</td>
<td class="COLUMNLONGNAME">AM-PAC Generic Outpatient Basic Mobility Short Form</td>
<td class="COLUMNNOTE"></td>
<td class="COLUMNRESTRICTEDYN">No</td>
<td class="COLUMNSPECIALTYID"></td>
<td class="COLUMNSEXFILTER">Both</td>
<td class="COLUMNAGEFILTERMIN">Any</td>
<td class="COLUMNTYPE">Score Only</td>
</tr>
<tr></tr>
<tr></tr>
</tbody></thead>
</table>
Likewise this table contains around 100 rows. I did the same using iteration and it is working fine.
Is it possible to find the parent of specific row without iteration?
You can use the parent method to find the parent of an element. Assuming that you have located a table cell, let's call it cell, you can get its row using parent and then the parent of the row with another call to parent:
cell.parent
#=> a <tr> element
cell.parent.parent
#=> the parent of the specific row - a <tbody> element in this case
Chaining multiple parent calls can become tedious and difficult to maintain. For example, you would have to call parent 4 times to get the table cell of the "copy" link. If you are after an ancestor (ie not immediate parent), you are better off using XPath:
cell.table(xpath: './ancestor::table')
#=> the <table> element containing the cell
browser.link(text: 'copy').tr(xpath: './ancestor::tr')
#=> the <tr> element containing a copy link
Hopefully Issue 451 will be implemented soon, which will remove the need for XPath. You would be able to call:
cell.parent(tag_name: 'table') # equivalent to `cell.table(xpath: './ancestor::table')`
There's no need for anything fancy, Watir has an Element#parent method.
You can use this one:
parent::node()
The below example will selects the parent node of the input tag of Id='email'.
Ex: //input[#id='email']/parent::*
the above can also be re-written as
//input[#id='email']/..
XPath tutorial for Selenium

Soritng Ascending and descending order after clicking on table header?

I am using Selenium WebDriver with Java.
I have a table where I have to click on header of each column and want to validate whether sorting functionality is working correctly or not. I need to check for both Asc and Desc order.
So I have to take the count of the table first and the get the header text so that I click on each col 1-1 and then need to validate the sorting.
How can achieve my expected result. Below is the HTML source code:
<div>
<table cellspacing="0" rules="all" border="1" id="ctl00_ContentPlaceHolder1_gvClinicalTrait" style="border-color:appworkspace;border-collapse:collapse;">
<tr class="gridTitleBar">
<th scope="col">Date Collected</th><th scope="col">Clinical Trait Data</th><th scope="col">Source</th><th scope="col">Value</th>
</tr><tr class="gridBody" align="left">
<td>11/6/2008</td><td style="width:200px;">A1C</td><td style="width:200px;">d</td><td>6.00</td>
</tr><tr class="gridBody" align="left">
<td>9/17/2008</td><td style="width:200px;">BP</td><td style="width:200px;">e)</td><td>104/54</td>
</tr><tr class="gridBody" align="left">
<td>7/12/2008</td><td style="width:200px;">BP</td><td style="width:200px;">g</td><td>124/56</td>
</tr><tr class="gridBody" align="left">
<td>6/21/2008</td><td style="width:200px;">BP</td><td style="width:200px;">t</td><td>110/72</td>
</tr><tr class="gridBody" align="left">
<td>6/14/2008</td><td style="width:200px;">BP</td><td style="width:200px;">n</td><td>120/70</td>
</tr>
</table>
</div>
How can achieve my expected result. Below is the HTML source code:
you need to have list or array with expected result.
You need to create loop like this for every test:
List<string> expectedValues = new List<string>("12","13","14");
int expectedNumber = expectedValues.Count;
int tdNumber = 2;
for (int i=1; i< expectedNumber; i++)
{
string result = driver.Find(by.xpath("//div[#id ='ctl00_ContentPlaceHolder1_gvClinicalTrait']/tr["+ i +"]/td["+tdNumber+"]")).Text;
if (result != expectedValues[i])
Assert.Fail("Wrong value!")
}
I didnt try this code, but even if its wrong it's will be pretty close to code that you need.

How to click on the table cell for particular text

The following is the DOM details:
<div id: "abc_440"
<table class = "listtable" >
<tbody>
<tr>
<td id = "someid" >
<td class = 'someclass_item"> This is Text </td>
<td class = 'someclass_button">
< a > Add </a>
</td>
</tr>
</tbody>
</table>
</div>
I need to click on 'Add' for particular text at "This is Text". I have to use div with ID (abc_440)to locate the corresponding table as there are may divs with this same dom layout. but index at div ID (for example 440) keeps changing to random number. How do I handle it in general ?
Please help.
I think that what you want to do is very similar to the previous Watir question.
Given that the id is randomly generated, it does not sounds like a good way of finding the link. You will likely need to use the text of the same row.
Assuming that the "This is Text" is unique (as you said you want to find the Add link for it), you can find that td, go to the parent row and then get the link.
b.td(:text => 'This is Text').parent.link.click
If you need to ensure that the text is in the second column, then you can do:
b.trs.find{ |tr|
tr.td.exists? and tr.td(:index => 1).text == 'This is Text'
}.link.click
The tr.td.exists? is added in case some of your rows do not have any tds (example a header row), which would cause an exception when checking the second criteria.
Don't mix quotes in HTML tags. id: doesn't work. <td>s should rarely be empty. The Add button should be a button, not an <a>nchor element. Buttons only work in <form>s.
<form id="abc_440" action='someURI'> <!-- The handler for the button. -->
<table class="listtable">
<tbody>
<tr>
<td id = "someid">
<!-- What goes here? -->
</td>
<td class='someclass_item'>
This is Text
</td>
<td class='someclass_button'>
<button name='add'>Add</button>
</td>
</tr>
</tbody>
</table>
</form>
You should be able to find the button through its name attribute, add, and through the form's id attribute, abc_440. Who generates the ids?
Once you have the problem of finding the add button solved, and figuring out where the form's id comes from, please then stop using tables for formatting. There's no need for that. Learn about <form>s, <fieldset>s, <legend>s, and <label>s. I doubt you need the *some_id* part, the text part should probably be a <label>, and you can use CSS to format your <label>s as:
label {
width: 150px;
float: left;
}
fieldset p {
clear: left;
}

select two cells from table with span class

How can I select two cells in table bases on span class?
my html looks like this.
what I want is to select innertext of span class="store-name-span"
and span class="price"
<table class="list mixed zebra-striped">
<tbody>
<tr data-pris_typ="normal">
<td class="span4-5">
<span class="store-name-span">Electroworld</span>
<a data-drg="store-2641" class="drg-sidebar"></a>
</td>
<td class="span3 cell-bar">
<span class="chart-bar price" style="width:50px"></span>
<span class="price" title="Uppdaterad 2013-02-18 08:23">1 690:-</span>
</td>
</tr>
<tr data-pris_typ="normal">
<td class="span4-5">
<span class="store-name-span">Webhallen</span>
<a data-drg="store-113" class="drg-sidebar"</a>
</td>
<td class="span3 cell-bar">
<span class="chart-bar price" style="width:50px"></span>
<span class="price" title="Uppdaterad 2013-02-18 13:55">1 690:-</span>
</td>
</tr>
</tbody>
</table>
var Nodes = from x in doc2.DocumentNode.Descendants()
//where x.Attributes["class"].Value == "store-name-span"
where x.Name == "span" && x.Attributes["class"].Value == "store-name-span"
select x.InnerText;
I'd use xpath for this:
var nodes = doc.DocumentNode.SelectNodes("//span[#class='store-name-span' or #class='price']");
foreach (var node in nodes)
Console.WriteLine(node.InnerText);
By using LINQ:
var nodes = doc.DocumentNode.Descendants("span")
.Where(s =>
s.GetAttributeValue("class", null) == "store-name-span" ||
s.GetAttributeValue("class", null) == "price"
);
this will get you:
Electroworld
1 690:-
Webhallen
1 690:-
In that particular HTML layout, you can do:
var items = doc.DocumentNode.SelectNodes("//tr[#data-pris_typ='normal']").Select(x => new
{
Store = x.SelectSingleNode(".//span[#class='store-name-span']").InnerText,
Price = x.SelectSingleNode(".//span[#class='price']").InnerText
});
On items you'll get what you need. Each item will be an anonymous type with the Store and Price fields.
One important thing:
You might want to clean the fields (like Price) using HttpUtility.HtmlDecode(). To do that you will have to add a reference to the System.Web assembly.
I would use a combination of querySelectorAll and fetching innerHTML.
queryselectors work both on calling globally (on document) as well as for a single element.

Resources