How to locate and click an ng-click button in a repeater - javascript

I need to test an AngularJS web page using Selenium WebDriver and Protractor.
HTML snippet to be tested:
<tr ng-repeat="item in items" class="ng-scope">
<td class="ng-binding">
Item1
</td>
<td class="ng-binding">
description
</td>
<td class="ng-binding">
1234
</td>
<td>
<div ng-click="AddItem(item.id)" class="btn">Add Item</div>
</td>
</tr>
<tr ng-repeat="item in items" class="ng-scope">
<td class="ng-binding">
Item2
</td>
<td class="ng-binding">
description
</td>
<td class="ng-binding">
1235
</td>
<td>
<div ng-click="AddItem(item.id)" class="btn">Add Item</div>
</td>
</tr>
it looks like this:
screenshot of the html angular page
I already went through the documentation, tutorials on youtube and googled it but I still don't understand how to locate and click "Add Item" for a specific item with a specific item id.
can you please explain to me how it's done?

The specific id you are looking for wont be in the DOM, which means you cannot locate an element using that approach.
The following is the code which i personally use:
element.all(by.repeater('item in items')).filter(function(row){
return row.all(by.tagName('td')).first().getText().then(function(val){
return val === "Item1" //this can be your per your wish
})
}).first().$('[ng-click="AddItem(item.id)"]').click();

In addition to #Danny's answer,it is possible to find the item.id from the DOM using evaluate() method.Try the below code.
element.all(by.repeater('item in items')).filter(function(row){
return row.evaluate('item.id').then(function(id){
return val === "1234";
})
}).first().$('.btn').click();
Source: http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.evaluate

Related

Angular 2, get edited data from HTML table

Our angular 2 application has a html table, that displays data from the database. Now we need to make few items editable in the table. I have made the needed items editable and added the button update, when clicked on the update button I need to get the edited value of the row along with the id of the row.
Please help me how to achieve it. Given below is my code.
<tr *ngFor="let row of tableData;">
<td >{{ row.id}}</td>
<td contenteditable="true">{{ row.editableData1 }}</td>
<td contenteditable="true">{{ row.editableData2}}</td>
<td style="color: black;">{{ row.user }}</td>
<td class="link">
<button (click)="updatetoDB()" type="button"> Update </button>
</td>
</tr>
So When user clicks on the Update Button, I need to get the edited data and the id of the row. Thank you in Advance :)
#data1 method maybe not work because td element does not have value attribute.
Can you try to add an input element into your td like this :
<tr *ngFor="let row of tableData;">
<td >{{ row.id}}</td>
<td contenteditable="true"><input type="text" [value]="row.editableData1" #data1/></td>
<td contenteditable="true"><input type="text" [value]="row.editableData2" #data2/></td>
<td style="color: black;">{{ row.user }}</td>
<td class="link">
<button (click)="updatetoDB(data1, data2, row.id)" type="button">Update</button>
</td>
</tr>
I think it should be working, but you habe to include an input element into your td.
It's annoying ?
Hi,
The best solution is to use reactive form.
But i think you can name the edited data and pass it to your fonction updateToDB like this :
<tr *ngFor="let row of tableData;">
<td >{{ row.id}}</td>
<td contenteditable="true" #data1>{{ row.editableData1 }}</td>
<td contenteditable="true" #data2>{{ row.editableData2}}</td>
<td style="color: black;">{{ row.user }}</td>
<td class="link">
<button (click)="updatetoDB(data1, data2, row.id)" type="button"> Update </button>
</td>
</tr>
Log the content of data1 ans data2 variables, i think tour data should be in data1.value ans data2.value.
Hope that help you.

Radio button is selecting wrongly from data list in angular js and dynamic validation

I have data list which will display in the form of multiple radio buttons . each row will have 3 fields .
ex: row1:abc , 123 , xyz
row2: xyz , 145 , xyz
now when i select radio button in first row it is selecting second row . How to make that radio row unique ?
<label ng-repeat="list in data">
<table>
<tr
<td rowspan="2" >
<input type="radio" name="select" />
</td>
<td>
{{list.name}}
</td>
<td>
{{list.number}}
</td>
</tr>
<tr>
<td>
{{list.value}}
</td>
</tr>
</table>
Also how do I validate these fields , if they are comming as null I have display error message since it is dynamic data and this page is read only page .
Thanks in advance
For the null validation you can use a conditional in a ng-bind directive and for selecting different values you need a different name for each row
<label ng-repeat="list in data">
<table>
<tr
<td rowspan="2" >
<input type="radio" ng-attr-name="{{'select' + list.number}}" />
</td>
<td ng-bind="{{list ? list.number: 'error name'}}">
</td>
<td ng-bind="{{list ? list.number: 'error number'}}">
</td>
</tr>
<tr>
<td ng-bind="{{list ? list.value : 'error value'}}">
</td>
</tr>
</table>
I'm not sure if the ng-attr-name will work :( please let me know if this works
In html
<table>
<tr ng-repeat="data in list">
<td>
<input type="radio" ng-model="val.value" value="{{$index}}">
</td>
<td>
<span ng-show="data.name">{{data.name}}</span>
<span ng-hide="data.name">Error in name</span>
</td>
<td>
<span ng-show="data.number">{{data.number}}</span>
<span ng-hide="data.name">Error in number</span>
</td>
</tr>
</table>
In controller
$scope.val ={} // take this object this will store current row number
//$scope.val.value // contain row number
var data = $scope.list[$scope.val.value] // this will give u data at that row

Protractor - Get text from table

I have list of "cases" in a table, where I need to find specific one just by NAME and click on it.
My HTML looks like:
<tr ng-repeat-start="case in cases | orderBy:order:order_reverse" class="middle ng-scope odd readonly" ng-class-odd="'odd'" ng-class-even="'even'" ng-class="{readonly: !caseManager.isWritable(case)}">
<td>
<span ax-sharing-circle="SHARED_WITH_PARTNER" class="ng-isolate-scope"><span class="icon sharing-circle sharing-circle-left" title="Sdílený"></span></span>
</td>
<td class="case-table-claim-number fix-v-align">
<a ui-sref="case.general({caseId: case.caseId})" class="ng-binding" href="#/case/0a0b1c2a-94b4-444c-a2b8-c62cbd3532ae/general">20150629-165000-65</a>
</td>
<td class="case-table-claim-number fix-v-align ng-binding"></td>
<td class="case-table-claim-number fix-v-align ng-binding"></td>
<td class="case-table-make fix-v-align ng-binding"></td>
<td class="case-table-make fix-v-align ng-binding"></td>
<td class="case-table-status fix-v-align ng-binding">
29.6.2015
</td>
<td class="case-table-status fix-v-align ng-binding"></td>
<td>
<span class="glyphicon glyphicon-menu-down" ng-class="{
'glyphicon glyphicon-menu-up': hasOverview(case.caseId),
'glyphicon glyphicon-menu-down': !hasOverview(case.caseId),
}" ng-click="toggleOverview(case.caseId)"></span>
</td>
</tr>
In example above you can find string 20150629-165000-65 which is name of the case.
I try to write something like this:
element(by.cssContainingText('a', global.caseNumber)).click();
which should find element and click on it, instead of that it throw error:
No element found using locator: by.cssContainingText("a", "20150629-165000-65")
Can someone advise me how to do this?
There is a relevant by.linkText() locator that should fit the use case:
var link = element(by.linkText(global.caseNumber));
If it still doesn't find the desired element, try adding a wait:
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf(link), 5000);

Simple jQuery selector isn't working

I'm trying to parse this HTML:
<tr id="a">
<td class="classA">
<span class="classB">Toronto</span>
</td>
<td class="classC">
<span class="classD">Winnipeg</span>
</td>
</tr>
<tr id="b">
<td class="classA">
<span class="classB">Montreal</span>
</td>
<td class="classC">
<span class="classD">Calgary</span>
</td>
</tr>
I have a variable team. I want to find the <span> that contains team. Then I want to navigate up to the <tr> and pull the id from it.
I tried:
var team = "Toronto";
var id = $("span:contains(" + team + ")").parent().parent().attr('id');
But it comes back undefined. I know the selector is right, because $("span:contains(" + team + ")").attr('class') comes back with classB. So I can't figure out what's wrong with my query. Can anyone help?
Edit: Here's the JSFiddle.
Your html is invalid but your selector is correct, you need to put tr in table tag for valid html. You better use closest("tr") instead of .parent().parent()
Live Demo
<table>
<tr id="a">
<td class="classA"> <span class="classB">Toronto</span>
</td>
<td class="classC"> <span class="classD">Winnipeg</span>
</td>
</tr>
<tr id="b">
<td class="classA"> <span class="classB">Montreal</span>
</td>
<td class="classC"> <span class="classD">Calgary</span>
</td>
</tr>
</table>
It's not working becuase the browser's automatically fixing your HTML. You can't have a TR without a table so it's just throwing it away. All that's actually part of the DOM by the time your JavaScript runs is the spans.
Wrap it in a <table> and your code will work. Even better wrap it in <table><tbody> because the browser will still be making a tbody for you with just a table & that might cause confusion next (If you look at the parent of the TR).
Currently your HTML markup is invalid, you need to wrap <tr> element inside <table>:
<table>
<tr id="a">
<td class="classA"> <span class="classB">Toronto</span>
</td>
<td class="classC"> <span class="classD">Winnipeg</span>
</td>
</tr>
<tr id="b">
<td class="classA"> <span class="classB">Montreal</span>
</td>
<td class="classC"> <span class="classD">Calgary</span>
</td>
</tr>
</table>
Also, it's better to use .closest() as well as .prop() instead of .parent() and .attr()
var id = $("span:contains(" + team + ")").closest('tr').prop('id');
Fiddle Demo
try:
var id = $("span:contains(" + team + ")").parent('td').parent('tr').attr('id');
Your code works good.Just wrap your code into <table></table>
HTML
<table>
<tr id="a">
<td class="classA">
<span class="classB">Toronto</span>
</td>
<td class="classC">
<span class="classD">Winnipeg</span>
</td>
</tr>
<tr id="b">
<td class="classA">
<span class="classB">Montreal</span>
</td>
<td class="classC">
<span class="classD">Calgary</span>
</td>
</tr>
</table>
Script
var team = "Toronto";
var id = $("span:contains(" + team + ")").closest('tr').prop('id');
console.log(id)
http://jsfiddle.net/7Eh7L/
Better use closest()
$("span:contains(" + team + ")").closest('tr').prop('id');

jQuery Traversing

My page has this design.
<tr id="master">
<td id="row1"> <textbot> </td>
<td id="row2"> <linkbutton> </td>
</tr>
I am cloning the same TR onclick of the link button and the current link button will be hidden. so a new row will be created.
consider I have cloned thrice
<tr id="master">
<td id="row1"> <textbot> </td>
<td id="row2"> <linkbutton> </td>
</tr>
<tr id="master">
<td id="row1"> <textbot> </td>
<td id="row2"> <linkbutton> </td>
</tr>
<tr id="master">
<td id="row1"> <textbot> </td>
<td id="row2"> <linkbutton> </td>
</tr>
Now I want to show the second row's link button.
I am trying with
$(this).parents('#master :last').prev().children('#row2).show();
But its not working.
You can't/shouldn't clone elements that have IDs, because IDs are supposed to be unique in a document. Although a browser will happily let you have the same ID twice, Javascript and jQuery do not forgive you for this and things will not work as expected. The proper way to group elements is by classes.
So if you switch your code to this:
<tr class="master">
<td class="row1"> <textbot> </td>
<td class="row2"> <linkbutton> </td>
</tr>
Your selector might look like this:
$('tr.master').eq(1).find('td.row2').show();
you know, ID must be unique .. so i recommend to change all id to class.
i think the syntax for parrent should :
$('#master:parent');

Categories

Resources