Skip to content
Permalink
Browse files

fix(ngAnimate): safe-guard against missing document

In tests, the $document service might be mocked out without providing a real
document, which can lead to errors when the animator is attempting to read properties from it.

This commit provides an object {hidden: true}, if the $document service doesn't have 
a document. This will prevent the animation process from trying to run any animations.

This commit also changes the check for document.hidden slightly. It
should be accessed independently of the current animationsEnabled state.
Since animations are only enabled after two digests, it's possible that
some tests never reach the animationsEnabled = true state and therefore
aren't actually checking the document.hidden state, which means that
the previous fix only works if no more than two digests happen in the test.

(#14633)
  • Loading branch information
Narretz committed May 24, 2016
1 parent 57a37fc commit 0d764b581d8b494190fa280f8870eee8fd039933
Showing with 35 additions and 2 deletions.
  1. +5 −2 src/ngAnimate/animateQueue.js
  2. +30 −0 test/ngAnimate/animateSpec.js
@@ -103,6 +103,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
var activeAnimationsLookup = new $$HashMap();
var disabledElementsLookup = new $$HashMap();
var animationsEnabled = null;
// $document might be mocked out in tests and won't include a real document.
// Providing an empty object with hidden = true will prevent animations from running
var rawDocument = $document[0] || {hidden: true};

function postDigestTaskFactory() {
var postDigestCalled = false;
@@ -331,7 +334,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {

var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;

var documentHidden = animationsEnabled && $document[0].hidden;
var documentHidden = rawDocument.hidden;

// this is a hard disable of all animations for the application or on
// the element itself, therefore there is no need to continue further
@@ -583,7 +586,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
* d) the element is not a child of the $rootElement
*/
function areAnimationsAllowed(element, parentElement, event) {
var bodyElement = jqLite($document[0].body);
var bodyElement = jqLite(rawDocument.body);
var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
var rootElementDetected = isMatchingElement(element, $rootElement);
var parentAnimationDetected = false;
@@ -1404,6 +1404,36 @@ describe("animations", function() {
});
});


it('should not run animations if the document is unavailable', function() {
var capturedAnimation;

module(function($provide) {
$provide.value('$document', {});

$provide.factory('$$animation', function($$AnimateRunner) {
return function(element, method, options) {
capturedAnimation = arguments;
return new $$AnimateRunner();
};
});
});

inject(function($animate, $rootScope, $rootElement, $document) {
$animate.enabled(true);

var spy = jasmine.createSpy();

element = jqLite('<div></div>');
var runner = $animate.enter(element, $rootElement);
$rootScope.$digest();

$animate.flush();

expect(capturedAnimation).toBeUndefined();
});
});

describe('[ng-animate-children]', function() {
var parent, element, child, capturedAnimation, captureLog;
beforeEach(module(function($provide) {

0 comments on commit 0d764b5

Please sign in to comment.
You can’t perform that action at this time.