spaceribs spaceribs - 3 months ago 10
AngularJS Question

Unit testing Angular directive expression attribute which passes promises

I've spent a bunch of time trying to unit test a directive which generates/tracks a promise within the controller, an example of how this is accomplished can be seen here: https://plnkr.co/edit/d8wMq0qlpiE4P25q5lhB

The directive:

.directive('testDirective', function() {
return {
scope: {
save: '&',
},
template: '<div><a href="#" ng-click="handleSave($event)">Click Here</a></div>',
link: function(scope, element, attr, controllers) {

scope.someData = {
saving: false
};

scope.handleSave = function() {
scope.someData.name = parseInt(Math.random() * 100);
scope.someData.saving = true;
scope.someData.error = false;
scope.someData.id = null;
return scope.save()(scope.someData)
.then(function(data) {
scope.someData = data;
})
.catch(function(err) {
scope.someData.error = true;
})
.finally(function() {
scope.someData.saving = false;
});
};

}
};
})


The controller:

.controller('TestCtrl', function($scope, $q, $timeout) {
$scope.saveCtrl = function(directiveData) {
return $q(function(resolve, reject) {
$timeout(function() {
if (Math.round(Math.random())) {
directiveData.id = parseInt(Math.random() * 100);
resolve(directiveData);
} else {
reject(new Error());
}
}, 2000)
});
}
});


The template:

<test-directive save="saveCtrl"></test-directive>


It's functional as a component as seen in the example, but the test fails to complete because the .finally() never gets called. I've tried a lot of variations to force $digest, but nothing seems to complete the promise.

Answer

Think I got it. https://plnkr.co/edit/CBvrZo4g9LjJC61VV0wp

```

$scope.saveCtrl = function(data) {
    return $q(function(resolve, reject) {
        setTimeout(function() {
            if (Math.round(Math.random())) {
                var id = parseInt(Math.random() * 100);
                resolve(id);
            } else {
                reject(new Error());
            }
            elementScope.$apply();
            $scope.$apply();
        }, 100);
   });
};

```

There was an undefined var at line 39 and the angular timeout was behaving oddly. This should cause the full flow of the directives save to be tested with the timeout. I think the timeout may not be needed however as you can assume the instant timeout case without any loss of testing.