AngularJS : How to test a promise resolved in a callback function - javascript

I have written a function which opens an indexedDB. I'm using a promise, because I think it is the cleanest way to handle this.
this.$get = function($q, $rootScope, $window) {
return {
finalize: function() {
var deferred = $q.defer();
var dbRequest = $window.indexedDB.open(dbName, dbVersion);
dbRequest.onupgradeneeded = function(event) {
// XXX do something here.
};
dbRequest.onsuccess = function(event) {
db = event.target.result;
console.log('Success!');
deferred.resolve(event);
};
dbRequest.onerror = deferred.reject;
return deferred.promise;
}
};
};
In order to test this, I have created the following test function:
describe('finalize', function() {
it('should initialize the database', function(done) {
var promise = resource.finalize();
promise.then(function() {
console.log('resolved');
var transaction = resource.db.transaction(['Component']);
expect(transaction).toBeDefined();
done();
});
$rootScope.$apply();
});
});
This prints 'Success!' in the console, but the promise is never resolved.
If I move $rootScope.$apply() to the end of the onsuccess function, the promise is resolved, but only for one test. Other tests then throw an error Error: [$rootScope:inprog] $digest already in progress.
How should I resolve this promise? Is a callback function better?

this.$get = function($q, $rootScope, $window) {
return {
finalize: function() {
var deferred = $q.defer();
var dbRequest = $window.indexedDB.open(dbName, dbVersion);
dbRequest.onupgradeneeded = function(event) {
// XXX do something here.
};
dbRequest.onsuccess = function(event) {
db = event.target.result;
console.log('Success!');
$rootScope.$apply(function () {
deferred.resolve(event);
});
};
dbRequest.onerror = function (error) {
$rootScope.$apply(function () {
deferred.reject(error);
});
}
return deferred.promise;
}
};
};
Also you should use var promise = resource.finalize(); one time for all tests

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.

jasmine testing a mock service in an angular 1.5 controller

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);

Firebase Queries In a Factory

I am struggling with the Firebase Queries, I have this Factory:
.factory("usuariosFac", ["$firebaseArray","$q","$firebaseObject",
function($firebaseArray,$q,$firebaseObject) {
return {
getByEmail: function(email){
var ref = firebase.database().ref("usuarios");
var query=ref.orderByChild("email").equalTo(email).on("child_added", function(data) {
console.log(data.val());
return data.val();
});
}
}
}
])
This function is in my Controller:
$scope.findUser = function() {
$scope.usuario=usuariosFac.traeGrupoPorEmail($scope.formLogin.usuario);
};
When I run it, the console Log inside the Factory prints fine. But $scope.usuario is Undefined, why is this?
But $scope.usuario is Undefined, why is this?
The callback function is being called asychronously. The return statement inside a nested function does not return values to the parent function.
Instead, create and return a promise:
app.factory("usuariosFac", ["$firebaseArray","$q","$firebaseObject",
function($firebaseArray,$q,$firebaseObject) {
return {
getByEmail: function(email){
//Create defer object
var future = $q.defer();
var ref = firebase.database().ref("usuarios");
var query=ref.orderByChild("email")
.equalTo(email)
.once("child_added",
function onSuccess(data) {
console.log(data.val());
//RESOLVE
future.resolve(data.val());
},
function onReject(error) {
//OR REJECT
future.reject(error);
}
);
//RETURN promise
return future.promise;
}
}
}
])
In the controller, use the .then method of the returned promise:
$scope.findUser = function() {
var promise = usuariosFac.traeGrupoPorEmail($scope.formLogin.usuario);
promise.then(function onSuccess(data) {
$scope.usuario = data;
}).catch(function onReject(error) {
console.log(error);
throw error;
});
};

Mocha & Chai- "timeout of 2000ms exceeded. Ensure the done() callback is being called in this test."

I'm getting " timeout of 2000ms exceeded. Ensure the done() callback is being called in this test." while unit testing a service call that responds back with a promise. I am expecting a rejected promise.
UNIT TEST - Karma-Mocha-Chai running on PhantomJS
describe('teamService', function () {
var teamSrvc, q;
beforeEach(angular.mock.module('scCommon'));
beforeEach(angular.mock.inject(function ($q, teamService) {
teamSrvc = teamService;
q = $q;
}));
describe('getTeamsByNameStartWith', function () {
it('should return reject promise when passing invalid text to search', function () {
var invalidFirstArg = 132;
var validSecondArg = 10;
return teamSrvc.getTeamsByNameStartWith(invalidFirstArg, validSecondArg).then(
function (result) {
},
function (err) {
err.should.equal("Invalid text to search argument passed.");
}
);
});
});
});
Below is the service i am testing. I tested the teamService while running the site and it does successfully return a rejected promise.
(function (ng) {
'use strict';
ng.module('scCommon')
.service('teamService', ['$q', '$http', function ($q, $http) {
var getTeamsByNameStartWith = function (textToSearch, optLimit) {
var defer = $q.defer();
if (typeof textToSearch != "string") {
defer.reject("Invalid text to search argument passed.");
return defer.promise;
} else if (typeof optLimit != "number") {
defer.reject("Invalid limit option argument passed.");
return defer.promise;
}
$http.get('url')
.success(function (data) {
defer.resolve(data);
})
.error(function () {
defer.reject("There was an error retrieving the teams");
});
return defer.promise;
};
return {
getTeamsByNameStartWith: getTeamsByNameStartWith
}
}])
})(angular);
I've read through other stack overflow answers and non was successful.
Any ideas?
I appreciate the help.
Thanks,
A friend took a look into it and immediately saw the issue. Apparently I needed do a rootScope.$apply.
describe('teamService', function () {
var teamSrvc, q, rootScope;
beforeEach(angular.mock.module('scCommon'));
beforeEach(angular.mock.inject(function ($q, teamService, $rootScope) {
teamSrvc = teamService;
q = $q;
rootScope = $rootScope;
}));
describe('getTeamsByNameStartWith', function () {
it('should return reject promise when passing invalid text to search', function () {
var invalidFirstArg = 132;
var validSecondArg = 10;
teamSrvc.getTeamsByNameStartWith(invalidFirstArg, validSecondArg).then(
function (result) {
},
function (err) {
err.should.equal("Invalid text to search argument passed.");
}
);
rootScope.$apply();
});
it('should return reject promise when passing invalid number to limit', function () {
var validFirstArg = "alex";
var invalidSecondArg = "10";
teamSrvc.getTeamsByNameStartWith(validFirstArg, invalidSecondArg).then(
function (result) {
},
function (err) {
err.should.equal("Invalid limit option argument passed.");
}
);
rootScope.$apply();
});
});

Data not received by Controller from Service

My Service looks like
app.service('SupportService', function ($http, $q, $timeout) {
var data = {result:[]};
var getData = function() {
$http.get('/rest/report/logs')
.then(function (response) {
data.result = response.data.result;
console.log("received logs:" + JSON.stringify(response.data.result));
});
};
getData();
return {
data: data.result
};
});
and in my controller, I do
var init = function () {
$scope.logs = SupportService.data;
console.log("logs = " + $scope.logs);
};
init();
When I run this, All I see on console is
logs =
received logs:[{"lastUpdated":"1430433095000","fileName":"java_pid1748.hprof","size":"2826611251","location":"/logs/java_pid1748.hprof"},{"lastUpdated":"1430862157000","fileName":"processor-debug.log","size":"910693","location":"/logs/processor-debug.log"},{"lastUpdated":"1430861106000","fileName":"processor-debug.log.1","size":"10242519","location":"processor-debug.log.1"},{"lastUpdated":"1430862156000","fileName":"processor-error.log","size":"1015578","location":"/logs/processor-error.log"},{"lastUpdated":"1430861106000","fileName":"logs","size":"204","location":"/logs"},{"lastUpdated":"1430862154000","fileName":"error.log","size":"2420","location":"/error.log"},{"lastUpdated":"1430862149000","fileName":"output.log","size":"71","location":"/output.log"}]
As you could see logs are empty, how to I have it wait while data comes from SupportService?
Thanks
When you say $scope.logs = SupportService.data; that is happening instantly - before your $http call is completed. You need to wait for the $http call to complete then extract the data. Generally the best way to do this is to return the promise that the $http creates:
app.service('SupportService', function ($http, $q, $timeout) {
return {
getData: function() {
return $http.get('/rest/report/logs');
};
};
});
And wait for the promise to resolve in your controller:
var init = function () {
SupportService.getData().then(function(response){
$scope.logs = response;
console.log("logs = " + $scope.logs);
}
};
init();

Categories

Resources