Cypress to loop to td values and get a specific tr to sum (TR length will be dynamic) - while-loop

I know it is not a very good option to loop and use a conditional test. However it was the only way I kinda managed to do so.
My issue is to loop correctly all the rows and wrap value in the column 10 when it is available.
I have a dynamic table were td=12 sometimes and td=4 another times according with the index tr. like in the images
when the td is = 12 the column 10 will have a valeu (the value I need to wrap and sum)
I wrote a test that is partially working and I am not sure why it does not get the value of column 10 when the td length =12 and when the row has length = 4 I need it to loop to the next line till it finds the line that has 10 columns.
function textAmountToNumber(textValue) {
const text = textValue.replace(/[£,]/g, '').trim()
return Number(text)
}
let haselemente = true
let index = 0
let sum = 0
const amountCol = 10
cy.get('tbody')
.find('tr')
.then((e) => {
while (haselemente) {
const length = e.length - 1
if (index < length) {
cy.get('tbody')
.find('tr')
.eq(index)
.then(($td) => {
if ($td.find('td').length < amountCol) {
} else {
cy.get('tbody')
.find('tr')
.eq(index)
.find('td')
.eq(amountCol)
.each(($el, index, $list) => {
cy.wrap($el).then((element) => {
sum += textAmountToNumber(element.text())
cy.log(sum)
})
})
}
})
index++
} else {
haselemente = false
}
}
})
It break in the second row due to td = 4.
It does loops all the rows, but when the column <= 10 it still get the value from the index = 0 the first line of the listl.
The reason to sum it, is to compare the total value to another element.
here is the CSS
<table data-testid="section-list-card" class="table table-sm table-nowrap card-table table-hover linked-transactions-table"><thead><tr><!----> <th class="sortable"><div class="sortable">
COLUMN 1
<i class="uil uil-sort" style="font-size: 12px;"></i></div></th><th class="sortable"><div class="sortable">
COLUMN 2
<i class="uil uil-sort" style="font-size: 12px;"></i></div></th><th class="sortable" style="width: 9%;"><div class="sortable">
COLUMN 3
<i class="uil uil-sort" style="font-size: 12px;"></i></div></th><th class="">
COLUMN 4
</th><th class="sortable" style="width: 100px;"><div class="sortable">
COLUMN 5
<i class="uil uil-sort" style="font-size: 12px;"></i></div></th><th class="" style="width: 10%; text-align: right;">
COLUMN 6
</th><th class="" style="width: 10%;">
COLUMN 7
</th><th class="" style="width: 10%;">
COLUMN 8
</th><th class="" style="width: 10%;">
COLUMN 9
</th><th class="" style="width: 10%;">
COLUMN 10
</th> <th style="width: 5%;"></th></tr></thead> <tbody class="list"><tr role="row"><!----> <td><button type="button" class="btn row-collapse-expand-btn btn-link btn-sm"><i class="uil uil-angle-down" style="font-size: 20px;"></i></button> <a href="/quotes/96f37a8b-20b3-4b7c-ba1b-129db2c09bcb/show" class="" target="_blank">
00001
</a> <!----> <!----> <!----></td><td><a href="/clients/96f37a8a-8e46-4789-8440-5f07dcd18586/show" class="" target="_blank"><span>
S Smith
</span></a> <!----> <!----> <!----></td><td> <!---->
2022-10-17
</td><td><div class="d-flex align-items-center"><div>
Amount
</div></div> <!----> <!----></td><td> <!----> <!----></td><td><div class="text-right">
£ 4,000.56
</div> <!----> <!----></td><td><span>
£ 0.00
</span> <!----> <!----></td><td><span>
£ 0.00
</span> <!----> <!----></td><td><span>
£ 0.00
</span> <!----> <!----></td><td><span>
£ 4,000.56
</span> <!----> <!----></td> <td class="text-right"></td></tr> <tr class="row-tight" style=""><td colspan="3"></td> <td colspan="2"><div class="d-flex align-items-center"><i class="mr-2 uil uil-corner-down-right" style="font-size: 12px;"></i>
Gross Premium (premium)
<!----> <div class="ml-1 con-separator-left"><a href="/entities/91d519ef-c04f-48c5-9902-2a5dd0227e59/show" class="">
MuS
</a></div></div> </td> <td class="text-right"><span class="balance-text text-success">
£ 5,064.00
</span> </td> <td colspan="4"></td></tr><tr class="row-tight" style=""><td colspan="3"></td> <td colspan="2"><div class="d-flex align-items-center"><i class="mr-2 uil uil-corner-down-right" style="font-size: 12px;"></i>
Fee
<!----> <div class="ml-1 con-separator-left"><a href="/entities/2316df55-9aa6-4777-9587-ec77939fa1db/show" class="">
M Smith
</a></div></div> </td> <td class="text-right"><span class="balance-text text-danger">
£ -354.48
</span> </td> <td colspan="4"></td></tr><tr class="row-tight" style=""><td colspan="3"></td> <td colspan="2"><div class="d-flex align-items-center"><i class="mr-2 uil uil-corner-down-right" style="font-size: 12px;"></i>
Commision
<!----> <div class="ml-1 con-separator-left"><a href="/entities/d935c1b4-e733-4c22-bc40-638d25160796/show" class="">
KKK
</a></div></div> </td> <td class="text-right"><span class="balance-text text-danger">
£ -708.96
</span> </td> <td colspan="4"></td></tr><tr role="row"><!----> <td><button type="button" class="btn row-collapse-expand-btn btn-link btn-sm"><i class="uil uil-angle-down" style="font-size: 20px;"></i></button> <a href="/quotes/96f37a8b-20b3-4b7c-ba1b-129db2c09bcb/show" class="" target="_blank">
00001
</a> <!----> <!----> <!----></td><td><a href="/clients/96f37a8a-8e46-4789-8440-5f07dcd18586/show" class="" target="_blank"><span>
S Smith
</span></a> <!----> <!----> <!----></td><td> <!---->
2022-10-17
</td><td><div class="d-flex align-items-center"><div>
Amount
</div></div> <!----> <!----></td><td> <!----> <!----></td><td><div class="text-right">
£ 1,591.85
</div> <!----> <!----></td><td><span>
£ 0.00
</span> <!----> <!----></td><td><span>
£ 0.00
</span> <!----> <!----></td><td><span>
£ 0.00
</span> <!----> <!----></td><td><span>
£ 1,591.85
</span> <!----> <!----></td> <td class="text-right"></td></tr> <tr class="row-tight" style=""><td colspan="3"></td> <td colspan="2"><div class="d-flex align-items-center"><i class="mr-2 uil uil-corner-down-right" style="font-size: 12px;"></i>
Gross
<!----> <div class="ml-1 con-separator-left"><a href="/entities/91d519ef-c04f-48c5-9902-2a5dd0227e59/show" class="">
MuS
</a></div></div> </td> <td class="text-right"><span class="balance-text text-success">
£ 2,015.00
</span> </td> <td colspan="4"></td></tr><tr class="row-tight" style=""><td colspan="3"></td> <td colspan="2"><div class="d-flex align-items-center"><i class="mr-2 uil uil-corner-down-right" style="font-size: 12px;"></i>
Fee
<!----> <div class="ml-1 con-separator-left"><a href="/entities/2316df55-9aa6-4777-9587-ec77939fa1db/show" class="">
S Smith
</a></div></div> </td> <td class="text-right"><span class="balance-text text-danger">
£ -141.05
</span> </td> <td colspan="4"></td></tr><tr class="row-tight" style=""><td colspan="3"></td> <td colspan="2"><div class="d-flex align-items-center"><i class="mr-2 uil uil-corner-down-right" style="font-size: 12px;"></i>
Commision
<!----> <div class="ml-1 con-separator-left"><a href="/entities/d935c1b4-e733-4c22-bc40-638d25160796/show" class="">
KKK
</a></div></div> </td> <td class="text-right"><span class="balance-text text-danger">
£ -282.10
</span> </td> <td colspan="4"></td></tr><tr role="row"><!----> <td><button type="button" class="btn row-collapse-expand-btn btn-link btn-sm"><i class="uil uil-angle-down" style="font-size: 20px;"></i></button> <a href="/quotes/96f37a8b-20b3-4b7c-ba1b-129db2c09bcb/show" class="" target="_blank">
00001
</a> <!----> <!----> <!----></td><td><a href="/clients/96f37a8a-8e46-4789-8440-5f07dcd18586/show" class="" target="_blank"><span>
S Smith
</span></a> <!----> <!----> <!----></td><td> <!---->
2022-10-17
</td><td><div class="d-flex align-items-center"><div>
Amount
</div></div> <!----> <!----></td><td><a href="/invoices/96f37a8d-a552-4db2-ba10-377810f2f78a/show" class="" target="_blank">
00001
</a> <!----> <!----></td><td><div class="text-right">
£ 5,000.70
</div> <!----> <!----></td><td><span>
£ 0.00
</span> <!----> <!----></td><td><span>
£ 0.00
</span> <!----> <!----></td><td><span>
£ 0.00
</span> <!----> <!----></td><td><span>
£ 5,000.70
</span> <!----> <!----></td> <td class="text-right"></td></tr> <tr class="row-tight" style=""><td colspan="3"></td> <td colspan="2"><div class="d-flex align-items-center"><i class="mr-2 uil uil-corner-down-right" style="font-size: 12px;"></i>
Gross Premium (premium)
<!----> <div class="ml-1 con-separator-left"><a href="/entities/91d519ef-c04f-48c5-9902-2a5dd0227e59/show" class="">
MuS
</a></div></div> </td> <td class="text-right"><span class="balance-text text-success">
£ 6,330.00
</span> </td> <td colspan="4"></td></tr><tr class="row-tight" style=""><td colspan="3"></td> <td colspan="2"><div class="d-flex align-items-center"><i class="mr-2 uil uil-corner-down-right" style="font-size: 12px;"></i>
Fee
<!----> <div class="ml-1 con-separator-left"><a href="/entities/2316df55-9aa6-4777-9587-ec77939fa1db/show" class="">
S Smith
</a></div></div> </td> <td class="text-right"><span class="balance-text text-danger">
£ -443.10
</span> </td> <td colspan="4"></td></tr><tr class="row-tight" style=""><td colspan="3"></td> <td colspan="2"><div class="d-flex align-items-center"><i class="mr-2 uil uil-corner-down-right" style="font-size: 12px;"></i>
Agent Commision (agent_commission 14.00%)
<!----> <div class="ml-1 con-separator-left"><a href="/entities/d935c1b4-e733-4c22-bc40-638d25160796/show" class="">
KKK
</a></div></div> </td> <td class="text-right"><span class="balance-text text-danger">
£ -886.20
</span> </td> <td colspan="4"></td></tr></tbody> <tfoot></tfoot></table>
```

Maybe try to filter out rows that have less than 10 <td>
let sum = 0;
const amountCol = 10
cy.get('tbody')
.find('tr')
.filter(function() {
return Cypress.$(this).find('td').length >= amountCol; // only rows enough <td>
})
.each($tr => {
const amountColumn = $tr.find('td').eq(amountCol);
sum += textAmountToNumber(amountColumn.text())
// cy.log(sum) // won't work, always shows initial value of 0
console.log(sum) // show total as incremented
})
cy.then(() => {
cy.log(sum);
expect(sum).to.eq(...)
})
OR
let sum = 0;
const amountCol = 10
cy.get('tbody')
.find('tr')
.filter(':not(.row-tight)')
.each($tr => {
...
})
Full test with live ammo (actual HTML)
it('sums the 5th column', () => {
let sum = 0;
const amountCol = 5
cy.get('tbody')
.find('tr')
.filter(':not(.row-tight)')
.each($tr => {
const amountColumn = $tr.find('td').eq(amountCol);
console.log($tr.find('td')) // take a look at the cols
sum += textAmountToNumber(amountColumn.text())
cy.log(sum) // this does show the incremental total
})
cy.then(() => {
cy.log(sum);
expect(sum).to.eq(10593.11) // ✅ passes
})
});

Assuming that there are x total td elements, with the x-1th being the sum, we can continuously check the new sum value.
let sum = 0;
...
cy.get('td')
.each(($el, index, $list) => {
if (index < $list.length - 1) {
sum += textAmountToNumber($el)
}
})
.last()
.invoke('text')
.then(textAmountToNumber)
.should(...) // whatever assertion to compare the `sum` variable to the actual sum `td` field.
One note: this would require changing your textAmountToNumber function from taking in a string (such as $el.text()) to taking in a JQuery element (and adding .text() to the element. For example:
const foo = (value: string) => {
return +value
}
// vs.
const foo = (value: JQuery<HTMLElement>) => {
return +value.text()
}
I wasn't positive on what outcome you wanted to achieve, so let me know if I've missed the mark.

An alternative to using .each(), would be fetch td's using jQuery :contains and then parse out the value and convert into a number to sum up.
It would look something like this:
cy.get("td:contains(£)")
// check if there will always be at least one row with price
.should("have.length.greaterThan", 0)
.then(($prices) => {
// we get the innerText to get the float value of the text before we sum them
const priceText = Cypress._.map($prices, (el) => el.innerText);
const removeAllNonNumeric = (str) => str.replace(/[^0-9.]/g, "");
const prices = priceText.map(removeAllNonNumeric).map(parseFloat);
// sum up the prices and return the value
const sum = prices.reduce((partialSum, a) => partialSum + a, 0);
return sum;
})
// now we are working with the sum of prices and assert it equals the total you expect
.should("eq", *expectedSumTotal*);
Here is a simple example.

Related

Secondary Ordering for Column in Datatables

As you can see from the screenshot below, I have a datatables displaying two related but disparate bits of data, Avg Score and # of Ratings:
When ordering by this column, it would be preferred if I could order by Avg Score first (numeric) and then # of Ratings second (numeric) but also keep them displayed in a single column.
I was thinking about how to modify the value of the data-order attribute to do manipulate a thing but I couldn't think of any solutions.
Anyone else solved this issue.
The data cell looks like this:
<td class="text-center sorting_1" data-search="-1" data-order="5.00">
<div class="mb-2">
<span class="badge badge-success">
5.00
</span>
</div>
<small class="text-muted">
10 ratings
</small>
</td>
You can define a custom sorting function for the column of your interest.
The snippet:
$('#example').DataTable({
"aoColumns": [{
"bSortable": true}, {"bSortable": true}, {"sType": "customAvgScoreSort", "bSortable": true}]
});
// custom sorting.......
jQuery.fn.dataTableExt.oSort["customAvgScoreSort-desc"] = function (x, y) {
return parseFloat($(y).find('span').text()) - parseFloat($(x).find('span').text()) || parseInt($(y).siblings('small').text()) - parseInt($(x).siblings('small').text());
};
jQuery.fn.dataTableExt.oSort["customAvgScoreSort-asc"] = function (x, y) {
return parseFloat($(x).find('span').text()) - parseFloat($(y).find('span').text()) || parseInt($(x).siblings('small').text()) - parseInt($(y).siblings('small').text());
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//cdn.datatables.net/1.10.23/css/jquery.dataTables.min.css">
<script src="//cdn.datatables.net/1.10.23/js/jquery.dataTables.min.js"></script>
<table id="example" class="display" style="width:100%">
<thead>
<tr>
<th>Type</th>
<th>Unique deals</th>
<th>AVG score</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sales development</td>
<td>0</td>
<td>
<div class="mb-2">
<span class="badge badge-success">
5.0
</span>
</div>
<small class="text-muted">
2 ratings
</small>
</td>
</tr>
<tr>
<td>Sales development</td>
<td>0</td>
<td>
<div class="mb-2">
<span class="badge badge-success">
5.0
</span>
</div>
<small class="text-muted">
1 ratings
</small>
</td>
</tr>
<tr>
<td>Sales development</td>
<td>0</td>
<td>
<div class="mb-2">
<span class="badge badge-success">
5.0
</span>
</div>
<small class="text-muted">
10 ratings
</small>
</td>
</tr>
<tr>
<td>Sales development</td>
<td>0</td>
<td>
<div class="mb-2">
<span class="badge badge-success">
5.0
</span>
</div>
<small class="text-muted">
3 ratings
</small>
</td>
</tr>
</tbody>
</table>

Selenium WebDriver XPath points to second row, but findElements returns first row

I have HTML that looks like this:
<table id="data-table" class="table table-striped table-bordered table-hover dataTable no-footer" role="grid" style="width: 100%;">
<thead>
<tr role="row" style="height: 0px;"><th scope="col" class="sorting" aria-controls="data-table" rowspan="1" colspan="1" style="width: 277px; padding-top: 0px; padding-bottom: 0px; border-top-width: 0px; border-bottom-width: 0px; height: 0px;"><div class="dataTables_sizing" style="height:0;overflow:hidden;">Store Number</div></th><th scope="col" class="sorting" aria-controls="data-table" rowspan="1" colspan="1" style="width: 241px; padding-top: 0px; padding-bottom: 0px; border-top-width: 0px; border-bottom-width: 0px; height: 0px;"><div class="dataTables_sizing" style="height:0;overflow:hidden;">Store Name</div></th><th scope="col" class="sorting" aria-controls="data-table" rowspan="1" colspan="1" style="width: 156px; padding-top: 0px; padding-bottom: 0px; border-top-width: 0px; border-bottom-width: 0px; height: 0px;"><div class="dataTables_sizing" style="height:0;overflow:hidden;">Active</div></th><th scope="col" class="sorting" aria-controls="data-table" rowspan="1" colspan="1" style="width: 237px; padding-top: 0px; padding-bottom: 0px; border-top-width: 0px; border-bottom-width: 0px; height: 0px;"><div class="dataTables_sizing" style="height:0;overflow:hidden;">Action</div></th></tr>
</thead>
<tbody>
<tr id="store-3" data-model="{"Id":3,"Name":"a","IsActive":true,"DBVersion":"","Address":{"Address":"","Address2":"","State":"","City":"","Zip":0},"Phone":""}" role="row" class="odd">
<th scope="row" class="sorting_1">3</th>
<td>a</td>
<td>
<label class="tgl">
<input onclick="ToggleStoreActive(this,event)" class="form-isactive" type="checkbox" checked="">
<span class="tgl_body">
<span class="tgl_switch"></span>
<span class="tgl_track">
<span class="tgl_bgd"></span>
<span class="tgl_bgd tgl_bgd-negative"></span>
</span>
</span>
</label>
</td>
<td>
<button class="btn btn-danger" onclick="DeleteStore(this)">
<i class="fa fa-trash-o" aria-hidden="true"></i>
</button>
<button class="btn btn-primary" onclick="PopModel(this)">
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
</td>
</tr><tr id="store-4" data-model="{"Id":4,"Name":"b","IsActive":true,"DBVersion":"","Address":{"Address":"","Address2":"","State":"","City":"","Zip":0},"Phone":""}" role="row" class="even">
<th scope="row" class="sorting_1">4</th>
<td>b</td>
<td>
<label class="tgl">
<input onclick="ToggleStoreActive(this,event)" class="form-isactive" type="checkbox" checked="">
<span class="tgl_body">
<span class="tgl_switch"></span>
<span class="tgl_track">
<span class="tgl_bgd"></span>
<span class="tgl_bgd tgl_bgd-negative"></span>
</span>
</span>
</label>
</td>
<td>
<button class="btn btn-danger" onclick="DeleteStore(this)">
<i class="fa fa-trash-o" aria-hidden="true"></i>
</button>
<button class="btn btn-primary" onclick="PopModel(this)">
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
</td>
</tr><tr id="store-5" data-model="{"Id":5,"Name":"c","IsActive":true,"DBVersion":"","Address":{"Address":"","Address2":"","State":"","City":"","Zip":0},"Phone":""}" role="row" class="odd">
<th scope="row" class="sorting_1">5</th>
<td>c</td>
<td>
<label class="tgl">
<input onclick="ToggleStoreActive(this,event)" class="form-isactive" type="checkbox" checked="">
<span class="tgl_body">
<span class="tgl_switch"></span>
<span class="tgl_track">
<span class="tgl_bgd"></span>
<span class="tgl_bgd tgl_bgd-negative"></span>
</span>
</span>
</label>
</td>
<td>
<button class="btn btn-danger" onclick="DeleteStore(this)">
<i class="fa fa-trash-o" aria-hidden="true"></i>
</button>
<button class="btn btn-primary" onclick="PopModel(this)">
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
</td>
</tr></tbody>
</table>
and am attempting to get the second row (the one with "Store Number" 4 and "Store Name" 'b'), with the following Groovy code:
'get the initial number of data rows'
String dataRowsSelector = '//table[contains(#id, "data-table")]/tbody/tr'
List<WebElement> dataRows = driver.findElements(By.xpath(dataRowsSelector))
int initialRowCount = dataRows.size()
assertNotEquals(index, 0)
'find the row pointed to by the index'
String rowSelector = String.format('//table[contains(#id, "data-table")]/tbody/tr[%d]', (index + 1))
assertEquals(rowSelector, '//table[contains(#id, "data-table")]/tbody/tr[2]')
WebElement rowToDelete = driver.findElement(By.xpath(rowSelector))
'get the delete button'
String deleteButtonSelector = '//button[./i[contains(concat(" ", #class, " "), " fa-trash-o ")]]'
WebElement deleteButton = rowToDelete.findElement(By.xpath(deleteButtonSelector))
println(rowToDelete.findElement(By.xpath('//td[1]')).getAttribute('innerText'))
assertTrue(rowToDelete.findElement(By.xpath('//td[1]')).getAttribute('innerText').contains('b'))
I run that code, and rowToDelete becomes the first data row instead of the second (I try again with 3 for the index, and it is still the first row), even though the assertion against the target xpath worked. Also, the first <td> for that WebElement contains a and not b. (When I tried with 3 as the index, I get a again, even though I was expecting c.
How do I fix this weird behavior?
The problem was with my XPath itself, apparently. When attempting to select WebElements relative to other WebElements, using XPath, I should put a dot in front of the // to give it the scope.

Using Dynamic Variables in Velocity Templates

I am using velocity templates to generate email templates and here I want to create tables for each 'tag' with respective values as table data.
I am passing a List tags with details of the tags including tagName, using which I am passing the respective List for each tag. (If tagName = "test", I am passing the data for the test table as $test)
As shown I have set the data of each table to $arrayobjs, but the table does not display the rows as I think it doesnt iterate through the $arrayobs. Please help me iterate through the data and set the data to the coloumns.
P.S.-Although I am able to print the value of $tags[0]
, $arrayobjs[0] does not print a value
#set ($d = '$')
#foreach($tag in $tags)
#set ($var = "${d}${tag.tagName}")
#set ($arrayobjs = "#evaluate($var)")
<table style='width:600px; padding-left: 11px; font:Segoe UI;'>
<tr>
<td> <span style='font-size: 17px; font-weight: 400; font-family:Georgia;'>$tag.tagName </span></td>
</tr>
</table>
<br>
<table width=583 style='width:437.5pt; margin-left:8.25pt;border-collapse:collapse;font-family:"Segoe UI",sans-serif;'>
<tr style='vertical-align:top;height:10px;'>
<td width=50px style='border:solid white 1.0pt;background: #a3a3c2;padding:1pt 1pt 1pt 1pt;'>
<p> <span style='font-size:10.0pt;color:#3d3d5c;'><b>COL1</b></span> <span style='font-size:9.0pt;color:#404040'> <br><b></b> </span> </p>
</td>
<td width=100px style='border:solid white 1.0pt;background: #a3a3c2;padding:1pt 1pt 1pt 1pt;'>
<p> <span style='font-size:10.0pt;color:#3d3d5c'><b>COL2</b></span> <span style='font-size:9.0pt;color:#404040'> <br><b></b> </span> </p>
</td>
<td width=50px style='border:solid white 1.0pt;background: #a3a3c2;padding:1pt 1pt 1pt 1pt;'>
<p> <span style='font-size:10.0pt;color:#3d3d5c'><b>COL3</b></span> <span style='font-size:9.0pt;color:#404040'> <br><b></b> </span> </p>
</td>
<td width=50px style='border:solid white 1.0pt;background: #a3a3c2;padding:1pt 1pt 1pt 1pt;'>
<p> <span style='font-size:10.0pt;color:#3d3d5c'><b>COL4</b></span> <span style='font-size:9.0pt;color:#404040'> <br><b></b> </span> </p>
</td>
<td width=50px style='border:solid white 1.0pt;background: #a3a3c2;padding:1pt 1pt 1pt 1pt;'>
<p> <span style='font-size:10.0pt;color:#3d3d5c'><b>COL5</b></span> <span style='font-size:9.0pt;color:#404040'> <br><b></b> </span> </p>
</td>
<td width=50px style='border:solid white 1.0pt;background: #a3a3c2;padding:1pt 1pt 1pt 1pt;'>
<p> <span style='font-size:10.0pt;color:#3d3d5c'><b>COL6</b></span> <span style='font-size:9.0pt;color:#404040'> <br><b></b> </span> </p>
</td>
</tr>
#foreach($o in $arrayobjs)
<tr style='vertical-align:top;height:5px;'>
<td width=50px style='border:solid white 1.0pt;background: #f0f0f5;padding:0.5pt 0.5pt 0.5pt 0.5pt;'>
<p> <span style='font-size:9.0pt;color:#7F7F7F'></span> <span style='font-size:9.0pt;color:#404040'>$o.col1 </span> </p>
</td>
<td width=180px style='border:solid white 1.0pt;background: #f0f0f5;padding:0.5pt 0.5pt 0.5pt 0.5pt;'>
<p> <span style='font-size:9.0pt;color:#7F7F7F'></span> <span style='font-size:9.0pt;color:#404040'>$o.col2</span> </p>
</td>
<td width=30px style='border:solid white 1.0pt;background: #f0f0f5;padding:0.5pt 0.5pt 0.5pt 0.5pt;'>
<p> <span style='font-size:9.0pt;color:#7F7F7F'></span> <span style='font-size:9.0pt;color:#404040'>$o.col3</span> </p>
</td>
<td width=30px style='border:solid white 1.0pt;background: #f0f0f5;padding:0.5pt 0.5pt 0.5pt 0.5pt;'>
<p> <span style='font-size:9.0pt;color:#7F7F7F'></span> <span style='font-size:9.0pt;color:#404040'>$o.col4</span> </p>
</td>
<td width=30px style='border:solid white 1.0pt;background: #f0f0f5;padding:0.5pt 0.5pt 0.5pt 0.5pt;'>
<p> <span style='font-size:9.0pt;color:#7F7F7F'></span> <span style='font-size:9.0pt;color:#404040'>$o.col5</span> </p>
</td>
<td width=30px style='border:solid white 1.0pt;background: #f0f0f5;padding:0.5pt 0.5pt 0.5pt 0.5pt;'>
<p> <span style='font-size:9.0pt;color:#7F7F7F'></span> <span style='font-size:9.0pt;color:#404040'>$o.col6</span> </p>
</td>
</tr>
#end
</table>
#end
After almost 2 years I suggest the following answer :-)
#set ($d = '$')
#set ($h = '#')
#foreach($tag in $tags)
#set ( $temp = "${h}set ( ${d}var = ${d}${tag.tagName} )" )
#evaluate($temp)
## html code for table header
#foreach($o in $var)
## table rows
#end
#end

How to get the updated count of rows in a table after changing the page number

Actually in the table i'm working on there is pagination. I could get the count of rows present in the first page.
When i change the page number, table row count is not getting updated as per the second page.
How to force selenium to update the count after changing the page number. page refresh is not the solution as i need to navigate to second page again
public Boolean checkMessageIsFailed(String message, String channelNameToCheck, String channelTypeToCheck) {
UIPublish uiPublish = new UIPublish(driver);
Boolean isMessageSent = null;
SimplifyUtils.waitTillElementFound(uiPublish.currentPageNumber, 60);
//getting the page count
int pageCount = Integer.parseInt( uiPublish.totalPages.getText());
for (int j=1; j< pageCount;pageCount++)
{
//getting the rows present in the table
int messagesCount = uiPublish.listOfTextInMessages.size();
for (int i = 0; i < messagesCount; i++)
{
//getting the text from each row to match
//matching the text of the required message sent with the messages present in the sent messages
if (uiPublish.listOfTextInMessages.get(i).getText().equalsIgnoreCase(message))
{
String currentChannel = driver.findElements(By.xpath("//*[#id='inbox-wrapper']//tr["+ (i + 1)+ "]")).get(i).getAttribute("data-original-title");
}
}
//navigating to naxt page if the condition is not matched
uiPublish.navigateToNextPage.click();
}
return isMessageSent;
}
<div class="grid simple">
<div class="grid-body no-border email-body">
<br>
<div class="row-fluid">
<div id="email-list" class="table-responsive">
<form id="schMsgFORM" name="schMsgFORM" action="/viewSentSchMessage.action"
method="post">
<table class="table table-striped table-hover" id="emails">
<thead>
<tr>
<th>
</th>
<th>Social Channel
</th>
<th>Message
</th>
<th>Scheduled Time
</th>
<th>Actions
</th>
</tr>
</thead>
<tbody>
<tr id="outputWTFrUjUyYlE1d1piNG1XUmNuUFBnUT09">
<td class="small-cell v-align-middle" style="width: 2%">
<div class="checkbox check-success ">
<input name="msgcheckbox" id="chkboxWTFrUjUyYlE1d1piNG1XUmNuUFBnUT09"
value="WTFrUjUyYlE1d1piNG1XUmNuUFBnUT09" type="checkbox">
<label for="chkboxWTFrUjUyYlE1d1piNG1XUmNuUFBnUT09"></label>
</div>
</td>
<td style="width: 150px">
<div class="account-sent">
<div class="row">
<div class="display-inline">
<div class="profile-pic" data-toggle="tooltip" title=""
data-original-title="galloway360">
<img class="account-profile"
src="http://pbs.twimg.com/profile_images/378800000473105984/b0ad2b50b4fb81303d32720afea274ea_normal.png"
alt="">
<div class="social-icon-pub">
<span
class="social-icon-twitter img-responsive social-icon-box">
</span>
</div>
</div>
</div>
</div>
</div>
</td>
<td style="width: 50%">
<div class="muted message-wrap">
<br>
Source: Auto 2016/12/08 17:35:30 Message: How's your day?
</div>
<p class="sch-details">
Sent by: Nagarjuna reddy
<em>Scheduled from Profile :
NA
</em>
</p>
</td>
<td style="width: 15%">
<p class="muted m-t-5">
2016-12-08 05:35 PM
</p>
<p class="region">
(Asia/Calcutta)
</p>
</td>
<td style="width: 10%">
<a
href="/social/editUpdate?s360securetoken=SU0wxc4jrR8DE8Lf5hzKnRYasjg&schMsgId=WTFrUjUyYlE1d1piNG1XUmNuUFBnUT09&pageSource=publish"
data-toggle="tooltip" title="" type="button"
class="btn btn-success btn-small" data-original-title="Edit">
<i class="fa fa-pencil">
</i>
</a>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
</div>
</div>

Assistance with a dynamically generated field

I have another dynamic question for you. I have three input fields that I need to enter values for. These "dijit_form_DateTextBox_36" are created dynamically (the 36 is variable). There is a value earlier in the code that is unique and can be searched for. I know I need to use something like:
driver.findElement(By.xpath("//div[contains(., 'QA GM 04012014 1424 Item Name')]/parent::...somepath.sendKeys("...");
But I am still too new to this stuff to be able to figure it out. And if someone can assist me with what I need to put, as well as how they figured it out, I would greatly appreciate it. I'd really love to solve these on my own! Code is below. Bolded portion is the static text that can be searched. Bolded and italicized lines are the three controls that need to be modified. NOTE: The id="shipDate0_0" is also dynamic, it can be shipDate0_0, shipDate1_0, etc. So can't key on that.
<tr class="tableControlHeader twTableHeaderTR"></tr>
<tr class="tableControlDataRow evenRow twTableTR">
<td class="twTableTD details" align="center" rowspan="2"></td>
<td class="twTableTD details" align="center" rowspan="2">
<p>
<b>
QA GM 04012014 1424 Item Name
</b>
</p>
<br></br>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
</td>
<td class="twTableTD" align="center" rowspan="2"></td>
<td class="twTableTD" align="center"></td>
<td class="twTableTD" align="center"></td>
<td id="shipDate0_0" class="twTableTD" align="center">
<div style="padding-right: 20px;">
<div id="dateWrap-projectedFirstShipDate_0_0" class="inputText_Full twControl twDateSelector" ;="" onblur="updateAvgPerWeek(0,0)" initialvalue="" value="" name="tw#local#quoteComparison#0#country#0#projectedFirstShipDate" style="white-space:nowrap;">
<div id="projectedFirstShipDate_0_0" lang="" dojoattachpoint="pickerDiv" widgetid="projectedFirstShipDate_0_0" name="tw#local#quoteComparison#0#country#0#projectedFirstShipDate">
<span dojoattachpoint="leftPicker">
<div id="widget_dijit_form_DateTextBox_36" class="dijit dijitReset dijitInlineTable dijitLeft dateSelectionSin…tBox dijitComboBox dijitDateTextBox dijitComboBoxOpenOnClick" role="combobox" widgetid="dijit_form_DateTextBox_36">
<div class="dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer" role="presentation" dojoattachpoint="_buttonNode, _popupStateNode" popupactive="true"></div>
<div class="dijitReset dijitValidationContainer"></div>
<div class="dijitReset dijitInputField dijitInputContainer">
<input id="dijit_form_DateTextBox_36" class="dijitReset dijitInputInner" type="text" aria-haspopup="true" role="textbox" dojoattachpoint="textbox,focusNode" autocomplete="off" aria-valuenow="null" aria-invalid="false" tabindex="0" value="" style="" delocalized="null" aria-disabled="false"></input>
<input type="hidden" value=""></input>
</div>
</div>
</span>
<span dojoattachpoint="rightPicker"></span>
<span style="position: absolute;" dojoattachpoint="calImage"></span>
<input id="projectedFirstShipDate_0_0" type="text" isdatefield="true" name="tw#local#quoteComparison#0#country#0#projectedFirstShipDate" style="display:none" dojoattachpoint="hiddenInput" delocalized="null"></input>
</div>
</div>
</div>
</td>
<td id="InDate0_0" class="twTableTD" align="center" ;="" onclick="updateAvgPerWeek(0,0)">
<div style="padding-right: 20px;">
<div id="dateWrap-inDate_0_0" class="inputText_Full twControl twDateSelector" ;="" onblur="updateAvgPerWeek(0,0)" initialvalue="" value="" name="tw#local#quoteComparison#0#country#0#inDate" style="white-space:nowrap;">
<div id="inDate_0_0" lang="" dojoattachpoint="pickerDiv" widgetid="inDate_0_0" name="tw#local#quoteComparison#0#country#0#inDate">
<span dojoattachpoint="leftPicker">
<div id="widget_dijit_form_DateTextBox_35" class="dijit dijitReset dijitInlineTable dijitLeft dateSelectionSin…tBox dijitComboBox dijitDateTextBox dijitComboBoxOpenOnClick" role="combobox" widgetid="dijit_form_DateTextBox_35">
<div class="dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer" role="presentation" dojoattachpoint="_buttonNode, _popupStateNode" popupactive="true"></div>
<div class="dijitReset dijitValidationContainer"></div>
<div class="dijitReset dijitInputField dijitInputContainer">
<input id="dijit_form_DateTextBox_35" class="dijitReset dijitInputInner" type="text" aria-haspopup="true" role="textbox" dojoattachpoint="textbox,focusNode" autocomplete="off" aria-valuenow="null" aria-invalid="false" tabindex="0" value="" style="" delocalized="null" aria-disabled="false"></input>
<input type="hidden" value=""></input>
</div>
</div>
</span>
<span dojoattachpoint="rightPicker"></span>
<span style="position: absolute;" dojoattachpoint="calImage"></span>
<input id="inDate_0_0" type="text" isdatefield="true" name="tw#local#quoteComparison#0#country#0#inDate" style="display:none" dojoattachpoint="hiddenInput" delocalized="null"></input>
</div>
</div>
</div>
</td>
<td id="OutDate0_0" class="twTableTD" align="center" onclick="updateAvgPerWeek(0,0)">
<div style="padding-right: 20px;">
<div id="dateWrap-outDate_0_0" class="inputText_Full twControl twDateSelector" ;="" onblur="updateAvgPerWeek(0,0)" initialvalue="" value="" name="tw#local#quoteComparison#0#country#0#outDate" style="white-space:nowrap;">
<div id="outDate_0_0" lang="" dojoattachpoint="pickerDiv" widgetid="outDate_0_0" name="tw#local#quoteComparison#0#country#0#outDate">
<span dojoattachpoint="leftPicker">
<div id="widget_dijit_form_DateTextBox_34" class="dijit dijitReset dijitInlineTable dijitLeft dateSelectionSin…tBox dijitComboBox dijitDateTextBox dijitComboBoxOpenOnClick" role="combobox" widgetid="dijit_form_DateTextBox_34">
<div class="dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer" role="presentation" dojoattachpoint="_buttonNode, _popupStateNode" popupactive="true"></div>
<div class="dijitReset dijitValidationContainer"></div>
<div class="dijitReset dijitInputField dijitInputContainer">
<input id="dijit_form_DateTextBox_34" class="dijitReset dijitInputInner" type="text" aria-haspopup="true" role="textbox" dojoattachpoint="textbox,focusNode" autocomplete="off" aria-valuenow="null" aria-invalid="false" tabindex="0" value="" style="" delocalized="null" aria-disabled="false"></input>
<input type="hidden" value=""></input>
</div>
</div>
</span>
<span dojoattachpoint="rightPicker"></span>
<span style="position: absolute;" dojoattachpoint="calImage"></span>
<input id="outDate_0_0" type="text" isdatefield="true" name="tw#local#quoteComparison#0#country#0#outDate" style="display:none" dojoattachpoint="hiddenInput" delocalized="null"></input>
</div>
</div>
</div>
</td>
<td id="BuyQuantity0_0" class="twTableTD" align="center" onblur="updateAvgPerWeek(0,0)" name="BuyQuantity0"></td>
<td id="TotalCost0_0" class="twTableTD" align="center" name="TotalCost0"></td>
<td id="NumberOfWarehouses0_0" class="twTableTD" align="center" onblur="updateAvgPerWeek(0,0)" td=""></td>
<!--
# of Warehouses
-->
<td id="AveragePerWarehouse0_0" class="twTableTD" align="center" name="AvgPerWhouseWeek0"></td>
<!--
Cost per Warehouse
-->`enter code here`
<td id="ProjectedSellPrice0_0" class="twTableTD" align="center" td=""></td>
<!--
Projected Sell Price
-->
<td id="PercentOfTotal0_0" class="twTableTD" align="center"></td>
<td class="twTableTD" align="center" rowspan="2"></td>
</tr>
The best xpath I am able to come up with is this:
//td[normalize-space()='QA GM 04012014 1424 Item Name']/following-sibling::td[contains(#id, 'shipDate')]//input[contains(#class,'dijitInputInner')]
Let me explain:
This part finds the td element with the text you're looking for, the normalize-space() call is just like text(), but trims any whitespace before/after the text & I've found it to be invaluable since discovering it a short time ago. text() will return whitespace and is often difficult to match
//td[normalize-space()='QA GM 04012014 1424 Item Name']
This next part finds ALL td's after the previous element. From the code snippet provided this will find ALL 13 td's that follow
/following-sibling::td
Narrow down that set of 13 by finding only the td containing the ID you're interested in, this is better than using a hardcoded number like /following-sibling::td[2] to find the second td
/following-sibling::td[contains(#id, 'shipDate')]
Then find the input field that you're interested in, there are number of ways you could do this, choose whichever you prefer
//input[contains(#class,'dijitInputInner')]
//input[contains(#id,'dijit_form_DateTextBox')]
I hope that's clear enough & works well for you, please let me know if not