Angular e2e test to check a list of elements - angularjs

My case is as follows - I have a simple page which lists available products:
<ul class="products ng-scope" id="products">
<!-- ngRepeat: tag in products -->
<li ng-repeat="product in products" class="ng-scope">
<a ng-href="/#/viewProduct/1" class="ng-binding" href="/#/viewProduct/1">Product A</a>
</li><li ng-repeat="product in products" class="ng-scope">
<a ng-href="/#/viewProduct/2" class="ng-binding" href="/#/viewProduct/2">Product B</a>
</li><li ng-repeat="product in products" class="ng-scope">
<a ng-href="/#/viewProduct/3" class="ng-binding" href="/#/viewProduct/3">Product C</a>
</li><li ng-repeat="product in products" class="ng-scope">
<a ng-href="/#/viewProduct/4" class="ng-binding" href="/#/viewProduct/4">Product D</a>
</li><li ng-repeat="product in products" class="ng-scope">
<a ng-href="/#/viewProduct/5" class="ng-binding" href="/#/viewProduct/5">Product E</a>
</li>
</ul>
In my angular scenario I want to test if the list of products is equal to expected. I know it can/should be a unit test, but suppose I want an e2e test.
After reading the official docs (http://docs.angularjs.org/guide/dev_guide.e2e-testing - which as usual cover 10% of the subject) and debugging angular code I managed to write the following test:
'use strict';
describe('my app', function() {
var productsList = [
'Product A',
'Product B',
'Product C',
'Product D',
'Product E'
];
it('should list all products', function() {
browser().navigateTo('/#/products');
var foundProducts = element('#products li').query(function(elements, done) {
var productsArray = [];
elements.each(function(index) {
productsArray.push(elements[index].innerText);
});
done(null, productsArray);
});
expect(foundProducts).toEqual(productsList);
});
});
Can anyone please tell me how this can be done easier?
And the second question, why elements[index].text() is undefined - which in fact is a question how to jet a jQuery object out of elements[index].

Found the solutution:
describe('my app', function() {
var productsList = [
['Product A'],
['Product B'],
['Product C'],
['Product D'],
['Product E']
];
it('should list all products', function() {
browser().navigateTo('/#/products');
expect(repeater('#products li').count()).toEqual(productsList.length);
for (var i = 0; i < productsList.length; i++) {
expect(repeater('#products li').row(i)).toEqual(productsList[i]);
}
});
});

Related

knockout js - Display selected nested checkboxes

I have some code that lists some categories and subcategories with checkboxes but i can not get it to show a which checkboxes have been checked in a nested ul. It does return the selected items but not in a ul list.
please could you show me how to display the selected categories and sub catagories in a nested ul. Thanks.
var ViewModel = function() {
var self = this;
self.selectedCategories = ko.observableArray();
self.selectedItems = ko.observableArray();
self.categories = ko.observableArray([
{ name: 'Hospitality', items: [ 'Bars', 'Caterers', 'Cafes', 'Food To Go', 'Pubs' ] },
{ name: 'Popup', items: [ 'Food Vans', 'Festivals', 'Markets', 'Beer Garden' ] }
]);
}
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<ul data-bind="foreach: { data: categories, as: 'category' }">
<li> <input type="checkbox" name="level-1" data-bind="checked: $root.selectedCategories, attr: {value: name}"><span data-bind="text: category.name"></span></input>
<ul data-bind="foreach: { data: items, as: 'item' }">
<li><input type="checkbox" name="level-2" data-bind="checked: $root.selectedItems, attr: {value: item}"><span data-bind="text: item"></span></input></li>
</ul>
</li>
</ul>
<div data-bind="text: selectedCategories"></div>
<div data-bind="text: selectedItems"></div>
I'd suggest to store whether a category is selected, and its selected items, in the categories own model. You can make a computed in your ViewModel to create an array with only selected categories. Here's an example:
var ViewModel = function() {
var self = this;
self.categories = ko.observableArray([{
name: 'Hospitality',
items: ['Bars', 'Caterers', 'Cafes', 'Food To Go', 'Pubs'],
selected: ko.observable(false),
selectedItems: ko.observableArray([])
}, {
name: 'Popup',
items: ['Food Vans', 'Festivals', 'Markets', 'Beer Garden'],
selected: ko.observable(false),
selectedItems: ko.observableArray([])
}]);
self.selectedCategories = ko.computed(function() {
return self.categories().filter(function(cat) {
return cat.selected()
});
});
}
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul data-bind="foreach: { data: categories, as: 'category' }">
<li>
<input type="checkbox" name="level-1"
data-bind="checked: selected, value: name">
<span data-bind="text: name"></span>
<ul data-bind="foreach: { data: items, as: 'item' }">
<li>
<input type="checkbox" name="level-2"
data-bind="checked: category.selectedItems, value: item, enable: category.selected">
<span data-bind="text: item, style: { 'opacity' : (category.selected() ? 1 : 0.5)}"></span>
</li>
</ul>
</li>
</ul>
<h2>Selections</h2>
<ul data-bind="foreach: selectedCategories">
<li>
<div data-bind="text: name"></div>
<ul data-bind="foreach: selectedItems">
<li data-bind="text: $data"></li>
</ul>
</li>
</ul>
Note: You had some invalid HTML in your snippet: you can't put a <span> element in an <input> element.

How to go from one <a> to another <a> in angularJs

CONTROLLER
$scope.employee = {};
$scope.setEmployee = function (employee) {
$scope.employeeId = employee.id;
$scope.employee = employee;
a($scope.employeeId);
};
var a = function () {
$scope.meetingTypes = MeetingService.findByEmployee({
'employeeId': $scope.employeeId
}, function () {
angular.forEach($scope.meetingTypes, function (meetingType) {
$scope.meetings = MeetingService.findByMeetingType(meetingType);
});
});
};
TEMPLATE
<tr>
<td>
<a ng-repeat="meetingType in meetingTypes">
- {{meetingType}}
</a>
<a ng-repeat="meeting in meetings">
- {{meeting}}
</a>
</td>
</tr>
Here I want to open second <a> tag on click of first <a> tag
The view should like tree view
LIKE :
- meeting_type
- meeting 1
- meeting 2
all services are declared in my .factory
Here I am getting meeting Type properly but i want to open list of meetings on click of meetingType <a> tag but i am not getting meetings here,
Is ng-href is helpfull here ?
Here is a some sample code which I think demonstrates what you want to acheive -
<table ng-app="myApp" ng-controller="TestCtrl">
<tr>
<td>Get Meetings</td>
</tr>
<tr>
<td>
<ul>
<li ng-repeat="meetingType in meetingTypes track by $index">
- {{meetingType.typeName}}
<ul ng-show="showMeetings && meetingType.meetings.length>0">
<li ng-repeat="meeting in meetingType.meetings track by $index">
<a>- {{meeting}}</a>
</li>
</ul>
</li>
</ul>
</td>
</tr>
</table>
javascripts -
'user strict';
angular.module('myApp', [])
.controller('TestCtrl', function($scope) {
$scope.employee = {};
$scope.meetingTypes = {};
$scope.setEmployee = function(employee) {
$scope.employeeId = 1;
//$scope.employee = {};
a($scope.employeeId);
};
var a = function() {
// your service should return this json
$scope.meetingTypes = [{
typeName: 'abc',
meetings: ['meeting1', 'meeting2', 'meeting3']
}, {
typeName: 'def',
meetings: ['meeting4', 'meeting5', 'meeting6']
}, {
typeName: 'ghi',
meetings: ['meeting7', 'meeting8', 'meeting8']
}];
};
});
jsfiddle

Conditional ngRepeat based on dropdown

I would like to display a list based on which item is selected in a dropdown.
When I use the code below, I'm getting this error:
TypeError: Cannot read property 'type' of undefined
Any suggestions on how to do this right?
HTML:
<select class="form-control" ng-model="editProject.project.type"
ng-options="project as project.type for project in editProject.options track by project.type">
</select>
<ul class="list-group">
<li class="list-group-item" ng-repeat="benefit in editProject.foods()">{{snack}}</li>
</ul>
Controller:
.controller('EditProjectCtrl', function () {
var editProject = this;
editProject.options = [
{'type': 'Fruits'},
{'type': 'Vegetables'},
{'type': 'Desserts'}
];
editProject.snacks = function() {
if(editProject.project.type == 'Fruits') {return [
'Grapes',
'Oranges',
'Apples',
]}
if(editProject.project.type == 'Vegetables') {return [
'Broccoli',
'Spinache',
'Kale',
]}
else {return [
'Cookies',
'Cake',
'Pie']}
};
You are almost there. There was a mismatch between the ng-model of the select element, and the conditions in the controller. I also replaced benefit in editProject.foods() with snack in editProject.snacks():
<select class="form-control" ng-model="editProject.project" ng-options="project as project.type for project in editProject.options track by project.type"></select>
<ul class="list-group">
<li class="list-group-item" ng-repeat="snack in editProject.snacks()">{{snack}}</li>
</ul>
Because editProject.project is not defined until you select a project, you have to initialize it in the controller:
editProject.project = {};
See fiddle

Add a class to last 3 list items in AngularJS

I have a list that is generated via ng-repeat. E.g i have the html like this
<ul class="my-list">
<li>List Item</li>
<li>List Item</li>
<li>List Item</li>
<li>List Item</li>
<li>List Item</li>
<li>List Item</li>
<li>List Item</li>
<li>List Item</li>
</ul>
I want to add a class of .last-itmes to the last 3 list items using angular. How can i do this?
In ng-repeat, Angular provides $index, $first, $last to get the index position of list item.
In your case you can use $last and substract from it like $last-1, $last-2.
Sample code :
<li ng-repeat="item in itemList"
ng-class="{'last':($index === $last) || ($index === ($last-1)) || ($index === ($last-2))}"
>List Item
</li>
Please see demo below
ie:
ng-class="{'last-itmes':$index>=data.length-3}"
var app = angular.module('app', []);
app.controller('homeCtrl', function($scope) {
$scope.data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
});
.last-itmes {
color: red
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="homeCtrl">
<ul class="my-list">
<li ng-repeat="i in data" ng-class="{'last-itmes':$index>=data.length-3}">{{i}}</li>
</ul>
</div>
</div>
My suggestion is that you can go for Directives which is a neat and clean way
var app = angular.module('myApp', []);
app.controller('ArrayController', function ($scope) {
$scope.datas = ['List Item',
'List Item',
'List Item',
'List Item',
'List Item',
'List Item',
'List Item',
'List Item'];
})
.directive("itemWatch", [function () {
return {
scope: {
index: '=index',
length: '=length'
},
replace: false,
link: function ($scope, $element, $attrs, Controller) {
if ($scope.length - $scope.index <= 3) {
$element.addClass('last-itmes');
}
}
};
}]);
.last-itmes
{
color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='myApp' ng-controller="ArrayController">
<ul class="my-list">
<li ng-repeat="item in datas track by $index" index="$index" length="datas.length" item-watch>{{item}}</li>
</ul>
</div>
You can use with js object and ng-show.
var items = [
{id:1,isLast:false}
{id:2,isLast:true}
{id:3,isLast:true}
]
And your html with angularjs:
<li ng-repeat="item in items">
<div class="last-items" ng-show="item.isLast">
</li>
When setting up your ng-repeat, you can use track by $index to determine which position you are on. Doing that, you can easily do things within the repeat. To get it to add a class for just the last three items, you can put a condition on the ng-class directive like so:
<table>
<tbody ng-repeat="row in people track by $index">
<tr ng-class="{'blue': $index > people.length - 4 }">
<td>
<p style="font-weight:bold;">{{ row }}</p>
</td>
</tr>
<tbody>
</table>
I've made a plunker to show an example:
http://plnkr.co/edit/g24eYggfIRFS2El8R5Mu

Data-binding is not updating ng-repeat section

I'm new to angularjs and I have a data binding issue.
After each repeated line, I want to show a number of mutually exclusive options. These would be images using a background-image, and when the option is selected I add the css class 'selected' to the span containing the image.
I've simplified it below to show options A, B and C and to make the currently selected one bold. By clicking on A, B or C you change the option.
The inner ng-repeat section is not evaluated again (it seems) when the model changes, but as the "Debugging category" shows, the outer ng-repeat is re-evaluated and the "Debugging category" shows correctly that the category has been changed.
I'm clearly missing something about angularjs; how can I get the inner ng-repeat to re-evaluate to that the correct style is applied to the A, B and C options?
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
var testApp = angular.module('testApp', []);
testApp.controller('NamesCtrl', function ($scope) {
$scope.categories = [ 'A', 'B', 'C' ];
$scope.names = [
{ name: 'John', category: 'C' },
{ name: 'Cindy', category: 'A' },
{ name: 'Patrick', category: 'B' }
];
$scope.changeCategory = function(name, category) {
name.category = category;
};
});
</script>
<body ng-app="testApp">
<h1>Names</h1>
<div ng-controller="NamesCtrl">
<ul>
<li ng-repeat="name in names">
<span>{{name.name}}</span>
<span ng-repeat="category in categories" ng-init="selected = (category == name.category)">
<span ng-style="{'true': {'font-weight': 'bold'}, false: {}}[selected]" ng-click="changeCategory(name, category)">{{category}}</span>
</span>
<em>(Debugging category: {{name.category}})</em>
</li>
</ul>
</div>
</body>
ng-init expression is not watched and updated automatically whenever related scope property value changes, you could just solve this by removing ng-init and using the expression directly
<span ng-style="{'true': {'font-weight': 'bold'}}[category == name.category]"
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
var testApp = angular.module('testApp', []);
testApp.controller('NamesCtrl', function ($scope) {
$scope.categories = [ 'A', 'B', 'C' ];
$scope.names = [
{ name: 'John', category: 'C' },
{ name: 'Cindy', category: 'A' },
{ name: 'Patrick', category: 'B' }
];
$scope.changeCategory = function(name, category) {
name.category = category;
};
});
</script>
<body ng-app="testApp">
<h1>Names</h1>
<div ng-controller="NamesCtrl">
<ul>
<li ng-repeat="name in names">
<span>{{name.name}}</span>
<span ng-repeat="category in categories" >
<span ng-style="{'true': {'font-weight': 'bold'}}[category == name.category]" ng-click="changeCategory(name, category)">{{category}}</span>
</span>
<em>(Debugging category: {{name.category}})</em>
</li>
</ul>
</div>
</body>

Resources