I've been working from this tutorial and have Googled ad nauseum, but I can't seem to get what appears to be a trivial ngAnimate
unit test running.
I have ngAnimate
working well in the app. All Angular core libraries are version 1.4.7.
Module
angular.module 'MyAnimation', [ 'ngAnimate' ]
.animation '.added-class', ->
addClass: (element, className, done) ->
console.log 'add class triggered'
element.css 'opacity', '0.5'
done()
Test
describe 'MyAnimation', ->
beforeEach -> module 'ngAnimate'
beforeEach -> module 'ngAnimateMock'
beforeEach -> module 'MyAnimation'
it 'animates', -> inject ($animate, $rootScope, $rootElement) ->
$animate.enabled(true)
divElement = angular.element '<div>my div</div>'
# Kick off initial digest loop in which no animations are run.
$rootScope.$digest()
# Trigger animation.
$animate.addClass divElement, 'added-class'
$rootScope.$digest()
# Tried this, doesn't seem to do anything.
# $animate.flush()
# Results in AssertionError: expected '' to equal '0.5'
expect(divElement.css('opacity')).to.eq '0.5'
I'm sure that the module is being included in the test, but triggering $animate.enter
doesn't even get me my log
output.
I've tried this with other $animate
functions as well and am getting nowhere. Help?
After some serious digging into Angular's source code, it appears that the culprit is the internal check areAnimationsAllowed
which Angular uses to determine whether to abort the animation early. Among other things, it checks that the node being animated is a descendant of the $rootElement
and the document body.
You have two options here.
$rootElement
and attach the $rootElement
to the body. The latter is necessary because ngMock
actually stubs $rootElement
with a detached <div>
node held in memory. Example:
var element, body, root;
beforeEach(module('ngAnimate', 'ngAnimateMock', 'MyAnimation'));
beforeEach(inject(function ($animate, $document, $rootElement, $rootScope) {
// enable animations globally
$animate.enabled(true);
// create a node to be animated and inject it into the DOM
element = angular.element('<div></div>');
root = $rootElement.append(element)[0];
body = $document[0].body;
body.appendChild(root);
// trigger initial digest
$rootScope.$digest();
}));
afterEach(function () {
// clean up
body.removeChild(root);
});
$animate.addClass
, but instead use the lower-level $$animateJs
service. Angular uses it inside their own tests, I assume to bypass the above check. Example:
it('should animate', inject(function ($animate, $$animateJs) {
// trigger animation
$$animateJs(element, 'addClass', {
addClass: 'added-class'
}).start();
$animate.flush();
}));
If you run the Plunkers and check the console, you should see your "addClass triggered" message. I have also added a couple assertions.
Both solutions are hacky and undocumented, and both become more involved if your addClass
animation does something asynchronous (which I assume it will). Unfortunately, I cannot find a better way to test JS animations. In the past I've actually ignored animation code from coverage because it's so difficult to test.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With