Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

feat(ngMock): add support for $animate.closeAndFlush() #13576

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/AngularPublic.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
$AnchorScrollProvider,
$AnimateProvider,
$CoreAnimateCssProvider,
$$CoreAnimateJsProvider,
$$CoreAnimateQueueProvider,
$$AnimateRunnerFactoryProvider,
$$AnimateAsyncRunFactoryProvider,
Expand Down Expand Up @@ -218,6 +219,7 @@ function publishExternalAPI(angular) {
$anchorScroll: $AnchorScrollProvider,
$animate: $AnimateProvider,
$animateCss: $CoreAnimateCssProvider,
$$animateJs: $$CoreAnimateJsProvider,
$$animateQueue: $$CoreAnimateQueueProvider,
$$AnimateRunner: $$AnimateRunnerFactoryProvider,
$$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
Expand Down
4 changes: 4 additions & 0 deletions src/ng/animate.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ function prepareAnimateOptions(options) {
: {};
}

var $$CoreAnimateJsProvider = function() {
this.$get = function() {};
};

// this is prefixed with Core since it conflicts with
// the animateQueueProvider defined in ngAnimate/animateQueue.js
var $$CoreAnimateQueueProvider = function() {
Expand Down
6 changes: 3 additions & 3 deletions src/ngAnimate/animateCss.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,9 +352,9 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
var gcsStaggerLookup = createLocalCacheLookup();

this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout',
'$$forceReflow', '$sniffer', '$$rAFScheduler', '$animate',
'$$forceReflow', '$sniffer', '$$rAFScheduler', '$$animateQueue',
function($window, $$jqLite, $$AnimateRunner, $timeout,
$$forceReflow, $sniffer, $$rAFScheduler, $animate) {
$$forceReflow, $sniffer, $$rAFScheduler, $$animateQueue) {

var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);

Expand Down Expand Up @@ -456,7 +456,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
var node = getDomNode(element);
if (!node
|| !node.parentNode
|| !$animate.enabled()) {
|| !$$animateQueue.enabled()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ngMock decorates the $animate service with the new closeAndFlush method. Both $animateCss and $$animateJs have their own closeAndFlush implementations so, $animate requires both services. Since $animateCss also requires $animate, this would create a circular dependency.

return closeAndReturnNoopAnimator();
}

Expand Down
33 changes: 28 additions & 5 deletions src/ngAnimate/animateJs.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
// $animateJs(element, 'enter');
return function(element, event, classes, options) {
var animationClosed = false;

// the `classes` argument is optional and if it is not used
// then the classes will be resolved from the element's className
// property as well as options.addClass/options.removeClass.
Expand Down Expand Up @@ -63,8 +65,32 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
applyAnimationClasses(element, options);
}

function close(success) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

success isn't used.

animationClosed = true;
applyOptions();
applyAnimationStyles(element, options);
}

var runner;

return {
$$willAnimate: true,
end: function() {
if (runner) {
runner.end();
} else {
close();
runner = new $$AnimateRunner();
runner.complete(true);
}
return runner;
},
start: function() {
if (runner) {
return runner;
}

runner = new $$AnimateRunner();
var closeActiveAnimations;
var chain = [];

Expand All @@ -89,8 +115,7 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
});
}

var animationClosed = false;
var runner = new $$AnimateRunner({
runner.setHost({
end: function() {
endAnimations();
},
Expand All @@ -103,9 +128,7 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
return runner;

function onComplete(success) {
animationClosed = true;
applyOptions();
applyAnimationStyles(element, options);
close(success);
runner.complete(success);
}

Expand Down
57 changes: 53 additions & 4 deletions src/ngMock/angular-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -783,9 +783,47 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
return queueFn;
});

$provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF',
$provide.decorator('$$animateJs', ['$delegate', function($delegate) {
var runners = [];

var animateJsConstructor = function() {
var animator = $delegate.apply($delegate, arguments);
runners.push(animator);
return animator;
};

animateJsConstructor.$closeAndFlush = function() {
runners.forEach(function(runner) {
runner.end();
});
runners = [];
};

return animateJsConstructor;
}]);

$provide.decorator('$animateCss', ['$delegate', function($delegate) {
var runners = [];

var animateCssConstructor = function(element, options) {
var animator = $delegate(element, options);
runners.push(animator);
return animator;
};

animateCssConstructor.$closeAndFlush = function() {
runners.forEach(function(runner) {
runner.end();
});
runners = [];
};

return animateCssConstructor;
}]);

$provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$animateCss', '$$animateJs',
'$$forceReflow', '$$animateAsyncRun', '$rootScope',
function($delegate, $timeout, $browser, $$rAF,
function($delegate, $timeout, $browser, $$rAF, $animateCss, $$animateJs,
$$forceReflow, $$animateAsyncRun, $rootScope) {
var animate = {
queue: [],
Expand All @@ -797,7 +835,18 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
return $$forceReflow.totalReflows;
},
enabled: $delegate.enabled,
flush: function() {
closeAndFlush: function() {
// we allow the flush command to swallow the errors
// because depending whether CSS or JS animations are
// used there may not be a RAF flush. The primary flush
// at the end of this function must throw an exception
// because it will track if there were pending animations
this.flush(true);
$animateCss.$closeAndFlush();
$$animateJs.$closeAndFlush();
this.flush();
},
flush: function(hideErrors) {
$rootScope.$digest();

var doNextRun, somethingFlushed = false;
Expand All @@ -814,7 +863,7 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
}
} while (doNextRun);

if (!somethingFlushed) {
if (!somethingFlushed && !hideErrors) {
throw new Error('No pending animations ready to be closed or flushed');
}

Expand Down
60 changes: 59 additions & 1 deletion test/ngMock/angular-mocksSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1924,7 +1924,7 @@ describe('ngMockE2E', function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));

var ss, element, trackedAnimations;
var ss, element, trackedAnimations, animationLog;

afterEach(function() {
if (element) {
Expand All @@ -1937,6 +1937,8 @@ describe('ngMockE2E', function() {

beforeEach(module(function($animateProvider) {
trackedAnimations = [];
animationLog = [];

$animateProvider.register('.animate', function() {
return {
leave: logFn('leave'),
Expand All @@ -1945,7 +1947,13 @@ describe('ngMockE2E', function() {

function logFn(method) {
return function(element) {
animationLog.push('start ' + method);
trackedAnimations.push(getDoneCallback(arguments));

return function closingFn(cancel) {
var lab = cancel ? 'cancel' : 'end';
animationLog.push(lab + ' ' + method);
};
};
}

Expand Down Expand Up @@ -2098,6 +2106,56 @@ describe('ngMockE2E', function() {
expect(spy.callCount).toBe(2);
}));
});

describe('$animate.closeAndFlush()', function() {
it('should close the currently running $animateCss animations',
inject(function($animateCss, $animate) {

if (!browserSupportsCssAnimations()) return;

var spy = jasmine.createSpy();
var runner = $animateCss(element, {
duration: 1,
to: { color: 'red' }
}).start();

runner.then(spy);

expect(spy).not.toHaveBeenCalled();
$animate.closeAndFlush();
expect(spy).toHaveBeenCalled();
}));

it('should close the currently running $$animateJs animations',
inject(function($$animateJs, $animate) {

var spy = jasmine.createSpy();
var runner = $$animateJs(element, 'leave', 'animate', {}).start();
runner.then(spy);

expect(spy).not.toHaveBeenCalled();
$animate.closeAndFlush();
expect(spy).toHaveBeenCalled();
}));

it('should run the closing javascript animation function upon flush',
inject(function($$animateJs, $animate) {

$$animateJs(element, 'leave', 'animate', {}).start();

expect(animationLog).toEqual(['start leave']);
$animate.closeAndFlush();
expect(animationLog).toEqual(['start leave', 'end leave']);
}));

it('should throw an error if there are no animations to close and flush',
inject(function($animate) {

expect(function() {
$animate.closeAndFlush();
}).toThrow('No pending animations ready to be closed or flushed');
}));
});
});
});

Expand Down