Let's fetch a larger dataset from our server using one of AngularJS's built-in services called $http
There is now a list of 20 phones, loaded from the server
We have to add a new folder with the phones list
[
{
"age": 0,
"id": "motorola-xoom-with-wi-fi",
"imageUrl": "img/phones/motorola-xoom-with-wi-fi.0.jpg",
"name": "Motorola XOOM\u2122 with Wi-Fi",
"snippet": "The Next, Next Generation\r\n\r\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb)."
},
{
"age": 1,
"id": "motorola-xoom",
"imageUrl": "img/phones/motorola-xoom.0.jpg",
"name": "MOTOROLA XOOM\u2122",
"snippet": "The Next, Next Generation\n\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb)."
},
...
]
This is the source code modifications
app/phone-list/phone-list.component.js
'use strict';
// Register `phoneList` component, along with its associated controller and template
angular.
module('phoneList').
component('phoneList', {
templateUrl: 'phone-list/phone-list.template.html',
controller: ['$http', function PhoneListController($http) {
var self = this;
self.orderProp = 'age';
$http.get('../phones/phones.json').then(function(response) {
self.phones = response.data;
});
}]
});
app/phone-list/phone-list.component.spec.js
'use strict';
describe('phoneList', function() {
// Load the module that contains the `phoneList` component before each test
beforeEach(module('phoneList'));
// Test the controller
describe('PhoneListController', function() {
var $httpBackend, ctrl;
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
// This allows us to inject a service and assign it to a variable with the same name
// as the service while avoiding a name conflict.
beforeEach(inject(function($componentController, _$httpBackend_) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('../phones/phones.json')
.respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
ctrl = $componentController('phoneList');
}));
it('should create a `phones` property with 2 phones fetched with `$http`', function() {
expect(ctrl.phones).toBeUndefined();
$httpBackend.flush();
expect(ctrl.phones).toEqual([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
});
it('should set a default value for the `orderProp` property', function() {
expect(ctrl.orderProp).toBe('age');
});
});
});
e2e-tests/scenarios.js
'use strict';
// AngularJS E2E Testing Guide:
// https://docs.angularjs.org/guide/e2e-testing
describe('PhoneCat Application', function() {
describe('phoneList', function() {
beforeEach(function() {
browser.ignoreSynchronization = true; // Disable Angular synchronization
browser.get('http://127.0.0.1:5500/app/index.html');
});
it('should filter the phone list as a user types into the search box', function() {
var EC = protractor.ExpectedConditions;
var phoneList = element.all(by.repeater('phone in $ctrl.phones'));
var query = element(by.model('$ctrl.query'));
browser.wait(EC.presenceOf(query), 5000, 'Query input taking too long to appear in the DOM');
expect(phoneList.count()).toBe(20);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(8);
});
it('should be possible to control phone order via the drop-down menu', function() {
var EC = protractor.ExpectedConditions;
var queryField = element(by.model('$ctrl.query'));
var orderSelect = element(by.model('$ctrl.orderProp'));
var nameOption = orderSelect.element(by.css('option[value="name"]'));
var phoneNameColumn = element.all(by.repeater('phone in $ctrl.phones').column('phone.name'));
function getNames() {
return phoneNameColumn.map(function(elem) {
return elem.getText();
});
}
browser.wait(EC.presenceOf(queryField), 5000, 'Query input taking too long to appear in the DOM');
browser.wait(EC.presenceOf(orderSelect), 5000, 'Order select taking too long to appear in the DOM');
queryField.sendKeys('tablet'); // Let's narrow the dataset to make the assertions shorter
expect(getNames()).toEqual([
'Motorola XOOM\u2122 with Wi-Fi',
'MOTOROLA XOOM\u2122'
]);
nameOption.click();
expect(getNames()).toEqual([
'MOTOROLA XOOM\u2122',
'Motorola XOOM\u2122 with Wi-Fi'
]);
});
});
});
Now let's run the application
Also we have to run the tests
Unit Tests
E2E Tests
In this step, we will add thumbnail images for the phones in the phone list, and links that, for now, will go nowhere
We first add the phone images list to our application
Now we introduce some changes in the code
app/app.css
body {
padding-top: 20px;
}
.phones {
list-style: none;
}
.phones li {
clear: both;
height: 115px;
padding-top: 15px;
}
.thumb {
float: left;
height: 100px;
margin: -0.5em 1em 1.5em 0;
padding-bottom: 1em;
width: 100px;
}
app/phone-list/phone-list.template.html
<div class="col-md-2">
<!--Sidebar content-->
<p>
Search:
<input ng-model="$ctrl.query" />
</p>
<p>
Sort by:
<select ng-model="$ctrl.orderProp">
<option value="name">Alphabetical</option>
<option value="age">Newest</option>
</select>
</p>
</div>
<div class="col-md-10">
<!--Body content-->
<ul class="phones">
<li ng-repeat="phone in $ctrl.phones | filter:$ctrl.query | orderBy:$ctrl.orderProp" class="thumbnail">
<a href="#!/phones/{{phone.id}}" class="thumb">
<img ng-src="{{phone.imageUrl}}" alt="{{phone.name}}" />
</a>
<a href="#!/phones/{{phone.id}}">{{phone.name}}</a>
<p>{{phone.snippet}}</p>
</li>
</ul>
</div>
</div>
</div>
e2e-tests/scenarios.js
'use strict';
// AngularJS E2E Testing Guide:
// https://docs.angularjs.org/guide/e2e-testing
describe('PhoneCat Application', function() {
describe('phoneList', function() {
beforeEach(function() {
browser.ignoreSynchronization = true; // Disable Angular synchronization
browser.get('http://127.0.0.1:5500/app/index.html');
});
it('should filter the phone list as a user types into the search box', function() {
var phoneList = element.all(by.repeater('phone in $ctrl.phones'));
var query = element(by.model('$ctrl.query'));
expect(phoneList.count()).toBe(20);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(8);
});
it('should be possible to control phone order via the drop-down menu', function() {
var queryField = element(by.model('$ctrl.query'));
var orderSelect = element(by.model('$ctrl.orderProp'));
var nameOption = orderSelect.element(by.css('option[value="name"]'));
var phoneNameColumn = element.all(by.repeater('phone in $ctrl.phones').column('phone.name'));
function getNames() {
return phoneNameColumn.map(function(elem) {
return elem.getText();
});
}
queryField.sendKeys('tablet'); // Let's narrow the dataset to make the assertions shorter
expect(getNames()).toEqual([
'Motorola XOOM\u2122 with Wi-Fi',
'MOTOROLA XOOM\u2122'
]);
nameOption.click();
expect(getNames()).toEqual([
'MOTOROLA XOOM\u2122',
'Motorola XOOM\u2122 with Wi-Fi'
]);
});
it('should render phone specific links', function() {
var query = element(by.model('$ctrl.query'));
query.sendKeys('nexus');
element.all(by.css('.phones li a')).first().click();
expect(browser.getCurrentUrl()).toContain('index.html#!/phones/nexus-s');
});
});
});
We run the application
We run the Test cases
Unit Tests
E2E Tests