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

Commit 17b139f

Browse files
committedJul 13, 2018
feat(*): implement more granular pending task tracking
Previously, all pending async tasks (tracked via `$browser`) are treated the same. I.e. things like `$$testability.whenStable()` and `ngMock#$timeout.verifyNoPendingTasks()` take all tasks into account. Yet, in some cases we might be interested in specific tasks only. For example, if one wants to verify there are no pending `$timeout`s, they don't care if there are other pending tasks, such as `$http` requests. Similarly, one might want to get notified when all `$http` requests have completed and does not care about pending promises. This commit adds support for more granular task tracking, by enabling callers to specify the type of task that is being added/removed from the queue and enabling listeners to be triggered when specific types of tasks are completed (even if there are more pending tasks of different types). The change is backwards compatible. I.e. calling the affected methods with no explicit task-type, behaves the same as before. Related to #14336.
1 parent 10973c3 commit 17b139f

13 files changed

+579
-194
lines changed
 

‎src/ng/browser.js

+76-25
Original file line numberDiff line numberDiff line change
@@ -23,35 +23,48 @@
2323
* @param {object} $sniffer $sniffer service
2424
*/
2525
function Browser(window, document, $log, $sniffer) {
26+
var ALL_TASKS_TYPE = '$$all$$',
27+
DEFAULT_TASK_TYPE = '$$default$$';
28+
2629
var self = this,
2730
location = window.location,
2831
history = window.history,
2932
setTimeout = window.setTimeout,
3033
clearTimeout = window.clearTimeout,
31-
pendingDeferIds = {};
34+
pendingDeferIds = {},
35+
outstandingRequestCounts = {},
36+
outstandingRequestCallbacks = [];
3237

3338
self.isMock = false;
3439

35-
var outstandingRequestCount = 0;
36-
var outstandingRequestCallbacks = [];
37-
3840
// TODO(vojta): remove this temporary api
3941
self.$$completeOutstandingRequest = completeOutstandingRequest;
40-
self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
42+
self.$$incOutstandingRequestCount = incOutstandingRequestCount;
4143

4244
/**
43-
* Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
44-
* counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
45+
* Executes the `fn` function and decrements the appropriate `outstandingRequestCounts` counter.
46+
* If the counter reaches 0, all the corresponding `outstandingRequestCallbacks` are executed.
47+
* @param {Function} fn - The function to execute.
48+
* @param {string=} [taskType=DEFAULT_TASK_TYPE] The type of task that is being completed.
4549
*/
46-
function completeOutstandingRequest(fn) {
50+
function completeOutstandingRequest(fn, taskType) {
51+
taskType = taskType || DEFAULT_TASK_TYPE;
4752
try {
48-
fn.apply(null, sliceArgs(arguments, 1));
53+
fn();
4954
} finally {
50-
outstandingRequestCount--;
51-
if (outstandingRequestCount === 0) {
52-
while (outstandingRequestCallbacks.length) {
55+
decOutstandingRequestCount(taskType);
56+
57+
var countForType = outstandingRequestCounts[taskType];
58+
var countForAll = outstandingRequestCounts[ALL_TASKS_TYPE];
59+
60+
// If at least one of the queues (`ALL_TASKS_TYPE` or `taskType`) is empty, run callbacks.
61+
if (!countForAll || !countForType) {
62+
var getNextCallback = !countForAll ? getLastCallback : getLastCallbackForType;
63+
var nextCb;
64+
65+
while ((nextCb = getNextCallback(taskType))) {
5366
try {
54-
outstandingRequestCallbacks.pop()();
67+
nextCb();
5568
} catch (e) {
5669
$log.error(e);
5770
}
@@ -60,6 +73,35 @@ function Browser(window, document, $log, $sniffer) {
6073
}
6174
}
6275

76+
function decOutstandingRequestCount(taskType) {
77+
taskType = taskType || DEFAULT_TASK_TYPE;
78+
if (outstandingRequestCounts[taskType]) {
79+
outstandingRequestCounts[taskType]--;
80+
outstandingRequestCounts[ALL_TASKS_TYPE]--;
81+
}
82+
}
83+
84+
function incOutstandingRequestCount(taskType) {
85+
taskType = taskType || DEFAULT_TASK_TYPE;
86+
outstandingRequestCounts[taskType] = (outstandingRequestCounts[taskType] || 0) + 1;
87+
outstandingRequestCounts[ALL_TASKS_TYPE] = (outstandingRequestCounts[ALL_TASKS_TYPE] || 0) + 1;
88+
}
89+
90+
function getLastCallback() {
91+
var cbInfo = outstandingRequestCallbacks.pop();
92+
return cbInfo && cbInfo.cb;
93+
}
94+
95+
function getLastCallbackForType(taskType) {
96+
for (var i = outstandingRequestCallbacks.length - 1; i >= 0; --i) {
97+
var cbInfo = outstandingRequestCallbacks[i];
98+
if (cbInfo.type === taskType) {
99+
outstandingRequestCallbacks.splice(i, 1);
100+
return cbInfo.cb;
101+
}
102+
}
103+
}
104+
63105
function getHash(url) {
64106
var index = url.indexOf('#');
65107
return index === -1 ? '' : url.substr(index);
@@ -68,13 +110,15 @@ function Browser(window, document, $log, $sniffer) {
68110
/**
69111
* @private
70112
* TODO(vojta): prefix this method with $$ ?
71-
* @param {function()} callback Function that will be called when no outstanding request
113+
* @param {function()} callback Function that will be called when no outstanding request.
114+
* @param {string=} [taskType=ALL_TASKS_TYPE] The type of tasks that will be waited for.
72115
*/
73-
self.notifyWhenNoOutstandingRequests = function(callback) {
74-
if (outstandingRequestCount === 0) {
116+
self.notifyWhenNoOutstandingRequests = function(callback, taskType) {
117+
taskType = taskType || ALL_TASKS_TYPE;
118+
if (!outstandingRequestCounts[taskType]) {
75119
callback();
76120
} else {
77-
outstandingRequestCallbacks.push(callback);
121+
outstandingRequestCallbacks.push({type: taskType, cb: callback});
78122
}
79123
};
80124

@@ -307,7 +351,8 @@ function Browser(window, document, $log, $sniffer) {
307351
/**
308352
* @name $browser#defer
309353
* @param {function()} fn A function, who's execution should be deferred.
310-
* @param {number=} [delay=0] of milliseconds to defer the function execution.
354+
* @param {number=} [delay=0] Number of milliseconds to defer the function execution.
355+
* @param {string=} [taskType=DEFAULT_TASK_TYPE] The type of task that is deferred.
311356
* @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
312357
*
313358
* @description
@@ -318,14 +363,19 @@ function Browser(window, document, $log, $sniffer) {
318363
* via `$browser.defer.flush()`.
319364
*
320365
*/
321-
self.defer = function(fn, delay) {
366+
self.defer = function(fn, delay, taskType) {
322367
var timeoutId;
323-
outstandingRequestCount++;
368+
369+
delay = delay || 0;
370+
taskType = taskType || DEFAULT_TASK_TYPE;
371+
372+
incOutstandingRequestCount(taskType);
324373
timeoutId = setTimeout(function() {
325374
delete pendingDeferIds[timeoutId];
326-
completeOutstandingRequest(fn);
327-
}, delay || 0);
328-
pendingDeferIds[timeoutId] = true;
375+
completeOutstandingRequest(fn, taskType);
376+
}, delay);
377+
pendingDeferIds[timeoutId] = taskType;
378+
329379
return timeoutId;
330380
};
331381

@@ -341,10 +391,11 @@ function Browser(window, document, $log, $sniffer) {
341391
* canceled.
342392
*/
343393
self.defer.cancel = function(deferId) {
344-
if (pendingDeferIds[deferId]) {
394+
if (pendingDeferIds.hasOwnProperty(deferId)) {
395+
var taskType = pendingDeferIds[deferId];
345396
delete pendingDeferIds[deferId];
346397
clearTimeout(deferId);
347-
completeOutstandingRequest(noop);
398+
completeOutstandingRequest(noop, taskType);
348399
return true;
349400
}
350401
return false;

‎src/ng/http.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,7 @@ function $HttpProvider() {
10541054
config.paramSerializer = isString(config.paramSerializer) ?
10551055
$injector.get(config.paramSerializer) : config.paramSerializer;
10561056

1057-
$browser.$$incOutstandingRequestCount();
1057+
$browser.$$incOutstandingRequestCount('$http');
10581058

10591059
var requestInterceptors = [];
10601060
var responseInterceptors = [];
@@ -1092,7 +1092,7 @@ function $HttpProvider() {
10921092
}
10931093

10941094
function completeOutstandingRequest() {
1095-
$browser.$$completeOutstandingRequest(noop);
1095+
$browser.$$completeOutstandingRequest(noop, '$http');
10961096
}
10971097

10981098
function executeHeaderFns(headers, config) {

‎src/ng/rootScope.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,7 @@ function $RootScopeProvider() {
11221122
if (asyncQueue.length) {
11231123
$rootScope.$digest();
11241124
}
1125-
});
1125+
}, null, '$evalAsync');
11261126
}
11271127

11281128
asyncQueue.push({scope: this, fn: $parse(expr), locals: locals});
@@ -1493,7 +1493,7 @@ function $RootScopeProvider() {
14931493
if (applyAsyncId === null) {
14941494
applyAsyncId = $browser.defer(function() {
14951495
$rootScope.$apply(flushApplyAsync);
1496-
});
1496+
}, null, '$applyAsync');
14971497
}
14981498
}
14991499
}];

‎src/ng/testability.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,15 @@ function $$TestabilityProvider() {
104104
* @name $$testability#whenStable
105105
*
106106
* @description
107-
* Calls the callback when $timeout and $http requests are completed.
107+
* Calls the callback when all pending tasks are completed.
108+
*
109+
* Types of tasks waited for include:
110+
* - Pending timeouts (via {@link $timeout}).
111+
* - Pending HTTP requests (via {@link $http}).
112+
* - In-progress route transitions (via {@link $route}).
113+
* - Pending tasks scheduled via {@link $rootScope#$applyAsync}.
114+
* - Pending tasks scheduled via {@link $rootScope#$evalAsync}.
115+
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises).
108116
*
109117
* @param {function} callback
110118
*/

‎src/ng/timeout.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function $TimeoutProvider() {
6363
}
6464

6565
if (!skipApply) $rootScope.$apply();
66-
}, delay);
66+
}, delay, '$timeout');
6767

6868
promise.$$timeoutId = timeoutId;
6969
deferreds[timeoutId] = deferred;

‎src/ngMock/angular-mocks.js

+114-45
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ angular.mock.$BrowserProvider = function() {
3232
};
3333

3434
angular.mock.$Browser = function() {
35+
var ALL_TASKS_TYPE = '$$all$$';
36+
var DEFAULT_TASK_TYPE = '$$default$$';
3537
var self = this;
3638

3739
this.isMock = true;
@@ -41,28 +43,67 @@ angular.mock.$Browser = function() {
4143

4244
// Testability API
4345

44-
var outstandingRequestCount = 0;
46+
var outstandingRequestCounts = {};
4547
var outstandingRequestCallbacks = [];
46-
self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
47-
self.$$completeOutstandingRequest = function(fn) {
48+
49+
self.$$completeOutstandingRequest = completeOutstandingRequest;
50+
self.$$incOutstandingRequestCount = incOutstandingRequestCount;
51+
self.notifyWhenNoOutstandingRequests = notifyWhenNoOutstandingRequests;
52+
53+
function decOutstandingRequestCount(taskType) {
54+
taskType = taskType || DEFAULT_TASK_TYPE;
55+
if (outstandingRequestCounts[taskType]) {
56+
outstandingRequestCounts[taskType]--;
57+
outstandingRequestCounts[ALL_TASKS_TYPE]--;
58+
}
59+
}
60+
function incOutstandingRequestCount(taskType) {
61+
taskType = taskType || DEFAULT_TASK_TYPE;
62+
outstandingRequestCounts[taskType] = (outstandingRequestCounts[taskType] || 0) + 1;
63+
outstandingRequestCounts[ALL_TASKS_TYPE] = (outstandingRequestCounts[ALL_TASKS_TYPE] || 0) + 1;
64+
}
65+
function completeOutstandingRequest(fn, taskType) {
66+
taskType = taskType || DEFAULT_TASK_TYPE;
4867
try {
4968
fn();
5069
} finally {
51-
outstandingRequestCount--;
52-
if (!outstandingRequestCount) {
53-
while (outstandingRequestCallbacks.length) {
54-
outstandingRequestCallbacks.pop()();
70+
decOutstandingRequestCount(taskType);
71+
72+
var countForType = outstandingRequestCounts[taskType];
73+
var countForAll = outstandingRequestCounts[ALL_TASKS_TYPE];
74+
75+
// If at least one of the queues (`ALL_TASKS_TYPE` or `taskType`) is empty, run callbacks.
76+
if (!countForAll || !countForType) {
77+
var getNextCallback = !countForAll ? getLastCallback : getLastCallbackForType;
78+
var nextCb;
79+
80+
while ((nextCb = getNextCallback(taskType))) {
81+
nextCb();
5582
}
5683
}
5784
}
58-
};
59-
self.notifyWhenNoOutstandingRequests = function(callback) {
60-
if (outstandingRequestCount) {
61-
outstandingRequestCallbacks.push(callback);
62-
} else {
85+
}
86+
function getLastCallback() {
87+
var cbInfo = outstandingRequestCallbacks.pop();
88+
return cbInfo && cbInfo.cb;
89+
}
90+
function getLastCallbackForType(taskType) {
91+
for (var i = outstandingRequestCallbacks.length - 1; i >= 0; --i) {
92+
var cbInfo = outstandingRequestCallbacks[i];
93+
if (cbInfo.type === taskType) {
94+
outstandingRequestCallbacks.splice(i, 1);
95+
return cbInfo.cb;
96+
}
97+
}
98+
}
99+
function notifyWhenNoOutstandingRequests(callback, taskType) {
100+
taskType = taskType || ALL_TASKS_TYPE;
101+
if (!outstandingRequestCounts[taskType]) {
63102
callback();
103+
} else {
104+
outstandingRequestCallbacks.push({type: taskType, cb: callback});
64105
}
65-
};
106+
}
66107

67108
// register url polling fn
68109

@@ -86,13 +127,22 @@ angular.mock.$Browser = function() {
86127
self.deferredFns = [];
87128
self.deferredNextId = 0;
88129

89-
self.defer = function(fn, delay) {
90-
// Note that we do not use `$$incOutstandingRequestCount` or `$$completeOutstandingRequest`
91-
// in this mock implementation.
130+
self.defer = function(fn, delay, taskType) {
131+
var timeoutId = self.deferredNextId++;
132+
92133
delay = delay || 0;
93-
self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
94-
self.deferredFns.sort(function(a, b) { return a.time - b.time;});
95-
return self.deferredNextId++;
134+
taskType = taskType || DEFAULT_TASK_TYPE;
135+
136+
incOutstandingRequestCount(taskType);
137+
self.deferredFns.push({
138+
id: timeoutId,
139+
type: taskType,
140+
time: (self.defer.now + delay),
141+
fn: fn
142+
});
143+
self.deferredFns.sort(function(a, b) { return a.time - b.time; });
144+
145+
return timeoutId;
96146
};
97147

98148

@@ -106,14 +156,15 @@ angular.mock.$Browser = function() {
106156

107157

108158
self.defer.cancel = function(deferId) {
109-
var fnIndex;
159+
var taskIndex;
110160

111-
angular.forEach(self.deferredFns, function(fn, index) {
112-
if (fn.id === deferId) fnIndex = index;
161+
angular.forEach(self.deferredFns, function(task, index) {
162+
if (task.id === deferId) taskIndex = index;
113163
});
114164

115-
if (angular.isDefined(fnIndex)) {
116-
self.deferredFns.splice(fnIndex, 1);
165+
if (angular.isDefined(taskIndex)) {
166+
var task = self.deferredFns.splice(taskIndex, 1)[0];
167+
completeOutstandingRequest(angular.noop, task.type);
117168
return true;
118169
}
119170

@@ -135,26 +186,51 @@ angular.mock.$Browser = function() {
135186
if (angular.isDefined(delay)) {
136187
// A delay was passed so compute the next time
137188
nextTime = self.defer.now + delay;
189+
} else if (self.deferredFns.length) {
190+
// No delay was passed so set the next time so that it clears the deferred queue
191+
nextTime = self.deferredFns[self.deferredFns.length - 1].time;
138192
} else {
139-
if (self.deferredFns.length) {
140-
// No delay was passed so set the next time so that it clears the deferred queue
141-
nextTime = self.deferredFns[self.deferredFns.length - 1].time;
142-
} else {
143-
// No delay passed, but there are no deferred tasks so flush - indicates an error!
144-
throw new Error('No deferred tasks to be flushed');
145-
}
193+
// No delay passed, but there are no deferred tasks so flush - indicates an error!
194+
throw new Error('No deferred tasks to be flushed');
146195
}
147196

148197
while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
149198
// Increment the time and call the next deferred function
150199
self.defer.now = self.deferredFns[0].time;
151-
self.deferredFns.shift().fn();
200+
var task = self.deferredFns.shift();
201+
completeOutstandingRequest(task.fn, task.type);
152202
}
153203

154204
// Ensure that the current time is correct
155205
self.defer.now = nextTime;
156206
};
157207

208+
/**
209+
* @name $browser#defer.verifyNoPendingTasks
210+
*
211+
* @description
212+
* Verifies that there are no pending tasks that need to be flushed.
213+
* You can check for a specific type of tasks only, by specifying a `taskType`.
214+
*
215+
* @param {string=} taskType - The type task to check for.
216+
*/
217+
self.defer.verifyNoPendingTasks = function(taskType) {
218+
var pendingTasks = !taskType
219+
? self.deferredFns
220+
: self.deferredFns.filter(function(task) { return task.type === taskType; });
221+
222+
if (pendingTasks.length) {
223+
var formattedTasks = pendingTasks
224+
.map(function(task) {
225+
return '{id: ' + task.id + ', type: ' + task.type + ', time: ' + task.time + '}';
226+
})
227+
.join('\n ');
228+
229+
throw new Error('Deferred tasks to flush (' + pendingTasks.length + '):\n ' +
230+
formattedTasks);
231+
}
232+
};
233+
158234
self.$$baseHref = '/';
159235
self.baseHref = function() {
160236
return this.$$baseHref;
@@ -2187,6 +2263,9 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
21872263
* @param {number=} delay maximum timeout amount to flush up until
21882264
*/
21892265
$delegate.flush = function(delay) {
2266+
// For historical reasons, `$timeout.flush()` flushes all types of pending tasks.
2267+
// Keep the same behavior for backwards compatibility (and because it doesn't make sense to
2268+
// selectively flush scheduled events out of order).
21902269
$browser.defer.flush(delay);
21912270
};
21922271

@@ -2198,21 +2277,11 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
21982277
* Verifies that there are no pending tasks that need to be flushed.
21992278
*/
22002279
$delegate.verifyNoPendingTasks = function() {
2201-
if ($browser.deferredFns.length) {
2202-
throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
2203-
formatPendingTasksAsString($browser.deferredFns));
2204-
}
2280+
// For historical reasons, `$timeout.verifyNoPendingTasks()` takes all types of pending tasks
2281+
// into account. Keep the same behavior for backwards compatibility.
2282+
$browser.defer.verifyNoPendingTasks();
22052283
};
22062284

2207-
function formatPendingTasksAsString(tasks) {
2208-
var result = [];
2209-
angular.forEach(tasks, function(task) {
2210-
result.push('{id: ' + task.id + ', time: ' + task.time + '}');
2211-
});
2212-
2213-
return result.join(', ');
2214-
}
2215-
22162285
return $delegate;
22172286
}];
22182287

‎src/ngRoute/route.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ function $RouteProvider() {
653653

654654
var nextRoutePromise = $q.resolve(nextRoute);
655655

656-
$browser.$$incOutstandingRequestCount();
656+
$browser.$$incOutstandingRequestCount('$route');
657657

658658
nextRoutePromise.
659659
then(getRedirectionData).
@@ -681,7 +681,7 @@ function $RouteProvider() {
681681
// `outstandingRequestCount` to hit zero. This is important in case we are redirecting
682682
// to a new route which also requires some asynchronous work.
683683

684-
$browser.$$completeOutstandingRequest(noop);
684+
$browser.$$completeOutstandingRequest(noop, '$route');
685685
});
686686
}
687687
}

‎test/ng/browserSpecs.js

+127-16
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ function MockWindow(options) {
3434
timeouts[id] = noop;
3535
};
3636

37-
this.setTimeout.flush = function() {
38-
var length = timeouts.length;
39-
while (length-- > 0) timeouts.shift()();
37+
this.setTimeout.flush = function(count) {
38+
count = count || timeouts.length;
39+
while (count-- > 0) timeouts.shift()();
4040
};
4141

4242
this.addEventListener = function(name, listener) {
@@ -144,21 +144,21 @@ function MockDocument() {
144144

145145
describe('browser', function() {
146146
/* global Browser: false */
147-
var browser, fakeWindow, fakeDocument, fakeLog, logs, scripts, removedScripts;
147+
var browser, fakeWindow, fakeDocument, fakeLog, logs;
148148

149149
beforeEach(function() {
150-
scripts = [];
151-
removedScripts = [];
152150
sniffer = {history: true};
153151
fakeWindow = new MockWindow();
154152
fakeDocument = new MockDocument();
155153

156154
logs = {log:[], warn:[], info:[], error:[]};
157155

158-
fakeLog = {log: function() { logs.log.push(slice.call(arguments)); },
159-
warn: function() { logs.warn.push(slice.call(arguments)); },
160-
info: function() { logs.info.push(slice.call(arguments)); },
161-
error: function() { logs.error.push(slice.call(arguments)); }};
156+
fakeLog = {
157+
log: function() { logs.log.push(slice.call(arguments)); },
158+
warn: function() { logs.warn.push(slice.call(arguments)); },
159+
info: function() { logs.info.push(slice.call(arguments)); },
160+
error: function() { logs.error.push(slice.call(arguments)); }
161+
};
162162

163163
browser = new Browser(fakeWindow, fakeDocument, fakeLog, sniffer);
164164
});
@@ -214,12 +214,66 @@ describe('browser', function() {
214214
}
215215
});
216216

217-
describe('outstanding requests', function() {
218-
it('should process callbacks immediately with no outstanding requests', function() {
217+
218+
describe('notifyWhenNoOutstandingRequests', function() {
219+
it('should invoke callbacks immediately if there are no pending tasks', function() {
220+
var callback = jasmine.createSpy('callback');
221+
browser.notifyWhenNoOutstandingRequests(callback);
222+
expect(callback).toHaveBeenCalled();
223+
});
224+
225+
226+
it('should invoke callbacks immediately if there are no pending tasks (for specific task-type)',
227+
function() {
228+
var callbackAll = jasmine.createSpy('callbackAll');
229+
var callbackFoo = jasmine.createSpy('callbackFoo');
230+
231+
browser.$$incOutstandingRequestCount();
232+
browser.notifyWhenNoOutstandingRequests(callbackAll);
233+
browser.notifyWhenNoOutstandingRequests(callbackFoo, 'foo');
234+
235+
expect(callbackAll).not.toHaveBeenCalled();
236+
expect(callbackFoo).toHaveBeenCalled();
237+
}
238+
);
239+
240+
241+
it('should invoke callbacks as soon as there are no pending tasks', function() {
219242
var callback = jasmine.createSpy('callback');
243+
244+
browser.$$incOutstandingRequestCount();
220245
browser.notifyWhenNoOutstandingRequests(callback);
246+
expect(callback).not.toHaveBeenCalled();
247+
248+
browser.$$completeOutstandingRequest(noop);
221249
expect(callback).toHaveBeenCalled();
222250
});
251+
252+
253+
it('should invoke callbacks as soon as there are no pending tasks (for specific task-type)',
254+
function() {
255+
var callbackAll = jasmine.createSpy('callbackAll');
256+
var callbackFoo = jasmine.createSpy('callbackFoo');
257+
258+
browser.$$incOutstandingRequestCount();
259+
browser.$$incOutstandingRequestCount('foo');
260+
browser.notifyWhenNoOutstandingRequests(callbackAll);
261+
browser.notifyWhenNoOutstandingRequests(callbackFoo, 'foo');
262+
263+
expect(callbackAll).not.toHaveBeenCalled();
264+
expect(callbackFoo).not.toHaveBeenCalled();
265+
266+
browser.$$completeOutstandingRequest(noop, 'foo');
267+
268+
expect(callbackAll).not.toHaveBeenCalled();
269+
expect(callbackFoo).toHaveBeenCalledOnce();
270+
271+
browser.$$completeOutstandingRequest(noop);
272+
273+
expect(callbackAll).toHaveBeenCalledOnce();
274+
expect(callbackFoo).toHaveBeenCalledOnce();
275+
}
276+
);
223277
});
224278

225279

@@ -236,13 +290,36 @@ describe('browser', function() {
236290

237291

238292
it('should update outstandingRequests counter', function() {
239-
var callback = jasmine.createSpy('deferred');
293+
var noPendingTasksSpy = jasmine.createSpy('noPendingTasks');
240294

241-
browser.defer(callback);
242-
expect(callback).not.toHaveBeenCalled();
295+
browser.defer(noop);
296+
browser.notifyWhenNoOutstandingRequests(noPendingTasksSpy);
297+
expect(noPendingTasksSpy).not.toHaveBeenCalled();
243298

244299
fakeWindow.setTimeout.flush();
245-
expect(callback).toHaveBeenCalledOnce();
300+
expect(noPendingTasksSpy).toHaveBeenCalledOnce();
301+
});
302+
303+
304+
it('should update outstandingRequests counter (for specific task-type)', function() {
305+
var noPendingFooTasksSpy = jasmine.createSpy('noPendingFooTasks');
306+
var noPendingTasksSpy = jasmine.createSpy('noPendingTasks');
307+
308+
browser.defer(noop, 0, 'foo');
309+
browser.defer(noop, 0, 'bar');
310+
311+
browser.notifyWhenNoOutstandingRequests(noPendingFooTasksSpy, 'foo');
312+
browser.notifyWhenNoOutstandingRequests(noPendingTasksSpy);
313+
expect(noPendingFooTasksSpy).not.toHaveBeenCalled();
314+
expect(noPendingTasksSpy).not.toHaveBeenCalled();
315+
316+
fakeWindow.setTimeout.flush(1);
317+
expect(noPendingFooTasksSpy).toHaveBeenCalledOnce();
318+
expect(noPendingTasksSpy).not.toHaveBeenCalled();
319+
320+
fakeWindow.setTimeout.flush(1);
321+
expect(noPendingFooTasksSpy).toHaveBeenCalledOnce();
322+
expect(noPendingTasksSpy).toHaveBeenCalledOnce();
246323
});
247324

248325

@@ -270,6 +347,40 @@ describe('browser', function() {
270347
expect(log).toEqual(['ok']);
271348
expect(browser.defer.cancel(deferId2)).toBe(false);
272349
});
350+
351+
352+
it('should update outstandingRequests counter', function() {
353+
var noPendingTasksSpy = jasmine.createSpy('noPendingTasks');
354+
var deferId = browser.defer(noop);
355+
356+
browser.notifyWhenNoOutstandingRequests(noPendingTasksSpy);
357+
expect(noPendingTasksSpy).not.toHaveBeenCalled();
358+
359+
browser.defer.cancel(deferId);
360+
expect(noPendingTasksSpy).toHaveBeenCalledOnce();
361+
});
362+
363+
364+
it('should update outstandingRequests counter (for specific task-type)', function() {
365+
var noPendingFooTasksSpy = jasmine.createSpy('noPendingFooTasks');
366+
var noPendingTasksSpy = jasmine.createSpy('noPendingTasks');
367+
368+
var deferId1 = browser.defer(noop, 0, 'foo');
369+
var deferId2 = browser.defer(noop, 0, 'bar');
370+
371+
browser.notifyWhenNoOutstandingRequests(noPendingFooTasksSpy, 'foo');
372+
browser.notifyWhenNoOutstandingRequests(noPendingTasksSpy);
373+
expect(noPendingFooTasksSpy).not.toHaveBeenCalled();
374+
expect(noPendingTasksSpy).not.toHaveBeenCalled();
375+
376+
browser.defer.cancel(deferId1);
377+
expect(noPendingFooTasksSpy).toHaveBeenCalledOnce();
378+
expect(noPendingTasksSpy).not.toHaveBeenCalled();
379+
380+
browser.defer.cancel(deferId2);
381+
expect(noPendingFooTasksSpy).toHaveBeenCalledOnce();
382+
expect(noPendingTasksSpy).toHaveBeenCalledOnce();
383+
});
273384
});
274385
});
275386

‎test/ng/httpSpec.js

+20-20
Original file line numberDiff line numberDiff line change
@@ -2002,7 +2002,7 @@ describe('$http', function() {
20022002
it('should immediately call `$browser.$$incOutstandingRequestCount()`', function() {
20032003
expect(incOutstandingRequestCountSpy).not.toHaveBeenCalled();
20042004
$http.get('');
2005-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2005+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
20062006
});
20072007

20082008

@@ -2012,7 +2012,7 @@ describe('$http', function() {
20122012
$http.get('');
20132013
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
20142014
$httpBackend.flush();
2015-
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
2015+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
20162016
});
20172017

20182018

@@ -2022,7 +2022,7 @@ describe('$http', function() {
20222022
$http.get('').catch(noop);
20232023
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
20242024
$httpBackend.flush();
2025-
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
2025+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
20262026
});
20272027

20282028

@@ -2033,13 +2033,13 @@ describe('$http', function() {
20332033

20342034
$http.get('', {transformRequest: function() { throw new Error(); }}).catch(noop);
20352035

2036-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2036+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
20372037
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
20382038

20392039
$rootScope.$digest();
20402040

2041-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2042-
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
2041+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
2042+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
20432043
}
20442044
);
20452045

@@ -2052,13 +2052,13 @@ describe('$http', function() {
20522052
$httpBackend.when('GET').respond(200);
20532053
$http.get('', {transformResponse: function() { throw new Error(); }}).catch(noop);
20542054

2055-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2055+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
20562056
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
20572057

20582058
$httpBackend.flush();
20592059

2060-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2061-
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
2060+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
2061+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
20622062
}
20632063
);
20642064
});
@@ -2112,24 +2112,24 @@ describe('$http', function() {
21122112

21132113
expect(reqInterceptorFulfilled).toBe(false);
21142114
expect(resInterceptorFulfilled).toBe(false);
2115-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2115+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
21162116
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
21172117

21182118
reqInterceptorDeferred.resolve();
21192119
$httpBackend.flush();
21202120

21212121
expect(reqInterceptorFulfilled).toBe(true);
21222122
expect(resInterceptorFulfilled).toBe(false);
2123-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2123+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
21242124
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
21252125

21262126
resInterceptorDeferred.resolve();
21272127
$rootScope.$digest();
21282128

21292129
expect(reqInterceptorFulfilled).toBe(true);
21302130
expect(resInterceptorFulfilled).toBe(true);
2131-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2132-
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
2131+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
2132+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
21332133
}
21342134
);
21352135

@@ -2144,15 +2144,15 @@ describe('$http', function() {
21442144
$rootScope.$digest();
21452145

21462146
expect(reqInterceptorFulfilled).toBe(false);
2147-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2147+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
21482148
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
21492149

21502150
reqInterceptorDeferred.reject();
21512151
$rootScope.$digest();
21522152

21532153
expect(reqInterceptorFulfilled).toBe(true);
2154-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2155-
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
2154+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
2155+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
21562156
}
21572157
);
21582158

@@ -2169,24 +2169,24 @@ describe('$http', function() {
21692169

21702170
expect(reqInterceptorFulfilled).toBe(false);
21712171
expect(resInterceptorFulfilled).toBe(false);
2172-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2172+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
21732173
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
21742174

21752175
reqInterceptorDeferred.resolve();
21762176
$httpBackend.flush();
21772177

21782178
expect(reqInterceptorFulfilled).toBe(true);
21792179
expect(resInterceptorFulfilled).toBe(false);
2180-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2180+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
21812181
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
21822182

21832183
resInterceptorDeferred.reject();
21842184
$rootScope.$digest();
21852185

21862186
expect(reqInterceptorFulfilled).toBe(true);
21872187
expect(resInterceptorFulfilled).toBe(true);
2188-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2189-
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
2188+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
2189+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
21902190
}
21912191
);
21922192
});

‎test/ng/rootScopeSpec.js

-1
Original file line numberDiff line numberDiff line change
@@ -2387,7 +2387,6 @@ describe('Scope', function() {
23872387

23882388

23892389
it('should be cancelled if a $rootScope digest occurs before the next tick', inject(function($rootScope, $browser) {
2390-
var apply = spyOn($rootScope, '$apply').and.callThrough();
23912390
var cancel = spyOn($browser.defer, 'cancel').and.callThrough();
23922391
var expression = jasmine.createSpy('expr');
23932392

‎test/ng/testabilitySpec.js

+9
Original file line numberDiff line numberDiff line change
@@ -194,5 +194,14 @@ describe('$$testability', function() {
194194
$$testability.whenStable(callback);
195195
expect(callback).toHaveBeenCalled();
196196
}));
197+
198+
it('should delegate to `$browser.notifyWhenNoOutstandingRequests()`',
199+
inject(function($$testability, $browser) {
200+
var spy = spyOn($browser, 'notifyWhenNoOutstandingRequests');
201+
var callback = noop;
202+
203+
$$testability.whenStable(callback);
204+
expect(spy).toHaveBeenCalledWith(callback);
205+
}));
197206
});
198207
});

‎test/ngMock/angular-mocksSpec.js

+203-44
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ describe('ngMock', function() {
601601
});
602602

603603

604-
describe('defer', function() {
604+
describe('$browser', function() {
605605
var browser, log;
606606
beforeEach(inject(function($browser) {
607607
browser = $browser;
@@ -614,47 +614,199 @@ describe('ngMock', function() {
614614
};
615615
}
616616

617-
it('should flush', function() {
618-
browser.defer(logFn('A'));
619-
expect(log).toEqual('');
620-
browser.defer.flush();
621-
expect(log).toEqual('A;');
617+
describe('defer.flush', function() {
618+
it('should flush', function() {
619+
browser.defer(logFn('A'));
620+
browser.defer(logFn('B'), null, 'taskType');
621+
expect(log).toEqual('');
622+
623+
browser.defer.flush();
624+
expect(log).toEqual('A;B;');
625+
});
626+
627+
it('should flush delayed', function() {
628+
browser.defer(logFn('A'));
629+
browser.defer(logFn('B'), 10, 'taskType');
630+
browser.defer(logFn('C'), 20);
631+
expect(log).toEqual('');
632+
expect(browser.defer.now).toEqual(0);
633+
634+
browser.defer.flush(0);
635+
expect(log).toEqual('A;');
636+
637+
browser.defer.flush();
638+
expect(log).toEqual('A;B;C;');
639+
});
640+
641+
it('should defer and flush over time', function() {
642+
browser.defer(logFn('A'), 1);
643+
browser.defer(logFn('B'), 2, 'taskType');
644+
browser.defer(logFn('C'), 3);
645+
646+
browser.defer.flush(0);
647+
expect(browser.defer.now).toEqual(0);
648+
expect(log).toEqual('');
649+
650+
browser.defer.flush(1);
651+
expect(browser.defer.now).toEqual(1);
652+
expect(log).toEqual('A;');
653+
654+
browser.defer.flush(2);
655+
expect(browser.defer.now).toEqual(3);
656+
expect(log).toEqual('A;B;C;');
657+
});
658+
659+
it('should throw an exception if there is nothing to be flushed', function() {
660+
expect(function() {browser.defer.flush();}).toThrowError('No deferred tasks to be flushed');
661+
});
662+
663+
it('should not throw an exception when passing a specific delay', function() {
664+
expect(function() {browser.defer.flush(100);}).not.toThrow();
665+
});
622666
});
623667

624-
it('should flush delayed', function() {
625-
browser.defer(logFn('A'));
626-
browser.defer(logFn('B'), 10);
627-
browser.defer(logFn('C'), 20);
628-
expect(log).toEqual('');
668+
describe('defer.cancel', function() {
669+
it('should cancel a pending task', function() {
670+
var taskId1 = browser.defer(logFn('A'), 100, 'fooType');
671+
var taskId2 = browser.defer(logFn('B'), 200);
672+
673+
expect(log).toBe('');
674+
expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).toThrow();
675+
expect(function() {browser.defer.verifyNoPendingTasks();}).toThrow();
676+
677+
browser.defer.cancel(taskId1);
678+
expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).not.toThrow();
679+
expect(function() {browser.defer.verifyNoPendingTasks();}).toThrow();
629680

630-
expect(browser.defer.now).toEqual(0);
631-
browser.defer.flush(0);
632-
expect(log).toEqual('A;');
681+
browser.defer.cancel(taskId2);
682+
expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).not.toThrow();
683+
expect(function() {browser.defer.verifyNoPendingTasks();}).not.toThrow();
633684

634-
browser.defer.flush();
635-
expect(log).toEqual('A;B;C;');
685+
browser.defer.flush(1000);
686+
expect(log).toBe('');
687+
});
636688
});
637689

638-
it('should defer and flush over time', function() {
639-
browser.defer(logFn('A'), 1);
640-
browser.defer(logFn('B'), 2);
641-
browser.defer(logFn('C'), 3);
690+
describe('defer.verifyNoPendingTasks', function() {
691+
it('should throw if there are pending tasks', function() {
692+
expect(browser.defer.verifyNoPendingTasks).not.toThrow();
693+
694+
browser.defer(noop);
695+
expect(browser.defer.verifyNoPendingTasks).toThrow();
696+
});
697+
698+
it('should list the pending tasks (in order) in the error message', function() {
699+
browser.defer(noop, 100);
700+
browser.defer(noop, 300, 'fooType');
701+
browser.defer(noop, 200, 'barType');
642702

643-
browser.defer.flush(0);
644-
expect(browser.defer.now).toEqual(0);
645-
expect(log).toEqual('');
703+
var expectedError =
704+
'Deferred tasks to flush (3):\n' +
705+
' {id: 0, type: $$default$$, time: 100}\n' +
706+
' {id: 2, type: barType, time: 200}\n' +
707+
' {id: 1, type: fooType, time: 300}';
708+
expect(browser.defer.verifyNoPendingTasks).toThrowError(expectedError);
709+
});
710+
711+
describe('with specific task type', function() {
712+
it('should throw if there are pending tasks', function() {
713+
browser.defer(noop, 0, 'fooType');
646714

647-
browser.defer.flush(1);
648-
expect(browser.defer.now).toEqual(1);
649-
expect(log).toEqual('A;');
715+
expect(function() {browser.defer.verifyNoPendingTasks('barType');}).not.toThrow();
716+
expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).toThrow();
717+
expect(function() {browser.defer.verifyNoPendingTasks();}).toThrow();
718+
});
650719

651-
browser.defer.flush(2);
652-
expect(browser.defer.now).toEqual(3);
653-
expect(log).toEqual('A;B;C;');
720+
it('should list the pending tasks (in order) in the error message', function() {
721+
browser.defer(noop, 100);
722+
browser.defer(noop, 300, 'fooType');
723+
browser.defer(noop, 200, 'barType');
724+
browser.defer(noop, 400, 'fooType');
725+
726+
var expectedError =
727+
'Deferred tasks to flush (2):\n' +
728+
' {id: 1, type: fooType, time: 300}\n' +
729+
' {id: 3, type: fooType, time: 400}';
730+
expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).
731+
toThrowError(expectedError);
732+
});
733+
});
654734
});
655735

656-
it('should throw an exception if there is nothing to be flushed', function() {
657-
expect(function() {browser.defer.flush();}).toThrowError('No deferred tasks to be flushed');
736+
describe('notifyWhenNoOutstandingRequests', function() {
737+
var callback;
738+
beforeEach(function() {
739+
callback = jasmine.createSpy('callback');
740+
});
741+
742+
it('should immediately run the callback if no pending tasks', function() {
743+
browser.notifyWhenNoOutstandingRequests(callback);
744+
expect(callback).toHaveBeenCalled();
745+
});
746+
747+
it('should run the callback as soon as there are no pending tasks', function() {
748+
browser.defer(noop, 100);
749+
browser.defer(noop, 200);
750+
751+
browser.notifyWhenNoOutstandingRequests(callback);
752+
expect(callback).not.toHaveBeenCalled();
753+
754+
browser.defer.flush(100);
755+
expect(callback).not.toHaveBeenCalled();
756+
757+
browser.defer.flush(100);
758+
expect(callback).toHaveBeenCalled();
759+
});
760+
761+
it('should not run the callback more than once', function() {
762+
browser.defer(noop, 100);
763+
browser.notifyWhenNoOutstandingRequests(callback);
764+
expect(callback).not.toHaveBeenCalled();
765+
766+
browser.defer.flush(100);
767+
expect(callback).toHaveBeenCalledOnce();
768+
769+
browser.defer(noop, 200);
770+
browser.defer.flush(100);
771+
expect(callback).toHaveBeenCalledOnce();
772+
});
773+
774+
describe('with specific task type', function() {
775+
it('should immediately run the callback if no pending tasks', function() {
776+
browser.notifyWhenNoOutstandingRequests(callback, 'fooType');
777+
expect(callback).toHaveBeenCalled();
778+
});
779+
780+
it('should run the callback as soon as there are no pending tasks', function() {
781+
browser.defer(noop, 100, 'fooType');
782+
browser.defer(noop, 200, 'barType');
783+
784+
browser.notifyWhenNoOutstandingRequests(callback, 'fooType');
785+
expect(callback).not.toHaveBeenCalled();
786+
787+
browser.defer.flush(100);
788+
expect(callback).toHaveBeenCalled();
789+
});
790+
791+
it('should not run the callback more than once', function() {
792+
browser.defer(noop, 100, 'fooType');
793+
browser.defer(noop, 200);
794+
795+
browser.notifyWhenNoOutstandingRequests(callback, 'fooType');
796+
expect(callback).not.toHaveBeenCalled();
797+
798+
browser.defer.flush(100);
799+
expect(callback).toHaveBeenCalledOnce();
800+
801+
browser.defer.flush(100);
802+
expect(callback).toHaveBeenCalledOnce();
803+
804+
browser.defer(noop, 100, 'fooType');
805+
browser.defer(noop, 200);
806+
browser.defer.flush();
807+
expect(callback).toHaveBeenCalledOnce();
808+
});
809+
});
658810
});
659811
});
660812

@@ -705,47 +857,53 @@ describe('ngMock', function() {
705857

706858
describe('$timeout', function() {
707859
it('should expose flush method that will flush the pending queue of tasks', inject(
708-
function($timeout) {
860+
function($rootScope, $timeout) {
709861
var logger = [],
710862
logFn = function(msg) { return function() { logger.push(msg); }; };
711863

712864
$timeout(logFn('t1'));
713865
$timeout(logFn('t2'), 200);
866+
$rootScope.$evalAsync(logFn('rs')); // Non-timeout tasks are flushed as well.
714867
$timeout(logFn('t3'));
715868
expect(logger).toEqual([]);
716869

717870
$timeout.flush();
718-
expect(logger).toEqual(['t1', 't3', 't2']);
871+
expect(logger).toEqual(['t1', 'rs', 't3', 't2']);
719872
}));
720873

721874

722-
it('should throw an exception when not flushed', inject(function($timeout) {
723-
$timeout(noop);
875+
it('should throw an exception when not flushed', inject(function($rootScope, $timeout) {
876+
$timeout(noop, 100);
877+
$rootScope.$evalAsync(noop);
724878

725-
var expectedError = 'Deferred tasks to flush (1): {id: 0, time: 0}';
726-
expect(function() {$timeout.verifyNoPendingTasks();}).toThrowError(expectedError);
879+
var expectedError =
880+
'Deferred tasks to flush (2):\n' +
881+
' {id: 1, type: $evalAsync, time: 0}\n' +
882+
' {id: 0, type: $timeout, time: 100}';
883+
expect($timeout.verifyNoPendingTasks).toThrowError(expectedError);
727884
}));
728885

729886

730-
it('should do nothing when all tasks have been flushed', inject(function($timeout) {
731-
$timeout(noop);
887+
it('should do nothing when all tasks have been flushed', inject(function($rootScope, $timeout) {
888+
$timeout(noop, 100);
889+
$rootScope.$evalAsync(noop);
732890

733891
$timeout.flush();
734-
expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow();
892+
expect($timeout.verifyNoPendingTasks).not.toThrow();
735893
}));
736894

737895

738896
it('should check against the delay if provided within timeout', inject(function($timeout) {
739897
$timeout(noop, 100);
740898
$timeout.flush(100);
741-
expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow();
899+
expect($timeout.verifyNoPendingTasks).not.toThrow();
742900

743901
$timeout(noop, 1000);
744902
$timeout.flush(100);
745-
expect(function() {$timeout.verifyNoPendingTasks();}).toThrow();
903+
expect($timeout.verifyNoPendingTasks).toThrow();
746904

747905
$timeout.flush(900);
748-
expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow();
906+
expect($timeout.verifyNoPendingTasks).not.toThrow();
749907
}));
750908

751909

@@ -763,6 +921,7 @@ describe('ngMock', function() {
763921
expect(count).toBe(2);
764922
}));
765923

924+
766925
it('should resolve timeout functions following the timeline', inject(function($timeout) {
767926
var count1 = 0, count2 = 0;
768927
var iterate1 = function() {
@@ -1056,7 +1215,7 @@ describe('ngMock', function() {
10561215

10571216

10581217
describe('$httpBackend', function() {
1059-
var hb, callback, realBackendSpy;
1218+
var hb, callback;
10601219

10611220
beforeEach(inject(function($httpBackend) {
10621221
callback = jasmine.createSpy('callback');

‎test/ngRoute/routeSpec.js

+14-35
Original file line numberDiff line numberDiff line change
@@ -2419,9 +2419,8 @@ describe('$route', function() {
24192419
it('should wait for $resolve promises before calling callbacks', function() {
24202420
var deferred;
24212421

2422-
module(function($provide, $routeProvider) {
2422+
module(function($routeProvider) {
24232423
$routeProvider.when('/path', {
2424-
template: '',
24252424
resolve: {
24262425
a: function($q) {
24272426
deferred = $q.defer();
@@ -2431,7 +2430,7 @@ describe('$route', function() {
24312430
});
24322431
});
24332432

2434-
inject(function($location, $route, $rootScope, $httpBackend, $$testability) {
2433+
inject(function($browser, $location, $rootScope, $$testability) {
24352434
$location.path('/path');
24362435
$rootScope.$digest();
24372436

@@ -2440,17 +2439,16 @@ describe('$route', function() {
24402439
expect(callback).not.toHaveBeenCalled();
24412440

24422441
deferred.resolve();
2443-
$rootScope.$digest();
2442+
$browser.defer.flush();
24442443
expect(callback).toHaveBeenCalled();
24452444
});
24462445
});
24472446

24482447
it('should call callback after $resolve promises are rejected', function() {
24492448
var deferred;
24502449

2451-
module(function($provide, $routeProvider) {
2450+
module(function($routeProvider) {
24522451
$routeProvider.when('/path', {
2453-
template: '',
24542452
resolve: {
24552453
a: function($q) {
24562454
deferred = $q.defer();
@@ -2460,7 +2458,7 @@ describe('$route', function() {
24602458
});
24612459
});
24622460

2463-
inject(function($location, $route, $rootScope, $httpBackend, $$testability) {
2461+
inject(function($browser, $location, $rootScope, $$testability) {
24642462
$location.path('/path');
24652463
$rootScope.$digest();
24662464

@@ -2469,15 +2467,15 @@ describe('$route', function() {
24692467
expect(callback).not.toHaveBeenCalled();
24702468

24712469
deferred.reject();
2472-
$rootScope.$digest();
2470+
$browser.defer.flush();
24732471
expect(callback).toHaveBeenCalled();
24742472
});
24752473
});
24762474

24772475
it('should wait for resolveRedirectTo promises before calling callbacks', function() {
24782476
var deferred;
24792477

2480-
module(function($provide, $routeProvider) {
2478+
module(function($routeProvider) {
24812479
$routeProvider.when('/path', {
24822480
resolveRedirectTo: function($q) {
24832481
deferred = $q.defer();
@@ -2486,7 +2484,7 @@ describe('$route', function() {
24862484
});
24872485
});
24882486

2489-
inject(function($location, $route, $rootScope, $httpBackend, $$testability) {
2487+
inject(function($browser, $location, $rootScope, $$testability) {
24902488
$location.path('/path');
24912489
$rootScope.$digest();
24922490

@@ -2495,15 +2493,15 @@ describe('$route', function() {
24952493
expect(callback).not.toHaveBeenCalled();
24962494

24972495
deferred.resolve();
2498-
$rootScope.$digest();
2496+
$browser.defer.flush();
24992497
expect(callback).toHaveBeenCalled();
25002498
});
25012499
});
25022500

25032501
it('should call callback after resolveRedirectTo promises are rejected', function() {
25042502
var deferred;
25052503

2506-
module(function($provide, $routeProvider) {
2504+
module(function($routeProvider) {
25072505
$routeProvider.when('/path', {
25082506
resolveRedirectTo: function($q) {
25092507
deferred = $q.defer();
@@ -2512,7 +2510,7 @@ describe('$route', function() {
25122510
});
25132511
});
25142512

2515-
inject(function($location, $route, $rootScope, $httpBackend, $$testability) {
2513+
inject(function($browser, $location, $rootScope, $$testability) {
25162514
$location.path('/path');
25172515
$rootScope.$digest();
25182516

@@ -2521,38 +2519,19 @@ describe('$route', function() {
25212519
expect(callback).not.toHaveBeenCalled();
25222520

25232521
deferred.reject();
2524-
$rootScope.$digest();
2522+
$browser.defer.flush();
25252523
expect(callback).toHaveBeenCalled();
25262524
});
25272525
});
25282526

25292527
it('should wait for all route promises before calling callbacks', function() {
25302528
var deferreds = {};
25312529

2532-
module(function($provide, $routeProvider) {
2533-
// While normally `$browser.defer()` modifies the `outstandingRequestCount`, the mocked
2534-
// version (provided by `ngMock`) does not. This doesn't matter in most tests, but in this
2535-
// case we need the `outstandingRequestCount` logic to ensure that we don't call the
2536-
// `$$testability.whenStable()` callbacks part way through a `$rootScope.$evalAsync` block.
2537-
// See ngRoute's commitRoute()'s finally() block for details.
2538-
$provide.decorator('$browser', function($delegate) {
2539-
var oldDefer = $delegate.defer;
2540-
var newDefer = function(fn, delay) {
2541-
var requestCountAwareFn = function() { $delegate.$$completeOutstandingRequest(fn); };
2542-
$delegate.$$incOutstandingRequestCount();
2543-
return oldDefer.call($delegate, requestCountAwareFn, delay);
2544-
};
2545-
2546-
$delegate.defer = angular.extend(newDefer, oldDefer);
2547-
2548-
return $delegate;
2549-
});
2550-
2530+
module(function($routeProvider) {
25512531
addRouteWithAsyncRedirect('/foo', '/bar');
25522532
addRouteWithAsyncRedirect('/bar', '/baz');
25532533
addRouteWithAsyncRedirect('/baz', '/qux');
25542534
$routeProvider.when('/qux', {
2555-
template: '',
25562535
resolve: {
25572536
a: function($q) {
25582537
var deferred = deferreds['/qux'] = $q.defer();
@@ -2572,7 +2551,7 @@ describe('$route', function() {
25722551
}
25732552
});
25742553

2575-
inject(function($browser, $location, $rootScope, $route, $$testability) {
2554+
inject(function($browser, $location, $rootScope, $$testability) {
25762555
$location.path('/foo');
25772556
$rootScope.$digest();
25782557

0 commit comments

Comments
 (0)
This repository has been archived.