Select cell according to column and row by xpath - xpath

I have next table sturcture:
<table>
<thead>
<tr>
<th>User Role</th>
<th>Inactive</th>
<th>Admin Area</th>
<th>Need Confirm</th>
</tr>
</thead>
<tbody>
<tr>
<td>Admin</td>
<td>False</td>
<td>True</td>
<td>False</td>
</tr>
<tr>
<td>Member</td>
<td>False</td>
<td>False</td>
<td>True</td>
</tr>
</tbody>
</table>
How can I select a cell according to column name and row from first column (e.g. select Admin Area cell value for Member)?

If you can guarantee that User Role is the first column, then the following will work:
//td[count(//th[text()="Admin Area"]/preceding-sibling::*)+1][../td[1]="Member"]
Explanation:
count(//th[text()="Admin Area"]/preceding-sibling::*)+1 gives you the index of "Admin Area" among the table headers, in this case 3
//td[3] selects all td cells in the third column
//[../td[1]="Member"] further qualifies this by selecting only the td cells whose sibling in the first column contains "Member"

Related

Edit model with all related tables

I have a table for comments, each comment could have a decision from a dropdown.
If the specific decision is selected the user then could select one/many services.
If a specific service is selected only one grade is selected.
So the result comments section could look like these examples:
This is a comment without a decision
This is a comment with a decision
decision: This is decision 1
This is a comment with a decision and services
decision: This is decision 2
services: ['This is service 1', 'This is service 2']
This is a comment with a decision, services, and grade
decision: This is decision 2
services: ['this is service 1', 'This is service 3 grade: 2']
The decision is only a single selection. The services are multiple checkboxes. The grade is only a single selection.
The form looks like that:
<form>
<textarea name="comment"></textarea>
<select name="decision">
<option value="1">Decision 1</option>
<option value="2">Decision 2</option>
</select>
<!-- Show if decision 2 is selected -->
<div class="services">
<input type="checkbox" name="services[]" value="1"/>
<input type="checkbox" name="services[]" value="2"/>
</div>
<!-- Show if service 2 is checked -->
<select name="grade">
<option value="1">1</option>
<option value="2">2</option>
</select>
</form>
DB structure:
comments table:
id | comment
1 | This is comment 1
2 | This is comment 2
decisions table:
id | decision
1 | This is decision 1
2 | This is decision 2
services table:
id | service
1 | This is service 1
2 | This is service 2
grades table:
id | grade
1 | 1
2 | 2
3 | 3
4 | 4
Finally, these tables are related using 3 tables:
comment_decisions table:
id | decision_id | comment_id
comment_services table:
id | service_id | comment_id
comment_grades table:
id | comment_services_id | grade_id
I did that to avoid duplication and NULL values in the case of one table:
id | comment | decision_id (nullable) | service_id (nullable) | grade_id (nullable)
This issue is when I try to update the decision, services, and grade. When I use updateOrCreate new records are created not updated. attach and sync not working either.
Is there a better structure or I'm missing the right functions to update the tables?

How to select a row in a table and select a specific radio button next to it

I would like to select a specific row in a table on a website and select a button next to it. The problem I have is each time I select the button it remembers the last button by position xpath=(//a[#id='DeleteLnkBtn'])[4] but i would like to select the row by name and select the button unique to that row. There are multiple rows with the same buttons all having the same name (Delete).
HTML:
<td>Auto Missed Session 2</td> <a id="DeleteLnkBtn" >Delete</a>
My aim is to reference the row by a text in the table "Auto Missed Session 2" and click on the delete button.
Here is what I've tried so far.
Option 1 - driver.findElement(By.xpath("//tr/td[contains(text() = 'Auto Missed ssion 2', + 'DeleteLnkBtn')]")).click();
Option 2 - driver.findElement(By.xpath("(//tr/td[contains(text(),'Auto Missed Session 2' + 'Delete')])")).click();
The above fails stating this is not a valid Xpath expresssion on Option 1 and No such element: unable to locate element on Option 2
The table data looks like this
td> Auto Missed Session 2 /td> - This is the name of the row in the table i would like to select.
a id= "DeleteLnkBtn" This is the button i would like to select.
Xpath of the button is //*[#id="DeleteLnkBtn"]
Thanks.
Try below code and let me know the result:
driver.findElement(By.xpath("//td[text() = 'Auto Missed Session 2']/following::a[text()='Delete']")).click();

Select one xpath element based upon the position of another

I want to select one html element based upon the position of another using xpath. For example:
<table>
<tr>
<th>
Col1
</th>
<th>
Col2
</th>
<th>
Col3
</th>
<th>
Col4
</th>
</tr>
<tr>
<td>
Value1
</td>
<td>
Value2
</td>
<td>
Value3
</td>
<td>
Value4
</td>
</tr>
</table>
In this example I want the td which is in the same position in the collection of tds that the th with the contents Col2 is.
I can find the position of the th
//th[contains(.,'Col2')]
I want to avoid doing this
//td[2]
Is there any way I can link the two?
This is one possible way :
//td[
position() = count(//th[contains(.,'Col2')]/preceding-sibling::th)+1
]
xpathtester demo
The XPath returns td at position equals to position of the th. position of the th is calculated by counting number of preceding sibling th, +1 since XPath position index starts from 1.

HTML Form: Insert id from related table

I'm relatively new to both php and mysql and here's my first question:
I have 3 tables. One as the "main table" and two as basically lists/categories of which I can choose and relate them to my main table. So:
Table 1
id name hobby color
1 Peter 1 2
2 Simon 3 2
3 Lisa 2 3
Table 2 would then be hobbies
id name
1 Swimming
2 Football
3 Piano
Same with table 3 and colors.
id name
1 red
2 blue
3 green
Now, I have managed to "relate" the tables with phpmyadmin, but how can I do the following:
I wanna insert data into the first table. But the thing is, I wanna insert a simple number in hobbies or colors. But within the html form, there should be the text related to the is.
So if i choose 'green'(as a string), it should insert 3 into Table 1.
I hope it's clear what i want :D
Thanks for every help in advance.
Simon
Create your html like this:
<select name="color">
<option value="1">red</option>
<option value="2">blue</option>
</select>
When an user select "blue" the value of input.color is "2". So an end user selects an string/color and you can insert the int/id

How to select specified node within Xpath node sets by index with Selenium?

I'm writing a Selenium testcase. And here's the xpath expression I use to match all 'Modify' buttons within a data table.
//img[#title='Modify']
My question is, how can I visit the matched node sets by index? I've tried with
//img[#title='Modify'][i]
and
//img[#title='Modify' and position() = i]
But neither works..
I also tried with XPath checker(One firefox extension). There're totally 13 matches found, then I have totally no idea how am I gonna select one of them..
Or does XPath support specified selection of nodes which are not under same parent node?
This is a FAQ:
//someName[3]
means: all someName elements in the document, that are the third someName child of their parent -- there may be many such elements.
What you want is exactly the 3rd someName element:
(//someName)[3]
Explanation: the [] has a higher precedence (priority) than //. Remember always to put expressions of the type //someName in brackets when you need to specify the Nth node of their selected node-list.
There is no i in XPath.
Either you use literal numbers: //img[#title='Modify'][1]
Or you build the expression string dynamically: '//img[#title='Modify']['+i+']' (but keep in mind that dynamic XPath expressions do not work from within XSLT).
Or does XPath support specified selection of nodes
which are not under same parent node?
Yes: (//img[#title='Modify'])[13]
This //img[#title='Modify'][i] means "any <img> with a title of 'Modify' and a child element named <i>."
There is no i in xpath is not entirely true. You can still use the count() to find the index.
Consider the following page
<html>
<head>
<title>HTML Sample table</title>
</head>
<style>
table, td, th {
border: 1px solid black;
font-size: 15px;
font-family: Trebuchet MS, sans-serif;
}
table {
border-collapse: collapse;
width: 100%;
}
th, td {
text-align: left;
padding: 8px;
}
tr:nth-child(even){background-color: #f2f2f2}
th {
background-color: #4CAF50;
color: white;
}
</style>
<body>
<table>
<thead>
<tr>
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
<th>Heading 4</th>
<th>Heading 5</th>
<th>Heading 6</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data row 1 col 1</td>
<td>Data row 1 col 2</td>
<td>Data row 1 col 3</td>
<td>Data row 1 col 4</td>
<td>Data row 1 col 5</td>
<td>Data row 1 col 6</td>
</tr>
<tr>
<td>Data row 2 col 1</td>
<td>Data row 2 col 2</td>
<td>Data row 2 col 3</td>
<td>Data row 2 col 4</td>
<td>Data row 2 col 5</td>
<td>Data row 2 col 6</td>
</tr>
<tr>
<td>Data row 3 col 1</td>
<td>Data row 3 col 2</td>
<td>Data row 3 col 3</td>
<td>Data row 3 col 4</td>
<td>Data row 3 col 5</td>
<td>Data row 3 col 6</td>
</tr>
<tr>
<td>Data row 4 col 1</td>
<td>Data row 4 col 2</td>
<td>Data row 4 col 3</td>
<td>Data row 4 col 4</td>
<td>Data row 4 col 5</td>
<td>Data row 4 col 6</td>
</tr>
<tr>
<td>Data row 5 col 1</td>
<td>Data row 5 col 2</td>
<td>Data row 5 col 3</td>
<td>Data row 5 col 4</td>
<td>Data row 5 col 5</td>
<td>Data row 5 col 6</td>
</tr>
<tr>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
</tr>
</tbody>
</table>
</br>
<table>
<thead>
<tr>
<th>Heading 7</th>
<th>Heading 8</th>
<th>Heading 9</th>
<th>Heading 10</th>
<th>Heading 11</th>
<th>Heading 12</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data row 1 col 1</td>
<td>Data row 1 col 2</td>
<td>Data row 1 col 3</td>
<td>Data row 1 col 4</td>
<td>Data row 1 col 5</td>
<td>Data row 1 col 6</td>
</tr>
<tr>
<td>Data row 2 col 1</td>
<td>Data row 2 col 2</td>
<td>Data row 2 col 3</td>
<td>Data row 2 col 4</td>
<td>Data row 2 col 5</td>
<td>Data row 2 col 6</td>
</tr>
<tr>
<td>Data row 3 col 1</td>
<td>Data row 3 col 2</td>
<td>Data row 3 col 3</td>
<td>Data row 3 col 4</td>
<td>Data row 3 col 5</td>
<td>Data row 3 col 6</td>
</tr>
<tr>
<td>Data row 4 col 1</td>
<td>Data row 4 col 2</td>
<td>Data row 4 col 3</td>
<td>Data row 4 col 4</td>
<td>Data row 4 col 5</td>
<td>Data row 4 col 6</td>
</tr>
<tr>
<td>Data row 5 col 1</td>
<td>Data row 5 col 2</td>
<td>Data row 5 col 3</td>
<td>Data row 5 col 4</td>
<td>Data row 5 col 5</td>
<td>Data row 5 col 6</td>
</tr>
<tr>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
</tr>
</tbody>
</table>
</body>
</html>
The page has 2 tables and has 6 columns each with unique column names and 6 rows with variable data. The last row has the Modify button in both the tables.
Assuming that the user has to select the 4th Modify button from the first table based on the heading
Use the xpath //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button
The count() operator comes in handy in situations like these.
Logic:
Find the header for the Modify button using //th[.='Heading 4']
Find the index of the header column using count(//tr/th[.='Heading 4']/preceding-sibling::th)+1
Note: Index starts at 0
Get the rows for the corresponding header using //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]
Get the Modify button from the extracted node list using //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button
//img[#title='Modify'][i]
is short for
/descendant-or-self::node()/img[#title='Modify'][i]
hence is returning the i'th node under the same parent node.
You want
/descendant-or-self::img[#title='Modify'][i]
(//*[#attribute='value'])[index]
to find target of element while your finding multiple matches in it
Here is the solution for index variable
Let's say, you have found 5 elements with same locator and you would like to perform action on each element by providing index number (here, variable is used for index as "i")
for(int i=1; i<=5; i++)
{
string xPathWithVariable = "(//div[#class='className'])" + "[" + i + "]";
driver.FindElement(By.XPath(xPathWithVariable)).Click();
}
It takes XPath :
(//div[#class='className'])[1]
(//div[#class='className'])[2]
(//div[#class='className'])[3]
(//div[#class='className'])[4]
(//div[#class='className'])[5]

Resources