Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ngAnimate 1.4.7 unit test not calling animation functions

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?

like image 245
jrh Avatar asked Oct 29 '15 03:10

jrh


1 Answers

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.

  1. Plunker. Attach the node you are animating to $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);
});
  1. Plunker. Do not test your animation using $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.

like image 136
Igor Raush Avatar answered Nov 20 '22 05:11

Igor Raush