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 #14336
This commit is contained in:
Vendored
+113
-3
@@ -121,6 +121,8 @@ angular.mock.$Browser = function($log, $$taskTrackerFactory) {
|
||||
* @description
|
||||
* Flushes all pending requests and executes the defer callbacks.
|
||||
*
|
||||
* See {@link ngMock.$flushPendingsTasks} for more info.
|
||||
*
|
||||
* @param {number=} number of milliseconds to flush. See {@link #defer.now}
|
||||
*/
|
||||
self.defer.flush = function(delay) {
|
||||
@@ -155,7 +157,9 @@ angular.mock.$Browser = function($log, $$taskTrackerFactory) {
|
||||
* Verifies that there are no pending tasks that need to be flushed.
|
||||
* You can check for a specific type of tasks only, by specifying a `taskType`.
|
||||
*
|
||||
* @param {string=} taskType - The type task to check for.
|
||||
* See {@link $verifyNoPendingTasks} for more info.
|
||||
*
|
||||
* @param {string=} taskType - The type tasks to check for.
|
||||
*/
|
||||
self.defer.verifyNoPendingTasks = function(taskType) {
|
||||
var pendingTasks = !taskType
|
||||
@@ -212,6 +216,82 @@ angular.mock.$Browser.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name $flushPendingTasks
|
||||
*
|
||||
* @description
|
||||
* Flushes all currently pending tasks and executes the corresponding callbacks.
|
||||
*
|
||||
* Optionally, you can also pass a `delay` argument to only flush tasks that are scheduled to be
|
||||
* executed within `delay` milliseconds. Currently, `delay` only applies to timeouts, since all
|
||||
* other tasks have a delay of 0 (i.e. they are scheduled to be executed as soon as possible, but
|
||||
* still asynchronously).
|
||||
*
|
||||
* If no delay is specified, it uses a delay such that all currently pending tasks are flushed.
|
||||
*
|
||||
* The types of tasks that are flushed include:
|
||||
*
|
||||
* - Pending timeouts (via {@link $timeout}).
|
||||
* - Pending tasks scheduled via {@link ng.$rootScope.Scope#$applyAsync $applyAsync}.
|
||||
* - Pending tasks scheduled via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}.
|
||||
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises).
|
||||
*
|
||||
* <div class="alert alert-info">
|
||||
* Periodic tasks scheduled via {@link $interval} use a different queue and are not flushed by
|
||||
* `$flushPendingTasks()`. Use {@link ngMock.$interval#flush $interval.flush([millis])} instead.
|
||||
* </div>
|
||||
*
|
||||
* @param {number=} delay - The number of milliseconds to flush.
|
||||
*/
|
||||
angular.mock.$FlushPendingTasksProvider = function() {
|
||||
this.$get = [
|
||||
'$browser',
|
||||
function($browser) {
|
||||
return function $flushPendingTasks(delay) {
|
||||
return $browser.defer.flush(delay);
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name $verifyNoPendingTasks
|
||||
*
|
||||
* @description
|
||||
* Verifies that there are no pending tasks that need to be flushed. It throws an error if there are
|
||||
* still pending tasks.
|
||||
*
|
||||
* You can check for a specific type of tasks only, by specifying a `taskType`.
|
||||
*
|
||||
* Available task types:
|
||||
*
|
||||
* - `$timeout`: Pending timeouts (via {@link $timeout}).
|
||||
* - `$http`: Pending HTTP requests (via {@link $http}).
|
||||
* - `$route`: In-progress route transitions (via {@link $route}).
|
||||
* - `$applyAsync`: Pending tasks scheduled via {@link ng.$rootScope.Scope#$applyAsync $applyAsync}.
|
||||
* - `$evalAsync`: Pending tasks scheduled via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}.
|
||||
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises).
|
||||
*
|
||||
* <div class="alert alert-info">
|
||||
* Periodic tasks scheduled via {@link $interval} use a different queue and are not taken into
|
||||
* account by `$verifyNoPendingTasks()`. There is currently no way to verify that there are no
|
||||
* pending {@link $interval} tasks.
|
||||
* </div>
|
||||
*
|
||||
* @param {string=} taskType - The type of tasks to check for.
|
||||
*/
|
||||
angular.mock.$VerifyNoPendingTasksProvider = function() {
|
||||
this.$get = [
|
||||
'$browser',
|
||||
function($browser) {
|
||||
return function $verifyNoPendingTasks(taskType) {
|
||||
return $browser.defer.verifyNoPendingTasks(taskType);
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc provider
|
||||
@@ -2179,6 +2259,15 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
|
||||
*
|
||||
* Flushes the queue of pending tasks.
|
||||
*
|
||||
* _This method is essentially an alias of {@link ngMock.$flushPendingTasks}._
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* For historical reasons, this method will also flush non-`$timeout` pending tasks, such as
|
||||
* {@link $q} promises and tasks scheduled via
|
||||
* {@link ng.$rootScope.Scope#$applyAsync $applyAsync} and
|
||||
* {@link ng.$rootScope.Scope#$evalAsync $evalAsync}.
|
||||
* </div>
|
||||
*
|
||||
* @param {number=} delay maximum timeout amount to flush up until
|
||||
*/
|
||||
$delegate.flush = function(delay) {
|
||||
@@ -2193,7 +2282,26 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
|
||||
* @name $timeout#verifyNoPendingTasks
|
||||
* @description
|
||||
*
|
||||
* Verifies that there are no pending tasks that need to be flushed.
|
||||
* Verifies that there are no pending tasks that need to be flushed. It throws an error if there
|
||||
* are still pending tasks.
|
||||
*
|
||||
* _This method is essentially an alias of {@link ngMock.$verifyNoPendingTasks} (called with no
|
||||
* arguments)._
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* <p>
|
||||
* For historical reasons, this method will also verify non-`$timeout` pending tasks, such as
|
||||
* pending {@link $http} requests, in-progress {@link $route} transitions, unresolved
|
||||
* {@link $q} promises and tasks scheduled via
|
||||
* {@link ng.$rootScope.Scope#$applyAsync $applyAsync} and
|
||||
* {@link ng.$rootScope.Scope#$evalAsync $evalAsync}.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is recommended to use {@link ngMock.$verifyNoPendingTasks} instead, which additionally
|
||||
* supports verifying a specific type of tasks. For example, you can verify there are no
|
||||
* pending timeouts with `$verifyNoPendingTasks('$timeout')`.
|
||||
* </p>
|
||||
* </div>
|
||||
*/
|
||||
$delegate.verifyNoPendingTasks = function() {
|
||||
// For historical reasons, `$timeout.verifyNoPendingTasks()` takes all types of pending tasks
|
||||
@@ -2422,7 +2530,9 @@ angular.module('ngMock', ['ng']).provider({
|
||||
$log: angular.mock.$LogProvider,
|
||||
$interval: angular.mock.$IntervalProvider,
|
||||
$rootElement: angular.mock.$RootElementProvider,
|
||||
$componentController: angular.mock.$ComponentControllerProvider
|
||||
$componentController: angular.mock.$ComponentControllerProvider,
|
||||
$flushPendingTasks: angular.mock.$FlushPendingTasksProvider,
|
||||
$verifyNoPendingTasks: angular.mock.$VerifyNoPendingTasksProvider
|
||||
}).config(['$provide', '$compileProvider', function($provide, $compileProvider) {
|
||||
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
|
||||
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
|
||||
|
||||
Vendored
+97
-4
@@ -626,16 +626,17 @@ describe('ngMock', function() {
|
||||
|
||||
it('should flush delayed', function() {
|
||||
browser.defer(logFn('A'));
|
||||
browser.defer(logFn('B'), 10, 'taskType');
|
||||
browser.defer(logFn('C'), 20);
|
||||
browser.defer(logFn('B'), 0, 'taskTypeB');
|
||||
browser.defer(logFn('C'), 10, 'taskTypeC');
|
||||
browser.defer(logFn('D'), 20);
|
||||
expect(log).toEqual('');
|
||||
expect(browser.defer.now).toEqual(0);
|
||||
|
||||
browser.defer.flush(0);
|
||||
expect(log).toEqual('A;');
|
||||
expect(log).toEqual('A;B;');
|
||||
|
||||
browser.defer.flush();
|
||||
expect(log).toEqual('A;B;C;');
|
||||
expect(log).toEqual('A;B;C;D;');
|
||||
});
|
||||
|
||||
it('should defer and flush over time', function() {
|
||||
@@ -663,6 +664,62 @@ describe('ngMock', function() {
|
||||
it('should not throw an exception when passing a specific delay', function() {
|
||||
expect(function() {browser.defer.flush(100);}).not.toThrow();
|
||||
});
|
||||
|
||||
describe('tasks scheduled during flushing', function() {
|
||||
it('should be flushed if they do not exceed the target delay (when no delay specified)',
|
||||
function() {
|
||||
browser.defer(function() {
|
||||
logFn('1')();
|
||||
browser.defer(function() {
|
||||
logFn('3')();
|
||||
browser.defer(logFn('4'), 1);
|
||||
}, 2);
|
||||
}, 1);
|
||||
browser.defer(function() {
|
||||
logFn('2')();
|
||||
browser.defer(logFn('6'), 4);
|
||||
}, 2);
|
||||
browser.defer(logFn('5'), 5);
|
||||
|
||||
browser.defer.flush(0);
|
||||
expect(browser.defer.now).toEqual(0);
|
||||
expect(log).toEqual('');
|
||||
|
||||
browser.defer.flush();
|
||||
expect(browser.defer.now).toEqual(5);
|
||||
expect(log).toEqual('1;2;3;4;5;');
|
||||
}
|
||||
);
|
||||
|
||||
it('should be flushed if they do not exceed the specified delay',
|
||||
function() {
|
||||
browser.defer(function() {
|
||||
logFn('1')();
|
||||
browser.defer(function() {
|
||||
logFn('3')();
|
||||
browser.defer(logFn('4'), 1);
|
||||
}, 2);
|
||||
}, 1);
|
||||
browser.defer(function() {
|
||||
logFn('2')();
|
||||
browser.defer(logFn('6'), 4);
|
||||
}, 2);
|
||||
browser.defer(logFn('5'), 5);
|
||||
|
||||
browser.defer.flush(0);
|
||||
expect(browser.defer.now).toEqual(0);
|
||||
expect(log).toEqual('');
|
||||
|
||||
browser.defer.flush(4);
|
||||
expect(browser.defer.now).toEqual(4);
|
||||
expect(log).toEqual('1;2;3;4;');
|
||||
|
||||
browser.defer.flush(6);
|
||||
expect(browser.defer.now).toEqual(10);
|
||||
expect(log).toEqual('1;2;3;4;5;6;');
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('defer.cancel', function() {
|
||||
@@ -811,6 +868,42 @@ describe('ngMock', function() {
|
||||
});
|
||||
|
||||
|
||||
describe('$flushPendingTasks', function() {
|
||||
var $flushPendingTasks;
|
||||
var browserDeferFlushSpy;
|
||||
|
||||
beforeEach(inject(function($browser, _$flushPendingTasks_) {
|
||||
$flushPendingTasks = _$flushPendingTasks_;
|
||||
browserDeferFlushSpy = spyOn($browser.defer, 'flush').and.returnValue('flushed');
|
||||
}));
|
||||
|
||||
it('should delegate to `$browser.defer.flush()`', function() {
|
||||
var result = $flushPendingTasks(42);
|
||||
|
||||
expect(browserDeferFlushSpy).toHaveBeenCalledOnceWith(42);
|
||||
expect(result).toBe('flushed');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('$verifyNoPendingTasks', function() {
|
||||
var $verifyNoPendingTasks;
|
||||
var browserDeferVerifySpy;
|
||||
|
||||
beforeEach(inject(function($browser, _$verifyNoPendingTasks_) {
|
||||
$verifyNoPendingTasks = _$verifyNoPendingTasks_;
|
||||
browserDeferVerifySpy = spyOn($browser.defer, 'verifyNoPendingTasks').and.returnValue('verified');
|
||||
}));
|
||||
|
||||
it('should delegate to `$browser.defer.verifyNoPendingTasks()`', function() {
|
||||
var result = $verifyNoPendingTasks('fortyTwo');
|
||||
|
||||
expect(browserDeferVerifySpy).toHaveBeenCalledOnceWith('fortyTwo');
|
||||
expect(result).toBe('verified');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('$exceptionHandler', function() {
|
||||
it('should rethrow exceptions', inject(function($exceptionHandler) {
|
||||
expect(function() { $exceptionHandler('myException'); }).toThrow('myException');
|
||||
|
||||
Reference in New Issue
Block a user