Reputation: 5901
I wrote a directive that will conditionally add a wrapper element which I modeled after Angular's ngIf directive. The directive works great when running in production, but in trying to add unit tests the $animate.enter function never calls my callback function. This is causing all my unit tests to fail when it assumes that the wrapper is not suppose to be there.
I'm using Angular.js version 1.2.16 and loading ngMock and ngAnimate for the unit test. The code fires the ngAnimate enter function, but then it never fires the callback.
You can view the code here, just uncomment the appSpec.js script tag and the directive no longer works.
Does anyone now how to trigger $animate.enter to call my callback function in a unit test?
addWrapperIf.js
angular.module('myModule', ['ngAnimate'])
.directive('addWrapperIf', ['$animate', function($animate) {
return {
transclude: 'element',
priority: 1000,
restrict: 'A',
compile: function (element, attr, transclude) {
return function ($scope, $element, $attr) {
var childElement, childScope;
$scope.$watch($attr.addWrapperIf, function addWrapperIfWatchAction(value) {
if (childElement) {
$animate.leave(childElement);
childElement = undefined;
}
if (childScope) {
childScope.$destroy();
childScope = undefined;
}
// add the wrapper
if (value) {
childScope = $scope.$new();
transclude(childScope, function (clone) {
childElement = clone
$animate.enter(clone, $element.parent(), $element);
});
}
// remove the wrapper
else {
childScope = $scope.$new();
transclude(childScope, function (clone) {
$animate.enter(clone, $element.parent(), $element, function() {
childElement = clone.contents();
clone.replaceWith(clone.contents());
});
});
}
});
}
}
};
}]);
addWrapperIfSpec.js
var expect = chai.expect;
describe('addWrapperIf', function () {
var $template;
var $compile;
var $scope;
beforeEach(window.module('myModule'));
beforeEach(inject(function(_$compile_, $rootScope){
$compile = _$compile_;
$scope = $rootScope.$new();
}));
function compileDirective(template) {
$template = $compile(template)($scope)[0];
$scope.$apply();
}
it('should output the correct values with default options', function() {
compileDirective('<div add-wrapper-if="false"><span>child</span></div>');
console.log($template); // <div add-wrapper-if="false"><span>child</span></div>
});
});
Upvotes: 1
Views: 491
Reputation: 5901
So I figured out what you have to do. I dug into the code and found out that inside ngAnimate it pushes the callback function to $$asyncCallback
. $$asyncCallback
has a flush
function that will call any functions pushed onto it. To get $animate.enter
to fire the callback, you have to inject $$asyncCallback
into your unit test and then call $$asyncCallback.flush()
. This will then run your callback function.
You can see this in this Plunker.
Upvotes: 2