Avoiding StaleElementReferenceException when dealing with a dynamic table in Selenium - c#

I am having trouble dealing with a dynamic table.
This is our table:
<table class="table" style="min-width: 870px; max-width: 870px">
<colgroup>
<col style="width: 30px">
<col style="width: 200px">
<col style="width: 80px">
<col style="width: 70px">
<col style="width: 200px">
<col style="width: 50px">
<col style="width: 50px">
</colgroup>
<tbody>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
</tbody>
<tfoot>
<tr>
<td class="text-right" colspan="3">Media del grupo</td>
<td class="text-center media" colspan="1">1</td>
<td class="text-center noeditable" colspan="1"></td>
<td class="text-center media" colspan="1"></td>
<td class="text-center media" colspan="1"></td>
</tr>
</tfoot>
</table>
Each <tr> contains the following:
<tr>
<td class="indice">1</td>
<td id="accion-1-alumno-0" data-tooltip="" class="has-tip titulo" title="">Ap_Alumno_1ESOA_1, Nb_Alumno_1ESOA_1</td>
<td data-tooltip="" class="has-tip titulo" data-selector="tooltipdtk00g" title="">1ESOA</td>
<td class="nota relative " style="text-align:center; color: #ed1c24!important">
<div id="accion-1-celda-0-0-0" class="elemento comentarios">1</div>
</td>
<td class="nota relative " style="text-align:center; color: #000000!important">
<div class="elemento comentarios"><span id="accion-1-editar-1-0" class="block left ellipsis span comentario" title=""></span><span id="accion-1-prismaticos-1-0" class="glyphicons glyph_observaciones observacion right"></span></div>
</td>
<td class="nota relative " style="text-align:center; color: #000000!important">
<div id="accion-1-celda-2-0-0" class="elemento comentarios"></div>
</td>
<td class="nota relative " style="text-align:center; color: #000000!important">
<div id="accion-1-celda-3-0-0" class="elemento comentarios"></div>
</td>
</tr>
We are interested in the elements
<div id="accion-1-celda-0-0-0" class="elemento comentarios">1</div>
which are being added to a IList<IWebElement>
However, when trying to SendKeys to the element, the first time it will work correctly, however the second time will always fail with StaleElementReferenceException, this is because the previous element (the first one) has changed and with it the page DOM has also changed.
I am trying to find a way to find the element again if StaleElementReferenceException is thrown.
So far, both this methods have failed:
Method one
public virtual void Introducir_NotasAlumnos(string nota)
{
IList<IWebElement> divNota = tablaNotas
.Select((element, index) => element.FindElement(By.Id("accion-1-celda-0-" + index + "-0")))
.ToList();
divNota.ToList().ForEach(element => Introducir_Nota(element, nota));
}
Method two
public virtual void Introducir_NotasAlumnos(string nota)
{
int index = 0;
foreach (IWebElement element in tablaNotas)
{
By locator = By.Id("accion-1-celda-0-" + index + "-0");
Introducir_Nota(element.FindElement(locator), nota);
index++;
}
}
Thanks for your time.

Here your locators:
table: .table tbody > tr
table row by index: .table tbody > tr:nth-child(1)
and you method (java code):
int size = driver.findElements(By.cssSelector(".table tbody > tr")).size();
for (int i = 0; i < size; i++) {
WebElement row = driver.findElement(By.cssSelector(".table tbody > tr:nth-child(" + i + ")"));
By locator = By.id("accion-1-celda-0-" + i + "-0");
Introducir_Nota(row.findElement(locator), nota);
}
You have certain count of rows and you find row element independently, should not throw StaleElementReferenceException exception.
Here shorter version:
int size = driver.findElements(By.cssSelector(".table tbody > tr")).size();
for (int i = 0; i < size; i++) {
Introducir_Nota(row.findElement(By.cssSelector("#accion-1-celda-0-" + i + "-0")), nota);
}

Related

Unable to locate element inside a table using xpath with selenium

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();

How do I sort a html table by clicking on column header in razor page?

I have this code below of my razor page that contains some information to be displayed in a table. I have named the headers as below and the data that are to be displayed under the headers. Now I do not know how to sort the data in ascending and descending order when I click on the column header respectively? I have a code bind razor.cs page to do some basic operations. How do I achieve this sorting function?
Here is my code below in my razor page:
<div class="col-12 col-xl-10 mt-2">
<div class="row">
<div class="col">
<div class="table-responsive">
<table class="table table-hover table-sm" id="item_list">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Amount</th>
<th scope="col">Profit</th>
<th scope="col">Registration</th>
<th scopt="col">Description</th>
<th scopt="col">Particulars</th>
<th scopt="col"></th>
</tr>
</thead>
<tbody>
#for(int i = 0; i < ViewModel.Count; i++)
{
var item = ViewModel[i];
var j = i;
if(item.IsDeleted)
{
continue;
}
<tr id="item_list_row_#j">
<td style="width: 20%;">#item.Pricing.Name</td>
<td style="width: 10%">#item.Pricing.RiskPricing.Amount</td>
<td style="width: 10%">#(item.Pricing.RiskPricing.Profit > 0? item.Pricing.RiskPricing.Profit : (item.MotorCoverPricing.RiskPricing.Amount * (item.Pricing.RiskPricing.Rate/100)))</td>
<td style="width: 10%">#item.Detail.Registration</td>
<td style="width: 10%">#item.Detail.Description</td>
<td style="width: 10%">#item.Detail.Particulars</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>

adding extra td element

I have this table.
<tr>
<td style="padding-left: 25pt;"><font style="background-color: white" color="black"> Hello</font></td>
<td style="padding-left: 25pt;"><font style="background-color: white" color="black">Bye</font></td>
</tr>
but I want to add an extra td element at the end so it looks like this:
<tr>
<td style="padding-left: 25pt;"><font style="background-color: white" color="black"> Hello</font></td>
<td style="padding-left: 25pt;"><font style="background-color: white" color="black">Bye</font></td>
<td>1</td>
</tr>
I am using htmlAgilityPack, but the examples online that I have looked don't exactly help.
i hope this may help you
string data = #"<tr><td style='padding-left: 25pt;'><font style='background-color: white' color='black'> Hello</font></td><td style='padding-left: 25pt;'><font style='background-color: white' color='black'>Bye</font></td></tr>";
string rowToAppend = "<td>1</td>";
var html = new HtmlAgilityPack.HtmlDocument();
html.LoadHtml(data);
var table = html.DocumentNode.SelectNodes("tr").FirstOrDefault();
var node = HtmlNode.CreateNode(rowToAppend);
table.AppendChild(node);
data += table.OuterHtml;

how to split table rows in ASP .Net C#

I have HTML Code in a string named gridHTML
<html>
<body>
<style>a{text-decoration:none; color: black;} th { border: solid thin; }
td{text-align: center;vertical-align: middle;font-family: Arial;font-size: 8pt; height: 50px;
border-width: 1px;border-left-style: solid;border-right-style: solid;}
table { border-collapse: collapse; } tr:nth-child(1) { border: solid thin; border-width: 2px;}
tr{ border: solid thin; border-style: dashed solid dashed solid;}
</style>
<div>
<table >
<tr class='leftColumnTableHeadO' align='center' style='font-family: Arial; font-size: 8pt; font-weight: normal; width: 100px;'>
<th scope='col'>TM No.</th>
<th scope='col' style='width: 83px;'>Filing Date</th>
<th scope='col'>TradeMark</th>
<th scope='col'>Class</th>
<th scope='col'>Jr#</th>
<th scope='col'>Applicant</th>
<th scope='col'>Agent / Attorney</th>
<th scope='col'>Status</th>
<th scope='col'>City</th>
<th scope='col'>Logo</th>
</tr>
<tr class='lightGrayBg' >
<td ><a title='View Report' class='calBtn' href='javascript:__doPostBack('ctl00$MainContent$grdTradeMarkNumber$ctl02$ctl00','')'>38255</a> </td>
<td ><span id='MainContent_grdTradeMarkNumber_lblFilingDate_0'>09-12-1962</span> </td>
<td >IMIDAN</td>
<td >5</td>
<td >158</td>
<td >test</td>
<td >test</td>
<td >Registered</td>
<td >DELWARE</td>
<td ></td>
</tr>
<tr >
<td ><a title='View Report' class='calBtn' href='javascript:__doPostBack('ctl00$MainContent$grdTradeMarkNumber$ctl03$ctl00','')'>188389</a> </td>
<td ><span id='MainContent_grdTradeMarkNumber_lblFilingDate_1'>09-09-2003</span> </td>
<td >RAND</td>
<td >16</td>
<td >682</td>
<td >Ttest </td>
<td >test </td>
<td >Advertised</td>
<td >CALIFORNIA</td>
<td ></td>
</tr>
<tr class='lightGrayBg' >
<td ><a title='View Report' class='calBtn' href='javascript:__doPostBack('ctl00$MainContent$grdTradeMarkNumber$ctl04$ctl00','')'>207063</a> </td>
<td ><span id='MainContent_grdTradeMarkNumber_lblFilingDate_2'>11-03-2005</span> </td>
<td >FP DIESEL</td>
<td >7</td>
<td >690</td>
<td >testtest</td>
<td >testtest</td>
<td >Advertised</td>
<td >-</td>
<td ></td>
</tr>
</table>
</div>
</body>
</html>
I want to get all rows separately in a list
i am using split method to do this
List<string> rows = gridHTML.Split(new string[] { "<tr" }, StringSplitOptions.None).ToList();
but the problem is when i look into the list "<td" is removed
Is there any (other) way to get all rows in a list ?
For this one, you could use Linq To XML easily. ie:
var rows = XElement.Parse(gridHTML).Descendants("tr");
var cells = rows.Elements("td");
var cellContentsAsString = cells.Select(c => (string)c);
etc.
You should not use string methods (or regex) to parse HTML, i recommend HtmlAgilityPack:
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(gridHTML);
List<HtmlNode> trList = doc.DocumentNode.Descendants("tr").ToList();
Since it seems that you want to load this table data into a collection, maybe following approach is better for your requirement. It will load the rows and cells into a DataTable, even the DataColumns are initialized correctly with the table-header values:
DataTable table = new DataTable();
bool firstRowContainsHeader = true;
var tableRows = doc.DocumentNode.Descendants("tr");
var tableData = tableRows.Skip(firstRowContainsHeader ? 1 : 0)
.Select(row => row.Descendants("td")
.Select((cell, index) => new { row, cell, index, cell.InnerText })
.ToList());
var headerCells = tableRows.First().Descendants()
.Where(n => n.Name == "td" || n.Name == "th");
int columnIndex = 0;
foreach (HtmlNode cell in headerCells)
{
string colName = firstRowContainsHeader
? cell.InnerText
: String.Format("Column {0}", (++columnIndex).ToString());
table.Columns.Add(colName, typeof(string));
}
foreach (var rowCells in tableData)
{
DataRow row = table.Rows.Add();
for (int i = 0; i < Math.Min(rowCells.Count, table.Columns.Count); i++)
{
row.SetField(i, rowCells[i].InnerText);
}
}

How do I access the cell content of a pure html table from asp.net

I have a table:
<table id="trTable" runat="server" clientidmode="Static">
<thead>
<tr>
<th style="display:none">
ID
</th>
<th style="width: 112px">
Item
</th>
<th style="width: 40px; text-align: left">
Price
</th>
<th style="width: 24px; text-align: center">
</th>
<th style="width: 26px; text-align: center">
Qty
</th>
<th style="width: 24px; text-align: center">
</th>
<th style="width: 40px; text-align: right">
Total
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
and new rows are added to it with jQuery:
var newRow = $("<tr> <td class='drinkID' style='display:none'>" + drinkID + "</td> <td class='drinkName'>" + dName + "</td> <td class='drinkPrice'>" + dPrice + "</td> <td style='text-align:center'><input id='Button' type='button' value='<' class='minusButton' /> </td> <td class='drinkQty'>1</td> <td style='text-align:center'><input id='Button' type='button' value='>' class=\"plusButton\" /></td> <td class='drinkTotal'>" + dPrice + "</td> </tr>");
How do I access the content of the cells using asp.net?
I am using:
foreach (HtmlTableRow row in trTable.Rows)
{
Response.Write("RowDetails:" + row.Cells[1].InnerHtml);
}
But the response.write just outputs:
RowDetails: Item
How come it doesn't get the cell contents?
What you change on the html struct page, on client side, is not send back on the server, and sever know nothing about.
With other words, what is on the page, is not fully sanded back on the server.
To solve this, you can make at the same time two edits, one on what user see and one hidden on a hidden input, to post back to the server the changes and recreate the table on the server side.
Hope this make scene.
You can also use Microsoft Ajax enabling partial rendering on in the row. This will do the same postback effect but will only send the afffected content to the client side.

Categories

Resources