Get the Dynamic table data from gui in selenium webDriver - selenium-webdriver

I am working on a web based Application that I am testing with Selenium. On one page the content is dynamically loaded in table. I want to get the Table data, i am geting a "org.openqa.selenium.NullPointerElementException" in this line.
WebElement table = log.driver.findElement(By.xpath(tableXpath));
I tried the following complete code.
public int selectfromtable(String tableXpath, String CompareValue, int columnnumber) throws Exception {
WebElement table = log.driver.findElement(By.xpath(tableXpath));
List<WebElement> rows = table.findElements(By.tagName("tr"));
int flag = 0;
for (WebElement row : rows) {
List<WebElement> cells = row.findElements(By.tagName("td"));
if (!cells.isEmpty() && cells.get(columnnumber).getText().equals(CompareValue)) {
flag = 1;
Thread.sleep(1000);
break;
} else {
Thread.sleep(2000);
flag = 0;
}
}
return flag;
}
I am calling the above method like
String tableXpath = ".//*[#id='event_list']/form/div[1]/table/tbody/tr/td/div/table";
selectfromtable(tableXpath, eventType, 3);
my html page is like
<table width="100%">
<tbody style="overflow: auto; background-color: #FFFFFF">
<tr class="trOdd">
<td width="2%" align="center">
<td width="20%" align="center"> Account </td>
<td width="20%" align="center"> Enter Collection </td>
<td width="20%" align="center">
<td width="20%" align="center"> 10 </td>
<td width="20%" align="center"> 1 </td>
</tr>
</tbody>
<tbody style="overflow: auto; background-color: #FFFFFF">
<tr class="trEven">
<td width="2%" align="center">
<td width="20%" align="center"> Account </td>
<td width="20%" align="center"> Resolved From Collection </td>
<td width="20%" align="center">
<td width="20%" align="center"> 10 </td>
<td width="20%" align="center"> 1 </td>
</tr>
</tbody>
</table>

My solution for similar case - I wanted to get a row number from table, which given column contains specific text.
// Method searches in given column of a table and return number of row where
// exactly first value is spotted (title row counts as 0 row and is
// skipped). If no value is found then 0 is returned. Given column number
// starts with 1.
public Integer getTableRowNumberWithValue(String tableId, String value,
Integer columnNumber) {
WebElement table = getDriver().findElement(By.id(tableId));
List<WebElement> rows = table.findElements(By.tagName("tr"));
int j = 0;
int i = 0;
for (WebElement row : rows) {
// Skip title row which counts as 0 row.
if (i > 0) {
if (row.findElements(By.tagName("td")).get(columnNumber - 1)
.getText().equals(value)) {
j = i;
break;
}
}
i++;
}
return j;
}

I had the similar issue. The solution I found was use the method and xpath together.
public void theSearch(String value) throws Exception {
String xpathExpression = "//*[starts-with(#id,'searchResultsTable:')]";
List<WebElement> elementTable= state.getDriver().findElements(By.xpath(xpathExpression));
for (WebElement listofElement : elementTable) {
String theElement= listofElement.getText();
if (theElement.contains(value)) {
Assert.assertEquals(value, theElement);
// System.out.println("The Expected Value " + value + " Equals the actual " + theElement);;
}
}
}

Related

How to display duplicate array values only once in React.js

I have a Bill To Print but in Bill I have 'Products' array object in which I want, I have 3 products array then product name, price, discount, etc. are same then it should show only one array (line) but in 'Products' array I have a 'SrNo.' column which means each product has unique serial number so it should product name, price, discount, etc. show in one line and 'SrNo.' column shows 3 rows. Currently I'm using map() to display array values but it showing like this
Products arrays
My Bill_Preview.js (Frontend)
<tr>
<td style={{width:"1%"}}>Sr<br></br>No.</td>
<td >Description Of Goods</td>
<td >Qty</td>
<td >Rate</td>
<td >Disc %</td>
<td >Amount</td>
</tr>
{/* Data row */}
{DataForPreview &&
DataForPreview.map((data, Id) => (
// This Row all data i want only once if its same product except 'Prod_SrNo'
<tr>
<td >{Id + 1}.</td>
<td >
{data.Product_Comp} {data.Product}
<br></br>
{data.Prod_SrNo}
<br></br>
</td>
<td >1</td>
<td >{data.Price}</td>
<td >{data.Discount}</td>
<td >{(data.Price - (data.Price * data.Discount) / 100).toFixed(2) }</td>
</tr>
))}
you can filter the duplicate values from the array before mapping:
function filterDuplicates(arr){
return arr.filter((item, index) => arr.findIndex(data=> data.Product === item.Product && data.Product_Comp === item.Product_Comp) === index);
}
function findDuplicatesRows(data){
return DataForPreview.filter(item => (data.Product === item.Product && data.Product_Comp === item.Product_Comp))
}
filterDuplicates(DataForPreview).map((data, Id) => (
<tr>
<td >{Id + 1}.</td>
<td >
{data.Product_Comp} {data.Product}
<br></br>
{findDuplicatesRows(data).map(d => (
<tr>{d.Prod_SrNo}</tr>
))}
<br></br>
</td>
<td >1</td>
<td >{data.Price}</td>
<td >{data.Discount}</td>
<td >{(data.Price - (data.Price * data.Discount) /100).toFixed(2)}</td>
</tr>
))}
but you have to calc the total values of the duplicates, for example
calc the total amount
function calcTotalAmount(data){
const amount = (data.Price - (data.Price * data.Discount) /100).toFixed(2);
const quantity = findDuplicatesRows(data).length;
return amount * quantity;
}
<td>{calcTotalAmount(data)}</td>
I think there is a simpler way to do it,
but get the fundamental

display element (span tag ) after last ocurence of repeated item inside ng-repeat

I want to display something like this inside an ng-repeat: display a span tag saying the total purchase right after the last purchase of each person.( I do not want to display the total after each purchase). I have an array of object like this :
let group=[{name:'Brandon Pack',city:'NY',purchase:25,accepted:true},
{name:'Josh Vilet',city:'Memphis',purchase:30,accepted:true},
{name:'Brandon Pack',city:'NY',purchase:62,accepted:true},
{name:'Patrick Whiteside',city:'NY',purchase:50,accepted:false},
{name:'Josh Vilet',city:'Memphis',purchase:50,accepted:true}]
I can get the total, my problem is with the view that I don't want to display the total only after the last ocurence for that person
First of all, you can create a function in your controller in order to get the total amount:
$scope.getTotal = function () {
var sum = 0;
$scope.group.forEach(function(customer){
sum += customer.purchase;
});
return sum;
}
By the way, you can't declare variables with whitespaces in their name, like "let group", better call it "letGroup".
Then, you just have to create your table in Html:
<table>
<thead>
<tr>
<th>Name</th>
<th>City</th>
<th>Purchase</th>
<th>Accepted</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="o in group">
<td ng-bind="o.name"></td>
<td ng-bind="o.city"></td>
<td ng-bind="o.purchase"></td>
<td ng-bind="o.accepted ? 'Yes': 'No'"></td>
</tr>
<tr>
<td></td>
<td></td>
<td>Total</td>
<td ng-bind="getTotal()"></td>
</tr>
</tbody>
</table>

Editable Pivot/Partially transposed grid for capturing

I have been tasked to develop a capturing web app for business. The aim of the app is to capture multiple (x24) KPI values for each day of the week.
Business wants to be able to capture these values in a pivot/transposed table.
Columns:
1. KPI ID
2. KPI Code
3. KPI Description
4. - 10. Days of the selected week.
5. for each KPI-Date combination there is a decimal value.
All KPI's are linked to a Country and Plant which is determined by the User Data of the logged in User Identity.
So far I have based the pivot on:
https://social.technet.microsoft.com/wiki/contents/articles/32770.dynamic-pivot-grid-using-mvc-angularjs-and-web-api.aspx
This helped A LOT!
(My current solution uses MVC 5, EF 5 Database First, Angularjs.)
At the moment my problem is with editing the values in the grid. Based on the above mentioned solution, the values are calculated using ng-bind-html sending parameters (KPI and Date column). I have set the contenteditable= true, which allows me to physically capture values....
BUT, it seems I can't actually do anything with the new/updated values since it is not bound to a model.
I hope all of this makes sense; My Question is, given the above mentioned scenario:
how can I use the changed value to be sent back to SQL via Stored Proc?
I have a feeling this approach is overkill for what I want to accomplish. Can anyone suggest a better approach?
My current thoughts are to maybe use
jquery to catch the change event on the cell and use the ID's of the row/column of the KPI and Date combinations.
So far I have been unsuccessful. The "change" event doesn't even give me an alert when triggered.
a pre-pivoted result from SQL using a stored procedure. BUT... the dates are dynamic and will change with every week selected. I am unable to specify a model for changing columns.
Any advise would be greatly appreciated.
Here is my angularjs controller function:
$scope.GetCaptureLinesPivot = function () {
var response = Dropdownfactory.GetCaptureLinesPivot($scope.UserID, $scope.CountryID, $scope.PlantID, $scope.WeekDate)
.then(function (success) {
$scope.lines = success.data;
angular.forEach($scope.lines, function (obj) {
obj["showEdit"] = false;
$scope.items = [];
$scope.ColDays = [];
$scope.values = [];
var uniqueUser = {},
uniqueKPICode = {},
uniqueKPIDesc = {},
uniqueDate = {},
uniqueKPIValue = {},
i;
//compile list of unique dates. should be 7
for (i = 0; i < $scope.lines.length; i += 1)
{
uniqueDate[$scope.lines[i].Date] = $scope.lines[i];
}
for (i in uniqueDate)
{
$scope.ColDays.push(uniqueDate[i]);
}
var UniqueItem = {}, i
for (i = 0; i < $scope.lines.length; i += 1) {
UniqueItem[$scope.lines[i].KPIID] = $scope.lines[i];
}
for (i in UniqueItem) {
var ItmDetails = {
UserName: UniqueItem[i].UserName,
KPIID: UniqueItem[i].KPIID,
KPICode: UniqueItem[i].Code,
KPIDesc: UniqueItem[i].Description,
KPIValue: UniqueItem[i].Value
};
$scope.items.push(ItmDetails);
}
Returned result from getting the data:
0:Object
Code:"XXXX"
Country:"Botswana"
CountryID:1
CurrentUserID:6
CurrentUserName:"DevPlant"
Date:"2017-03-20T00:00:00"
Description:"PD Km Travelled"
KPIID:38
Plant:"Francistown"
PlantID:186
UserID:6
UserName:"DevPlant"
Value:150
1:Object
Code:"XXXX"
Country:"Botswana"
CountryID:1
CurrentUserID:6
CurrentUserName:"DevPlant"
Date:"2017-03-21T00:00:00"
Description:"PD Km Travelled"
KPIID:38
Plant:"Francistown"
PlantID:186
UserID:6
UserName:"DevPlant"
Value:160
The function in the controller that return the Value to the cshtml:
$scope.showWeekItemDetails = function (colUser, colKPI, colDay) {
$scope.getLineVaue = 0;
$scope.DayCount = 0;
//alert(colUser + " " + colKPI + " " + colDay);
for (i = 0; i < $scope.lines.length; i++) {
if (colUser == $scope.lines[i].UserName) {
if (colKPI == $scope.lines[i].KPIID) {
if (colDay == $scope.lines[i].Date) {
$scope.getLineVaue = parseFloat($scope.lines[i].Value);
}
}
}
}
return $sce.trustAsHtml("<b>" + $scope.getLineVaue.toString() + "</b>");
}
My current cshtml:
<td width="20"></td>
<td width="1" align="left" valign="bottom" ng-show="false"><b>KPIID</b></td>
<td width="1" align="left" valign="bottom" ng-show="false"><b>CID</b></td>
<td width="1" align="left" valign="bottom" ng-show="false"><b>PID</b></td>
<td width="180" align="left" valign="bottom"><b>Description</b></td>
<td width="30" align="left"><b></b></td>
<td align="center" valign="bottom" data-ng-repeat="Cols in ColDays | orderBy: 'Date':false">
<div>
<table class="table table-condensed" font-size 50%; >
<tr>
<td align="center" width="80"><b>{{Cols.Date | date: 'EEE'}}</b></td>
</tr>
<tr>
<td align="center" width="80"><b>{{Cols.Date | date: 'dd MMM yy'}}</b></td>
</tr>
</table>
</div>
</td>
</tr>
<tbody data-ng-repeat="itm in items">
<tr>
<td width="20" class="lineNo" >{{$index+1}}</td>
<td width="1" class="kpiID" ng-show="false" align="left">{{itm.KPIID}}</td>
<td width="1" align="left" class="kpiDesc" ng-show="false">{{itm.CountryID}} </td>
<td width="1" align="left" class="kpiDesc" ng-show="false">{{itm.PlantID}}</td>
<td width="180" align="left">
<span class="kpiDesc">{{itm.KPIDesc}}</span>
</td>
<td width="30" align="left">
<span></span>
</td>
<td align="center" data-ng-repeat="Col in ColDays| orderBy:'Date':false" >
<table class="table table-bordered batch-edit">
<tr>
<td width="40" align="center">
<span ng-bind-html="showWeekItemDetails(itm.UserName,itm.KPIID,ColsNew.Date)" contenteditable="true"></span>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>

AngularJS - ng-repeat not working with a map-entry named length and value 0

I would like to show Events with Subevents I got from an API as JSON
[
{
"class":"de.ff.prg.EventItem",
"id":27667,
"additional_info":null,
"comments":null,
"event":{"class":"Event","id":27657},
"length":0,
"runningorder":0,
"screening":{"class":"Screening","id":27529},
"title_eng":"'71",
"title_ger":"'71",
"venue":{"class":"Venue","id":1}},
{"class":"de.ff.prg.EventItem",
"id":27676,
"additional_info":null,
"comments":null,
"event":{"class":"Event","id":27657},
"length":5,
"runningorder":0,
"screening":null,
"title_eng":"NEW",
"title_ger":"NEW",
"venue":{"class":"Venue","id":8}
}
]
In order to display the fields of the items in rows and not in columns, I have nested two tables with ng-repeat so that I get a table of tables.
<!--Items-->
<table>
<thead>
<td colspan="6" style="background-color: #b9da73">
<button class="btnAdd" ng-click="addAndEditEventItem()">Add Item</button>
</td>
</thead>
<tbody>
<tr ng-repeat="item in eventItems">
<h1>{{eventItems.length}}</h1>
<th>
<table>
<thead>
<td colspan="2" style="background-color: #c0da86">{{item.runningorder}}</td>
<td colspan="4" style="background-color: #c0da86">
<button class="btnAdd" ng-click="deleteEventItem(item.id)">Delete Item</button>
</td>
</thead>
<tbody>
{{item}}
<tr ng-repeat="(key,value) in item">
<th colspan="2" style="background-color: #ceeca1">{{key}}</th>
<th colspan="4" style="font-weight: normal;">{{value}} </th>
</tr>
</tbody>
</table>
</th>
</tr>
</tbody>
</table>
Up until now this was no problem, but somewhere along the way I have lost the possibility to display the body of the first sub-table (all other rows render fine). I have inserted {{item}} before the body tag and it shows the missing data, so it's there all right.
Any ideas? Or do you need to see the other code to tell? I have no clue...
Here is a Fiddle
Interesting case.
Empirically, I found that this is because of
"length":0
Just looked into angular source code and found that it transforms object's properties in repeat to an array, and then takes it's length property to iterate through it.
...ngRepeatDirective...
if (isArrayLike(collection)) {
collectionKeys = collection;
trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
} else {
trackByIdFn = trackByIdExpFn || trackByIdObjFn;
// if object, extract keys, sort them and use to determine order of iteration over obj props
collectionKeys = [];
for (key in collection) {
if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
collectionKeys.push(key);
}
}
collectionKeys.sort();
}
arrayLength = collectionKeys.length; <<<--- HERE
// locate existing items
length = nextBlockOrder.length = collectionKeys.length; <<<--- AND HERE
So nothing really happens.
Seems like a bug actually. I've posted an issue.
Just try then iterating through your array and change name of this property. Like:
for(var i = 0; i < $scope.eventItems.length; i++){
$scope.eventItems[i].itemLength = $scope.eventItems[i].length;
delete $scope.eventItems[i].length;
}

Disable and Enable Checkboxes Javascript

I have a series of Checkboxes:
<tr id="tr5" onmouseover="changeBackgroundColor(this.id)" onmouseout="changeBackgroundColor2(this.id)">
<td class="td5"><input name="benefit" value="Bonuses" id="benefit5" type="checkbox" onchange='addition();'</td>
<td class="td5"><label for="benefit5"> <b>Bonuses</b></label></td>
<tr id="tr6" onmouseover="changeBackgroundColor(this.id)" onmouseout="changeBackgroundColor2(this.id)">
<td class="td6"><input name="benefit" value="Final salary pension" id="benefit6" type="checkbox" onchange='addition();'</td>
<td class="td6"><label for="benefit6"> <b>Final salary pension</b></label></td>
Once a user has selected 3 checkboxes, is it possible to disable the rest in one hit (there are 30 checkboxes - I could do it individually but that seems a pain)? Is so, how would one go about doing that? Also, if the user then un-selected one of the check boxes, is it possible to enable them again?
EDIT: If possible - could someone point me in the right direction, code wise please?
Thanks in advance,
H.
DEMO
var chk=0;
function checkCheckboxes() {
var checkboxes = document.getElementsByName("benefit");
for (var i=0;i<checkboxes.length;i++) {
chk += checkboxes[i].checked?1:0; // count in case we reload
checkboxes[i].onclick=function() { // set up event handler for each
chk+=this.checked?1:-1; // add or subtract one
if (chk > 3) {
console.log(chk,"too many")
this.checked=false;
chk--; // we counted too many
}
}
}
}
function changeBackgroundColor(row,on) {
var id = row.id; // if you need that
row.style.backgroundColor=(on)?"red":"white";
}
window.onload=function() {
var trs = document.getElementById("table1").rows;
for (var i=0;i<trs.length;i++) {
trs[i].onmouseover=function() {
changeBackgroundColor(this,1);
}
trs[i].onmouseout=function() {
changeBackgroundColor(this,0);
}
}
checkCheckboxes();
}
using
<table id="table1">
<tr id="tr1">
<td class="td1"><input name="benefit" value="Bonuses" id="benefit1" type="checkbox"</td>
<td class="td1"><label for="benefit1"> <b>Bonuses</b></label></td>
</tr>
<tr id="tr2">
<td class="td2"><input name="benefit" value="Bonuses" id="benefit2" type="checkbox"</td>
<td class="td2"><label for="benefit2"> <b>Bonuses</b></label></td>
</tr>
<tr id="tr3">
<td class="td3"><input name="benefit" value="Bonuses" id="benefit3" type="checkbox"</td>
<td class="td3"><label for="benefit3"> <b>Bonuses</b></label></td>
</tr>
<tr id="tr4">
<td class="td4"><input name="benefit" value="Bonuses" id="benefit4" type="checkbox"</td>
<td class="td4"><label for="benefit4"> <b>Bonuses</b></label></td>
</tr>
</table>

Resources