Unable to locate element inside a table using xpath with selenium - c#

I want to click on a button inside my table each row has a update button I want to click on a specfic button inside my table.
Here is a what my table looks like:
<table _ngcontent-vhp-c82="" datatable="" id="dtOptionsComments" class="display table table-striped table-bordered dt-responsive dataTable dtr-inline" aria-describedby="dtOptionsComments_info" style="width: 100%;" width="100%">
<thead _ngcontent-vhp-c82="">
<tr _ngcontent-vhp-c82="">
<th _ngcontent-vhp-c82="" class="no-marking sorting_disabled" rowspan="1" colspan="1" style="width: 50.4px;" aria-label=""></th>
<th _ngcontent-vhp-c82="" class="sorting sorting_asc" tabindex="0" aria-controls="dtOptionsComments" rowspan="1" colspan="1" style="width: 1109.4px;" aria-label="Comment.Comment Shipping.ShippingDatatable.aria.sortDescending" aria-sort="ascending">Comentario Shipping.Shipping</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td class="no-marking dtr-control">
<a href="javascript:void(0);">
<span data-toggle="modal" data-target="#update-modal" update-comment-text="6 MESES DE GARANTIA" update-comment-id="5" class="material-icons md-18 clickable"> edit </span>
</a>
<a href="javascript:void(0);">
<span data-toggle="modal" data-target="#delete-modal" delete-comment-id="5" class="material-icons clickable">delete</span>
</a>
</td>
<td class="sorting_1">6 MESES DE GARANTIA</td>
</tr>
<!-- MORE ROWS!!! -->
</tbody>
<tfoot _ngcontent-vhp-c82="">
<tr _ngcontent-vhp-c82="">
<td _ngcontent-vhp-c82="" class="no-marking" rowspan="1" colspan="1">
<a _ngcontent-vhp-c82="" href="javascript:void(0);">
<span _ngcontent-vhp-c82="" class="material-icons clickable"> add_box </span>
</a>
</td>
<td _ngcontent-vhp-c82="" rowspan="1" colspan="1">
<input _ngcontent-vhp-c82="" formcontrolname="addComment" type="text" id="addComment" name="addComment" class="form-control ng-untouched ng-pristine ng-invalid">
<!---->
</td>
</tr>
</tfoot>
</table>
Here is my code trials:
IWebElement btnUpdate = _driver.FindElement(By.XPath("//*[update-comment-id='" + commentAction.GetLastQuoteInsertId().ToString() + "']"));
btnUpdate.Click();
I have validated that the function GetLastQuoteInsertId returns the proper value
Why is my xPath selector wrong how can I fix it thank you for your help.

You were almost there. While considering a xpath the attribute_name should be always preceded by a # sign.
Additionally to make the xpath more canonical as the element is a <span> element you can mention //span to start the xpath.
Effectively, your line of code will be:
IWebElement btnUpdate = _driver.FindElement(By.XPath("//span[#update-comment-id='" + commentAction.GetLastQuoteInsertId().ToString() + "']"));
btnUpdate.Click();

Related

Unable to click button which is inside iframe and table in Selenium webDriver with c#

I am new to c# and I am working with Selenium chrome webdriver in c#. I am trying to click button which inside table. I am not able to identify and click button.The hierarchy of button is (Page > PopOver > PopOverFrame > Table1 > Table2 > Button to click )
Any help with this would be much appreciated thank you.
my code is :
*/
// Closing old tab, keeping control in new tab and trying to perform click operation
var currentWindow = BaseTest.Driver.CurrentWindowHandle;
var availableWindows = new List<string>(BaseTest.Driver.WindowHandles);
foreach (string mywindows in availableWindows)
{
if (mywindows != currentWindow)
{
Driver.SwitchTo().Window(mywindows).Close();
}
else
{
Driver.SwitchTo().Window(currentWindow);
// performing click action on RUN REPORT button
IWebElement popOver = Driver.FindElement(By.Id("popOver"));
IWebElement popOverFrame = popOver.FindElement(By.Id("popOverFrame"))
IWebElement table1 = popOverFrame.FindElement(By.XPath("//*[#id='form1']/table"));
IWebElement Table2 = table1.FindElement(By.XPath("//*[#id='tblReport']"));
Table2.FindElement(By.Id("contentPlaceholder_btnPrint")).Click();
}
}
Please refer to attached screenshot of what html page looks like.
<html xmlns="http://www.w3.org/1999/xhtml" ><title>
<div id="divReportHeader">
<span id="contentPlaceholder_lblReportDescHeader" class="reportHeader">Description</span>
<br>
<span id="contentPlaceholder_lblReportDescription">Offer</span>
</div>
<table>
<tbody><tr>
<td style="vertical-align: top;">
<div style="margin: 2px; padding: 8px;">
<table width="1000px" style="border-spacing: 0; padding: 0" id="tblReport">
<tbody><tr>
<td></td>
</tr>
<tr>
<td><div id="contentPlaceholder_generalInformation" class="reportHeader">Information</div></td>
</tr>
<tr>
<td>
<span id="contentPlaceholder_lblRptInfo">
This report may require more information. Click "Run Report" to view the report inline.
</span>
<br>
</td>
</tr>
<tr>
<td></td>
</tr>
<tr>
</tr>
<tr>
</tr>
<tr>
<td>
<span id="contentPlaceholder_lblfrom">abc </span>
<select name="ctl00$contentPlaceholder$ddlfromYear" onchange="javascript:setTimeout('__doPostBack(\'ctl00$contentPlaceholder$ddlfromYear\',\'\')', 0)" id="contentPlaceholder_ddlfromYear">
</select>
<span id="contentPlaceholder_lblFilter5" style="display: block; margin-top: 10px; margin-bottom: 4px;">Additional columns to be included:<br>Due to potential page size limitations, additional custom columns should only be selected when intending to view inline or export as Excel.</span>
<input type="submit" name="ctl00$contentPlaceholder$btnSelectAll" value="Select All" id="contentPlaceholder_btnSelectAll" class="roundedButton">
<input type="submit" name="ctl00$contentPlaceholder$btnDeselectAll" value="Deselect All" id="contentPlaceholder_btnDeselectAll" class="roundedButton" style="margin-bottom:5px;">
<table id="chkList2" class="correctCheckboxes">
<tbody><tr>hkList2_0" value="Select"><label for="chkList2_0">Help</label></td>
</tr>
</tbody></table>
</td>
</tr>
<tr>
<td>
<br>
<div id="contentPlaceholder_parameterNotes" class="reportHeader">
Notes
</div>
<br>
<span id="contentPlaceholder_txtParameterNotes">None</span>
</td>
</tr>
<tr>
<td>
</td>
</tr>
<tr>
<td>
<input type="submit" name="ctl00$contentPlaceholder$btnPrint" value="RUN REPORT" onclick="disable('contentPlaceholder_btnPrint');__doPostBack('ctl00$contentPlaceholder$btnPrint','');" id="contentPlaceholder_btnPrint" class="roundedButton" style="width:130px;">
</td>
</tr>
</tbody></table>
</div></td>
</tr>
</tbody></table>
<iframe id="ifrmDownload" style="display: none;"></iframe>
<iframe id="ifrmStatus" src="statuscheck.aspx" style="display: none;"></iframe>
</form>
</body></html>
Try this:
//remove this line IWebElement popOver = Driver.FindElement(By.Id("popOver"));
Driver.SwitchTo().DefaultContent();
IWebElement popOverFrame = Driver.FindElement(By.Id("popOverFrame"))
Driver.SwitchTo().Frame(popOverFrame);
//remove this line IWebElement table1 = popOverFrame.FindElement(By.XPath("//*[#id='form1']/table"));
IWebElement Table2 = Driver.FindElement(By.XPath("//*[#id='tblReport']"));
Table2.FindElement(By.Id("contentPlaceholder_btnPrint")).Click();
Try the below one:
driver.switchTo().frame(driver.findElement(By.xpath(iframeXpath)));
And once the operations are completed inside iFrame, switch back to default content.
driver.switchTo().defaultContent();

Unable to locate input element under div in Selenium

I am trying to located input element "mpcname" in js file which is under div and table elements.
The code I have written to locate the element is as follows but is not detecting the Input element with the name mpcName in it.
Driver.FindElement(By.XPath(".//input[contains(.,'mpcName')]"));
The HTML code looks like below.
<div enable-tooltips="" class="ng-scope">
<form name="InstanceDetailForm" ng-submit="doApply()" novalidate="" class="ng-pristine ng-invalid ng-invalid-required ng-valid-unnamed ng-valid-maxlength ng-valid-not-integer">
<table class="table-condensed" style="width: 100%;">
<tbody><tr>
<td></td>
</tr>
<tr>
<td>
<table style="width: 100%;">
<tbody><tr>
<td class="pull-left">
<h4 class="ng-binding">Controller Configuration<b ng-show="InstanceIsDirty()" class="ng-hide"> *</b></h4>
</td>
<td class="pull-right">
<button id="btnApply" type="submit" class="btn btn-primary" data-toggle="tooltip" data-placement="bottom" title="Save an APC instance">
<span class="glyphicon glyphicon-save"></span> Apply
</button>
<button id="btnExportConfig" type="button" class="btn btn-primary" ng-click="goToInstanceList()" data-toggle="tooltip" data-placement="bottom" title="Cancel save and redirect to APC instance list page.">
<span class="glyphicon glyphicon-remove"></span> Cancel
</button>
</td>
</tr>
</tbody></table>
</td>
</tr>
<tr>
<td>
<table style="width: 100%" class="table-condensed table-striped table-bordered">
<tbody><tr>
<td>Name</td>
<td>
**<input id="mpcname" name="mpcname" ng-model="mpcname" value="" ng-required="true" is-named="" size="50" autocomplete="off" ng-disabled="!allowEditInstanceName(mpcname,state)" class="ng-pristine ng-empty ng-invalid ng-invalid-required ng-valid-unnamed ng-touched" required="required" type="text">**
With xpath ".//input[contains(.,'mpcName')]" you are actually looking for an <input> element with text mpcName. mpcName is actually the id (and name and ng-model) attribute. You can use
Driver.FindElement(By.Id("mpcName"));
Or
Driver.FindElement(By.Name("mpcName"));
following code may be helpful
Driver.FindElement(By.XPath(".//input[contains(#id,'mpc')]");
Driver.FindElement(By.XPath(".//input[contains(#class,'ng-pris')]");

Move a button to another tr programatically

I have a login form designed using a <table> in an ASP.NET application.
A client is asking to modify the layout by moving around a few UI controls. They want to move the Forgot Password link below the Login button so they are on separate lines.
However, for all other clients, everything needs to remain the same, so if any CSS or style changes need to be done, I would need to do it programatically. Are there any easy ways to do this?
I am trying to avoid creating duplicate user controls to fit the layout they want, and then hide/show controls, depending on the client.
<table style="TABLE-LAYOUT: fixed; WIDTH: 370px;">
<COLGROUP>
<COL width="120px">
<COL width="250px">
</COLGROUP>
<tr>
<td>Username:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" name="password"></td>
</tr>
<tr>
<td> </td>
<td>
<table>
<tr>
<td style="text-align:right;">
Forgot Password
</td>
<td>
<button type="submit">Login</button>
</td>
</tr>
</table>
</td>
</tr>
</table>
JSfiddle Demo
EDIT: jQuery NOT allowed. JavaScript OK.
Jquery can do this for you. the code might look something like this:
$("#toggle").click(toggleButtonPosition);
function toggleButtonPosition() {
var buttonRow = $("button[type=submit]").closest("tr"), buttonCell, newTR, tr;
if (buttonRow.find("td").length === 2) {
// the table is in its initial configuration. Need to extract the button cell and add it to a new row
buttonCell = buttonRow.find("button").closest("td");
buttonCell.remove();
newTR = $("<tr />").append(buttonCell);
newTR.insertBefore(buttonRow);
} else {
// Reverse the process
buttonCell = buttonRow.find("td");
tr = buttonRow.siblings().first();
tr.append(buttonCell);
buttonRow.remove();
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table style="TABLE-LAYOUT: fixed; WIDTH: 370px;">
<COLGROUP>
<COL width="120px">
<COL width="250px">
</COLGROUP>
<tr>
<td>Username:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" name="password"></td>
</tr>
<tr>
<td> </td>
<td>
<table>
<tr>
<td style="text-align:right;">
Forgot Password
</td>
<td>
<button type="submit">Login</button>
</td>
</tr>
</table>
</td>
</tr>
</table>
Toggle Button Position
You don't need to duplicate the layout. You can do it by adding/ removing the css class depending upon the client using jquery or javascript.
$(document).ready(function(){
var client1 = true; // variable to store client
if(client1){
$('button').removeClass('client2').addClass('client1');
}
else {
$('button').removeClass('client1').addClass('client2');
}});
Css classes to be added/removed from the button depending upon the client. I have put 'client1' css class on the button.
button.client1{display:block;margin:0 auto}
button.client2{float:right;margin-left:4px}
Html code as follows:
<table style="TABLE-LAYOUT: fixed; WIDTH: 370px;">
<COLGROUP>
<COL width="120px">
<COL width="250px">
</COLGROUP>
<tr>
<td>Username:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" name="password"></td>
</tr>
<tr>
<td> </td>
<td>
<table>
<tr>
<td>
<button type="submit" class='client1'>Login</button>
Forgot Password
</td>
</tr>
</table>
</td>
</tr>
You could use jQuery to archive that.
Here is an example of moving the button one step back
$(document).ready(function() {
var loginLayOut = $(".login"); // login Table
var btnTd = loginLayOut.find("button").parent(); // button container, in this case its td
var tbody = btnTd.parent().parent(); // we get the tbody by moving two step back
var tr = $("<tr></tr>"); // create a new tr
tr.append(btnTd); // append the td to the new created tr
tbody.prepend(tr); // insert the tr to the tbody at position 0
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class='login' style="TABLE-LAYOUT: fixed; WIDTH: 370px;">
<COLGROUP>
<COL width="120px">
<COL width="250px">
</COLGROUP>
<tr>
<td>Username:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" name="password"></td>
</tr>
<tr>
<td> </td>
<td>
<table>
<tr>
<td style="text-align:right;">
Forgot Password
</td>
<td>
<button type="submit">Login</button>
</td>
</tr>
</table>
</td>
</tr>
</table>

Handling tables in c# selenium

<table cellpadding="0" cellspacing="0" onclick="" style="width: 1345px;">
<tbody>
<tr id="item_tcm:222-382904-131104" title="2. Publish to WIP (tcm:222-382904-131104)" class="item even" c:drawn="true">
<td class="col0 icon odd" value="T131104L0P0">
<div class="icon" style="background-image: url("/WebUI/Editors/CME/Themes/Carbon2/icon_v7.1.0.66.55_.png?name=T131104L0P0&size=16");"></div>
</td>
<td class="col1 even">
<div class="text">2. Publish to WIP</div>
</td>
<td class="col2 odd">
<div class="text">JH Anchor link 2</div>
</td>
<td class="col3 even">
<div class="text">S070 Public Site US English</div>
</td>
<td class="col4 odd" value="2015-12-23T14:41:04">
<div class="text">12/23/2015 2:41 PM</div>
</td>
<td class="col5 even">
<div class="text">NT AUTHORITY\SYSTEM</div>
</td>
<td class="col6 odd" value="">
<div class="text">
<span style="color: #f00"></span>
</div>
</td>
<td class="col7 even" value="16">
<div class="text">Suspended</div>
</td>
<td class="col8 odd">
<div class="text">NT AUTHORITY\SYSTEM</div>
</td>
<td class="col9 even">
<div class="text">Publishing Failed</div>
</td>
</tr>
<tr id="item_tcm:222-382901-131104" title="2. Publish to WIP (tcm:222-382901-131104)" class="item even" c:drawn="true">
<td class="col0 icon odd" value="T131104L0P0">
<div class="icon" style="background-image: url("/WebUI/Editors/CME/Themes/Carbon2/icon_v7.1.0.66.55_.png?name=T131104L0P0&size=16");"></div>
</td>
<td class="col1 even">
<div class="text">2. Publish to WIP</div>
</td>
<td class="col2 odd">
<div class="text">JH_anchor link</div>
</td>
<td class="col3 even">
<div class="text">S070 Public Site US English</div>
</td>
<td class="col4 odd" value="2015-12-23T14:17:51">
<div class="text">12/23/2015 2:17 PM</div>
</td>
<td class="col5 even">
<div class="text">NT AUTHORITY\SYSTEM</div>
</td>
<td class="col6 odd" value="">
<div class="text">
<span style="color: #f00"></span>
</div>
</td>
<td class="col7 even" value="16">
<div class="text">Suspended</div>
</td>
<td class="col8 odd">
<div class="text">NT AUTHORITY\SYSTEM</div>
</td>
<td class="col9 even">
<div class="text">Publishing Failed</div>
</td>
</tr>
.....
</tbody>
</table>
I have collection of rows. Inside each row i have 10 columns(td). I want to iterate to each row. For each row I want to get the 8th and 10 th column.
Note :- The test case will get Fail if the 8th column value is "Suspended" and 10th column value is "Publishing Failed" or else the test case would get Pass
I tried the below logic
IWebElement tableElement = driver.FindElement(By.XPath("/html/body/table"));
IList<IWebElement> tableRow = tableElement.FindElements(By.TagName("tr"));
foreach (var item in tableRow)
{
}
I'm not sure how to proceed further. Could anyone help me? Thanks in advance
Your logic is good:
IWebElement tableElement = driver.FindElement(By.XPath("/html/body/table"));
IList<IWebElement> tableRow = tableElement.FindElements(By.TagName("tr"));
IList<IWebElement> rowTD;
foreach (IWebElement row in tableRow)
{
rowTD = row.FindElements(By.TagName("td"));
if(rowTD.Count > 9)
{
if(rowTD[8].Text.Equals("Suspended") && rowTD[10].Text.Equals("Publishing Failed");
//test failed
}
}
What if you would just try to find the rows having the 8th column value "Suspended" and 10th column value "Publishing Failed":
IList<IWebElement> rows = tableElement.FindElements(By.TagName("//table//tr[td[8]/div = 'Suspended' and td[10]/div = 'Publishing Failed']"));
Then, you can fail the test if rows list is not empty.
Try this:
foreach (var item in tableRow)
{
IWebElement column7 = item.FindElement(By.CssSelector("[class*='col7']"));
IWebElement column9 = item.FindElement(By.CssSelector("[class*='col9']"));
if (column7.Text.Equals("Suspended") && column9.Text.Equals("Publishing Failed"))
Assert.Fail("Failed because column8 is 'Suspended' and column10 is 'Publishing Failed'");
else
Assert.Pass();
}
Please note that this code will stop testing when it has found the "Suspended" and "Publishing Failed". If you want to continue testing until the final row in table, you have to use multiple assertions. NUnit, is it possible to continue executing test after Assert fails?

Selenium C# Any alternative way of finding if element is present on browser other than IsElementPresent(By by)

I have a scenario where there are three distinct html parent elements
<tr class="s3288" data-id="s3288">
<tr class="s3288" data-id="s3288">
<tr class="s1" data-id="s1">
Two these elements have a child element
<a class="gylink mapvl" title="No Map Values" href="#">Map Values</a>
but third one does not have this child element.
in my test scenario, I have to click this child element if present.
Is there any function by which i can detect if this element is present?
I have used following function bug for some reason it does not work properly
function i used
protected bool IsElementPresent(By by)
{
try
{
wait.Until(drv => (drv.FindElement(by)));
return true;
}
catch
{
return false;
}
}
Here is how the html of browser looks like
<tr class="s1" data-id="s1">
<td class="ftc1">
<td class="ftc2">
<td class="ftc3 valuecol">
<td class="ftc4 headcol">
<ul class="multihead">
<li>
<input class="txtinput chghead headcolfield pholder changit requiref" type="text" placeholder="Enter Column Header Value" validate="{required: true, messages: {required: 'Required'}}" name="th_s1_0_true" value="">
<a class="gylink mapvl" title="No Map Values" href="#">Map Values</a>
</li>
</ul>
</td>
<td class="ftc5 deletecol"> </td>
</tr>
<tr class="s3288" data-id="s3288">
<td class="ftc1">
<td class="ftc2">
<td class="ftc3 valuecol">
<td class="ftc4 headcol">
<ul class="multihead">
<li>
<input class="txtinput chghead headcolfield pholder changit requiref" type="text" placeholder="Enter Column Header Value" validate="{required: true, messages: {required: 'Required'}}" name="th_s3288_0_true" value="">
<a class="gylink mapvl" title="No Map Values" href="#">Map Values</a>
</li>
</ul>
</td>
<td class="ftc5 deletecol"> </td>
</tr>
<tr class="t1" data-id="t1">
<td class="ftc1">
<td class="ftc2">
<td class="ftc3 valuecol">
<td class="ftc4 headcol">
<ul class="multihead">
<li>
<input class="txtinput chghead headcolfield pholder changit requiref" type="text" placeholder="Enter Column Header Value" validate="{required: true, messages: {required: 'Required'}}" name="th_t1_0_true" value="">
</li>
</ul>
</td>
<td class="ftc5 deletecol"> </td>
</tr>

Categories

Resources