jasmine testing a mock service in an angular 1.5 controller - javascript

Given the following test.
How do I ensure that the promise is resolved, and the data is provided.
describe("component: FicaStatusComponent",
function () {
var fs;
beforeEach(function () {
module("aureus",
function ($provide) {
$provide.service("ficaService", function () {
this.status = function () {
return $q(function (resolve, reject) {
resolve([{ documentType: { id: 1 } }]);
});
}
})
});
});
beforeEach(inject(function (_$componentController_, _ficaService_) {
$componentController = _$componentController_;
fs = _ficaService_;
}));
it("should expose a `fica` object", function () {
console.log('should expose');
var bindings = {};
var ctrl = $componentController("ficaStatus", null, bindings);
expect(ctrl.fica).toBeDefined();
});
it("compliant with no documents should not be compliant",
function () {
var ctrl = $componentController("ficaStatus");
expect(ctrl.fica.length).toEqual(1);
});
}
);
The second test compliant with no documents... is failing. The length is zero. The other test is passing, so I have the correct controller being instantiated, the property is defined.
The mock service is not resolving the data correctly, probably because the Promise is still executing, or not being called at all.
Here is the implementation of the controller for the component:
var FicaStatusController = (function () {
function FicaStatusController($log, $loc, ficaService) {
var _this = this;
this.$log = $log;
this.$loc = $loc;
this.ficaService = ficaService;
this.fica = [];
this.ficaService.status(1234).then(function (_) { return _this.fica = _; });
}
The service is as follows:
var FicaStatusService = (function () {
function FicaStatusService($log, $http) {
this.$log = $log;
this.$http = $http;
}
FicaStatusService.prototype.status = function (accountNumber) {
var url = "api/fica/status/" + accountNumber;
this.$log.log("status: " + url);
return this.$http
.get(url)
.then(function (_) { return _.data; });
};
return FicaStatusService;
}());
...

First, u can use $q like:
this.status = function () {
return $q.when([{ documentType: { id: 1 } }]);
}
Second, to resolve promise use $scope.$digest, $rootScope.$digest:
var a = $q.when({test: 1});
expect(a.test === 1).toBe(false);
$rootScope.$digest();
expect(a.test === 1).toBe(true);

Related

AngularJS service does not return value from http.get

can someone help me with this code? I have problem with return value, function in controller return only
var products = {"id": 3};
I want to collect value from http.get, can someone tell me how to do that??
Controller:
$scope.product = {};
$scope.init = function () {
$scope.loadProducts()
}
$scope.loadProducts = function () {
// $http.get("/products/list").then(function (resp) {
// $scope.products = resp.data;
// })
$scope.products = getListProducts.loadProducts();
}
Service
var myServices = angular.module('myServices', []);
myServices.service('getListProducts', ['$http', function ($http) {
var products = {"id": 3};
this.loadProducts = function () {
$http.get("/products/list").then(function (resp) {
products = resp.data;
})
return products;
}
}]);
you are returning products before http success , instead use promises and resolve when http success
$scope.product = {};
$scope.init = function () {
$scope.loadProducts()
}
$scope.loadProducts = function () {
// $http.get("/products/list").then(function (resp) {
// $scope.products = resp.data;
// })
$scope.productPromise = getListProducts.loadProducts();
productPromise..then(function (resp) {
$scope.products = resp.data;
});
}
Service
var myServices = angular.module('myServices', []);
myServices.service('getListProducts', ['$http', function ($http) {
var products = {"id": 3};
this.loadProducts = function () {
return $http.get("/products/list");
}
}]);
Make use of promises to enforce serialization of your async code.
Refactor your service method as:
this.loadProducts = function () {
var getProducts = new Promise(function(resolve,reject){
$http.get("/products/list").then(function (resp) {
resolve(resp.data);
})
});
return getProducts;
};
And your Controller method as:
getListProducts.loadProducts().then(function(data){
//success callback
$scope.products = data;
});
You can provide the error callbacks as well.
Hope this helps !
You should use promises to return values from your service.
You can use $q in your service. It would help functions to run asynchronously.
myServices.service('getListProducts', ['$http','$q', function ($http,$q) {
var products = {"id": 3};
this.loadProducts = function () {
var deferred = $q.defer();
$http.get("/products/list").then(function (resp) {
products = resp.data;
deferred.resolve(products);
},function(error){
deferred.reject(error);
});
return deferred.promise;
}
}]);
And Your method in controller should handle success and error callbacks :
$scope.loadProducts = function () {
getListProducts.loadProducts().then(function(response){
$scope.products=response;
},function(error){
//your processing logic
});
}
I hope this would help you.

Testing method in angular resolve

I am writing the test case for testing a service call in angular. I am returning a variable value in the resolve. My issue is that i am getting an undefined value as a response after the promise is resolved. following is the piece of code i am using.
resolve: {
peopleData: ($q, service, apiOperations, errorService) => {
const getHelloWorld = () => {
{
return‘ hello - world ';
}
};
const errorHandler = (error, operation) => {
var deferred = $q.defer();
errorService.handle(error, operation);
deferred.resolve({});
return deferred.promise;
};
service.getPeopleName().then((response) => {
console.log('called then method');
if (!angular.isUndefined(response)) {
return response;
} else {
return {};
}
}).catch((error) => {
errorHandler(error, apiOperations.GET_PEOPLE);
return {};
});
}
Now my test case is
describe('state peopleService application', function() {
let $q, authService, $state, state_name = 'main.application',
result, $rootScope, applicationService;
let errorService = {
handle: function(error, operation) {}
};
let apiOperations = {
GET_People: ‘getPeople ',
TEST_ERROR: 'testError'
};
angular.module('mock.Service', []).service('mockPeopleService', function() {
var peopleService = {};
peopleService.getPeople = function() {
let deferred = $q.defer();
deferred.resolve(‘dummy response ');
return deferred.promise;
}
return applicationService;
}); beforeEach(angular.mock.module(home)); beforeEach(angular.mock.module('mock.Service')); beforeEach(angular.mock.inject(function(_$q_, _$state_, _mockPeopleService_, _$rootScope_) {
$q = _$q_;
$state = _$state_;
$rootScope = _$rootScope_;
peopleService = _mockPeopleService_;
})); it('should load the data', () => {
let state = $state.get(state_name);
let result = state.resolve.peopleData($q, peopleService, apiOperations, errorService);
$rootScope.$apply();
console.log('result ' + result);
});
})
});
The log.console is returning result undefined
Remember that in an arrow function with {curly braces}, a return needs to be explicit, just like in regular functions. So with resolve.peopleData() as currently written, undefined is hardly surprising.
Adding a return and tidying, you might end up with something like this :
resolve: {
peopleData: ($q, service, apiOperations, errorService) => {
return service.getPeopleName().then(response => {
^^^^^^
if (angular.isUndefined(response)) {
throw new Error('response is undefined');
}
return response;
}).catch(error => {
errorService.handle(error, apiOperations.GET_PEOPLE);
return {};
});
}
}
Now, the test stands at least a chance of working.

Angular testing controller using mock factory which returns promise

I'm trying to test an Angular controller and mock a factory so that I can use it within this same test. I'm fairly new to Angular testing & have been having trouble figuring out how to this. My factory, doesn't use the $http rather the $q service, returns a promise. I'm also unsure of what to put inside my mock factory given that the factory's function calls return a promise.
My end goal is to call my mock factory from my controller and then check the two arrays in my controller for the data which is supposed to populate them. If you have any tips for restructuring my tests for testability, please do give feedback.
Angular Controller
export class workListController {
constructor(dataService, $q) {
this.$q = $q;
this.work = [];
this.tasks = [];
this.dataService = dataService;
this.setup();
}
setup() {
this.$q.all([this.dataService.getWorkItems(), this.dataService.getTasks()])
.then(() => {
this.work = this.dataService.getState().work;
this.tasks = this.dataService.getState().tasks;
this.dataService.addNumberOTasksToWork();
});
}
tasksForWork(workId) {
var workTasks = [];
for (let task of this.tasks) {
if (task.agf__Work__c === workId) {
workTasks.push(task);
}
}
return workTasks;
};
}
Angular Factory
const dataService = ($q) => {
let work = [];
let tasks = [];
let connection = new Connection{/**/};
return { getWorkItems, getTasks, addNumberOTasksToWork, getState};
function queryWrapper(query) {
var deferred = $q.defer();
connection.query(query)
.then(function(result) {
deferred.resolve(result);
}, function(error) {
deferred.reject(error);
});
return deferred.promise;
}
function getWorkItems() {
return queryWrapper(`SELECT Id, ......`)
.then((data) => {
//data looks like this: {totalSize: 3, done: true, records: [......]}
work = data.records;
});
}
function getTasks() {
return queryWrapper(`SELECT Id,...`)
.then((data) => {
//data looks like this: {totalSize: 3, done: true, records: [......]}
tasks = data.records;
});
}
function addNumberOTasksToWork() {
work.forEach((workItem) => {
workItem.numberOfTasks = 0;
});
work.forEach((workItem) => {
tasks.forEach((taskItem) => {
if (taskItem.agf__Work__c === workItem.Id) {
workItem.numberOfTasks++;
}
});
});
}
function getState(){
return {work,tasks};
}
};
export {dataService};
Test file
import {workList} from './work-list.module.js';
import {workListDirective} from './work-list.directive.js';
import template from './work-list.html';
import {workListController} from './work-list.controller.js';
describe('AA_TaskBoard - workList', function () {
let $scope;
let $controller;
let $httpBackend;
let mockDataService;
beforeEach(angular.mock.module(workList.name));
//trying to mock factory
beforeEach(angular.mock.module(function($provide) {
$provide.value('dataService', mockDataService);
mockDataService = {
getWorkItems: function(){
//this should return a promise, but unsure of what to put here
return {
};
},
getTasks: function(){
return {
};
}
};
}));
beforeEach(inject(function(_$rootScope_, _$controller_, _$httpBackend_) {
$rootScope = _$rootScope_;
$controller = _$controller_;
$httpBackend = _$httpBackend_;
}));
});

not able to pass data to a data object in angularjs

N.B: I'm pretty much new to angularJS programming. What I'm trying to do is, to save info returned by a service to an object. My object looks like this.
var userObject = {
"id": "1",
"firstName": "Amelia",
"lastName": "Earheart"
};
I have a factory that returns data from back end and it looks like this:
.factory('myService', function($http) {
var baseUrl = 'backendservice/';
return {
myInfo:function() {
return $http.get(baseUrl + 'getmyInfo');
}
};
})
And this is how my Controller communicates with the factory service:
.controller('myController', function($routeParams,myService) {
var my = this;
my.basicInfo = function () {
//to get my info
myService.myInfo().success(function(data) {
my.activeUser.myData.id = data.id;
my.activeUser.myData.firstName = data.firstName;
my.activeUser.myData.lastName = data.lastName;
});
};
my.addSomething = function(post) {
var userObject = my.basicInfo();
};
});
and this is how I assign the data to userObject
var userObject = my.basicInfo();
I don't know why it's not working. Factory service runs but the value is not assigned to userObject.
My Controller as whole looks like this:
(function() {
angular
.module('myApp.spa', [])
.factory('myService', function($http) {
var baseUrl = 'backendservice/';
return {
myInfo:function() {
return $http.get(baseUrl + 'getmyInfo');
}
};
})
.controller('myController', function($routeParams,myService) {
var my = this;
my.basicInfo = function () {
//to get my info
myService.myInfo().success(function(data) {
my.activeUser.myData.id = data.id;
my.activeUser.myData.firstName = data.firstName;
my.activeUser.myData.lastName = data.lastName;
});
};
my.addSomething = function(post) {
var userObject = my.basicInfo();
};
});
})();
Your function my.basicInfo() does not return anything so the value of your variable userObject is undefined. Also if you want to use userObject on view expose it on your controller instance as my.userObject.
If you want to assign a value to userObject, do it either within the success callback of my.basicInfo() method or return a promise from the method my.basicInfo() and assign the value in then callback of the promise
Approach 1
my.basicInfo = function () {
//to get my info
var activeUser = {};
return myService.myInfo()
.then(function(response) {
angular.extend(activeUser, response.data);
my.userObject = activeUser;
});
};
Approach 2
my.basicInfo = function () {
//to get my info
var activeUser = {};
return myService.myInfo()
.then(function(data) {
angular.extend(activeUser, response.data);
return activeUser;
});
};
my.addSomething = function(post) {
my.basicInfo()
.then(function (response) {
my.userObject = response;
});
};
Reason is my.basicInfo does not return anything and also from $http.success/failure, you can not return any value.
So in this case, following steps you would have to do:
Define var userObject at the top of your controller so that can be accessible to all the methods.
Assign data to userObject inside success callback of $http
(function() {
angular
.module('myApp.spa', [])
.factory('myService', function($http) {
var baseUrl = 'backendservice/';
return {
myInfo:function() {
return $http.get(baseUrl + 'getmyInfo');
}
};
})
.controller('myController', function($routeParams,myService) {
var my = this;
var userObject;
my.basicInfo = function () {
//to get my info
myService.myInfo().success(function(data) {
my.activeUser.myData.id = data.id;
my.activeUser.myData.firstName = data.firstName;
my.activeUser.myData.lastName = data.lastName;
userObject = data;
});
};
my.addSomething = function(post) {
my.basicInfo();
};
});
})();
.factory('UserInfo', function($resource, apiHost) {
return $resource(apiHost + '/userinfo/:userId');
});
.controller('myController', function($routeParams,UserInfo) {
var vm = this;
// suppose that you have stored the userId somewhere after the login
vm.userObject = {};
var myUserInfo = UserInfo.get({
userId: userId
});
vm.refreshData = function (){
myUserInfo.$promise
.then(function(response) {
vm.userObject = response;
}, function(error) {
// doSomething
});
};
vm.update = function(){
myUserInfo.save(vm.userObject, function() {
// console.log('success');
}, function(error) {
// console.log('error');
});
};
});

Angular and PhoneGap Event Queuing

I have this:
app.factory('contacts', function ($rootScope, $q, cordovaReady) {
return {
find: cordovaReady(function (filter) {
var deferred = $q.defer();
var options = new ContactFindOptions();
options.filter = filter;
options.multiple = true;
var fields = ["displayName", "name", "addresses", "emails"];
navigator.contacts.find(fields, function (contacts) {
$rootScope.$apply(function () {
deferred.resolve(contacts);
});
}, function (error) {
$rootScope.$apply(function () {
deferred.reject(error);
});
}, options);
return deferred.promise;
})
};
and
app.factory('cordovaReady', function () {
return function (fn) {
var queue = [];
var impl = function () {
queue.push(Array.prototype.slice.call(arguments));
};
document.addEventListener('deviceready', function () {
queue.forEach(function (args) {
fn.apply(this, args);
});
impl = fn;
}, false);
return function () {
return impl.apply(this, arguments);
};
};
});
Whenever I call from the controller:
var contactSearch = '';
contacts.find(contactSearch).then(function (contacts) {
$scope.contacts = contacts;
}, function (error) {
console.log(error);
});
I get:
ReferenceError: ContactFindOptions is not defined
at Object.<anonymous>
I made sure to wrap the function with cordovaReady. Why is this happening?
Can you go through this answer -
Uncaught ReferenceError: ContactFindOptions is not defined
Also make sure that your app.js should be included after cordova.js or phonegap JS in index.html.
I also suggest use ng-cordova wrapper for contact plugin.
include ng-cordova.js before your js in index file.
Inject ngCordova to your app module.
Inject $cordovaContacts to your service/factory.
For more visit http://ngcordova.com/
Ex.
var services = angular.module("services", ['ngCordova']);
services.service('contact', contact);
function contact($cordovaContacts, $q) {
return {
find : function() {
var deferred = $q.defer();
var options = {};
options.filter = "";
options.multiple = true;
$cordovaContacts.find(options).then(function(contacts) {
for (var i = 0; i < contacts.length; i++) {
if (null != contacts[i].phoneNumbers) {
for (var j = 0; j < contacts[i].phoneNumbers.length; j++) {
alert(contacts[i].phoneNumbers[j].value);
if (null != contacts[i].emails) {
alert(contacts[i].emails[0].value);
}
alert(contacts[i].displayName);
}
}
deferred.resolve();
}, function(err) {
deferred.reject();
alert("error in contact find");
});
return deferred.promise;
};
};
Hope this answer help you.

Categories

Resources