In my script (selenium & Java) I need to select a table column by it's header.
Basically, I send the header as a parameter and then just fetch through that column.
But, the problem is that I'm not able to get the header column's index.
Any idea or suggestion?
<table class="table table-hover tablesorter ib-table" data-reactid=".0.0.0.1.2.0">
<thead data-reactid=".0.0.0.1.2.0.0">
<tr data-reactid=".0.0.0.1.2.0.0.0">
<th data-reactid=".0.0.0.1.2.0.0.0.0">ID</th>
<th data-reactid=".0.0.0.1.2.0.0.0.1">To</th>
<th data-reactid=".0.0.0.1.2.0.0.0.2">From</th>
<th data-reactid=".0.0.0.1.2.0.0.0.3">Text</th>
</tr>
</thead>
<tbody data-reactid=".0.0.0.1.2.0.1" style="height: auto;">
<tr data-reactid=".0.0.0.1.2.0.1.$0">
<td data-reactid=".0.0.0.1.2.0.1.$0.0">123456</td>
<td data-reactid=".0.0.0.1.2.0.1.$0.1">+0156477889785</td>
<td data-reactid=".0.0.0.1.2.0.1.$0.2">+0156477889784</td>
<td data-reactid=".0.0.0.1.2.0.1.$0.3">sample textM</td>
</tr>
<tr data-reactid=".0.0.0.1.2.0.1.$1">
<tr data-reactid=".0.0.0.1.2.0.1.$2">
First you need to do a List that has all the headers by
List<WebElement> headersList = driver.findElement(By.xpath("//table[#class='table-hover tablesorter ib-table']")).findElements(By.tagName("th"));
after that you going threw all the tr in loop
List<WebElement> trList = driver.findElement(By.xpath("//table[#class='table-hover tablesorter ib-table']")).findElements(By.tagName("tr"));
List<WebElement> tdList;
for(int = i ; i < trList.Count ; i++)
{
tdList = trList[i].findElements(By.tagName("td"));
//tdList[0] its header in index[0]... and so on...
}
This will not work in odd cases such as when using colspan. It should work for any "normal" table of data. The trim is in use for avoiding the inevitable whitespace that appears around many WebElements.
public static List<WebElement> getColumnByHeaderText(WebDriver driver, By by, String header) {
WebElement table = driver.findElement(by);
Function<WebElement, String> elementToString = (WebElement w) -> w.getText().trim();
List<WebElement> list = table.findElements(By.tagName("th"));
int index = Lists.transform(list, elementToString).indexOf(header);
if (index == -1) {
throw new RuntimeException("Unable to locate header");
} else {
return table.findElements(By.cssSelector("td:nth-child(" + (index + 1 ) + ")"));
}
}
Related
I am trying to grab a column from a data table.
Here is my table to use as an example, what I am looking for is to extract the Firstname from the table.
<table style="width:100%">
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td class="abc">Jill</td>
<td class="abc">Smith</td>
<td class="abc">50</td>
</tr>
<tr>
<td class="abc">Eve</td>
<td class="abc">Jackson</td>
<td class="abc">94</td>
</tr>
</table>
How can i modify the code below to give me this result:
Jill
Eve
WebElement table = driver.findElement(By.id("searchResultsGrid"));
// Now get all the TR elements from the table
List<WebElement> allRows = table.findElements(By.tagName("tr"));
// And iterate over them, getting the cells
for (WebElement row : allRows) {
List<WebElement> cells = row.findElements(By.tagName("td"));
for (WebElement cell : cells) {
System.out.println("content >> " + cell.getText());
}
}
Using Java 8 you can iterate list using .forEach after getting only Firstname column list as below :-
WebElement table = driver.findElement(By.id("searchResultsGrid"));
List<WebElement> firstCells = table.findElements(By.xpath(".//tr/td[1]"));
firstCells.forEach(firstCell->System.out.println("Firstname >> " + firstCell.getText()));
Requirement is to verify the column order of the following table is in the correct order from its column header names.
Therefore, I have written down a method to cover up my requirement, which accepts an array list [String] of the table header names in the required order.
But it apparently does not do my requirement through the soft-Asserts as 'isDisplayed()' method is always returning 'false'.
Can anyone suggest me kindly further points to modify it and to get it fixed?
HTLM Code for the table :
<table id="examMarkEntryExamList" class="display table table-bordered table-striped dynamic-table display_header_class">
<thead>
<tr>
<th class="text-center sortable sorted order1">Academic Year</th>
<th class="text-center sortable sorted order1">Curriculum</th>
<th class="text-center sortable sorted order1">Grade</th>
<th class="text-center sortable sorted order1">Semester/Term</th>
<th class="text-center sortable sorted order1">
<a class="pagination-cuser-point">Exam Code</a>
</th>
<th class="text-center sortable sorted order1">Actions</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>2016-2016</td>
<td>LOCAL</td>
<td>GRADE11</td>
<td>2nd Term</td>
<td>G11SecondTerm</td>
<td>
</tr>
</tbody>
</table>
Selenium [Java] method to verify the column order
public void verifyColumnOrder(WebDriver driver, String tableId, ArrayList<String> columnHeaderList) {
SoftAssert softassert = new SoftAssert();
String relativeXpath = "//table[contains(#id,'"+tableId+"')]/";
for (String columnHeader : columnHeaderList) {
relativeXpath = relativeXpath + "/following-sibling::th[contains(.,'" + columnHeader + "')]";
softassert.assertTrue(driver.findElement(By.xpath(relativeXpath)).isDisplayed());
}
softassert.assertAll();
}
An other solution would be to directly get the text of thead with getText(). It will return a concatenation of all the visible titles which will be easier to compare:
public void verifyColumnOrder(WebDriver driver, String tableId, ArrayList<String> columnHeaderList) {
String expectedHeaders = String.join(" ", columnHeaderList);
String visibleHeaders = driver.findElement(By.cssSelector("[id='" + tableId + "'] thead")).getText();
Assert.assertEquals(visibleHeaders, expectedHeaders);
}
The xpath you are using is incorrect. th is not a sibling of table tag. It is 2 levels inside.
Try this CSS locator - "table[id='examMarkEntryExamList'] th". You can use it directly by using By.cssSelector or xpath "//table[#id='examMarkEntryExamList']//th".
Use this locator to get a List of webelements. From this get the list of text in the th tags.
List<WebElement> thelem = driver.findElements(By.xpath....);
List<String> thText = thelem.stream().map(e -> e.getText()).collect(toList());
Just assert -- `softassert.assertTrue(thText.equals(columnheaders));
Please check actual syntax.
Please visit the website "http://www.cricbuzz.com/cricket-series/2223/icc-cricket-world-cup-2015/points-table"
<table class="table cb-srs-pnts">
<thead>
<tr class="cb-srs-gray-strip">
<th class="cb-col-20 cb-srs-pnts-th text-left" style="padding-left: 6px;">Pool B</th>
<td class="cb-srs-pnts-th">Mat</td>
<td class="cb-srs-pnts-th">Won</td>
<td class="cb-srs-pnts-th">Lost</td>
<td class="cb-srs-pnts-th">Tied</td>
<td class="cb-srs-pnts-th">NR</td>
<th class=" cb-srs-pnts-th">Pts</th>
<td class="cb-srs-pnts-th">NRR</td>
<th/>
</tr>
</thead>
<tbody>
</table>
I have to print all the table headings present for first table. It has a combination of "td" and "th" tags.
I am using the following xpath to retrive them.
//h3/../table[1]/thead/tr/*[self::td or self::th]
All the values are getting printed except for the text "Pool B"
Can somebody tell me why "Pool B" text is not getting selected?
Code to print the output:
driver.get("cricbuzz.com/cricket-series/2223/icc-cricket-world-cup-2015/…);
System.out.println(driver.findElement(By.xpath(" //h3/../table[1]/thead/tr/th")).getText());
List<WebElement> tableHeading = driver.findElements(By .xpath("//h3/../table[1]/thead/tr/*[self: : tdorself: : th]"));
for (int i = 1; i < tableHeading.size(); i++)
{
System.out.println(i+""+tableHeading.get(i).getText());
}
Set the index to 0 in your for loop:
for (int i = 0; i < tableHeading.size(); i++)
{
System.out.println(i+""+tableHeading.get(i).getText());
}
I'm learning to work with Maven, Java, testng and webdriver by doing a self training course. I been doing great with all exercises until I got to the last one, I honestly don't know how to solve it. Let me give you some context:
Exercise description: Open the site homepage and using the calendar(html calendar in a wordpress site), check how many days of
the current month contain posts, then access the first day which has
post and print the titles of the posts in the console.
When any day in the calendar has posts, instead of showing the day in
plain text, the site shows the day number as a link that once clicked
opens the lists of posts in the main page.
This is a screen capture of how this site looks when I click on the day which has posts, in this case October 10th 2013: http://i.stack.imgur.com/wGRO2.jpg
Like I said the site is a wordpress site (unfortunately can only be accessed through VPN so I can't share it), and I need to use the calendar widget which is a table. I was able to go over each row and column of the table and print each column's text.
But how do I find if any of those days have a link in it and then having located that element how can I access it and print it's content (is this case they want me to print the titles of all posts created under that date)?
This is the code I'm using to go over the entire table and print the content:
//Test Case 6
#Test (timeOut=20000)
public void TestCalendar(){
HomePage homePage = PageFactory.initElements(driver, HomePage.class);
homePage.Go(driver);
WebElement table = homePage.ReturnCalendarElement();
//Actual Month has no posts, so we select previous one which has 2 posts
homePage.ClickPreviousMonth();
//We Get All rows of the calendar then we get all columns and its data
List<WebElement> rows = table.findElements(By.tagName("tr"));
for (WebElement row: rows) {
List<WebElement> cols= row.findElements(By.tagName("td"));
for (WebElement col: cols){
System.out.print(col.getText() + "\t");
}
}
}
I'm adding the HTML code of that page in case it helps you to provide some answers:
Follow this link to open a Public Gist with the HTML code of the page: https://gist.github.com/anonymous/599cd722b5c906808528
The following is the HTML code for the Table I'm testing:
<aside id="calendar-2" class="widget widget_calendar"><h3 class="widget-title">Posts by date:</h3><div id="calendar_wrap"><table id="wp-calendar">
<caption>October 2013</caption>
<thead>
<tr>
<th scope="col" title="Monday">M</th>
<th scope="col" title="Tuesday">T</th>
<th scope="col" title="Wednesday">W</th>
<th scope="col" title="Thursday">T</th>
<th scope="col" title="Friday">F</th>
<th scope="col" title="Saturday">S</th>
<th scope="col" title="Sunday">S</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="3" id="prev" class="pad"> </td>
<td class="pad"> </td>
<td colspan="3" id="next" class="pad"> </td>
</tr>
</tfoot>
<tbody>
<tr>
<td colspan="1" class="pad"> </td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
</tr>
<tr>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
</tr>
<tr>
<td>14</td>
<td>15</td>
<td>16</td>
<td>17</td>
<td>18</td>
<td>19</td>
<td>20</td>
</tr>
<tr>
<td>21</td>
<td>22</td>
<td>23</td>
<td>24</td>
<td>25</td>
<td>26</td>
<td>27</td>
</tr>
<tr>
<td>28</td>
<td>29</td>
<td>30</td>
<td>31</td>
<td class="pad" colspan="3"> </td>
</tr>
</tbody>
</table></div></aside>
This is the last problem I need to solve to end with this training, so any help and orientation will be highly appreciated.
Use a better CSS query to get the links of the calendar.
It would be something like:
td a
Elements TD with a link A inside.
Not much help without the HTML code of the page.
I ended up finding a solution, while i'm sure there should be a better way to do this. But this worked for me & might help someone else somehow:
// Test Case 6
#Test(timeOut = 40000)
public void TestCalendar() {
HomePage homePage = PageFactory.initElements(driver, HomePage.class);
CalendarPage calendarPage = PageFactory.initElements(driver,
CalendarPage.class);
homePage.Go(driver); //this just navigates to the page
homePage.ReturnPreviousMonth().click();
//returns the calendar element mapped in the page
WebElement table = homePage.ReturnCalendarElement();
WebElement dayWithPost = calendarPage.GetFirstDayWithPosts(driver,
table);
Assert.assertNotNull(dayWithPost);
System.out.println("We'll be working with the following day: " + dayWithPost.getText());
dayWithPost.click();
List<WebElement> articles = driver.findElements(By
.className("entry-title"));
for (WebElement article : articles) {
System.out.println("Retrieved Post's Title: [ "
+ article.findElement(By.tagName("a")).getText() + " ]");
}
public class CalendarPage {
public List<WebElement> ReturnArticlesOnPage(WebDriver driver) {
List<WebElement> articles = driver.findElements(By.tagName("article"));
return articles;
}
public WebElement GetFirstDayWithPosts(WebDriver driver, WebElement table) {
List<WebElement> rows = table.findElements(By.tagName("tr"));
for (WebElement row : rows) {
List<WebElement> cols = row.findElements(By.tagName("td"));
for (WebElement col : cols) {
try {
if (col.findElement(By.tagName("a")).getAttribute("href") != null ){
System.out.println("Day: " + col.getText()+ " has posts and was returned.");
return col;
}
}
catch (NoSuchElementException e){
//System.out.println("Day: " + col.getText()+ " has no posts.");
}
}
}
return null;
}
}
<table id="tblListViewHeader" class="adminlist" cellspacing="1" cellpadding="0" style="table-layout: fixed; width: 1003px;">
<tbody>
</table>
</td>
</tr>
<tr>
<td>
<div id="divListView" style="width: 100%; height: 300px; overflow: auto; display: block;">
<table id="tblListView" class="adminlist" cellspacing="1" cellpadding="0" style="table-layout: fixed; width: 100%;">
<tbody data-bind="template: { name: 'ActiveGradeTemplate', foreach: ActiveGrade }">
<tr class="row0">
<td data-bind="text:$index()+1" style="width: 5%;">1</td>
<td data-bind="text: GradeName" style="width: 20%;">Vantage Point</td>
<td align="right" data-bind="text: DisplayCreatedDate" style="width: 10%;">27 Mar 2013</td>
<td align="right" data-bind="text: CreatedByUser" style="width: 10%;">Name</td>
<td align="right" data-bind="text: DisplayModifiedDate" style="width: 10%;">27 Mar 2013</td>
<td align="right" data-bind="text: ModifiedByUser" style="width: 10%;">Name</td>
<td align="center" data-bind="text: Status" style="width: 5%;">Active</td>
<td align="center" style="width: 10%;">
<a id="lnkEdit_7" data-bind="click: $root.lnkEdit, attr:{'id':'lnkEdit_' + GradeID}" href="#">Edit</a>
<span id="spanEdit_7" data-bind="attr:{'id':'spanEdit_' + GradeID}"></span>
</td>
</tr>
<tr class="row0">
<td data-bind="text:$index()+1" style="width: 5%;">2</td>
<td data-bind="text: GradeName" style="width: 20%;">test grade</td>
<td align="right" data-bind="text: DisplayCreatedDate" style="width: 10%;">Yesterday</td>
<td align="right" data-bind="text: CreatedByUser" style="width: 10%;">Name</td>
<td align="right" data-bind="text: DisplayModifiedDate" style="width: 10%;">Yesterday</td>
<td align="right" data-bind="text: ModifiedByUser" style="width: 10%;">Name</td>
<td align="center" data-bind="text: Status" style="width: 5%;">Active</td>
<td align="center" style="width: 10%;">
<a id="lnkEdit_11" data-bind="click: $root.lnkEdit, attr:{'id':'lnkEdit_' + GradeID}" href="#">Edit</a>
<span id="spanEdit_11" data-bind="attr:{'id':'spanEdit_' + GradeID}"></span>
</td>
</tr>
How can I retrieve the td values for each and every row, this is for Dynamic generation. All the tr class names are the same: <tr class="row0">. How do I retreive the table data for the above formatted table?
Try below code, this will print all cells data,
// Grab the table
WebElement table = driver.findElement(By.id("divListView"));
// Now get all the TR elements from the table
List<WebElement> allRows = table.findElements(By.tagName("tr"));
// And iterate over them, getting the cells
for (WebElement row : allRows) {
List<WebElement> cells = row.findElements(By.tagName("td"));
// Print the contents of each cell
for (WebElement cell : cells) {
System.out.println(cell.getText());
}
}
// Grab the table
WebElement table = driver.findElement(By.id("table-6"));
//Get number of rows in table
int numOfRow = table.findElements(By.tagName("tr")).size();
//Get number of columns In table.
int numOfCol = driver.findElements(By.xpath("//*[#id='table-6']/tbody/tr[1]/td")).size();
//divided Xpath In three parts to pass Row_count and Col_count values.
String first_part = "//*[#id='table-6']/tbody/tr[";
String second_part = "]/td[";
String third_part = "]";
//take the second column values
int j=2;
//List to store the second column
List<String> secondColumnList=new ArrayList<String>();
//Loop through the rows and get the second column and put it in a list
for (int i=1; i<=numOfRow; i++){
//Prepared final xpath of specific cell as per values of i and j.
String final_xpath = first_part+i+second_part+j+third_part;
//Will retrieve value from located cell and print It.
String test_name = driver.findElement(By.xpath(final_xpath)).getText();
secondColumnList.add(test_name);
System.out.println(test_name);
}
Dynamic table data capturing:
1.First of all Capture Table Head Count.
[int tHeadCount = driver.findElements(By.xpath("//table//tr//th")).size();]
2.Capture Table Row Count in which row your actual data exists.
[-in my point of view i need data from first row it self, so i am hard coding it to zero.]
If you want please add one for loop to existing code.
3.The actual solution starts from here.
Following is function call "Deposited By" is table heading text of corresponding table data.
String tableDataValue = managePackageTableData("Deposited By");
public String managePackageTableData(String columnName) {
//In Following line i am capturing table contains how many headers.
int tHeadCount = driver.findElements(By.xpath("//table//tr//th")).size();
int statusIndex = 0;
for(int i=0;i<tHeadCount-1;i++)
{
String theadValue = driver.findElements(By.className("table")).get(0).findElements(By.tagName("tr")).get(0).findElements(By.tagName("th")).get(i).getText();
if(theadValue.equalsIgnoreCase(columnName))
{
statusIndex = i;
break;
}
}
String tableData = driver.findElements(By.tagName("tbody")).get(0).findElements(By.tagName("tr")).get(0).findElements(By.tagName("td")).get(statusIndex).getText();
return tableData;
}
You have many options, but there's mine.
You catch all tds into a list.
List<WebElement> tdlist = driver.findElements(By.cssSelector("table[id='divListView'] tr td"));
and if you want to have the value, you can use a loop.
for(WebElement el: tdlist) {
Systeme.out.println(el.getText());
}
Check out this
Most common Challenge Automation tester face during iterating through Table and list. they often want to find some value from the table cell or list and want to perform action on the same value or find corresponding other element in the same block and perform action on it.
http://qeworks.com/iterate-table-lists-selenium-webdriver/
I have done this code using TestComplete and have replicated it now with Selenium C#, I have learnt this hard way but will work for any table control and you don't have to hardcode any xpath elements in it. Also if you have a nested table control within a td like for example where you have a nested table structure where your data is interpreted like below(happens in complex tables when you use developer express grids or angular grid controls).This case if you see the td tag again has a nested table structure which again has duplicate data. You can either capture such data or leave it depending on the case using the code which I am giving below.
Html
<table>
<tr>
<td>Account #
<table>
<tr>
<td>
Account #
</td>
</tr>
</table>
</td>
<td>Name
<table>
<tr>
<td>
Name
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>1234
<table>
<tr>
<td>
1234
</td>
</tr>
</table>
</td>
<td>Bharat
<table>
<tr>
<td>
Bharat
</td>
</tr>
</table>
</td>
</tr>
</table>
Code
public List<TableDataCollection> StoreHtmlTableToList(IWebElement tblObj)
{
DataTable dataTbl = new DataTable();
int rowIndex = 1;
try
{
_tblDataCollection = new List<TableDataCollection>();
var tblRows = ((IJavaScriptExecutor)DriverContext.Driver).ExecuteScript("return arguments[0].rows; ", tblObj);
if (tblRows != null)
{
//Iterate through each row of the table
foreach (IWebElement tr in (IEnumerable)tblRows)
{
int colIndx = 1;
// Iterate through each cell of the table row
var tblCols = ((IJavaScriptExecutor)DriverContext.Driver).ExecuteScript("return arguments[0].cells; ", tr);
foreach (IWebElement td in (IEnumerable)tblCols)
{
//loop through any child or nested table structures if you want using the same approach
//Write Table to List : This part is not done yet
//Print the values
Console.WriteLine("Row[" + rowIndex.ToString() + "] Col[" + colIndx.ToString() + "] : " + td.Text);
colIndx++;
}
rowIndex++;
}
}
}
catch (Exception)
{
throw;
}
return _tblDataCollection;
}
# Ripon Al Wasim
The below code will helps you to find values column by column
WebElement customtable = t.driver.findElement(By.cssSelector("div.custom-table"));
List<WebElement> r = customtable.findElements(By.tagName("tr"));
for (WebElement row : r) {
List<WebElement> d = row.findElements(By.tagName("td"));
for(int i = 0; i<d.size(); i++) {
if(i==0) {
WebElement x =d.get(i);
JavascriptExecutor js = (JavascriptExecutor) t.driver;
js.executeScript("arguments[0].scrollIntoView();", x);
System.out.println(i+"."+d.get(i).getText()+"\n");
if(d.get(i).getText().contains(searchtext)) {
System.out.println(i+".yes\n");
}
else
{
System.out.println("No\n");
}
}
}
}
its working for me.
//tbody/tr
this will give you the total no of row-
//tbody/tr/td
this will give you all the cell for the above rows and you can iterate it based on your requiredment.
Below are different approach we can follow to handle dynamic data in application [Its not for dynamic elements];
Using excel approach;
a. Get the web-element of the field
b. Get its text .
c. Store the data in the excel and validate with actual result pattern.
Note : There are multiple data validation options in excel like compare columns, get duplicate etc..
Using collection;
a. Get the web-element of the field.
b. Get its data inside the collection [List, set, map etc]
c. Write the java code to compare the pattern of the application data.
i. Pattern can be data type, data length, data range ,Decimal places of amount field or other ,currency type, date and time pattern etc.
ii. You can write the java conditions to verify the values of charts/graphs/dashboard if you are using in your application.
d. Compare the actual data pattern[from the collection] and the expected data pattern[From the java code]
Use JDBC API to handle it through the database where you can check the actual data by using different commands.