Skip to content

Commit f661b4d

Browse files
committed
feat(ngMock): add $flushPendingTasks() and $verifyNoPendingTasks()
`$flushPendingTasks([delay])` allows flushing all pending tasks (or up to a specific delay). This includes `$timeout`s, `$q` promises and tasks scheduled via `$rootScope.$applyAsync()` and `$rootScope.$evalAsync()`. (ATM, it only flushes tasks scheduled via `$browser.defer()`, which does not include `$http` requests and `$route` transitions.) `$verifyNoPendingTasks([taskType])` allows verifying that there are no pending tasks (in general or of a specific type). This includes tasks flushed by `$flushPendingTasks()` as well as pending `$http` requests and in-progress `$route` transitions. Background: `ngMock/$timeout` has `flush()` and `verifyNoPendingTasks()` methods, but they take all kinds of tasks into account which is confusing. For example, `$timeout.verifyNoPendingTasks()` can fail (even if there are no pending timeouts) because of an unrelated pending `$http` request. This behavior is retained for backwards compatibility, but the new methods are more generic (and thus less confusing) and also allow more fine-grained control (when appropriate). Closes angular#14336
1 parent 0cb8eb5 commit f661b4d

File tree

2 files changed

+133
-2
lines changed

2 files changed

+133
-2
lines changed

src/ngMock/angular-mocks.js

+97-2
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ angular.mock.$Browser = function($log, $$taskTrackerFactory) {
121121
* @description
122122
* Flushes all pending requests and executes the defer callbacks.
123123
*
124+
* See {@link ngMock.$flushPendingsTasks} for more info.
125+
*
124126
* @param {number=} number of milliseconds to flush. See {@link #defer.now}
125127
*/
126128
self.defer.flush = function(delay) {
@@ -155,7 +157,9 @@ angular.mock.$Browser = function($log, $$taskTrackerFactory) {
155157
* Verifies that there are no pending tasks that need to be flushed.
156158
* You can check for a specific type of tasks only, by specifying a `taskType`.
157159
*
158-
* @param {string=} taskType - The type task to check for.
160+
* See {@link $verifyNoPendingsTasks} for more info.
161+
*
162+
* @param {string=} taskType - The type tasks to check for.
159163
*/
160164
self.defer.verifyNoPendingTasks = function(taskType) {
161165
var pendingTasks = !taskType
@@ -212,6 +216,72 @@ angular.mock.$Browser.prototype = {
212216
}
213217
};
214218

219+
/**
220+
* @ngdoc function
221+
* @name $flushPendingTasks
222+
*
223+
* @description
224+
* Flushes all pending tasks and executes the corresponding callbacks.
225+
*
226+
* The types of tasks that are flushed include:
227+
*
228+
* - Pending timeouts (via {@link $timeout}).
229+
* - Pending tasks scheduled via {@link ng.$rootScope.Scope#$applyAsync}.
230+
* - Pending tasks scheduled via {@link ng.$rootScope.Scope#$evalAsync}.
231+
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises).
232+
*
233+
* <div class="alert alert-info">
234+
* Periodic tasks scheduled via {@link $interval} use a different queue and are flushed by
235+
* `$flushPendingTasks()`. Use {@link ngMock.$interval#flush $interval.flush([millis])} instead.
236+
* </div>
237+
*
238+
* @param {number=} delay - The number of milliseconds to flush.
239+
*/
240+
angular.mock.$FlushPendingTasksProvider = function() {
241+
this.$get = [
242+
'$browser',
243+
function($browser) {
244+
return function $flushPendingTasks(delay) {
245+
return $browser.defer.flush(delay);
246+
};
247+
}
248+
];
249+
};
250+
251+
/**
252+
* @ngdoc function
253+
* @name $verifyNoPendingTasks
254+
*
255+
* @description
256+
* Verifies that there are no pending tasks that need to be flushed.
257+
* You can check for a specific type of tasks only, by specifying a `taskType`.
258+
*
259+
* Available task types:
260+
*
261+
* - `$timeout`: Pending timeouts (via {@link $timeout}).
262+
* - `$http`: Pending HTTP requests (via {@link $http}).
263+
* - `$route`: In-progress route transitions (via {@link $route}).
264+
* - `$applyAsync`: Pending tasks scheduled via {@link ng.$rootScope.Scope#$applyAsync}.
265+
* - `$evalAsync`: Pending tasks scheduled via {@link ng.$rootScope.Scope#$evalAsync}.
266+
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises).
267+
*
268+
* <div class="alert alert-info">
269+
* Periodic tasks scheduled via {@link $interval} use a different queue and are not taken into
270+
* account by `$verifyNoPendingTasks()`.
271+
* </div>
272+
*
273+
* @param {string=} taskType - The type of tasks to check for.
274+
*/
275+
angular.mock.$VerifyNoPendingTasksProvider = function() {
276+
this.$get = [
277+
'$browser',
278+
function($browser) {
279+
return function $verifyNoPendingTasks(taskType) {
280+
return $browser.defer.verifyNoPendingTasks(taskType);
281+
};
282+
}
283+
];
284+
};
215285

216286
/**
217287
* @ngdoc provider
@@ -2178,6 +2248,13 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
21782248
* @description
21792249
*
21802250
* Flushes the queue of pending tasks.
2251+
* _This method is essentially an alias of {@link ngMock.$flushPendingTasks}._
2252+
*
2253+
* <div class="alert alert-warning">
2254+
* For historical reasons, this method will also flush non-`$timeout` pending tasks, such as
2255+
* {@link $q} promises and tasks scheduled via {@link ng.$rootScope.Scope#$applyAsync} and
2256+
* {@link ng.$rootScope.Scope#$evalAsync}.
2257+
* </div>
21812258
*
21822259
* @param {number=} delay maximum timeout amount to flush up until
21832260
*/
@@ -2194,6 +2271,22 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
21942271
* @description
21952272
*
21962273
* Verifies that there are no pending tasks that need to be flushed.
2274+
* _This method is essentially an alias of {@link ngMock.$verifyNoPendingTasks} (called with no
2275+
* arguments)._
2276+
*
2277+
* <div class="alert alert-warning">
2278+
* <p>
2279+
* For historical reasons, this method will also verify non-`$timeout` pending tasks, such as
2280+
* pending {@link $http} requests, in-progress {@link $route} transitions, unresolved
2281+
* {@link $q} promises and tasks scheduled via {@link ng.$rootScope.Scope#$applyAsync} and
2282+
* {@link ng.$rootScope.Scope#$evalAsync}.
2283+
* </p>
2284+
* <p>
2285+
* It is recommended to use {@link ngMock.$verifyNoPendingTasks} instead, which additionally
2286+
* supports verifying a specific type of tasks. For example, you can verify there are no
2287+
* pending timeouts with `$verifyNoPendingTasks('$timeout')`.
2288+
* </p>
2289+
* </div>
21972290
*/
21982291
$delegate.verifyNoPendingTasks = function() {
21992292
// For historical reasons, `$timeout.verifyNoPendingTasks()` takes all types of pending tasks
@@ -2422,7 +2515,9 @@ angular.module('ngMock', ['ng']).provider({
24222515
$log: angular.mock.$LogProvider,
24232516
$interval: angular.mock.$IntervalProvider,
24242517
$rootElement: angular.mock.$RootElementProvider,
2425-
$componentController: angular.mock.$ComponentControllerProvider
2518+
$componentController: angular.mock.$ComponentControllerProvider,
2519+
$flushPendingTasks: angular.mock.$FlushPendingTasksProvider,
2520+
$verifyNoPendingTasks: angular.mock.$VerifyNoPendingTasksProvider
24262521
}).config(['$provide', '$compileProvider', function($provide, $compileProvider) {
24272522
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
24282523
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);

test/ngMock/angular-mocksSpec.js

+36
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,42 @@ describe('ngMock', function() {
811811
});
812812

813813

814+
describe('$flushPendingTasks', function() {
815+
var $flushPendingTasks;
816+
var browserDeferFlushSpy;
817+
818+
beforeEach(inject(function($browser, _$flushPendingTasks_) {
819+
$flushPendingTasks = _$flushPendingTasks_;
820+
browserDeferFlushSpy = spyOn($browser.defer, 'flush').and.returnValue('flushed');
821+
}));
822+
823+
it('should delegate to `$browser.defer.flush()`', function() {
824+
var result = $flushPendingTasks(42);
825+
826+
expect(browserDeferFlushSpy).toHaveBeenCalledOnceWith(42);
827+
expect(result).toBe('flushed');
828+
});
829+
});
830+
831+
832+
describe('$verifyNoPendingTasks', function() {
833+
var $verifyNoPendingTasks;
834+
var browserDeferVerifySpy;
835+
836+
beforeEach(inject(function($browser, _$verifyNoPendingTasks_) {
837+
$verifyNoPendingTasks = _$verifyNoPendingTasks_;
838+
browserDeferVerifySpy = spyOn($browser.defer, 'verifyNoPendingTasks').and.returnValue('verified');
839+
}));
840+
841+
it('should delegate to `$browser.defer.verifyNoPendingTasks()`', function() {
842+
var result = $verifyNoPendingTasks('fortyTwo');
843+
844+
expect(browserDeferVerifySpy).toHaveBeenCalledOnceWith('fortyTwo');
845+
expect(result).toBe('verified');
846+
});
847+
});
848+
849+
814850
describe('$exceptionHandler', function() {
815851
it('should rethrow exceptions', inject(function($exceptionHandler) {
816852
expect(function() { $exceptionHandler('myException'); }).toThrow('myException');

0 commit comments

Comments
 (0)