Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ae4d082

Browse files
committedMar 24, 2015
WIP: refactor(ngAnimate): use $animateRunner in code
1 parent a4e0055 commit ae4d082

File tree

8 files changed

+2561
-169
lines changed

8 files changed

+2561
-169
lines changed
 

‎src/ngAnimate/animate.js

Lines changed: 2131 additions & 0 deletions
Large diffs are not rendered by default.

‎src/ngAnimate/animateCss.js

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ var ONE_SECOND = 1000;
4242
var BASE_TEN = 10;
4343

4444
function computeCSSStyles($window, element, properties) {
45-
var styles = {};
46-
45+
var styles = Object.create(null);
4746
var detectedStyles = $window.getComputedStyle(element) || {};
4847
forEach(properties, function(style, prop) {
4948
var val = detectedStyles[style];
@@ -61,9 +60,7 @@ function computeCSSStyles($window, element, properties) {
6160

6261
function parseMaxTime(str) {
6362
var maxValue = 0;
64-
var values = isString(str) ?
65-
str.split(/\s*,\s*/) :
66-
[];
63+
var values = str.split(/\s*,\s*/);
6764
forEach(values, function(value) {
6865
maxValue = Math.max(parseFloat(value) || 0, maxValue);
6966
});
@@ -90,15 +87,15 @@ function getCssDelayStyle(delay, isKeyframeAnimation) {
9087
return [prop, delay + 's'];
9188
}
9289

93-
function blockTransitions(node, bool) {
94-
var value = bool ? 'none' : '';
90+
function blockTransitions(node, applyBlock) {
91+
var value = applyBlock ? 'none' : '';
9592
var key = TRANSITION_PROP + PROPERTY_KEY;
9693
applyInlineStyle(node, key, value);
9794
return [key, value];
9895
}
9996

100-
function blockAnimations(node, bool) {
101-
var value = bool ? 'paused' : '';
97+
function blockAnimations(node, applyBlock) {
98+
var value = applyBlock ? 'paused' : '';
10299
var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;
103100
applyInlineStyle(node, key, value);
104101
return [key, value];
@@ -112,8 +109,8 @@ function applyInlineStyle(node, key, value) {
112109
node.style[key] = value;
113110
}
114111

115-
function LocalCacheLookup() {
116-
var cache = {};
112+
function createLocalCacheLookup() {
113+
var cache = Object.create(null);
117114
return {
118115
flush: function() {
119116
cache = {};
@@ -131,17 +128,17 @@ function LocalCacheLookup() {
131128

132129
put: function(key, value) {
133130
if (!cache[key]) {
134-
cache[key] = { total: 0 };
131+
cache[key] = { total: 1, value: value };
132+
} else {
133+
cache[key].total++;
135134
}
136-
cache[key].total++;
137-
cache[key].value = value;
138135
}
139136
};
140137
}
141138

142139
var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
143-
var gcsLookup = new LocalCacheLookup();
144-
var gcsStaggerLookup = new LocalCacheLookup();
140+
var gcsLookup = createLocalCacheLookup();
141+
var gcsStaggerLookup = createLocalCacheLookup();
145142

146143
this.$get = ['$window', '$$jqLite', '$animateRunner', '$timeout',
147144
'$document', '$$animateOptions', '$sniffer', '$$rAF',
@@ -161,8 +158,8 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
161158

162159
if (!timings) {
163160
timings = computeCSSStyles($window, node, properties);
164-
if (timings.animationIterationCount) {
165-
timings.animationIterationCount = parseInt(timings.animationIterationCount || 1, BASE_TEN);
161+
if(timings.animationIterationCount === 'infinite') {
162+
timings.animationIterationCount = 1;
166163
}
167164
}
168165

@@ -173,12 +170,13 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
173170
}
174171

175172
function computeCachedCSSStaggerStyles(node, className, cacheKey, properties) {
176-
var stagger = {};
177-
var isRepeated = gcsLookup.count(cacheKey) > 0;
173+
var stagger;
178174

179-
if (isRepeated) {
180-
var staggerCacheKey = cacheKey + '-stagger';
181-
stagger = gcsStaggerLookup.get(staggerCacheKey);
175+
// if we have one or more existing matches of matching elements
176+
// containing the same parent + CSS styles (which is how cacheKey works)
177+
// then staggering is possible
178+
if (gcsLookup.count(cacheKey) > 0) {
179+
stagger = gcsStaggerLookup.get(cacheKey);
182180

183181
if (!stagger) {
184182
var staggerClassName = pendClasses(className, '-stagger');
@@ -189,20 +187,23 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
189187

190188
$$jqLite.removeClass(node, staggerClassName);
191189

192-
gcsStaggerLookup.put(staggerCacheKey, stagger);
190+
gcsStaggerLookup.put(cacheKey, stagger);
193191
}
194192
}
195193

196-
return stagger;
194+
return stagger || {};
197195
}
198196

199-
var cancelLastRefRequest, bod = $document[0].body;
197+
var bod = $document[0].body;
198+
var cancelLastRAFRequest;
199+
var rafWaitQueue = [];
200200
function waitUntilQuiet(callback) {
201-
if (cancelLastRafRequest) {
202-
cancelLastRafRequest(); //cancels the request
201+
if (cancelLastRAFRequest) {
202+
cancelLastRAFRequest(); //cancels the request
203203
}
204-
cancelLastRafRequest = $$rAF(function() {
205-
cancelLastRequest = null;
204+
rafWaitQueue.push(callback);
205+
cancelLastRAFRequest = $$rAF(function() {
206+
cancelLastRAFRequest = null;
206207
gcsLookup.flush();
207208
gcsStaggerLookup.flush();
208209

@@ -212,7 +213,10 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
212213
//ensure that the the preparation animation is properly flushed so that
213214
//the active state picks up from there. DO NOT REMOVE THIS LINE.
214215
var a = bod.offsetWidth + 1;
215-
callback();
216+
forEach(rafWaitQueue, function(cb) {
217+
cb();
218+
});
219+
rafWaitQueue.length = 0;
216220
});
217221
}
218222

@@ -247,6 +251,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
247251
var animationPaused;
248252
var animationCompleted;
249253
var runner;
254+
var runnerHost;
250255

251256
if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) {
252257
close();
@@ -261,7 +266,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
261266
// class-based animations so that we can avoid applying CSS blocking on
262267
// the element to allow for normal transitions to work (this is how enter,
263268
// leave and move can perform the `ng-EVENT` methods without causing an
264-
// unpected transition animation to occur).
269+
// unexpected transition animation to occur).
265270
var structural = method &&
266271
[' setClass ', ' addClass ',' removeClass '].indexOf(' ' + method + ' ') == -1;
267272

@@ -288,7 +293,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
288293
var activeClasses = pendClasses(setupClasses, '-active');
289294
var hasToStyles = styles.to && Object.keys(styles.to).length > 0;
290295

291-
// there is no way we can trigger an animation since no styles or
296+
// there is no way we can trigger an animation since no styles and
292297
// no classes are being applied which would then trigger a transition
293298
if (!hasToStyles && !setupClasses) {
294299
close();
@@ -443,13 +448,15 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
443448
start: function() {
444449
if (animationClosed) return;
445450

446-
runner = new $animateRunner({
451+
runnerHost = {
447452
end: endFn,
448453
cancel: cancelFn
449-
});
454+
};
455+
456+
runner = new $animateRunner(runnerHost);
450457

451458
waitUntilQuiet(function() {
452-
start(deferred);
459+
start();
453460
});
454461

455462
// we don't have access to pause/resume the animation
@@ -505,7 +512,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
505512

506513
// if the preparation function fails then the promise is not setup
507514
if (runner) {
508-
rejected ? runner.end() : runner.cancel();
515+
runner.complete(!rejected);
509516
}
510517
}
511518

@@ -523,16 +530,16 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
523530
if (animationClosed) return;
524531

525532
var startTime, events = [];
526-
var playPause = function(bool) {
533+
var playPause = function(playAnimation) {
527534
if (!animationCompleted) {
528-
animationPaused = !bool;
535+
animationPaused = !playAnimation;
529536
if (timings.animationDuration) {
530537
var value = blockAnimations(node, animationPaused);
531538
animationPaused
532539
? temporaryStyles.push(value)
533540
: removeFromArray(temporaryStyles, value);
534541
}
535-
} else if (animationPaused && bool) {
542+
} else if (animationPaused && playAnimation) {
536543
animationPaused = false;
537544
close();
538545
}
@@ -554,16 +561,13 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
554561
}
555562

556563
// this will decorate the existing promise runner with pause/resume methods
557-
$$animateRunner(deferred.promise, {
558-
resume: function() {
559-
playPause(true);
560-
},
561-
pause: function() {
562-
playPause(false);
563-
}
564-
});
564+
runnerHost.resume = function() {
565+
playPause(true);
566+
};
565567

566-
return deferred.promise;
568+
runnerHost.pause = function() {
569+
playPause(false);
570+
};
567571

568572
function triggerAnimationStart() {
569573
// just incase a stagger animation kicks in when the animation

‎src/ngAnimate/animateQueue.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
5858
});
5959

6060
this.$get = ['$qRaf', '$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
61-
'$$animation', '$$animateRunner', '$templateRequest', '$$animateOptions',
61+
'$$animation', '$animateRunner', '$templateRequest', '$$animateOptions',
6262
function($qRaf, $$rAF, $rootScope, $rootElement, $document, $$HashMap,
63-
$$animation, $$animateRunner, $templateRequest, $$animateOptions) {
63+
$$animation, $animateRunner, $templateRequest, $$animateOptions) {
6464

6565
var activeAnimationsLookup = new $$HashMap(); // jshint ignore:line
6666
var disabledElementsLookup = new $$HashMap(); // jshint ignore:line

‎src/ngAnimate/animateRunner.js

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,15 @@ var $$rAFMutexFactory = ['$$rAF', function($$rAF) {
1212
}
1313
}];
1414

15-
var $AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAfMutex) {
15+
var $AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
1616
var DONE_PENDING_STATE = 1;
1717
var DONE_COMPLETE_STATE = 2;
1818

19-
var AnimateRunner = function(host) {
20-
this._doneCallbacks = [];
21-
this.setHost(host);
22-
this._atleastOneRAFPassedSinceStart = $$rAFMutex();
23-
this._state = 0;
24-
};
25-
2619
AnimateRunner.chain = function(chain, callback) {
2720
var status = true;
2821
var index = 0;
2922

23+
next();
3024
function next() {
3125
if (index === chain.length) {
3226
callback(status);
@@ -56,6 +50,14 @@ var $AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAfMutex) {
5650
}
5751
};
5852

53+
function AnimateRunner(host) {
54+
this.setHost(host);
55+
56+
this._doneCallbacks = [];
57+
this._atleastOneRAFPassedSinceStart = $$rAFMutex();
58+
this._state = 0;
59+
};
60+
5961
AnimateRunner.prototype = {
6062
setHost: function(host) {
6163
this.host = host || {};
@@ -88,55 +90,43 @@ var $AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAfMutex) {
8890
},
8991

9092
pause: function() {
91-
(this.host.pause || noop)();
93+
this.host.pause && this.host.pause();
9294
},
9395

9496
resume: function() {
95-
(this.host.resume || noop)();
97+
this.host.resume && this.host.resume();
9698
},
9799

98100
end: function() {
99-
this._complete(true);
101+
this.host.end && this.host.end();
102+
this._resolve(true);
100103
},
101104

102105
cancel: function() {
103-
this._complete(false);
106+
this.host.cancel && this.host.cancel();
107+
this._resolve(false);
104108
},
105109

106-
_complete: function(response) {
107-
if (this._state === 0) {
108-
this._state = DONE_PENDING_STATE;
110+
complete: function(response) {
111+
var self = this;
112+
if (self._state === 0) {
113+
self._state = DONE_PENDING_STATE;
114+
self._atleastOneRAFPassedSinceStart(function() {
115+
self._resolve(response);
116+
});
117+
}
118+
},
109119

110-
var self = this;
111-
this._atleastOneRAFPassedSinceStart(function() {
112-
invoke(self._doneCallbacks, response);
113-
self._doneCallbacks.length = 0;
114-
self._state = DONE_COMPLETE_STATE;
120+
_resolve: function(response) {
121+
if (this._state !== DONE_COMPLETE_STATE) {
122+
angular.forEach(this._doneCallbacks, function(fn) {
123+
fn(response);
115124
});
125+
this._doneCallbacks.length = 0;
126+
this._state = DONE_COMPLETE_STATE;
116127
}
117128
}
118129
};
119130

120131
return AnimateRunner;
121-
122-
function invoke(fns, data) {
123-
forEach(fns, function(fn) {
124-
fn(data);
125-
});
126-
}
127-
// there is a lot of variable switching going on here. The main idea
128-
// is that the runner can be "updated" later on without having to create
129-
// a new variable. The driver that is given will apply it's new methods to
130-
// the pre-existing runner/promise object.
131-
return function(obj, driver) {
132-
if (!isPromiseLike(obj)) throw new Error();
133-
driver = driver || {};
134-
obj.next = driver.next || obj.next || noop;
135-
obj.end = driver.end || obj.end || noop;
136-
obj.pause = driver.pause || obj.pause || noop;
137-
obj.resume = driver.resume || obj.resume || noop;
138-
obj.cancel = driver.cancel || obj.cancel || noop;
139-
obj.progress = driver.progress || obj.progress || noop;
140-
return obj;
141-
};
142132
}];

‎src/ngAnimate/animation.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,19 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2020
return element.data(RUNNER_STORAGE_KEY);
2121
}
2222

23-
this.$get = ['$qRaf', '$$jqLite', '$rootScope', '$injector', '$$animateRunner', '$$animateOptions',
24-
function($qRaf, $$jqLite, $rootScope, $injector, $$animateRunner, $$animateOptions) {
23+
this.$get = ['$$jqLite', '$rootScope', '$injector', '$animateRunner', '$$animateOptions',
24+
function($$jqLite, $rootScope, $injector, $animateRunner, $$animateOptions) {
2525

2626
var animationQueue = [];
2727

2828
return function(element, event, options) {
2929
options = $$animateOptions(element, options);
30-
var deferred = $qRaf.defer();
3130

3231
// there is no animation at the current moment, however
3332
// these runner methods will get later updated with the
3433
// methods leading into the driver's end/cancel methods
3534
// for now they just stop the animation from starting
36-
var runner = $$animateRunner(deferred.promise, {
35+
var runner = new $animateRunner({
3736
end: function() { close(); },
3837
cancel: function() { close(true); }
3938
});
@@ -252,7 +251,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
252251
}
253252

254253
function update(element) {
255-
$$animateRunner(getRunner(element), newRunner);
254+
getRunner(element).setHost(newRunner);
256255
}
257256
}
258257

@@ -276,7 +275,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
276275
}
277276

278277
element.removeClass(NG_ANIMATE_CLASSNAME);
279-
rejected ? deferred.reject() : deferred.resolve();
278+
runner.complete(!rejected);
280279
}
281280
};
282281
}];

‎test/ngAnimate/animateCssSpec.js

Lines changed: 46 additions & 38 deletions
Large diffs are not rendered by default.

‎test/ngAnimate/animateRunnerSpec.js

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
'use strict';
2+
3+
describe("$$rAFMutex", function() {
4+
beforeEach(module('ngAnimate'));
5+
6+
it('should fire the callback only when one or more RAFs have passed',
7+
inject(function($$rAF, $$rAFMutex) {
8+
9+
var trigger = $$rAFMutex();
10+
var called = false;
11+
trigger(function() {
12+
called = true;
13+
});
14+
15+
expect(called).toBe(false);
16+
$$rAF.flush();
17+
expect(called).toBe(true);
18+
}));
19+
20+
it('should immediately fire the callback if a RAF has passed since construction',
21+
inject(function($$rAF, $$rAFMutex) {
22+
23+
var trigger = $$rAFMutex();
24+
$$rAF.flush();
25+
26+
var called = false;
27+
trigger(function() {
28+
called = true;
29+
});
30+
expect(called).toBe(true);
31+
}));
32+
});
33+
34+
describe("$animateRunner", function() {
35+
36+
beforeEach(module('ngAnimate'));
37+
38+
they("should trigger the host $prop function",
39+
['end', 'cancel', 'pause', 'resume'], function(method) {
40+
41+
inject(function($animateRunner) {
42+
var host = {};
43+
var spy = host[method] = jasmine.createSpy();
44+
var runner = new $animateRunner(host);
45+
runner[method]();
46+
expect(spy).toHaveBeenCalled();
47+
});
48+
});
49+
50+
they("should trigger the inner runner's host $prop function",
51+
['end', 'cancel', 'pause', 'resume'], function(method) {
52+
53+
inject(function($animateRunner) {
54+
var host = {};
55+
var spy = host[method] = jasmine.createSpy();
56+
var runner1 = new $animateRunner();
57+
var runner2 = new $animateRunner(host);
58+
runner1.setHost(runner2);
59+
runner1[method]();
60+
expect(spy).toHaveBeenCalled();
61+
});
62+
});
63+
64+
it("should resolve the done function only if one RAF has passed",
65+
inject(function($animateRunner, $$rAF) {
66+
67+
var runner = new $animateRunner();
68+
var spy = jasmine.createSpy();
69+
runner.done(spy);
70+
runner.complete(true);
71+
expect(spy).not.toHaveBeenCalled();
72+
$$rAF.flush();
73+
expect(spy).toHaveBeenCalled();
74+
}));
75+
76+
it("should resolve with the status provided in the completion function",
77+
inject(function($animateRunner, $$rAF) {
78+
79+
var runner = new $animateRunner();
80+
var capturedValue;
81+
runner.done(function(val) {
82+
capturedValue = val;
83+
});
84+
runner.complete('special value');
85+
$$rAF.flush();
86+
expect(capturedValue).toBe('special value');
87+
}));
88+
89+
they("should immediately resolve each combined runner in depth-first order when $prop is called",
90+
['end', 'cancel'], function(method) {
91+
92+
inject(function($animateRunner, $$rAF) {
93+
var runner1 = new $animateRunner();
94+
var runner2 = new $animateRunner();
95+
runner1.setHost(runner2);
96+
97+
var status1, status2, signature = '';
98+
runner1.done(function(status) {
99+
signature += '1';
100+
status1 = status;
101+
});
102+
103+
runner2.done(function(status) {
104+
signature += '2';
105+
status2 = status;
106+
});
107+
108+
runner1[method]();
109+
110+
var expectedStatus = method === 'end' ? true : false;
111+
expect(status1).toBe(expectedStatus);
112+
expect(status2).toBe(expectedStatus);
113+
expect(signature).toBe('21');
114+
});
115+
});
116+
117+
they("should resolve/reject using a newly created promise when .then() is used upon $prop",
118+
['end', 'cancel'], function(method) {
119+
120+
inject(function($animateRunner, $rootScope) {
121+
var runner1 = new $animateRunner();
122+
var runner2 = new $animateRunner();
123+
runner1.setHost(runner2);
124+
125+
var status1;
126+
runner2.then(
127+
function() { status1 = 'pass'; },
128+
function() { status1 = 'fail'; });
129+
130+
var status2;
131+
runner2.then(
132+
function() { status2 = 'pass'; },
133+
function() { status2 = 'fail'; });
134+
135+
runner1[method]();
136+
137+
var expectedStatus = method === 'end' ? 'pass' : 'fail';
138+
139+
expect(status1).toBeFalsy();
140+
expect(status2).toBeFalsy();
141+
142+
$rootScope.$digest();
143+
expect(status1).toBe(expectedStatus);
144+
expect(status2).toBe(expectedStatus);
145+
});
146+
});
147+
148+
it("should expose/create the contained promise when getPromise() is called",
149+
inject(function($animateRunner, $rootScope) {
150+
151+
var runner = new $animateRunner();
152+
expect(isPromiseLike(runner.getPromise()));
153+
}));
154+
155+
describe(".all()", function() {
156+
it("should resolve when all runners have naturally resolved",
157+
inject(function($$rAF, $animateRunner) {
158+
159+
var runner1 = new $animateRunner();
160+
var runner2 = new $animateRunner();
161+
var runner3 = new $animateRunner();
162+
163+
var status;
164+
$animateRunner.all([runner1, runner2, runner3], function(response) {
165+
status = response;
166+
});
167+
168+
runner1.complete(true);
169+
runner2.complete(true);
170+
runner3.complete(true);
171+
172+
expect(status).toBeUndefined();
173+
174+
$$rAF.flush();
175+
176+
expect(status).toBe(true);
177+
}));
178+
179+
they("should immediately resolve if and when all runners have resolved been $prop",
180+
{ ended: 'end', cancelled: 'cancel' }, function(method) {
181+
182+
inject(function($$rAF, $animateRunner) {
183+
var runner1 = new $animateRunner();
184+
var runner2 = new $animateRunner();
185+
var runner3 = new $animateRunner();
186+
187+
var expectedStatus = method === 'end' ? true : false;
188+
189+
var status;
190+
$animateRunner.all([runner1, runner2, runner3], function(response) {
191+
status = response;
192+
});
193+
194+
runner1[method]();
195+
runner2[method]();
196+
runner3[method]();
197+
198+
expect(status).toBe(expectedStatus);
199+
});
200+
});
201+
202+
it("should return a status of `false` if more than one runner was cancelled",
203+
inject(function($$rAF, $animateRunner) {
204+
205+
var runner1 = new $animateRunner();
206+
var runner2 = new $animateRunner();
207+
var runner3 = new $animateRunner();
208+
209+
var status;
210+
$animateRunner.all([runner1, runner2, runner3], function(response) {
211+
status = response;
212+
});
213+
214+
runner1.end();
215+
runner2.end();
216+
runner3.cancel();
217+
218+
expect(status).toBe(false);
219+
}));
220+
});
221+
222+
describe(".chain()", function() {
223+
it("should evaluate an array of functions in a chain",
224+
inject(function($$rAF, $animateRunner) {
225+
226+
var runner1 = new $animateRunner();
227+
var runner2 = new $animateRunner();
228+
var runner3 = new $animateRunner();
229+
230+
var log = [];
231+
232+
var items = [];
233+
items.push(function(fn) { log.push(1); runner1.done(fn); });
234+
items.push(function(fn) { log.push(2); runner2.done(fn); });
235+
items.push(function(fn) { log.push(3); runner3.done(fn); });
236+
237+
var status;
238+
$animateRunner.chain(items, function(response) {
239+
status = response;
240+
});
241+
242+
runner1.complete(true);
243+
runner2.complete(true);
244+
runner3.complete(true);
245+
$$rAF.flush();
246+
247+
expect(log).toEqual([1,2,3]);
248+
expect(status).toBe(true);
249+
}));
250+
});
251+
});

‎test/ngAnimate/animationSpec.js

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
describe('$$animation', function() {
3+
ddescribe('$$animation', function() {
44

55
beforeEach(module('ngAnimate'));
66

@@ -14,14 +14,15 @@ describe('$$animation', function() {
1414
}));
1515

1616
it("should not run an animation if there are no drivers",
17-
inject(function($$animation, $$rAF) {
17+
inject(function($$animation, $$rAF, $rootScope) {
1818

1919
element = jqLite('<div></div>');
2020
var done = false;
2121
$$animation(element, 'someEvent').then(function() {
2222
done = true;
2323
});
2424
$$rAF.flush();
25+
$rootScope.$digest();
2526
expect(done).toBe(true);
2627
}));
2728

@@ -40,6 +41,7 @@ describe('$$animation', function() {
4041
});
4142
$rootScope.$digest();
4243
$$rAF.flush();
44+
$rootScope.$digest();
4345
expect(done).toBe(true);
4446
});
4547
});
@@ -195,6 +197,7 @@ describe('$$animation', function() {
195197
// the resolve/rejection digest
196198
$rootScope.$digest();
197199
$$rAF.flush();
200+
$rootScope.$digest();
198201

199202
expect(status).toBe(event);
200203
});
@@ -207,13 +210,12 @@ describe('$$animation', function() {
207210

208211
module(function($$animationProvider, $provide) {
209212
$$animationProvider.drivers.push('actualDriver');
210-
$provide.factory('actualDriver', function($qRaf, $$animateRunner) {
213+
$provide.factory('actualDriver', function($animateRunner) {
211214
return function() {
212215
return {
213216
start: function() {
214217
log.push('start');
215-
var promise = $qRaf.when(true);
216-
return $$animateRunner(promise, {
218+
return new $animateRunner({
217219
end: function() {
218220
log.push('end');
219221
},
@@ -249,12 +251,11 @@ describe('$$animation', function() {
249251
capturedAnimation = null;
250252

251253
$$animationProvider.drivers.push('interceptorDriver');
252-
$provide.factory('interceptorDriver', function($q, $$animateRunner) {
254+
$provide.factory('interceptorDriver', function($animateRunner) {
253255
return function(details) {
254256
captureLog.push(capturedAnimation = details); //only one param is passed into the driver
255257
return function() {
256-
var runner = $q.when(true);
257-
return $$animateRunner(runner, {
258+
return new $animateRunner({
258259
end: runnerEvent('end'),
259260
cancel: runnerEvent('cancel')
260261
});
@@ -302,24 +303,34 @@ describe('$$animation', function() {
302303
});
303304
});
304305

305-
it('should patch the runner methods to the ones provided by the driver when the animation starts',
306-
inject(function($$animation, $rootScope) {
307-
308-
var runner = $$animation(element, 'someEvent');
309-
expect(runner.end).not.toEqual(noop);
310-
expect(runner.cancel).not.toEqual(noop);
311-
312-
var oldEnd = runner.end;
313-
var oldCancel = runner.cancel;
314-
315-
$rootScope.$digest();
306+
they('should update the runner methods to the ones provided by the driver when the animation starts',
307+
['end', 'cancel'], function(method) {
316308

317-
expect(oldEnd).not.toBe(runner.end);
318-
expect(oldCancel).not.toBe(runner.cancel);
309+
var spy = jasmine.createSpy();
310+
module(function($$animationProvider, $provide) {
311+
$$animationProvider.drivers.push('animalDriver');
312+
$provide.factory('animalDriver', function($animateRunner) {
313+
return function() {
314+
return function() {
315+
var data = {};
316+
data[method] = spy;
317+
return new $animateRunner(data);
318+
};
319+
};
320+
});
321+
});
322+
inject(function($$animation, $rootScope, $rootElement) {
323+
var r1 = $$animation(element, 'someEvent');
324+
r1[method]();
325+
expect(spy).not.toHaveBeenCalled();
326+
$rootScope.$digest(); // this clears the digest which cleans up the mess
319327

320-
runner.end();
321-
expect(runnerLog).toEqual(['end']);
322-
}));
328+
var r2 = $$animation(element, 'otherEvent');
329+
$rootScope.$digest();
330+
r2[method]();
331+
expect(spy).toHaveBeenCalled();
332+
});
333+
});
323334

324335
it('should not start the animation if the element is removed from the DOM before the postDigest kicks in',
325336
inject(function($$animation) {
@@ -559,8 +570,6 @@ describe('$$animation', function() {
559570
var runner2 = $$animation(toElement, 'enter');
560571

561572
expect(runner1).not.toBe(runner2);
562-
expect(runner1.end).not.toBe(runner2.end);
563-
expect(runner1.cancel).not.toBe(runner2.cancel);
564573

565574
fromAnchors[0].attr('ng-animate-ref', 'abc');
566575
toAnchors[0].attr('ng-animate-ref', 'abc');
@@ -661,15 +670,13 @@ describe('$$animation', function() {
661670
element = jqLite('<div></div>');
662671
parent = jqLite('<div></div>');
663672

664-
return function($$animateRunner, $q, $rootElement, $document) {
673+
return function($animateRunner, $q, $rootElement, $document) {
665674
jqLite($document[0].body).append($rootElement);
666675
$rootElement.append(parent);
667676

668677
mockedDriverFn = function(element, method, options, domOperation) {
669678
return function() {
670-
defered = $q.defer();
671-
runner = $$animateRunner(defered.promise);
672-
return runner;
679+
return runner = new $animateRunner();
673680
};
674681
};
675682
};
@@ -686,7 +693,7 @@ describe('$$animation', function() {
686693
expect(element).toHaveClass('temporary');
687694
expect(element).toHaveClass('fudge');
688695

689-
defered.resolve();
696+
runner.end();
690697
$rootScope.$digest();
691698

692699
expect(element).not.toHaveClass('temporary');
@@ -700,7 +707,7 @@ describe('$$animation', function() {
700707
$rootScope.$digest();
701708
expect(element).toHaveClass('ng-animate');
702709

703-
defered.resolve();
710+
runner.end();
704711
$rootScope.$digest();
705712

706713
expect(element).not.toHaveClass('ng-animate');
@@ -719,7 +726,7 @@ describe('$$animation', function() {
719726
$rootScope.$digest();
720727

721728
expect(domOperationFired).toBeFalsy();
722-
defered.resolve();
729+
runner.end();
723730
$rootScope.$digest();
724731

725732
expect(domOperationFired).toBeTruthy();
@@ -762,6 +769,7 @@ describe('$$animation', function() {
762769
$rootScope.$digest(); //runs the animation
763770
$rootScope.$digest(); //flushes the step code
764771
$$rAF.flush(); //runs the $$animation promise
772+
$rootScope.$digest(); //the runner promise
765773

766774
expect(completed).toBe(true);
767775
expect(element.css('background')).toBe('blue');
@@ -813,6 +821,7 @@ describe('$$animation', function() {
813821
$rootScope.$digest(); //runs the animation
814822
$rootScope.$digest(); //flushes the step code
815823
$$rAF.flush(); //runs the $$animation promise
824+
$rootScope.$digest(); //the runner promise
816825

817826
expect(completed).toBe(true);
818827
expect(element).toHaveClass('one');

0 commit comments

Comments
 (0)
This repository has been archived.