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.
This commit is contained in:
+76
-25
@@ -23,35 +23,48 @@
|
||||
* @param {object} $sniffer $sniffer service
|
||||
*/
|
||||
function Browser(window, document, $log, $sniffer) {
|
||||
var ALL_TASKS_TYPE = '$$all$$',
|
||||
DEFAULT_TASK_TYPE = '$$default$$';
|
||||
|
||||
var self = this,
|
||||
location = window.location,
|
||||
history = window.history,
|
||||
setTimeout = window.setTimeout,
|
||||
clearTimeout = window.clearTimeout,
|
||||
pendingDeferIds = {};
|
||||
pendingDeferIds = {},
|
||||
outstandingRequestCounts = {},
|
||||
outstandingRequestCallbacks = [];
|
||||
|
||||
self.isMock = false;
|
||||
|
||||
var outstandingRequestCount = 0;
|
||||
var outstandingRequestCallbacks = [];
|
||||
|
||||
// TODO(vojta): remove this temporary api
|
||||
self.$$completeOutstandingRequest = completeOutstandingRequest;
|
||||
self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
|
||||
self.$$incOutstandingRequestCount = incOutstandingRequestCount;
|
||||
|
||||
/**
|
||||
* Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
|
||||
* counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
|
||||
* Executes the `fn` function and decrements the appropriate `outstandingRequestCounts` counter.
|
||||
* If the counter reaches 0, all the corresponding `outstandingRequestCallbacks` are executed.
|
||||
* @param {Function} fn - The function to execute.
|
||||
* @param {string=} [taskType=DEFAULT_TASK_TYPE] The type of task that is being completed.
|
||||
*/
|
||||
function completeOutstandingRequest(fn) {
|
||||
function completeOutstandingRequest(fn, taskType) {
|
||||
taskType = taskType || DEFAULT_TASK_TYPE;
|
||||
try {
|
||||
fn.apply(null, sliceArgs(arguments, 1));
|
||||
fn();
|
||||
} finally {
|
||||
outstandingRequestCount--;
|
||||
if (outstandingRequestCount === 0) {
|
||||
while (outstandingRequestCallbacks.length) {
|
||||
decOutstandingRequestCount(taskType);
|
||||
|
||||
var countForType = outstandingRequestCounts[taskType];
|
||||
var countForAll = outstandingRequestCounts[ALL_TASKS_TYPE];
|
||||
|
||||
// If at least one of the queues (`ALL_TASKS_TYPE` or `taskType`) is empty, run callbacks.
|
||||
if (!countForAll || !countForType) {
|
||||
var getNextCallback = !countForAll ? getLastCallback : getLastCallbackForType;
|
||||
var nextCb;
|
||||
|
||||
while ((nextCb = getNextCallback(taskType))) {
|
||||
try {
|
||||
outstandingRequestCallbacks.pop()();
|
||||
nextCb();
|
||||
} catch (e) {
|
||||
$log.error(e);
|
||||
}
|
||||
@@ -60,6 +73,35 @@ function Browser(window, document, $log, $sniffer) {
|
||||
}
|
||||
}
|
||||
|
||||
function decOutstandingRequestCount(taskType) {
|
||||
taskType = taskType || DEFAULT_TASK_TYPE;
|
||||
if (outstandingRequestCounts[taskType]) {
|
||||
outstandingRequestCounts[taskType]--;
|
||||
outstandingRequestCounts[ALL_TASKS_TYPE]--;
|
||||
}
|
||||
}
|
||||
|
||||
function incOutstandingRequestCount(taskType) {
|
||||
taskType = taskType || DEFAULT_TASK_TYPE;
|
||||
outstandingRequestCounts[taskType] = (outstandingRequestCounts[taskType] || 0) + 1;
|
||||
outstandingRequestCounts[ALL_TASKS_TYPE] = (outstandingRequestCounts[ALL_TASKS_TYPE] || 0) + 1;
|
||||
}
|
||||
|
||||
function getLastCallback() {
|
||||
var cbInfo = outstandingRequestCallbacks.pop();
|
||||
return cbInfo && cbInfo.cb;
|
||||
}
|
||||
|
||||
function getLastCallbackForType(taskType) {
|
||||
for (var i = outstandingRequestCallbacks.length - 1; i >= 0; --i) {
|
||||
var cbInfo = outstandingRequestCallbacks[i];
|
||||
if (cbInfo.type === taskType) {
|
||||
outstandingRequestCallbacks.splice(i, 1);
|
||||
return cbInfo.cb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getHash(url) {
|
||||
var index = url.indexOf('#');
|
||||
return index === -1 ? '' : url.substr(index);
|
||||
@@ -68,13 +110,15 @@ function Browser(window, document, $log, $sniffer) {
|
||||
/**
|
||||
* @private
|
||||
* TODO(vojta): prefix this method with $$ ?
|
||||
* @param {function()} callback Function that will be called when no outstanding request
|
||||
* @param {function()} callback Function that will be called when no outstanding request.
|
||||
* @param {string=} [taskType=ALL_TASKS_TYPE] The type of tasks that will be waited for.
|
||||
*/
|
||||
self.notifyWhenNoOutstandingRequests = function(callback) {
|
||||
if (outstandingRequestCount === 0) {
|
||||
self.notifyWhenNoOutstandingRequests = function(callback, taskType) {
|
||||
taskType = taskType || ALL_TASKS_TYPE;
|
||||
if (!outstandingRequestCounts[taskType]) {
|
||||
callback();
|
||||
} else {
|
||||
outstandingRequestCallbacks.push(callback);
|
||||
outstandingRequestCallbacks.push({type: taskType, cb: callback});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -307,7 +351,8 @@ function Browser(window, document, $log, $sniffer) {
|
||||
/**
|
||||
* @name $browser#defer
|
||||
* @param {function()} fn A function, who's execution should be deferred.
|
||||
* @param {number=} [delay=0] of milliseconds to defer the function execution.
|
||||
* @param {number=} [delay=0] Number of milliseconds to defer the function execution.
|
||||
* @param {string=} [taskType=DEFAULT_TASK_TYPE] The type of task that is deferred.
|
||||
* @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
|
||||
*
|
||||
* @description
|
||||
@@ -318,14 +363,19 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* via `$browser.defer.flush()`.
|
||||
*
|
||||
*/
|
||||
self.defer = function(fn, delay) {
|
||||
self.defer = function(fn, delay, taskType) {
|
||||
var timeoutId;
|
||||
outstandingRequestCount++;
|
||||
|
||||
delay = delay || 0;
|
||||
taskType = taskType || DEFAULT_TASK_TYPE;
|
||||
|
||||
incOutstandingRequestCount(taskType);
|
||||
timeoutId = setTimeout(function() {
|
||||
delete pendingDeferIds[timeoutId];
|
||||
completeOutstandingRequest(fn);
|
||||
}, delay || 0);
|
||||
pendingDeferIds[timeoutId] = true;
|
||||
completeOutstandingRequest(fn, taskType);
|
||||
}, delay);
|
||||
pendingDeferIds[timeoutId] = taskType;
|
||||
|
||||
return timeoutId;
|
||||
};
|
||||
|
||||
@@ -341,10 +391,11 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* canceled.
|
||||
*/
|
||||
self.defer.cancel = function(deferId) {
|
||||
if (pendingDeferIds[deferId]) {
|
||||
if (pendingDeferIds.hasOwnProperty(deferId)) {
|
||||
var taskType = pendingDeferIds[deferId];
|
||||
delete pendingDeferIds[deferId];
|
||||
clearTimeout(deferId);
|
||||
completeOutstandingRequest(noop);
|
||||
completeOutstandingRequest(noop, taskType);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
+2
-2
@@ -1054,7 +1054,7 @@ function $HttpProvider() {
|
||||
config.paramSerializer = isString(config.paramSerializer) ?
|
||||
$injector.get(config.paramSerializer) : config.paramSerializer;
|
||||
|
||||
$browser.$$incOutstandingRequestCount();
|
||||
$browser.$$incOutstandingRequestCount('$http');
|
||||
|
||||
var requestInterceptors = [];
|
||||
var responseInterceptors = [];
|
||||
@@ -1092,7 +1092,7 @@ function $HttpProvider() {
|
||||
}
|
||||
|
||||
function completeOutstandingRequest() {
|
||||
$browser.$$completeOutstandingRequest(noop);
|
||||
$browser.$$completeOutstandingRequest(noop, '$http');
|
||||
}
|
||||
|
||||
function executeHeaderFns(headers, config) {
|
||||
|
||||
+2
-2
@@ -1122,7 +1122,7 @@ function $RootScopeProvider() {
|
||||
if (asyncQueue.length) {
|
||||
$rootScope.$digest();
|
||||
}
|
||||
});
|
||||
}, null, '$evalAsync');
|
||||
}
|
||||
|
||||
asyncQueue.push({scope: this, fn: $parse(expr), locals: locals});
|
||||
@@ -1493,7 +1493,7 @@ function $RootScopeProvider() {
|
||||
if (applyAsyncId === null) {
|
||||
applyAsyncId = $browser.defer(function() {
|
||||
$rootScope.$apply(flushApplyAsync);
|
||||
});
|
||||
}, null, '$applyAsync');
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
@@ -104,7 +104,15 @@ function $$TestabilityProvider() {
|
||||
* @name $$testability#whenStable
|
||||
*
|
||||
* @description
|
||||
* Calls the callback when $timeout and $http requests are completed.
|
||||
* Calls the callback when all pending tasks are completed.
|
||||
*
|
||||
* Types of tasks waited for include:
|
||||
* - Pending timeouts (via {@link $timeout}).
|
||||
* - Pending HTTP requests (via {@link $http}).
|
||||
* - In-progress route transitions (via {@link $route}).
|
||||
* - Pending tasks scheduled via {@link $rootScope#$applyAsync}.
|
||||
* - Pending tasks scheduled via {@link $rootScope#$evalAsync}.
|
||||
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises).
|
||||
*
|
||||
* @param {function} callback
|
||||
*/
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ function $TimeoutProvider() {
|
||||
}
|
||||
|
||||
if (!skipApply) $rootScope.$apply();
|
||||
}, delay);
|
||||
}, delay, '$timeout');
|
||||
|
||||
promise.$$timeoutId = timeoutId;
|
||||
deferreds[timeoutId] = deferred;
|
||||
|
||||
Vendored
+115
-46
@@ -32,6 +32,8 @@ angular.mock.$BrowserProvider = function() {
|
||||
};
|
||||
|
||||
angular.mock.$Browser = function() {
|
||||
var ALL_TASKS_TYPE = '$$all$$';
|
||||
var DEFAULT_TASK_TYPE = '$$default$$';
|
||||
var self = this;
|
||||
|
||||
this.isMock = true;
|
||||
@@ -41,28 +43,67 @@ angular.mock.$Browser = function() {
|
||||
|
||||
// Testability API
|
||||
|
||||
var outstandingRequestCount = 0;
|
||||
var outstandingRequestCounts = {};
|
||||
var outstandingRequestCallbacks = [];
|
||||
self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
|
||||
self.$$completeOutstandingRequest = function(fn) {
|
||||
|
||||
self.$$completeOutstandingRequest = completeOutstandingRequest;
|
||||
self.$$incOutstandingRequestCount = incOutstandingRequestCount;
|
||||
self.notifyWhenNoOutstandingRequests = notifyWhenNoOutstandingRequests;
|
||||
|
||||
function decOutstandingRequestCount(taskType) {
|
||||
taskType = taskType || DEFAULT_TASK_TYPE;
|
||||
if (outstandingRequestCounts[taskType]) {
|
||||
outstandingRequestCounts[taskType]--;
|
||||
outstandingRequestCounts[ALL_TASKS_TYPE]--;
|
||||
}
|
||||
}
|
||||
function incOutstandingRequestCount(taskType) {
|
||||
taskType = taskType || DEFAULT_TASK_TYPE;
|
||||
outstandingRequestCounts[taskType] = (outstandingRequestCounts[taskType] || 0) + 1;
|
||||
outstandingRequestCounts[ALL_TASKS_TYPE] = (outstandingRequestCounts[ALL_TASKS_TYPE] || 0) + 1;
|
||||
}
|
||||
function completeOutstandingRequest(fn, taskType) {
|
||||
taskType = taskType || DEFAULT_TASK_TYPE;
|
||||
try {
|
||||
fn();
|
||||
} finally {
|
||||
outstandingRequestCount--;
|
||||
if (!outstandingRequestCount) {
|
||||
while (outstandingRequestCallbacks.length) {
|
||||
outstandingRequestCallbacks.pop()();
|
||||
decOutstandingRequestCount(taskType);
|
||||
|
||||
var countForType = outstandingRequestCounts[taskType];
|
||||
var countForAll = outstandingRequestCounts[ALL_TASKS_TYPE];
|
||||
|
||||
// If at least one of the queues (`ALL_TASKS_TYPE` or `taskType`) is empty, run callbacks.
|
||||
if (!countForAll || !countForType) {
|
||||
var getNextCallback = !countForAll ? getLastCallback : getLastCallbackForType;
|
||||
var nextCb;
|
||||
|
||||
while ((nextCb = getNextCallback(taskType))) {
|
||||
nextCb();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
self.notifyWhenNoOutstandingRequests = function(callback) {
|
||||
if (outstandingRequestCount) {
|
||||
outstandingRequestCallbacks.push(callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
function getLastCallback() {
|
||||
var cbInfo = outstandingRequestCallbacks.pop();
|
||||
return cbInfo && cbInfo.cb;
|
||||
}
|
||||
function getLastCallbackForType(taskType) {
|
||||
for (var i = outstandingRequestCallbacks.length - 1; i >= 0; --i) {
|
||||
var cbInfo = outstandingRequestCallbacks[i];
|
||||
if (cbInfo.type === taskType) {
|
||||
outstandingRequestCallbacks.splice(i, 1);
|
||||
return cbInfo.cb;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
function notifyWhenNoOutstandingRequests(callback, taskType) {
|
||||
taskType = taskType || ALL_TASKS_TYPE;
|
||||
if (!outstandingRequestCounts[taskType]) {
|
||||
callback();
|
||||
} else {
|
||||
outstandingRequestCallbacks.push({type: taskType, cb: callback});
|
||||
}
|
||||
}
|
||||
|
||||
// register url polling fn
|
||||
|
||||
@@ -86,13 +127,22 @@ angular.mock.$Browser = function() {
|
||||
self.deferredFns = [];
|
||||
self.deferredNextId = 0;
|
||||
|
||||
self.defer = function(fn, delay) {
|
||||
// Note that we do not use `$$incOutstandingRequestCount` or `$$completeOutstandingRequest`
|
||||
// in this mock implementation.
|
||||
self.defer = function(fn, delay, taskType) {
|
||||
var timeoutId = self.deferredNextId++;
|
||||
|
||||
delay = delay || 0;
|
||||
self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
|
||||
self.deferredFns.sort(function(a, b) { return a.time - b.time;});
|
||||
return self.deferredNextId++;
|
||||
taskType = taskType || DEFAULT_TASK_TYPE;
|
||||
|
||||
incOutstandingRequestCount(taskType);
|
||||
self.deferredFns.push({
|
||||
id: timeoutId,
|
||||
type: taskType,
|
||||
time: (self.defer.now + delay),
|
||||
fn: fn
|
||||
});
|
||||
self.deferredFns.sort(function(a, b) { return a.time - b.time; });
|
||||
|
||||
return timeoutId;
|
||||
};
|
||||
|
||||
|
||||
@@ -106,14 +156,15 @@ angular.mock.$Browser = function() {
|
||||
|
||||
|
||||
self.defer.cancel = function(deferId) {
|
||||
var fnIndex;
|
||||
var taskIndex;
|
||||
|
||||
angular.forEach(self.deferredFns, function(fn, index) {
|
||||
if (fn.id === deferId) fnIndex = index;
|
||||
angular.forEach(self.deferredFns, function(task, index) {
|
||||
if (task.id === deferId) taskIndex = index;
|
||||
});
|
||||
|
||||
if (angular.isDefined(fnIndex)) {
|
||||
self.deferredFns.splice(fnIndex, 1);
|
||||
if (angular.isDefined(taskIndex)) {
|
||||
var task = self.deferredFns.splice(taskIndex, 1)[0];
|
||||
completeOutstandingRequest(angular.noop, task.type);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -135,26 +186,51 @@ angular.mock.$Browser = function() {
|
||||
if (angular.isDefined(delay)) {
|
||||
// A delay was passed so compute the next time
|
||||
nextTime = self.defer.now + delay;
|
||||
} else if (self.deferredFns.length) {
|
||||
// No delay was passed so set the next time so that it clears the deferred queue
|
||||
nextTime = self.deferredFns[self.deferredFns.length - 1].time;
|
||||
} else {
|
||||
if (self.deferredFns.length) {
|
||||
// No delay was passed so set the next time so that it clears the deferred queue
|
||||
nextTime = self.deferredFns[self.deferredFns.length - 1].time;
|
||||
} else {
|
||||
// No delay passed, but there are no deferred tasks so flush - indicates an error!
|
||||
throw new Error('No deferred tasks to be flushed');
|
||||
}
|
||||
// No delay passed, but there are no deferred tasks so flush - indicates an error!
|
||||
throw new Error('No deferred tasks to be flushed');
|
||||
}
|
||||
|
||||
while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
|
||||
// Increment the time and call the next deferred function
|
||||
self.defer.now = self.deferredFns[0].time;
|
||||
self.deferredFns.shift().fn();
|
||||
var task = self.deferredFns.shift();
|
||||
completeOutstandingRequest(task.fn, task.type);
|
||||
}
|
||||
|
||||
// Ensure that the current time is correct
|
||||
self.defer.now = nextTime;
|
||||
};
|
||||
|
||||
/**
|
||||
* @name $browser#defer.verifyNoPendingTasks
|
||||
*
|
||||
* @description
|
||||
* 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.
|
||||
*/
|
||||
self.defer.verifyNoPendingTasks = function(taskType) {
|
||||
var pendingTasks = !taskType
|
||||
? self.deferredFns
|
||||
: self.deferredFns.filter(function(task) { return task.type === taskType; });
|
||||
|
||||
if (pendingTasks.length) {
|
||||
var formattedTasks = pendingTasks
|
||||
.map(function(task) {
|
||||
return '{id: ' + task.id + ', type: ' + task.type + ', time: ' + task.time + '}';
|
||||
})
|
||||
.join('\n ');
|
||||
|
||||
throw new Error('Deferred tasks to flush (' + pendingTasks.length + '):\n ' +
|
||||
formattedTasks);
|
||||
}
|
||||
};
|
||||
|
||||
self.$$baseHref = '/';
|
||||
self.baseHref = function() {
|
||||
return this.$$baseHref;
|
||||
@@ -2187,6 +2263,9 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
|
||||
* @param {number=} delay maximum timeout amount to flush up until
|
||||
*/
|
||||
$delegate.flush = function(delay) {
|
||||
// For historical reasons, `$timeout.flush()` flushes all types of pending tasks.
|
||||
// Keep the same behavior for backwards compatibility (and because it doesn't make sense to
|
||||
// selectively flush scheduled events out of order).
|
||||
$browser.defer.flush(delay);
|
||||
};
|
||||
|
||||
@@ -2198,21 +2277,11 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
|
||||
* Verifies that there are no pending tasks that need to be flushed.
|
||||
*/
|
||||
$delegate.verifyNoPendingTasks = function() {
|
||||
if ($browser.deferredFns.length) {
|
||||
throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
|
||||
formatPendingTasksAsString($browser.deferredFns));
|
||||
}
|
||||
// For historical reasons, `$timeout.verifyNoPendingTasks()` takes all types of pending tasks
|
||||
// into account. Keep the same behavior for backwards compatibility.
|
||||
$browser.defer.verifyNoPendingTasks();
|
||||
};
|
||||
|
||||
function formatPendingTasksAsString(tasks) {
|
||||
var result = [];
|
||||
angular.forEach(tasks, function(task) {
|
||||
result.push('{id: ' + task.id + ', time: ' + task.time + '}');
|
||||
});
|
||||
|
||||
return result.join(', ');
|
||||
}
|
||||
|
||||
return $delegate;
|
||||
}];
|
||||
|
||||
|
||||
@@ -653,7 +653,7 @@ function $RouteProvider() {
|
||||
|
||||
var nextRoutePromise = $q.resolve(nextRoute);
|
||||
|
||||
$browser.$$incOutstandingRequestCount();
|
||||
$browser.$$incOutstandingRequestCount('$route');
|
||||
|
||||
nextRoutePromise.
|
||||
then(getRedirectionData).
|
||||
@@ -681,7 +681,7 @@ function $RouteProvider() {
|
||||
// `outstandingRequestCount` to hit zero. This is important in case we are redirecting
|
||||
// to a new route which also requires some asynchronous work.
|
||||
|
||||
$browser.$$completeOutstandingRequest(noop);
|
||||
$browser.$$completeOutstandingRequest(noop, '$route');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+127
-16
@@ -34,9 +34,9 @@ function MockWindow(options) {
|
||||
timeouts[id] = noop;
|
||||
};
|
||||
|
||||
this.setTimeout.flush = function() {
|
||||
var length = timeouts.length;
|
||||
while (length-- > 0) timeouts.shift()();
|
||||
this.setTimeout.flush = function(count) {
|
||||
count = count || timeouts.length;
|
||||
while (count-- > 0) timeouts.shift()();
|
||||
};
|
||||
|
||||
this.addEventListener = function(name, listener) {
|
||||
@@ -144,21 +144,21 @@ function MockDocument() {
|
||||
|
||||
describe('browser', function() {
|
||||
/* global Browser: false */
|
||||
var browser, fakeWindow, fakeDocument, fakeLog, logs, scripts, removedScripts;
|
||||
var browser, fakeWindow, fakeDocument, fakeLog, logs;
|
||||
|
||||
beforeEach(function() {
|
||||
scripts = [];
|
||||
removedScripts = [];
|
||||
sniffer = {history: true};
|
||||
fakeWindow = new MockWindow();
|
||||
fakeDocument = new MockDocument();
|
||||
|
||||
logs = {log:[], warn:[], info:[], error:[]};
|
||||
|
||||
fakeLog = {log: function() { logs.log.push(slice.call(arguments)); },
|
||||
warn: function() { logs.warn.push(slice.call(arguments)); },
|
||||
info: function() { logs.info.push(slice.call(arguments)); },
|
||||
error: function() { logs.error.push(slice.call(arguments)); }};
|
||||
fakeLog = {
|
||||
log: function() { logs.log.push(slice.call(arguments)); },
|
||||
warn: function() { logs.warn.push(slice.call(arguments)); },
|
||||
info: function() { logs.info.push(slice.call(arguments)); },
|
||||
error: function() { logs.error.push(slice.call(arguments)); }
|
||||
};
|
||||
|
||||
browser = new Browser(fakeWindow, fakeDocument, fakeLog, sniffer);
|
||||
});
|
||||
@@ -214,12 +214,66 @@ describe('browser', function() {
|
||||
}
|
||||
});
|
||||
|
||||
describe('outstanding requests', function() {
|
||||
it('should process callbacks immediately with no outstanding requests', function() {
|
||||
|
||||
describe('notifyWhenNoOutstandingRequests', function() {
|
||||
it('should invoke callbacks immediately if there are no pending tasks', function() {
|
||||
var callback = jasmine.createSpy('callback');
|
||||
browser.notifyWhenNoOutstandingRequests(callback);
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should invoke callbacks immediately if there are no pending tasks (for specific task-type)',
|
||||
function() {
|
||||
var callbackAll = jasmine.createSpy('callbackAll');
|
||||
var callbackFoo = jasmine.createSpy('callbackFoo');
|
||||
|
||||
browser.$$incOutstandingRequestCount();
|
||||
browser.notifyWhenNoOutstandingRequests(callbackAll);
|
||||
browser.notifyWhenNoOutstandingRequests(callbackFoo, 'foo');
|
||||
|
||||
expect(callbackAll).not.toHaveBeenCalled();
|
||||
expect(callbackFoo).toHaveBeenCalled();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
it('should invoke callbacks as soon as there are no pending tasks', function() {
|
||||
var callback = jasmine.createSpy('callback');
|
||||
|
||||
browser.$$incOutstandingRequestCount();
|
||||
browser.notifyWhenNoOutstandingRequests(callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
browser.$$completeOutstandingRequest(noop);
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should invoke callbacks as soon as there are no pending tasks (for specific task-type)',
|
||||
function() {
|
||||
var callbackAll = jasmine.createSpy('callbackAll');
|
||||
var callbackFoo = jasmine.createSpy('callbackFoo');
|
||||
|
||||
browser.$$incOutstandingRequestCount();
|
||||
browser.$$incOutstandingRequestCount('foo');
|
||||
browser.notifyWhenNoOutstandingRequests(callbackAll);
|
||||
browser.notifyWhenNoOutstandingRequests(callbackFoo, 'foo');
|
||||
|
||||
expect(callbackAll).not.toHaveBeenCalled();
|
||||
expect(callbackFoo).not.toHaveBeenCalled();
|
||||
|
||||
browser.$$completeOutstandingRequest(noop, 'foo');
|
||||
|
||||
expect(callbackAll).not.toHaveBeenCalled();
|
||||
expect(callbackFoo).toHaveBeenCalledOnce();
|
||||
|
||||
browser.$$completeOutstandingRequest(noop);
|
||||
|
||||
expect(callbackAll).toHaveBeenCalledOnce();
|
||||
expect(callbackFoo).toHaveBeenCalledOnce();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -236,13 +290,36 @@ describe('browser', function() {
|
||||
|
||||
|
||||
it('should update outstandingRequests counter', function() {
|
||||
var callback = jasmine.createSpy('deferred');
|
||||
var noPendingTasksSpy = jasmine.createSpy('noPendingTasks');
|
||||
|
||||
browser.defer(callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
browser.defer(noop);
|
||||
browser.notifyWhenNoOutstandingRequests(noPendingTasksSpy);
|
||||
expect(noPendingTasksSpy).not.toHaveBeenCalled();
|
||||
|
||||
fakeWindow.setTimeout.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(noPendingTasksSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
it('should update outstandingRequests counter (for specific task-type)', function() {
|
||||
var noPendingFooTasksSpy = jasmine.createSpy('noPendingFooTasks');
|
||||
var noPendingTasksSpy = jasmine.createSpy('noPendingTasks');
|
||||
|
||||
browser.defer(noop, 0, 'foo');
|
||||
browser.defer(noop, 0, 'bar');
|
||||
|
||||
browser.notifyWhenNoOutstandingRequests(noPendingFooTasksSpy, 'foo');
|
||||
browser.notifyWhenNoOutstandingRequests(noPendingTasksSpy);
|
||||
expect(noPendingFooTasksSpy).not.toHaveBeenCalled();
|
||||
expect(noPendingTasksSpy).not.toHaveBeenCalled();
|
||||
|
||||
fakeWindow.setTimeout.flush(1);
|
||||
expect(noPendingFooTasksSpy).toHaveBeenCalledOnce();
|
||||
expect(noPendingTasksSpy).not.toHaveBeenCalled();
|
||||
|
||||
fakeWindow.setTimeout.flush(1);
|
||||
expect(noPendingFooTasksSpy).toHaveBeenCalledOnce();
|
||||
expect(noPendingTasksSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
@@ -270,6 +347,40 @@ describe('browser', function() {
|
||||
expect(log).toEqual(['ok']);
|
||||
expect(browser.defer.cancel(deferId2)).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
it('should update outstandingRequests counter', function() {
|
||||
var noPendingTasksSpy = jasmine.createSpy('noPendingTasks');
|
||||
var deferId = browser.defer(noop);
|
||||
|
||||
browser.notifyWhenNoOutstandingRequests(noPendingTasksSpy);
|
||||
expect(noPendingTasksSpy).not.toHaveBeenCalled();
|
||||
|
||||
browser.defer.cancel(deferId);
|
||||
expect(noPendingTasksSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
it('should update outstandingRequests counter (for specific task-type)', function() {
|
||||
var noPendingFooTasksSpy = jasmine.createSpy('noPendingFooTasks');
|
||||
var noPendingTasksSpy = jasmine.createSpy('noPendingTasks');
|
||||
|
||||
var deferId1 = browser.defer(noop, 0, 'foo');
|
||||
var deferId2 = browser.defer(noop, 0, 'bar');
|
||||
|
||||
browser.notifyWhenNoOutstandingRequests(noPendingFooTasksSpy, 'foo');
|
||||
browser.notifyWhenNoOutstandingRequests(noPendingTasksSpy);
|
||||
expect(noPendingFooTasksSpy).not.toHaveBeenCalled();
|
||||
expect(noPendingTasksSpy).not.toHaveBeenCalled();
|
||||
|
||||
browser.defer.cancel(deferId1);
|
||||
expect(noPendingFooTasksSpy).toHaveBeenCalledOnce();
|
||||
expect(noPendingTasksSpy).not.toHaveBeenCalled();
|
||||
|
||||
browser.defer.cancel(deferId2);
|
||||
expect(noPendingFooTasksSpy).toHaveBeenCalledOnce();
|
||||
expect(noPendingTasksSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+20
-20
@@ -2002,7 +2002,7 @@ describe('$http', function() {
|
||||
it('should immediately call `$browser.$$incOutstandingRequestCount()`', function() {
|
||||
expect(incOutstandingRequestCountSpy).not.toHaveBeenCalled();
|
||||
$http.get('');
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
});
|
||||
|
||||
|
||||
@@ -2012,7 +2012,7 @@ describe('$http', function() {
|
||||
$http.get('');
|
||||
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
|
||||
$httpBackend.flush();
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
|
||||
});
|
||||
|
||||
|
||||
@@ -2022,7 +2022,7 @@ describe('$http', function() {
|
||||
$http.get('').catch(noop);
|
||||
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
|
||||
$httpBackend.flush();
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
|
||||
});
|
||||
|
||||
|
||||
@@ -2033,13 +2033,13 @@ describe('$http', function() {
|
||||
|
||||
$http.get('', {transformRequest: function() { throw new Error(); }}).catch(noop);
|
||||
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
|
||||
}
|
||||
);
|
||||
|
||||
@@ -2052,13 +2052,13 @@ describe('$http', function() {
|
||||
$httpBackend.when('GET').respond(200);
|
||||
$http.get('', {transformResponse: function() { throw new Error(); }}).catch(noop);
|
||||
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -2112,7 +2112,7 @@ describe('$http', function() {
|
||||
|
||||
expect(reqInterceptorFulfilled).toBe(false);
|
||||
expect(resInterceptorFulfilled).toBe(false);
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
|
||||
|
||||
reqInterceptorDeferred.resolve();
|
||||
@@ -2120,7 +2120,7 @@ describe('$http', function() {
|
||||
|
||||
expect(reqInterceptorFulfilled).toBe(true);
|
||||
expect(resInterceptorFulfilled).toBe(false);
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
|
||||
|
||||
resInterceptorDeferred.resolve();
|
||||
@@ -2128,8 +2128,8 @@ describe('$http', function() {
|
||||
|
||||
expect(reqInterceptorFulfilled).toBe(true);
|
||||
expect(resInterceptorFulfilled).toBe(true);
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
|
||||
}
|
||||
);
|
||||
|
||||
@@ -2144,15 +2144,15 @@ describe('$http', function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(reqInterceptorFulfilled).toBe(false);
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
|
||||
|
||||
reqInterceptorDeferred.reject();
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(reqInterceptorFulfilled).toBe(true);
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
|
||||
}
|
||||
);
|
||||
|
||||
@@ -2169,7 +2169,7 @@ describe('$http', function() {
|
||||
|
||||
expect(reqInterceptorFulfilled).toBe(false);
|
||||
expect(resInterceptorFulfilled).toBe(false);
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
|
||||
|
||||
reqInterceptorDeferred.resolve();
|
||||
@@ -2177,7 +2177,7 @@ describe('$http', function() {
|
||||
|
||||
expect(reqInterceptorFulfilled).toBe(true);
|
||||
expect(resInterceptorFulfilled).toBe(false);
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
|
||||
|
||||
resInterceptorDeferred.reject();
|
||||
@@ -2185,8 +2185,8 @@ describe('$http', function() {
|
||||
|
||||
expect(reqInterceptorFulfilled).toBe(true);
|
||||
expect(resInterceptorFulfilled).toBe(true);
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
|
||||
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnceWith('$http');
|
||||
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnceWith(noop, '$http');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2387,7 +2387,6 @@ describe('Scope', function() {
|
||||
|
||||
|
||||
it('should be cancelled if a $rootScope digest occurs before the next tick', inject(function($rootScope, $browser) {
|
||||
var apply = spyOn($rootScope, '$apply').and.callThrough();
|
||||
var cancel = spyOn($browser.defer, 'cancel').and.callThrough();
|
||||
var expression = jasmine.createSpy('expr');
|
||||
|
||||
|
||||
@@ -194,5 +194,14 @@ describe('$$testability', function() {
|
||||
$$testability.whenStable(callback);
|
||||
expect(callback).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should delegate to `$browser.notifyWhenNoOutstandingRequests()`',
|
||||
inject(function($$testability, $browser) {
|
||||
var spy = spyOn($browser, 'notifyWhenNoOutstandingRequests');
|
||||
var callback = noop;
|
||||
|
||||
$$testability.whenStable(callback);
|
||||
expect(spy).toHaveBeenCalledWith(callback);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
Vendored
+203
-44
@@ -601,7 +601,7 @@ describe('ngMock', function() {
|
||||
});
|
||||
|
||||
|
||||
describe('defer', function() {
|
||||
describe('$browser', function() {
|
||||
var browser, log;
|
||||
beforeEach(inject(function($browser) {
|
||||
browser = $browser;
|
||||
@@ -614,47 +614,199 @@ describe('ngMock', function() {
|
||||
};
|
||||
}
|
||||
|
||||
it('should flush', function() {
|
||||
browser.defer(logFn('A'));
|
||||
expect(log).toEqual('');
|
||||
browser.defer.flush();
|
||||
expect(log).toEqual('A;');
|
||||
describe('defer.flush', function() {
|
||||
it('should flush', function() {
|
||||
browser.defer(logFn('A'));
|
||||
browser.defer(logFn('B'), null, 'taskType');
|
||||
expect(log).toEqual('');
|
||||
|
||||
browser.defer.flush();
|
||||
expect(log).toEqual('A;B;');
|
||||
});
|
||||
|
||||
it('should flush delayed', function() {
|
||||
browser.defer(logFn('A'));
|
||||
browser.defer(logFn('B'), 10, 'taskType');
|
||||
browser.defer(logFn('C'), 20);
|
||||
expect(log).toEqual('');
|
||||
expect(browser.defer.now).toEqual(0);
|
||||
|
||||
browser.defer.flush(0);
|
||||
expect(log).toEqual('A;');
|
||||
|
||||
browser.defer.flush();
|
||||
expect(log).toEqual('A;B;C;');
|
||||
});
|
||||
|
||||
it('should defer and flush over time', function() {
|
||||
browser.defer(logFn('A'), 1);
|
||||
browser.defer(logFn('B'), 2, 'taskType');
|
||||
browser.defer(logFn('C'), 3);
|
||||
|
||||
browser.defer.flush(0);
|
||||
expect(browser.defer.now).toEqual(0);
|
||||
expect(log).toEqual('');
|
||||
|
||||
browser.defer.flush(1);
|
||||
expect(browser.defer.now).toEqual(1);
|
||||
expect(log).toEqual('A;');
|
||||
|
||||
browser.defer.flush(2);
|
||||
expect(browser.defer.now).toEqual(3);
|
||||
expect(log).toEqual('A;B;C;');
|
||||
});
|
||||
|
||||
it('should throw an exception if there is nothing to be flushed', function() {
|
||||
expect(function() {browser.defer.flush();}).toThrowError('No deferred tasks to be flushed');
|
||||
});
|
||||
|
||||
it('should not throw an exception when passing a specific delay', function() {
|
||||
expect(function() {browser.defer.flush(100);}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should flush delayed', function() {
|
||||
browser.defer(logFn('A'));
|
||||
browser.defer(logFn('B'), 10);
|
||||
browser.defer(logFn('C'), 20);
|
||||
expect(log).toEqual('');
|
||||
describe('defer.cancel', function() {
|
||||
it('should cancel a pending task', function() {
|
||||
var taskId1 = browser.defer(logFn('A'), 100, 'fooType');
|
||||
var taskId2 = browser.defer(logFn('B'), 200);
|
||||
|
||||
expect(browser.defer.now).toEqual(0);
|
||||
browser.defer.flush(0);
|
||||
expect(log).toEqual('A;');
|
||||
expect(log).toBe('');
|
||||
expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).toThrow();
|
||||
expect(function() {browser.defer.verifyNoPendingTasks();}).toThrow();
|
||||
|
||||
browser.defer.flush();
|
||||
expect(log).toEqual('A;B;C;');
|
||||
browser.defer.cancel(taskId1);
|
||||
expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).not.toThrow();
|
||||
expect(function() {browser.defer.verifyNoPendingTasks();}).toThrow();
|
||||
|
||||
browser.defer.cancel(taskId2);
|
||||
expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).not.toThrow();
|
||||
expect(function() {browser.defer.verifyNoPendingTasks();}).not.toThrow();
|
||||
|
||||
browser.defer.flush(1000);
|
||||
expect(log).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
it('should defer and flush over time', function() {
|
||||
browser.defer(logFn('A'), 1);
|
||||
browser.defer(logFn('B'), 2);
|
||||
browser.defer(logFn('C'), 3);
|
||||
describe('defer.verifyNoPendingTasks', function() {
|
||||
it('should throw if there are pending tasks', function() {
|
||||
expect(browser.defer.verifyNoPendingTasks).not.toThrow();
|
||||
|
||||
browser.defer.flush(0);
|
||||
expect(browser.defer.now).toEqual(0);
|
||||
expect(log).toEqual('');
|
||||
browser.defer(noop);
|
||||
expect(browser.defer.verifyNoPendingTasks).toThrow();
|
||||
});
|
||||
|
||||
browser.defer.flush(1);
|
||||
expect(browser.defer.now).toEqual(1);
|
||||
expect(log).toEqual('A;');
|
||||
it('should list the pending tasks (in order) in the error message', function() {
|
||||
browser.defer(noop, 100);
|
||||
browser.defer(noop, 300, 'fooType');
|
||||
browser.defer(noop, 200, 'barType');
|
||||
|
||||
browser.defer.flush(2);
|
||||
expect(browser.defer.now).toEqual(3);
|
||||
expect(log).toEqual('A;B;C;');
|
||||
var expectedError =
|
||||
'Deferred tasks to flush (3):\n' +
|
||||
' {id: 0, type: $$default$$, time: 100}\n' +
|
||||
' {id: 2, type: barType, time: 200}\n' +
|
||||
' {id: 1, type: fooType, time: 300}';
|
||||
expect(browser.defer.verifyNoPendingTasks).toThrowError(expectedError);
|
||||
});
|
||||
|
||||
describe('with specific task type', function() {
|
||||
it('should throw if there are pending tasks', function() {
|
||||
browser.defer(noop, 0, 'fooType');
|
||||
|
||||
expect(function() {browser.defer.verifyNoPendingTasks('barType');}).not.toThrow();
|
||||
expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).toThrow();
|
||||
expect(function() {browser.defer.verifyNoPendingTasks();}).toThrow();
|
||||
});
|
||||
|
||||
it('should list the pending tasks (in order) in the error message', function() {
|
||||
browser.defer(noop, 100);
|
||||
browser.defer(noop, 300, 'fooType');
|
||||
browser.defer(noop, 200, 'barType');
|
||||
browser.defer(noop, 400, 'fooType');
|
||||
|
||||
var expectedError =
|
||||
'Deferred tasks to flush (2):\n' +
|
||||
' {id: 1, type: fooType, time: 300}\n' +
|
||||
' {id: 3, type: fooType, time: 400}';
|
||||
expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).
|
||||
toThrowError(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an exception if there is nothing to be flushed', function() {
|
||||
expect(function() {browser.defer.flush();}).toThrowError('No deferred tasks to be flushed');
|
||||
describe('notifyWhenNoOutstandingRequests', function() {
|
||||
var callback;
|
||||
beforeEach(function() {
|
||||
callback = jasmine.createSpy('callback');
|
||||
});
|
||||
|
||||
it('should immediately run the callback if no pending tasks', function() {
|
||||
browser.notifyWhenNoOutstandingRequests(callback);
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should run the callback as soon as there are no pending tasks', function() {
|
||||
browser.defer(noop, 100);
|
||||
browser.defer(noop, 200);
|
||||
|
||||
browser.notifyWhenNoOutstandingRequests(callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
browser.defer.flush(100);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
browser.defer.flush(100);
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not run the callback more than once', function() {
|
||||
browser.defer(noop, 100);
|
||||
browser.notifyWhenNoOutstandingRequests(callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
browser.defer.flush(100);
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
||||
browser.defer(noop, 200);
|
||||
browser.defer.flush(100);
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
describe('with specific task type', function() {
|
||||
it('should immediately run the callback if no pending tasks', function() {
|
||||
browser.notifyWhenNoOutstandingRequests(callback, 'fooType');
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should run the callback as soon as there are no pending tasks', function() {
|
||||
browser.defer(noop, 100, 'fooType');
|
||||
browser.defer(noop, 200, 'barType');
|
||||
|
||||
browser.notifyWhenNoOutstandingRequests(callback, 'fooType');
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
browser.defer.flush(100);
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not run the callback more than once', function() {
|
||||
browser.defer(noop, 100, 'fooType');
|
||||
browser.defer(noop, 200);
|
||||
|
||||
browser.notifyWhenNoOutstandingRequests(callback, 'fooType');
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
browser.defer.flush(100);
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
||||
browser.defer.flush(100);
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
||||
browser.defer(noop, 100, 'fooType');
|
||||
browser.defer(noop, 200);
|
||||
browser.defer.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -705,47 +857,53 @@ describe('ngMock', function() {
|
||||
|
||||
describe('$timeout', function() {
|
||||
it('should expose flush method that will flush the pending queue of tasks', inject(
|
||||
function($timeout) {
|
||||
function($rootScope, $timeout) {
|
||||
var logger = [],
|
||||
logFn = function(msg) { return function() { logger.push(msg); }; };
|
||||
|
||||
$timeout(logFn('t1'));
|
||||
$timeout(logFn('t2'), 200);
|
||||
$rootScope.$evalAsync(logFn('rs')); // Non-timeout tasks are flushed as well.
|
||||
$timeout(logFn('t3'));
|
||||
expect(logger).toEqual([]);
|
||||
|
||||
$timeout.flush();
|
||||
expect(logger).toEqual(['t1', 't3', 't2']);
|
||||
expect(logger).toEqual(['t1', 'rs', 't3', 't2']);
|
||||
}));
|
||||
|
||||
|
||||
it('should throw an exception when not flushed', inject(function($timeout) {
|
||||
$timeout(noop);
|
||||
it('should throw an exception when not flushed', inject(function($rootScope, $timeout) {
|
||||
$timeout(noop, 100);
|
||||
$rootScope.$evalAsync(noop);
|
||||
|
||||
var expectedError = 'Deferred tasks to flush (1): {id: 0, time: 0}';
|
||||
expect(function() {$timeout.verifyNoPendingTasks();}).toThrowError(expectedError);
|
||||
var expectedError =
|
||||
'Deferred tasks to flush (2):\n' +
|
||||
' {id: 1, type: $evalAsync, time: 0}\n' +
|
||||
' {id: 0, type: $timeout, time: 100}';
|
||||
expect($timeout.verifyNoPendingTasks).toThrowError(expectedError);
|
||||
}));
|
||||
|
||||
|
||||
it('should do nothing when all tasks have been flushed', inject(function($timeout) {
|
||||
$timeout(noop);
|
||||
it('should do nothing when all tasks have been flushed', inject(function($rootScope, $timeout) {
|
||||
$timeout(noop, 100);
|
||||
$rootScope.$evalAsync(noop);
|
||||
|
||||
$timeout.flush();
|
||||
expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow();
|
||||
expect($timeout.verifyNoPendingTasks).not.toThrow();
|
||||
}));
|
||||
|
||||
|
||||
it('should check against the delay if provided within timeout', inject(function($timeout) {
|
||||
$timeout(noop, 100);
|
||||
$timeout.flush(100);
|
||||
expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow();
|
||||
expect($timeout.verifyNoPendingTasks).not.toThrow();
|
||||
|
||||
$timeout(noop, 1000);
|
||||
$timeout.flush(100);
|
||||
expect(function() {$timeout.verifyNoPendingTasks();}).toThrow();
|
||||
expect($timeout.verifyNoPendingTasks).toThrow();
|
||||
|
||||
$timeout.flush(900);
|
||||
expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow();
|
||||
expect($timeout.verifyNoPendingTasks).not.toThrow();
|
||||
}));
|
||||
|
||||
|
||||
@@ -763,6 +921,7 @@ describe('ngMock', function() {
|
||||
expect(count).toBe(2);
|
||||
}));
|
||||
|
||||
|
||||
it('should resolve timeout functions following the timeline', inject(function($timeout) {
|
||||
var count1 = 0, count2 = 0;
|
||||
var iterate1 = function() {
|
||||
@@ -1056,7 +1215,7 @@ describe('ngMock', function() {
|
||||
|
||||
|
||||
describe('$httpBackend', function() {
|
||||
var hb, callback, realBackendSpy;
|
||||
var hb, callback;
|
||||
|
||||
beforeEach(inject(function($httpBackend) {
|
||||
callback = jasmine.createSpy('callback');
|
||||
|
||||
+14
-35
@@ -2419,9 +2419,8 @@ describe('$route', function() {
|
||||
it('should wait for $resolve promises before calling callbacks', function() {
|
||||
var deferred;
|
||||
|
||||
module(function($provide, $routeProvider) {
|
||||
module(function($routeProvider) {
|
||||
$routeProvider.when('/path', {
|
||||
template: '',
|
||||
resolve: {
|
||||
a: function($q) {
|
||||
deferred = $q.defer();
|
||||
@@ -2431,7 +2430,7 @@ describe('$route', function() {
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($location, $route, $rootScope, $httpBackend, $$testability) {
|
||||
inject(function($browser, $location, $rootScope, $$testability) {
|
||||
$location.path('/path');
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -2440,7 +2439,7 @@ describe('$route', function() {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
deferred.resolve();
|
||||
$rootScope.$digest();
|
||||
$browser.defer.flush();
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -2448,9 +2447,8 @@ describe('$route', function() {
|
||||
it('should call callback after $resolve promises are rejected', function() {
|
||||
var deferred;
|
||||
|
||||
module(function($provide, $routeProvider) {
|
||||
module(function($routeProvider) {
|
||||
$routeProvider.when('/path', {
|
||||
template: '',
|
||||
resolve: {
|
||||
a: function($q) {
|
||||
deferred = $q.defer();
|
||||
@@ -2460,7 +2458,7 @@ describe('$route', function() {
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($location, $route, $rootScope, $httpBackend, $$testability) {
|
||||
inject(function($browser, $location, $rootScope, $$testability) {
|
||||
$location.path('/path');
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -2469,7 +2467,7 @@ describe('$route', function() {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
deferred.reject();
|
||||
$rootScope.$digest();
|
||||
$browser.defer.flush();
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -2477,7 +2475,7 @@ describe('$route', function() {
|
||||
it('should wait for resolveRedirectTo promises before calling callbacks', function() {
|
||||
var deferred;
|
||||
|
||||
module(function($provide, $routeProvider) {
|
||||
module(function($routeProvider) {
|
||||
$routeProvider.when('/path', {
|
||||
resolveRedirectTo: function($q) {
|
||||
deferred = $q.defer();
|
||||
@@ -2486,7 +2484,7 @@ describe('$route', function() {
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($location, $route, $rootScope, $httpBackend, $$testability) {
|
||||
inject(function($browser, $location, $rootScope, $$testability) {
|
||||
$location.path('/path');
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -2495,7 +2493,7 @@ describe('$route', function() {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
deferred.resolve();
|
||||
$rootScope.$digest();
|
||||
$browser.defer.flush();
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -2503,7 +2501,7 @@ describe('$route', function() {
|
||||
it('should call callback after resolveRedirectTo promises are rejected', function() {
|
||||
var deferred;
|
||||
|
||||
module(function($provide, $routeProvider) {
|
||||
module(function($routeProvider) {
|
||||
$routeProvider.when('/path', {
|
||||
resolveRedirectTo: function($q) {
|
||||
deferred = $q.defer();
|
||||
@@ -2512,7 +2510,7 @@ describe('$route', function() {
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($location, $route, $rootScope, $httpBackend, $$testability) {
|
||||
inject(function($browser, $location, $rootScope, $$testability) {
|
||||
$location.path('/path');
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -2521,7 +2519,7 @@ describe('$route', function() {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
deferred.reject();
|
||||
$rootScope.$digest();
|
||||
$browser.defer.flush();
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -2529,30 +2527,11 @@ describe('$route', function() {
|
||||
it('should wait for all route promises before calling callbacks', function() {
|
||||
var deferreds = {};
|
||||
|
||||
module(function($provide, $routeProvider) {
|
||||
// While normally `$browser.defer()` modifies the `outstandingRequestCount`, the mocked
|
||||
// version (provided by `ngMock`) does not. This doesn't matter in most tests, but in this
|
||||
// case we need the `outstandingRequestCount` logic to ensure that we don't call the
|
||||
// `$$testability.whenStable()` callbacks part way through a `$rootScope.$evalAsync` block.
|
||||
// See ngRoute's commitRoute()'s finally() block for details.
|
||||
$provide.decorator('$browser', function($delegate) {
|
||||
var oldDefer = $delegate.defer;
|
||||
var newDefer = function(fn, delay) {
|
||||
var requestCountAwareFn = function() { $delegate.$$completeOutstandingRequest(fn); };
|
||||
$delegate.$$incOutstandingRequestCount();
|
||||
return oldDefer.call($delegate, requestCountAwareFn, delay);
|
||||
};
|
||||
|
||||
$delegate.defer = angular.extend(newDefer, oldDefer);
|
||||
|
||||
return $delegate;
|
||||
});
|
||||
|
||||
module(function($routeProvider) {
|
||||
addRouteWithAsyncRedirect('/foo', '/bar');
|
||||
addRouteWithAsyncRedirect('/bar', '/baz');
|
||||
addRouteWithAsyncRedirect('/baz', '/qux');
|
||||
$routeProvider.when('/qux', {
|
||||
template: '',
|
||||
resolve: {
|
||||
a: function($q) {
|
||||
var deferred = deferreds['/qux'] = $q.defer();
|
||||
@@ -2572,7 +2551,7 @@ describe('$route', function() {
|
||||
}
|
||||
});
|
||||
|
||||
inject(function($browser, $location, $rootScope, $route, $$testability) {
|
||||
inject(function($browser, $location, $rootScope, $$testability) {
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user