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)
This commit is contained in:
Martin Staffa
2016-05-24 16:44:11 +02:00
parent 57a37fcc20
commit 0d764b581d
2 changed files with 35 additions and 2 deletions
+5 -2
View File
@@ -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;
+30
View File
@@ -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) {