RobotFramework : How to retrieve the row of an array corresponding to a cell value - selenium

In the UI I'm working with, for an object there there is a pencil or a basket icon; I want to click the pencil one (which will trigger an edit modal), and want to do it for the specific object.
For now I use this command line:
Click Element xpath = (//a[#ng-click="openCreateEditModal (holding)"])[2]
I would like to replace the "2" by the value of the line corresponding to the value "RF-Account".
Here is a sample of the HTML:
<tbody ng-if="!isEmpty" class="body-table ng-scope" style="height: 165px;">
<!-- ngRepeat: tenant in tenants track by $index --><tr ng-repeat="tenant in tenants track by $index" class="ng-scope" style="">
<td class="ng-binding">RF-Accou</td>
<td>
<span class="text-bold ng-binding text-muted" ng-class="{'text-green' : tenant.status == 'ACTIVE', 'text-muted' : tenant.status == 'INACTIVE'}">INACTIVE</span>
</td>
<td style="width:118px">
<div class="pull-right">
<a name="editBtn" type="submit" class="btn-icon" ng-click="openCreateEditModal(tenant)">
<span class="icon-i-edit fs" data-toggle="tooltip" title=""></span>
</a>
<a name="deleteBtn" type="submit" class="btn-icon" ng-click="deleteTenant(tenant)">
<span class="icon-i-trash fs" data-toggle="tooltip" title=""></span>
</a>
</div>
</td>
</tr><!-- end ngRepeat: tenant in tenants track by $index --><tr ng-repeat="tenant in tenants track by $index" class="ng-scope">
**<td class="ng-binding">RF-Account</td>**
<td>
<span class="text-bold ng-binding text-muted" ng-class="{'text-green' : tenant.status == 'ACTIVE', 'text-muted' : tenant.status == 'INACTIVE'}">INACTIVE</span>
</td>
<td style="width:118px">
<div class="pull-right">
<a name="editBtn" type="submit" class="btn-icon" ng-click="openCreateEditModal(tenant)">
<span class="icon-i-edit fs" data-toggle="tooltip" title=""></span>
</a>
<a name="deleteBtn" type="submit" class="btn-icon" ng-click="deleteTenant(tenant)">
<span class="icon-i-trash fs" data-toggle="tooltip" title=""></span>
</a>
</div>
</td>
</tr><!-- end ngRepeat: tenant in tenants track by $index --><tr ng-repeat="tenant in tenants track by $index" class="ng-scope">
<td class="ng-binding">RF-Accountfirst</td>
<td>
<span class="text-bold ng-binding text-muted" ng-class="{'text-green' : tenant.status == 'ACTIVE', 'text-muted' : tenant.status == 'INACTIVE'}">INACTIVE</span>
</td>
<td style="width:118px">
<div class="pull-right">
<a name="editBtn" type="submit" class="btn-icon" ng-click="openCreateEditModal(tenant)">
<span class="icon-i-edit fs" data-toggle="tooltip" title=""></span>
</a>
<a name="deleteBtn" type="submit" class="btn-icon" ng-click="deleteTenant(tenant)">
<span class="icon-i-trash fs" data-toggle="tooltip" title=""></span>
</a>
</div>
</td>
</tr><!-- end ngRepeat: tenant in tenants track by $index -->
</tbody>
I am trying to find the line that corresponds to the text value "RF-Account".
For your information, the line of "RF-Account" can change.
Could you please help me find the right keyword to use for Robot Framework ?

To rephrase your question - you need a locator that'll get you that, not a robotframework keyword (the keyword is known, Click Element).
If your anchor is the text, then this xpath will do it for you:
//td[text()="RF-Account"]/following-sibling::td//a[#ng-click="openCreateEditModal(tenant)"]
Let me explain: the first part (//td[text()="RF-Account"]) will select a td element which text is "RF-Account". Then it will look for a follow-up td (the "following-sibling axis), that has an a child with that value for ng-click - and return it (the a).
BTW, in the sample source there is no "openCreateEditModal(holding)", thus I've changed it with "tenant", which is present.

For getting count you can use
Get Matching XPath Count //td[.="RF-Account"]

Related

Xpath to find offset parent in element

I have an automated Selenium test which uses the following XPath
"//tr[td/strong='Riparian 2m Ungrazed - RBS' and td/button[#id='btnDeleteOption_WG']]"
Currently, whenever I query this XPath it returns the element I need but also returns another hidden element which is not on screen.
I have compared the two elements and the only difference is the following
offsetHeight: 39 offsetHeight: 0
offsetLeft: 8 offsetLeft: 0
offsetParent: td offsetParent: null
offsetTop: 10 offsetTop: 0
offsetWidth: 179 offsetWidth: 0
I need to find the element on the left with the offsetParent of <td>.
Can anybody help with an XPath to find this?
Thanks!
Below is the HTML of the element:
<td class="text-center" style="white-space: nowrap;">
<button id="btnShowItems_WG" class="btn btn-sm btn-primary" ng-disabled="!WG_field.optionHasItems" ng-click="WG_field.optionHasItems ? items.visible = !items.visible : false;" title="View Items" disabled="disabled">
<span class="glyphicon glyphicon-list"></span>
</button>
<button ng-hide="!efsEditRole_RoleAssignedToUser" ng-disabled="false" id="btnAddOptionItem_16_WG" class="btn btn-primary btn-sm" ng-click="appSummaryVm.addItem($index, 'WG'); appSummaryVm.addItemsByOption(WG_field.fieldId, WG_field.tranche, WG_field.optionCode, WG_field.optionTypeId, $index, 'WG');"
title="Add Item" aria-hidden="false">
<span class="glyphicon glyphicon-plus"></span>
</button>
</td>
<td><strong>Riparian 2m Ungrazed - RBS</strong></td>
<td style="width: 10%;">
<input name="optionQuantityWG_16" type="number" class="form-control ng-pristine ng-untouched ng-valid ng-not-empty ng-valid-min ng-valid-max ng-valid-required ng-valid-pattern" ng-class="{
'form-control input-error' : (existingOptionsWG.optionQuantityWG_16.$dirty && (
existingOptionsWG.optionQuantityWG_16.$error.max
|| existingOptionsWG.optionQuantityWG_16.$error.min
|| existingOptionsWG.optionQuantityWG_16.$error.pattern
|| existingOptionsWG.optionQuantityWG_16.$invalid
|| existingOptionsWG.optionQuantityWG_16.$error.required
))
}" ng-change="appSummaryVm.updateOptionTotals(WG_field, 'WG'); appSummaryVm.edit();" ng-model="WG_field.optionQuantity" ng-pattern="/^\d+(\.\d{1,2})?$/" min="" max="" step="0.01" style="text-align: right;" ng-true-value="10.00"
required="" id="optionWGW0316" ng-disabled="!efsEditRole_RoleAssignedToUser" aria-invalid="false">
<div class="form-control inline-errorUp ng-hide" ng-show="existingOptionsWG.optionQuantityWG_16.$dirty && (
existingOptionsWG.optionQuantityWG_16.$error.max
|| existingOptionsWG.optionQuantityWG_16.$error.min
|| existingOptionsWG.optionQuantityWG_16.$error.pattern
|| existingOptionsWG.optionQuantityWG_16.$error.required
|| existingOptionsWG.optionQuantityWG_16.$invalid
)" aria-hidden="true">
<span ng-show="existingOptionsWG.optionQuantityWG_16.$dirty && existingOptionsWG.optionQuantityWG_16.$error.max" aria-hidden="true" class="ng-hide">
maximum of 999999999.99
</span>
<span ng-show="existingOptionsWG.optionQuantityWG_16.$dirty && existingOptionsWG.optionQuantityWG_16.$error.min" aria-hidden="true" class="ng-hide">
cannot be zero or less
</span>
<span ng-show="existingOptionsWG.optionQuantityWG_16.$dirty && (existingOptionsWG.optionQuantityWG_16.$error.pattern || existingOptionsWG.optionQuantityWG_16.$invalid)" ng-hide="existingOptionsWG.optionQuantityWG_16.$error.max || existingOptionsWG.optionQuantityWG_16.$error.min || existingOptionsWG.optionQuantityWG_16.$error.required"
aria-hidden="false" class="">
2 decimal digits only
</span>
<span ng-show="existingOptionsWG.optionQuantityWG_16.$dirty && existingOptionsWG.optionQuantityWG_16.$error.required" aria-hidden="true" class="ng-hide">
required
</span>
</div>
</td>
<td class="text-right">6.00
<!----><span ng-if="WG_field.optionUnitType">/</span>
<!---->m</td>
<td class="text-right">60.00</td>
<td class="text-right">0.70</td>
<td class="text-right">96.00</td>
<td class="text-center">
<button ng-hide="!efsEditRole_RoleAssignedToUser" ng-disabled="false" id="btnDeleteOption_WG" class="btn btn-warning btn-sm" style="width: 110px;" ng-click="appSummaryVm.showRemovePrompt(WG_field, 'option', 'WG', $event); appSummaryVm.edit();" title="Delete 'Riparian 2m Ungrazed' Option from field 3/003/003/3"
aria-hidden="false">
<span class="glyphicon glyphicon-trash"></span> remove option
</button>
</td>
You can get all elements and them filter by visibility. Below is a Java code example, how to get first visible element from list of web elements.
With Java 8:
List<WebElement> elements = driver.findElements(By.xpath("//tr[td/strong='Riparian 2m Ungrazed - RBS' and td/button[#id='btnDeleteOption_WG']]"));
WebElement element = elements
.stream()
.filter(WebElement::isDisplayed)
.collect(Collectors.toList())
.get(0);
Python:
elements = driver.find_elements_by_xpath("//tr[td/strong='Riparian 2m Ungrazed - RBS' and td/button[#id='btnDeleteOption_WG']]")
visible_one = list(filter(lambda x: x.is_displayed(), elements))[0]
visible_one.click()

Protractor - Element Not Visible when selecting Drop down.(but visible when selected manually)

i have been searching for 2 days but i couldnt come to a solution, i want a drop down to be selected. When i click on the drop down using some element.all css locator it clicks on the drop down(Drop down Opens up but error displayed). so i tried to use linkText to open up the drop down(Opens up perfectly). But after that i was not able to select the option in the drop down. (Element Not Visible displayed). also i was not able to select the option through the linkText. below r the details
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav navbar-right" style="margin-right:0px">
<li>
<div uib-dropdown="" style="margin-right: 5px;" class="dropdown">
Tenant
<a href="" id="simple-dropdown" uib-dropdown-toggle="" class="dropdown-toggle" aria-haspopup="true" aria-expanded="false">
<span class="breadcrumb ng-binding" style="padding-bottom: 2px; padding-top: 2px;">cust1</span>
</a>
<ul class="uib-dropdown-menu dropdown-menu" role="menu" aria-labelledby="single-button">
<!-- ngRepeat: tenant in tenantList --><li ng-repeat="tenant in tenantList" class="ng-scope">
<a ng-click="selectTenant(0)" href="">
<span ng-show="selectedTenant.Identity == tenant.Identity" class="glyphicon glyphicon-ok"></span>
<span ng-show="selectedTenant.Identity != tenant.Identity" style="margin-left: 17px" class="ng-hide"></span>
<span class="ng-binding">cust1</span>
</a>
</li><!-- end ngRepeat: tenant in tenantList --><li ng-repeat="tenant in tenantList" class="ng-scope">
<a ng-click="selectTenant(1)" href="">
<span ng-show="selectedTenant.Identity == tenant.Identity" class="glyphicon glyphicon-ok ng-hide"></span>
<span ng-show="selectedTenant.Identity != tenant.Identity" style="margin-left: 17px" class=""></span>
<span class="ng-binding">NewTenant1</span>
</a>
</li><!-- end ngRepeat: tenant in tenantList --><li ng-repeat="tenant in tenantList" class="ng-scope">
<a ng-click="selectTenant(2)" href="">
<span ng-show="selectedTenant.Identity == tenant.Identity" class="glyphicon glyphicon-ok ng-hide"></span>
<span ng-show="selectedTenant.Identity != tenant.Identity" style="margin-left: 17px" class=""></span>
<span class="ng-binding">cust2</span>
</a>
</li><!-- end ngRepeat: tenant in tenantList -->
</ul>
</div>
I tried to do through below ways to select but i cant make it work.
wait for the element to become visible.
LinkText
Options to select. (via repeater name as the option)
kindly help me out
element(by.partialLinkText('cust2')); i dono how come linktext is not working but partialLinkText is working. thanks for replies.
Would this suffice? I use this to identify dropdown elements which contain a specific label value...
this.clickDropChoice = function(dropdown, optionChoice) {
dropdown.element(by.cssContainingText('option', optionChoice)).click()
};

How can I Selecting Kendo Drop Down using xpath

I have the following HTML:
<table class="w98">
<tbody>
<tr>
<tr>
<tr>
<td class="p-controlSetLabel">
<td class="editor-field">
**<span class="k-widget k-dropdown k-header" style="width: 165px;" title="" unselectable="on" role="listbox" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-owns="VatRateId_listbox" aria-disabled="false" aria-readonly="false" aria-busy="false" aria-activedescendant="304dd433-4a68-458b-a6d6-03b72335f1cb">**
<span class="k-dropdown-wrap k-state-default" unselectable="on">
<span class="k-input" unselectable="on">Standard - Standard</span>
<span class="k-select" unselectable="on">
<span class="k-icon k-i-arrow-s" unselectable="on">select</span>
</span>
</span>
<input id="VatRateId" class="k-valid" type="text" value="d20b" style="width: 165px; display: none;" name="VatRateId" data-val-required="Please select a VAT Rate." data-val="true" data-role="dropdownlist">
The input id="VatRateId" is hidden, so I can't access it.
My current solution is the following:
var allDropDownFields = FindElements(By.XPath(".//*[#class='k-dropdown-wrap k-state-default']") -- This will return a list of all drop down fields
Then I loop each one of them to find the correct drop down field I want.
Is there a better way of finding the element without getting a list, I tried the following:
FindElement(By.XPath(".//*[#class='k-dropdown-wrap k-state-default'and #aria-owns = 'VatRateId_listbox']")
and the above code is not work.
HTML code is as below:-
<span class="k-widget k-dropdown k-header" style="width: 165px;" title="" unselectable="on" role="listbox" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-owns="VatRateId_listbox" aria-disabled="false" aria-readonly="false" aria-busy="false" aria-activedescendant="304dd433-4a68-458b-a6d6-03b72335f1cb">* <span class="k-dropdown-wrap k-state-default" unselectable="on"> <span class="k-input" unselectable="on">Standard - Standard</span>
Any better suggestion?

Is it possible for selenium to find a label element by its "for" attribute using xpath?

Is it possible for selenium to find a label element by its "for" attribute using xpath?
I'm testing a site that has 7 yes or no questions in row. The way the CSS is done, you click on the label instead of the input radio button. Normally I'd just find the element by text content, but there is a yes and no for each question.
I'm thinking that a work-around would be to do a javascript click on the input button to get around the "element is not visible" error that I get when trying to .click() the input directly, but I wanted to see if there was a better way to locate the label which is what users would actually be clicking.
*Edit for all of your who can't remember what a label element looks like. How would you select the middle one from this group?
<label for="superLongSetOfRandomCharacters123" class="common-to-all-buttons"> No </label>
<label for="superLongSetOfRandomCharacters345" class="common-to-all-buttons"> No </label>
<label for="superLongSetOfRandomCharacters456" class="common-to-all-buttons"> No </label>
More specifically, this is how they sit in the DOM
<ul class="list-view list-view-dividers">
<li class="list-view-item">
<div class="content">
<div id="variableValue1" class="multipleChoice">
<div class="form-row">
<label>Question 1</label>
<div class="form-row">
<table class="choices" id="randomlyGenerated-1">
<tbody>
<tr>
<td>
<div class="ui-radio"><label for="randomlyGenerated1" class="ui-btn ui-corner-all ui-btn-inherit ui-btn-icon-left ui-radio-off"> Yes</label><input id="randomlyGenerated1" type="radio" name="randomlyGenerated1" value="Yes"></div>
</td>
<td>
<div class="ui-radio"><label for="randomlyGenerated2" class="ui-btn ui-corner-all ui-btn-inherit ui-btn-icon-left ui-radio-off"> No</label><input id="randomlyGenerated2" type="radio" name="randomlyGenerated2" value="No"></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="randomlyGenerated1-Error" class="component-error"></div>
</div>
</div>
</li>
<li class="list-view-item">
<div class="content">
<div id="variableValue2" class="multipleChoice">
<div class="form-row">
<label>Question 2</label>
<div class="form-row">
<table class="choices" id="randomlyGenerated-2">
<tbody>
<tr>
<td>
<div class="ui-radio"><label for="randomlyGenerated21" class="ui-btn ui-corner-all ui-btn-inherit ui-btn-icon-left ui-radio-off"> Yes</label><input id="randomlyGenerated21" type="radio" name="randomlyGenerated21" value="Yes"></div>
</td>
<td>
<div class="ui-radio"><label for="randomlyGenerated22" class="ui-btn ui-corner-all ui-btn-inherit ui-btn-icon-left ui-radio-off"> No</label><input id="randomlyGenerated22" type="radio" name="randomlyGenerated22" value="No"></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="randomlyGenerated2-Error" class="component-error"></div>
</div>
</div>
</li>
</ul>
It works for selecting all labels with target 'for':
label[#for='superLongSetOfRandomCharacters123']
or you can leave #for='' blank for selecting all labels with any values (i.e. label[#for])
I guess, the XPath-Option you are looking for would be "#". so in your case something like [...]/label/#for would return you that for-value, which you could then use to check where you are ;)
Cheers
D
Use find_element_by_xpath
label = self.browser.find_element_by_xpath('//label[#for="id_field"]')

Selenium inside siblings

Is there a way to click further inside siblings in Selenium Webdriver? For example, here's the HTML:
<tr class="rich-table-row xx-datalist-even" onmouseover="Element.addClassName(this, 'xx-datalist-mouseover')" onmouseout="Element.removeClassName(this, 'xx-datalist-mouseover')">
<td class="rich-table-cell xx-datalist " id="userTableForm01:usersTableData:264:j_id102" style="text-align:center; width:20px;">
<img src="/static/images/graphics/status1.gif" title="Disabled" />
</td>
<td id="userTableForm01:usersTableData:264:col1" class="rich-table-cell xx-datalist">
<span id="userTableForm01:usersTableData:264:author_id">2377</span>
</td>
<td id="userTableForm01:usersTableData:264:col2" class="rich-table-cell xx-datalist">seleniumtest2312</td>
<td id="userTableForm01:usersTableData:264:col3" class="rich-table-cell xx-datalist">
<span style="margin-right:3px">Test2312</span>
Selenium
</td>
<td class="rich-table-cell xx-datalist " id="userTableForm01:usersTableData:264:col4" style="text-align:center;">
<input type="checkbox" name="userTableForm01:usersTableData:264:j_id111" onclick="A4J.AJAX.Submit('userTableForm01',event,{'similarityGroupingId':'userTableForm01:usersTableData:264:j_id112','parameters':{'userTableForm01:usersTableData:264:j_id112':'userTableForm01:usersTableData:264:j_id112'} } )" />
</td>
<td id="userTableForm01:usersTableData:264:col6" class="rich-table-cell xx-datalist">
<a id="userTableForm01:usersTableData:264:enable_link" href="#" style="margin-right:5px" onclick="if(typeof jsfcljs == 'function'){jsfcljs(document.getElementById('userTableForm01'),{'userTableForm01:usersTableData:264:enable_link':'userTableForm01:usersTableData:264:enable_link'},'');}return false">
<span id="userTableForm01:usersTableData:264:enable_link_text" class="xx-datalist">Enable</span>
</a>
<a id="userTableForm01:usersTableData:264:edit_link" href="#" style="margin-right:5px" onclick="if(typeof jsfcljs == 'function'){jsfcljs(document.getElementById('userTableForm01'),{'userTableForm01:usersTableData:264:edit_link':'userTableForm01:usersTableData:264:edit_link'},'');}return false">
<span id="userTableForm01:usersTableData:264:edit_link_text" class="xx-datalist">Edit</span>
</a>
<a id="userTableForm01:usersTableData:264:remove_link" href="#" onclick="if(typeof jsfcljs == 'function'){jsfcljs(document.getElementById('userTableForm01'),{'userTableForm01:usersTableData:264:remove_link':'userTableForm01:usersTableData:264:remove_link'},'');}return false">
<span id="userTableForm01:usersTableData:264:remove_link_text" class="xx-datalist">Delete</span>
</a>
</td>
</tr>
Now, this is a much bigger table than shown, so there are a lot more tr's with more users. The ID's are all auto-generated and different each time, and I am trying to click the 'Enable'-link, though the only thing unique on the same row that I can use is the username seleniumtest2312, which has brought me to this:
driver.findElement(By.xpath("//td[text()='seleniumtest2312']/following-sibling::td[3]/span[text()='Enable']")).click();
But it just won't work (no such element).
If there are better ways of solving this issue I'll be happy to try them.
The last part of your XPath is a bit off :
following-sibling::td[3]/span[text()='Enable']
because <span> you're looking for isn't direct child of <td>. You can try this XPath instead :
//td[.='seleniumtest2312']/following-sibling::td[3]/a/span[.='Enable']