then method of resolved promise not called - angularjs

I am trying to stub a method using sinon, jasmine and $q.
I want that method to return my fake data.
The problem is that the defined then statement is never called and i can not figure out why.
This already is a simplified version but it still isn't working:
The stub is called
The console log Steven Stub is called gets called
None of the then callbacks are called
No error message
Here is my code
var p = {steven: function() {console.log('original steven');}},
pStub = sinon.stub(p, 'steven', function(){
console.log('Steven Stub is called');
var defer = $q.defer();
defer.resolve({item: 5});
return defer.promise;
});
var promise = p.steven();
promise.then(
function(data){console.log('Peter?');},
function(data) {console.log('ERROR?');},
function(data) {console.log('progress?');});
Any idea?

You need to call a digest in order to resolve a promise. In Angular 2.0 this will be fixed, (and Angular 1.2 is slightly better here than Angular 1.1) but in the meanwhile you have to call
$rootScope.$digest()
In order to cause the promises to resolve. This is because promises work via evalAsync. See this question to learn more about how the digest cycle interacts with $q promises lifecycle.

Related

purpose of $q service in angular?

I am not able to understand the use of $q service in angular js. Can some one please give elaboration on this topic that
what is the $q service in angularjs? How can we use that
I think article i wrote about $q might help you.
Introduction to $q
$q is an angular define a service. It’s same as new Promise(). But $q takes things to the next level by enhancing additional feature that developers can use to perform complex tasks more simply.
This is a sample for creating promise using $q
angular.module("app",[])
.controller("ctrl",function($scope,$q){
var work = "resolve";
var promise = $q(function(resolve, reject) {
if (work === "resolve") {
resolve('response 1!');
} else {
reject('Oops... something went wrong');
}
});
promise.then(function(data) {
alert(data)
})
})
$q.defer()
$q.defer() return the instance of the promise constructor. Once you create a defer object there are following methods and properties that you can access from that object
resolve(value) – resolves the derived promise with the value. If the value is a rejection constructed via $q.reject, the promise will be rejected instead.
reject(reason) – rejects the derived promise with the reason. This is equivalent to resolving it with a rejection constructed via $q.reject.
notify(value) - provides updates on the status of the promise's execution. This may be called multiple times before the promise is either resolved or rejected.
promise – {Promise} – promise object associated with this deferred
Conclusion
Use $q for constructing promises from non-promise Objects/callbacks, and utilize $q.all() and $q.race() to work with existing promises.

In AngularJS, what is the best way to force a view update when the model changes?

This is a very common question, but I have never found the answer that works properly. I have come across three answers, but none always works.
$apply: This will force an update, but will randomly through an error if it gets called while a digest is in progress.
"safe apply" where there is a check for a digest in progress before calling $apply. This doesn't always update the view for some reason I haven't been able to determine. In addition, there is a small chance that a digest will start between the check and $apply.
$timeout: according to the documentation, this should work reliably but doesn't seem to always update the view.
from $timeout documentation, the parameter invokeApply:
If set to false skips model dirty checking, otherwise will invoke fn within the $apply block. (default: true)
It never throughs an error, but sometimes doesn't update the view during a page load.
Here is a code sample where the problem occurs during page initialization:
EditService.getEvents(Gparams.curPersonID)
.then(function successCallback(response) {
if (response.status=='200') {
do some stuff
} else {
handle an error
}
var timer = $timeout(function() { })
.then(function successCallback(response) {
do something
});
$scope.$on("$destroy", function(event {
$timeout.cancel(timer)});
}); });
What is the correct answer? Please don't just say what but also discuss why.
Here is a code sample where the problem occurs during page initialization
A common cause of .then methods not updating the DOM is that the promise is not an AngularJS $q service promise. The solution is to convert the suspect promise to a $q service promise with the $q.when method.
//EditService.getEvents(Gparams.curPersonID)
//CONVERT to $q service promise
$q.when(EditService.getEvents(Gparams.curPersonID))
.then(function successCallback(response) {
if (response.status=='200') {
do some stuff
} else {
handle an error
}
The .then method of a $q service promise is integrated with the AngularJS framework and its digest cycle. Changes to the scope model will automatically update the DOM.
when
Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.
--AngularJS $q Service API Reference - $q.when

Best solution to test angular services making use of Parse SDK with Karma

I'm trying to automate the testing of Angular services which happen to make calls to Parse.com through Parse SDK.
The problem I have got is that the promises dont get resolved unless I explicitely trigger a digest cycle, and the way my services are done, I have to do that in my services implementations which is not sustainable.
My service code is the following :
factory('myService', function($http, $q, $rootScope) {
var myService = {};
myService.simplePromiseTest = function() {
var p = $q.defer();
var query = new Parse.Query("AnyObjectInParse");
query.find().then(function(results){
p.resolve(results);
// *** I have to include that line for the jasmine test to run ***
$rootScope.$apply();
});
return p.promise;
}
}
return myService;
}
And here is my jasmine test
async.it('should resolve the promise', function(done) {
myService.simplePromiseTest().then(function(results) {
// this is never called if don't trigger the digest from the service code
done();
});
// This line is use less as when I get into that line, the promise is not resolved.
// $scope.$root.$digest();
});
So the situation is as following :
I have to wait for the call to parse to end before triggering a digest cycle
I can't find any other solution than to pollute my service's code with this code
I'd like to find a sustainable solution which doesn't require me to update my service's code to pass the test.
Thanks in advance I'm lost with that, I may be missing something obvious :-)
Call $rootScope.$apply(); in the test itself rather than in the promise implementation. Tests with done are asynchronous so it's ok to resolve it afterwards. Alternatively use Angular 1.3.
In general for testing promises I'd probably recommend mocha rather than Jasmine since it supports promise tests out of the box with return statements.

How to test a method which returns a promise in sinon?

I have the following controller:
app.controller('SearchVideosController',
function SearchVideosController($scope, videoRepository) {
$scope.DoSearch(id, text) {
// Do some work...
videoRepository.getVideosForUserBasedOnSearchText(id,text)
.then(function(data){
// Do something with the data.
});
};
};
My videoRepository.getVideosForUserBasedOnSearchText() method uses $q and I want to create stub to ensure that the call is made.
I tried :
it("should have 3 searched videos", function(){
...
mockVideoRepository.getVideosForUserBasedOnSearchText.returns([]);
but get .then() is undefined.
Not sure how to handle the then() call.
You would need to get hold of $q service instance and use $q.when to create a promise wrapped value:-
mockVideoRepository.getVideosForUserBasedOnSearchText.returns($q.when([]));
Also remember you would need to manually perform a digest cycle in your test before the expecation to evaluate the result of the getVideosForUserBasedOnSearchText call. Only when a digest cycle is invoked promise will be resolved in your test. You can do it by getting hold of scope, and perform $digest or $apply. Example:- rootScope.$apply()

Testing promises with angularjs order of execution confusion

I'm testing a promise with angularjs jasmine, and sinonjs.
I'm puzzled by something regarding promises. Here is my code:
it('should return data with length 4 ', inject(function ($rootScope) {
var storageData;
mockDualStorage.getData.returns($.when(''));
// mockDualStorage.getData is called by getStorageData
// $rootScope.$digest() // not working here
dataGetter.getStorageData().then(function (data) {
console.log(1);
storageData = data;
});
$rootScope.$digest(); // only working here
console.log(2);
expect(storageData.length).toBe(4)// ok
}));
Couple of things are strange here.
If I put $rootScope.$digest() above the dataGetter.getStorageData() then function is never executed.
When the $rootScope.$digest() is below, then gets executed, and order of console.log is 1,2
Why won't then execute when $rootScope.$digest() is above? As I understand promise is already resolved?
After more carefully reading the documentation, found the answer right there.
Differences between Kris Kowal's Q and $q :
There are two main differences: $q is integrated with the $rootScope.Scope Scope model observation mechanism in angular, which means faster propagation of resolution or rejection into your models and avoiding unnecessary browser repaints, which would result in flickering UI.
AngularJS $q service documentation

Resources