I'm checking for a user on a web interface and clicking an edit button in the corresponding table, but the button and table itself are identical and therefore not uniquely identifiable. I can find the text in the table, so my approach was to grab the xpath where that's found, and derive the button's xpath from that. The xpath itself will be variable based on how/what users are on the page so I can't use anything absolute.
I can find the text element using this, but I don't know of a way to use that to derive the corresponding button. Feel free to point me towards a different solution for the problem.
userLookupElement = driver.find_element(By.XPATH,"//*[contains(text(), 'SeleniumTest')]")
e.g.
xpath holding text (found using browser ext) /html/body/main/div[2]/div/div/div/div[2]/div/div[2]/div/div/form/div[5]/table/tbody/tr[18]/td[1]
corresponding button /html/body/main/div[2]/div/div/div/div[2]/div/div[2]/div/div/form/div[5]/table/tbody/tr[18]/td[3]/label
Edit: Included HTML. Attempting to select based on username "SeleniumTest" and find the corresponding "Edit User" button element.
<tr ng-repeat="user in userData track by $index" ng-class-odd="'o-table-bkodd'" ng-class-even="'o-table-bkeven'" class="ng-scope o-table-bkeven">
<td colspan="2" class="ng-binding">
SeleniumTest
<!-- ngIf: user.IsLockedOut -->
</td>
<td class="ng-binding">
</td>
<th style="width:58px;">
<span ng-show="user.AuthorizedFor.indexOf('Operations') != -1" ng-hide="user.AuthorizedFor.indexOf('Operations') == -1"><i class="fa fa-check"></i></span>
</th>
<th style="width:58px;">
<span ng-show="user.AuthorizedFor.indexOf('Overrides') != -1" ng-hide="user.AuthorizedFor.indexOf('Overrides') == -1"><i class="fa fa-check"></i></span>
</th>
<th style="width:58px;">
<span ng-show="user.AuthorizedFor.indexOf('Reports') != -1" ng-hide="user.AuthorizedFor.indexOf('Reports') == -1"><i class="fa fa-check"></i></span>
</th>
<th style="width:58px;">
<span ng-show="user.AuthorizedFor.indexOf('Setup') != -1" ng-hide="user.AuthorizedFor.indexOf('Setup') == -1" class="ng-hide"><i class="fa fa-check"></i></span>
</th>
<th style="width:58px;">
<span ng-show="user.AuthorizedFor.indexOf('Users') != -1" ng-hide="user.AuthorizedFor.indexOf('Users') == -1" class="ng-hide"><i class="fa fa-check"></i></span>
</th>
<th style="width:58px;">
<span ng-show="user.RemoteAccessEnabled" ng-hide="!user.RemoteAccessEnabled"><i class="fa fa-check"></i></span>
</th>
<td>
<label class="btn btn-sm btn-default texture-blue" data-ng-click="editUser(user)">Edit User</label>
</td>
</tr>
You can nest element with text in []
to search its parent tr
and later you can search label
in this parent.
'//tr[td[contains(text(), "SeleniumTest")]]//label'
You may also use following-sibling::td
to search next td
'//td[contains(text(), "SeleniumTest")]/following-sibling::td/label'
Full working code with example HTML directly in code.
html = '''<table>
<tr ng-repeat="user in userData track by $index" ng-class-odd="'o-table-bkodd'" ng-class-even="'o-table-bkeven'" class="ng-scope o-table-bkeven">
<td colspan="2" class="ng-binding">
SeleniumTest
<!-- ngIf: user.IsLockedOut -->
</td>
<td class="ng-binding">
</td>
<th style="width:58px;">
<span ng-show="user.AuthorizedFor.indexOf('Operations') != -1" ng-hide="user.AuthorizedFor.indexOf('Operations') == -1"><i class="fa fa-check"></i></span>
</th>
<th style="width:58px;">
<span ng-show="user.AuthorizedFor.indexOf('Overrides') != -1" ng-hide="user.AuthorizedFor.indexOf('Overrides') == -1"><i class="fa fa-check"></i></span>
</th>
<th style="width:58px;">
<span ng-show="user.AuthorizedFor.indexOf('Reports') != -1" ng-hide="user.AuthorizedFor.indexOf('Reports') == -1"><i class="fa fa-check"></i></span>
</th>
<th style="width:58px;">
<span ng-show="user.AuthorizedFor.indexOf('Setup') != -1" ng-hide="user.AuthorizedFor.indexOf('Setup') == -1" class="ng-hide"><i class="fa fa-check"></i></span>
</th>
<th style="width:58px;">
<span ng-show="user.AuthorizedFor.indexOf('Users') != -1" ng-hide="user.AuthorizedFor.indexOf('Users') == -1" class="ng-hide"><i class="fa fa-check"></i></span>
</th>
<th style="width:58px;">
<span ng-show="user.RemoteAccessEnabled" ng-hide="!user.RemoteAccessEnabled"><i class="fa fa-check"></i></span>
</th>
<td>
<label class="btn btn-sm btn-default texture-blue" data-ng-click="editUser(user)">Edit User</label>
</td>
</tr>
<tr>
<td>Other Text</td>
<td><label>Other Label</label></td>
</tr>
</table>'''
from selenium import webdriver
from selenium.webdriver.common.by import By
# ---
import selenium
print('Selenium:', selenium.__version__)
# ---
#driver = webdriver.Chrome()
driver = webdriver.Firefox()
driver.get("data:text/html;charset=utf-8," + html)
#driver.implicitly_wait(3)
#item = driver.find_element(By.XPATH, '//*[contains(text(), "SeleniumTest")]')
print('-----')
print("Checking: SeleniumTest")
item = driver.find_element(By.XPATH, '//tr[td[contains(text(), "SeleniumTest")]]//label')
print('Found:', item.text)
print('-----')
print("Checking: Other Text")
item = driver.find_element(By.XPATH, '//tr[td[contains(text(), "Other Text")]]//label')
print('Found:', item.text)
# ---
for text in ('SeleniumTest', 'Other Text'):
print('-----')
print(f"Checking: {text}")
xpath = f'//tr[td[contains(text(), "{text}")]]//label'
print(f"XPath: {xpath}")
item = driver.find_element(By.XPATH, xpath)
print('Found:', item.text)
for text in ('SeleniumTest', 'Other Text'):
print('-----')
print(f"Checking: {text}")
xpath = f'//td[contains(text(), "{text}")]/following-sibling::td/label'
print(f"XPath: {xpath}")
item = driver.find_element(By.XPATH, xpath)
print('Found:', item.text)
driver.close()
Result:
Selenium: 4.31.0
-----
Checking: SeleniumTest
Found: Edit User
-----
Checking: Other Text
Found: Other Label
-----
Checking: SeleniumTest
XPath: //tr[td[contains(text(), "SeleniumTest")]]//label
Found: Edit User
-----
Checking: Other Text
XPath: //tr[td[contains(text(), "Other Text")]]//label
Found: Other Label
-----
Checking: SeleniumTest
XPath: //td[contains(text(), "SeleniumTest")]/following-sibling::td/label
Found: Edit User
-----
Checking: Other Text
XPath: //td[contains(text(), "Other Text")]/following-sibling::td/label
Found: Other Label