mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
feat($interval): add a service wrapping setInterval
The $interval service simplifies creating and testing recurring tasks. This service does not increment $browser's outstanding request count, which means that scenario tests and Protractor tests will not timeout when a site uses a polling function registered by $interval. Provides a workaround for #2402. For unit tests, repeated tasks can be controlled using ngMock$interval's tick(), tickNext(), and tickAll() functions.
This commit is contained in:
parent
a80e96cea1
commit
2b5ce84fca
7 changed files with 725 additions and 0 deletions
1
angularFiles.js
vendored
1
angularFiles.js
vendored
|
|
@ -20,6 +20,7 @@ angularFiles = {
|
|||
'src/ng/http.js',
|
||||
'src/ng/httpBackend.js',
|
||||
'src/ng/interpolate.js',
|
||||
'src/ng/interval.js',
|
||||
'src/ng/locale.js',
|
||||
'src/ng/location.js',
|
||||
'src/ng/log.js',
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ function publishExternalAPI(angular){
|
|||
$exceptionHandler: $ExceptionHandlerProvider,
|
||||
$filter: $FilterProvider,
|
||||
$interpolate: $InterpolateProvider,
|
||||
$interval: $IntervalProvider,
|
||||
$http: $HttpProvider,
|
||||
$httpBackend: $HttpBackendProvider,
|
||||
$location: $LocationProvider,
|
||||
|
|
|
|||
90
src/ng/interval.js
Normal file
90
src/ng/interval.js
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
function $IntervalProvider() {
|
||||
this.$get = ['$rootScope', '$window', '$q',
|
||||
function($rootScope, $window, $q) {
|
||||
var intervals = {};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$interval
|
||||
*
|
||||
* @description
|
||||
* Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
|
||||
* milliseconds.
|
||||
*
|
||||
* The return value of registering an interval function is a promise. This promise will be
|
||||
* notified upon each tick of the interval, and will be resolved after `count` iterations, or
|
||||
* run indefinitely if `count` is not defined. The value of the notification will be the
|
||||
* number of iterations that have run.
|
||||
* To cancel an interval, call `$interval.cancel(promise)`.
|
||||
*
|
||||
* In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
|
||||
* move forward by `millis` milliseconds and trigger any functions scheduled to run in that
|
||||
* time.
|
||||
*
|
||||
* @param {function()} fn A function that should be called repeatedly.
|
||||
* @param {number} delay Number of milliseconds between each function call.
|
||||
* @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
|
||||
* indefinitely.
|
||||
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
|
||||
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
|
||||
* @returns {promise} A promise which will be notified on each iteration.
|
||||
*/
|
||||
function interval(fn, delay, count, invokeApply) {
|
||||
var setInterval = $window.setInterval,
|
||||
clearInterval = $window.clearInterval;
|
||||
|
||||
var deferred = $q.defer(),
|
||||
promise = deferred.promise,
|
||||
count = (isDefined(count)) ? count : 0,
|
||||
iteration = 0,
|
||||
skipApply = (isDefined(invokeApply) && !invokeApply);
|
||||
|
||||
promise.then(null, null, fn);
|
||||
|
||||
promise.$$intervalId = setInterval(function tick() {
|
||||
deferred.notify(iteration++);
|
||||
|
||||
if (count > 0 && iteration >= count) {
|
||||
deferred.resolve(iteration);
|
||||
clearInterval(promise.$$intervalId);
|
||||
delete intervals[promise.$$intervalId];
|
||||
}
|
||||
|
||||
if (!skipApply) $rootScope.$apply();
|
||||
|
||||
}, delay);
|
||||
|
||||
intervals[promise.$$intervalId] = deferred;
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$interval#cancel
|
||||
* @methodOf ng.$interval
|
||||
*
|
||||
* @description
|
||||
* Cancels a task associated with the `promise`.
|
||||
*
|
||||
* @param {number} promise Promise returned by the `$interval` function.
|
||||
* @returns {boolean} Returns `true` if the task was successfully canceled.
|
||||
*/
|
||||
interval.cancel = function(promise) {
|
||||
if (promise && promise.$$intervalId in intervals) {
|
||||
intervals[promise.$$intervalId].reject('canceled');
|
||||
clearInterval(promise.$$intervalId);
|
||||
delete intervals[promise.$$intervalId];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return interval;
|
||||
}];
|
||||
}
|
||||
114
src/ngMock/angular-mocks.js
vendored
114
src/ngMock/angular-mocks.js
vendored
|
|
@ -438,6 +438,119 @@ angular.mock.$LogProvider = function() {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name ngMock.$interval
|
||||
*
|
||||
* @description
|
||||
* Mock implementation of the $interval service.
|
||||
*
|
||||
* Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
|
||||
* move forward by `millis` milliseconds and trigger any functions scheduled to run in that
|
||||
* time.
|
||||
*
|
||||
* @param {function()} fn A function that should be called repeatedly.
|
||||
* @param {number} delay Number of milliseconds between each function call.
|
||||
* @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
|
||||
* indefinitely.
|
||||
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
|
||||
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
|
||||
* @returns {promise} A promise which will be notified on each iteration.
|
||||
*/
|
||||
angular.mock.$IntervalProvider = function() {
|
||||
this.$get = ['$rootScope', '$q',
|
||||
function($rootScope, $q) {
|
||||
var repeatFns = [],
|
||||
nextRepeatId = 0,
|
||||
now = 0;
|
||||
|
||||
var $interval = function(fn, delay, count, invokeApply) {
|
||||
var deferred = $q.defer(),
|
||||
promise = deferred.promise,
|
||||
count = (isDefined(count)) ? count : 0,
|
||||
iteration = 0,
|
||||
skipApply = (isDefined(invokeApply) && !invokeApply);
|
||||
|
||||
promise.then(null, null, fn);
|
||||
|
||||
promise.$$intervalId = nextRepeatId;
|
||||
|
||||
function tick() {
|
||||
deferred.notify(iteration++);
|
||||
|
||||
if (count > 0 && iteration >= count) {
|
||||
var fnIndex;
|
||||
deferred.resolve(iteration);
|
||||
|
||||
angular.forEach(repeatFns, function(fn, index) {
|
||||
if (fn.id === promise.$$intervalId) fnIndex = index;
|
||||
});
|
||||
|
||||
if (fnIndex !== undefined) {
|
||||
repeatFns.splice(fnIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipApply) $rootScope.$apply();
|
||||
};
|
||||
|
||||
repeatFns.push({
|
||||
nextTime:(now + delay),
|
||||
delay: delay,
|
||||
fn: tick,
|
||||
id: nextRepeatId,
|
||||
deferred: deferred
|
||||
});
|
||||
repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
|
||||
|
||||
nextRepeatId++;
|
||||
return promise;
|
||||
};
|
||||
|
||||
$interval.cancel = function(promise) {
|
||||
var fnIndex;
|
||||
|
||||
angular.forEach(repeatFns, function(fn, index) {
|
||||
if (fn.id === promise.$$intervalId) fnIndex = index;
|
||||
});
|
||||
|
||||
if (fnIndex !== undefined) {
|
||||
repeatFns[fnIndex].deferred.reject('canceled');
|
||||
repeatFns.splice(fnIndex, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngMock.$interval#flush
|
||||
* @methodOf ngMock.$interval
|
||||
* @description
|
||||
*
|
||||
* Runs interval tasks scheduled to be run in the next `millis` milliseconds.
|
||||
*
|
||||
* @param {number=} millis maximum timeout amount to flush up until.
|
||||
*
|
||||
* @return {number} The amount of time moved forward.
|
||||
*/
|
||||
$interval.flush = function(millis) {
|
||||
now += millis;
|
||||
while (repeatFns.length && repeatFns[0].nextTime <= now) {
|
||||
var task = repeatFns[0];
|
||||
task.fn();
|
||||
task.nextTime += task.delay;
|
||||
repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
|
||||
}
|
||||
return millis;
|
||||
};
|
||||
|
||||
return $interval;
|
||||
}];
|
||||
};
|
||||
|
||||
|
||||
(function() {
|
||||
var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
|
||||
|
||||
|
|
@ -1581,6 +1694,7 @@ angular.module('ngMock', ['ng']).provider({
|
|||
$browser: angular.mock.$BrowserProvider,
|
||||
$exceptionHandler: angular.mock.$ExceptionHandlerProvider,
|
||||
$log: angular.mock.$LogProvider,
|
||||
$interval: angular.mock.$IntervalProvider,
|
||||
$httpBackend: angular.mock.$HttpBackendProvider,
|
||||
$rootElement: angular.mock.$RootElementProvider
|
||||
}).config(function($provide) {
|
||||
|
|
|
|||
270
test/ng/intervalSpec.js
Normal file
270
test/ng/intervalSpec.js
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
'use strict';
|
||||
|
||||
describe('$interval', function() {
|
||||
|
||||
beforeEach(module(function($provide){
|
||||
var repeatFns = [],
|
||||
nextRepeatId = 0,
|
||||
now = 0,
|
||||
$window;
|
||||
|
||||
$window = {
|
||||
setInterval: function(fn, delay, count) {
|
||||
repeatFns.push({
|
||||
nextTime:(now + delay),
|
||||
delay: delay,
|
||||
fn: fn,
|
||||
id: nextRepeatId,
|
||||
});
|
||||
repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
|
||||
|
||||
return nextRepeatId++;
|
||||
},
|
||||
|
||||
clearInterval: function(id) {
|
||||
var fnIndex;
|
||||
|
||||
angular.forEach(repeatFns, function(fn, index) {
|
||||
if (fn.id === id) fnIndex = index;
|
||||
});
|
||||
|
||||
if (fnIndex !== undefined) {
|
||||
repeatFns.splice(fnIndex, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
flush: function(millis) {
|
||||
now += millis;
|
||||
while (repeatFns.length && repeatFns[0].nextTime <= now) {
|
||||
var task = repeatFns[0];
|
||||
task.fn();
|
||||
task.nextTime += task.delay;
|
||||
repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
|
||||
}
|
||||
return millis;
|
||||
}
|
||||
};
|
||||
|
||||
$provide.provider('$interval', $IntervalProvider);
|
||||
$provide.value('$window', $window);
|
||||
}));
|
||||
|
||||
it('should run tasks repeatedly', inject(function($interval, $window) {
|
||||
var counter = 0;
|
||||
$interval(function() { counter++; }, 1000);
|
||||
|
||||
expect(counter).toBe(0);
|
||||
|
||||
$window.flush(1000)
|
||||
expect(counter).toBe(1);
|
||||
|
||||
$window.flush(1000);
|
||||
|
||||
expect(counter).toBe(2);
|
||||
}));
|
||||
|
||||
it('should call $apply after each task is executed',
|
||||
inject(function($interval, $rootScope, $window) {
|
||||
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
|
||||
|
||||
$interval(noop, 1000);
|
||||
expect(applySpy).not.toHaveBeenCalled();
|
||||
|
||||
$window.flush(1000);
|
||||
expect(applySpy).toHaveBeenCalledOnce();
|
||||
|
||||
applySpy.reset();
|
||||
|
||||
$interval(noop, 1000);
|
||||
$interval(noop, 1000);
|
||||
$window.flush(1000);
|
||||
expect(applySpy.callCount).toBe(3);
|
||||
}));
|
||||
|
||||
|
||||
it('should NOT call $apply if invokeApply is set to false',
|
||||
inject(function($interval, $rootScope, $window) {
|
||||
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
|
||||
|
||||
$interval(noop, 1000, 0, false);
|
||||
expect(applySpy).not.toHaveBeenCalled();
|
||||
|
||||
$window.flush(2000);
|
||||
expect(applySpy).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it('should allow you to specify the delay time', inject(function($interval, $window) {
|
||||
var counter = 0;
|
||||
$interval(function() { counter++; }, 123);
|
||||
|
||||
expect(counter).toBe(0);
|
||||
|
||||
$window.flush(122);
|
||||
expect(counter).toBe(0);
|
||||
|
||||
$window.flush(1);
|
||||
expect(counter).toBe(1);
|
||||
}));
|
||||
|
||||
|
||||
it('should allow you to specify a number of iterations', inject(function($interval, $window) {
|
||||
var counter = 0;
|
||||
$interval(function() {counter++}, 1000, 2);
|
||||
|
||||
$window.flush(1000);
|
||||
expect(counter).toBe(1);
|
||||
$window.flush(1000);
|
||||
expect(counter).toBe(2);
|
||||
$window.flush(1000);
|
||||
expect(counter).toBe(2);
|
||||
}));
|
||||
|
||||
|
||||
it('should return a promise which will be updated with the count on each iteration',
|
||||
inject(function($interval, $window) {
|
||||
var log = [],
|
||||
promise = $interval(function() { log.push('tick'); }, 1000);
|
||||
|
||||
promise.then(function(value) { log.push('promise success: ' + value); },
|
||||
function(err) { log.push('promise error: ' + err); },
|
||||
function(note) { log.push('promise update: ' + note); });
|
||||
expect(log).toEqual([]);
|
||||
|
||||
$window.flush(1000);
|
||||
expect(log).toEqual(['tick', 'promise update: 0']);
|
||||
|
||||
$window.flush(1000);
|
||||
expect(log).toEqual(['tick', 'promise update: 0', 'tick', 'promise update: 1']);
|
||||
}));
|
||||
|
||||
|
||||
it('should return a promise which will be resolved after the specified number of iterations',
|
||||
inject(function($interval, $window) {
|
||||
var log = [],
|
||||
promise = $interval(function() { log.push('tick'); }, 1000, 2);
|
||||
|
||||
promise.then(function(value) { log.push('promise success: ' + value); },
|
||||
function(err) { log.push('promise error: ' + err); },
|
||||
function(note) { log.push('promise update: ' + note); });
|
||||
expect(log).toEqual([]);
|
||||
|
||||
$window.flush(1000);
|
||||
expect(log).toEqual(['tick', 'promise update: 0']);
|
||||
$window.flush(1000);
|
||||
|
||||
expect(log).toEqual([
|
||||
'tick', 'promise update: 0', 'tick', 'promise update: 1', 'promise success: 2']);
|
||||
|
||||
}));
|
||||
|
||||
|
||||
describe('exception handling', function() {
|
||||
beforeEach(module(function($exceptionHandlerProvider) {
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
}));
|
||||
|
||||
|
||||
it('should delegate exception to the $exceptionHandler service', inject(
|
||||
function($interval, $exceptionHandler, $window) {
|
||||
$interval(function() { throw "Test Error"; }, 1000);
|
||||
expect($exceptionHandler.errors).toEqual([]);
|
||||
|
||||
$window.flush(1000);
|
||||
expect($exceptionHandler.errors).toEqual(["Test Error"]);
|
||||
|
||||
$window.flush(1000);
|
||||
expect($exceptionHandler.errors).toEqual(["Test Error", "Test Error"]);
|
||||
}));
|
||||
|
||||
|
||||
it('should call $apply even if an exception is thrown in callback', inject(
|
||||
function($interval, $rootScope, $window) {
|
||||
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
|
||||
|
||||
$interval(function() { throw "Test Error"; }, 1000);
|
||||
expect(applySpy).not.toHaveBeenCalled();
|
||||
|
||||
$window.flush(1000);
|
||||
expect(applySpy).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it('should still update the interval promise when an exception is thrown',
|
||||
inject(function($interval, $window) {
|
||||
var log = [],
|
||||
promise = $interval(function() { throw "Some Error"; }, 1000);
|
||||
|
||||
promise.then(function(value) { log.push('promise success: ' + value); },
|
||||
function(err) { log.push('promise error: ' + err); },
|
||||
function(note) { log.push('promise update: ' + note); });
|
||||
$window.flush(1000);
|
||||
|
||||
expect(log).toEqual(['promise update: 0']);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('cancel', function() {
|
||||
it('should cancel tasks', inject(function($interval, $window) {
|
||||
var task1 = jasmine.createSpy('task1', 1000),
|
||||
task2 = jasmine.createSpy('task2', 1000),
|
||||
task3 = jasmine.createSpy('task3', 1000),
|
||||
promise1, promise3;
|
||||
|
||||
promise1 = $interval(task1, 200);
|
||||
$interval(task2, 1000);
|
||||
promise3 = $interval(task3, 333);
|
||||
|
||||
$interval.cancel(promise3);
|
||||
$interval.cancel(promise1);
|
||||
$window.flush(1000);
|
||||
|
||||
expect(task1).not.toHaveBeenCalled();
|
||||
expect(task2).toHaveBeenCalledOnce();
|
||||
expect(task3).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it('should cancel the promise', inject(function($interval, $rootScope, $window) {
|
||||
var promise = $interval(noop, 1000),
|
||||
log = [];
|
||||
promise.then(function(value) { log.push('promise success: ' + value); },
|
||||
function(err) { log.push('promise error: ' + err); },
|
||||
function(note) { log.push('promise update: ' + note); });
|
||||
expect(log).toEqual([]);
|
||||
|
||||
$window.flush(1000);
|
||||
$interval.cancel(promise);
|
||||
$window.flush(1000);
|
||||
$rootScope.$apply(); // For resolving the promise -
|
||||
// necessary since q uses $rootScope.evalAsync.
|
||||
|
||||
expect(log).toEqual(['promise update: 0', 'promise error: canceled']);
|
||||
}));
|
||||
|
||||
|
||||
it('should return true if a task was successfully canceled',
|
||||
inject(function($interval, $window) {
|
||||
var task1 = jasmine.createSpy('task1'),
|
||||
task2 = jasmine.createSpy('task2'),
|
||||
promise1, promise2;
|
||||
|
||||
promise1 = $interval(task1, 1000, 1);
|
||||
$window.flush(1000);
|
||||
promise2 = $interval(task2, 1000, 1);
|
||||
|
||||
expect($interval.cancel(promise1)).toBe(false);
|
||||
expect($interval.cancel(promise2)).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it('should not throw a runtime exception when given an undefined promise',
|
||||
inject(function($interval) {
|
||||
expect($interval.cancel()).toBe(false);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
@ -165,6 +165,20 @@ describe('$timeout', function() {
|
|||
}));
|
||||
|
||||
|
||||
it('should cancel the promise', inject(function($timeout, log) {
|
||||
var promise = $timeout(noop);
|
||||
promise.then(function(value) { log('promise success: ' + value); },
|
||||
function(err) { log('promise error: ' + err); },
|
||||
function(note) { log('promise update: ' + note); });
|
||||
expect(log).toEqual([]);
|
||||
|
||||
$timeout.cancel(promise);
|
||||
$timeout.flush();
|
||||
|
||||
expect(log).toEqual(['promise error: canceled']);
|
||||
}));
|
||||
|
||||
|
||||
it('should return true if a task was successfully canceled', inject(function($timeout) {
|
||||
var task1 = jasmine.createSpy('task1'),
|
||||
task2 = jasmine.createSpy('task2'),
|
||||
|
|
|
|||
235
test/ngMock/angular-mocksSpec.js
vendored
235
test/ngMock/angular-mocksSpec.js
vendored
|
|
@ -283,6 +283,241 @@ describe('ngMock', function() {
|
|||
});
|
||||
|
||||
|
||||
describe('$interval', function() {
|
||||
it('should run tasks repeatedly', inject(function($interval) {
|
||||
var counter = 0;
|
||||
$interval(function() { counter++; }, 1000);
|
||||
|
||||
expect(counter).toBe(0);
|
||||
|
||||
$interval.flush(1000);
|
||||
expect(counter).toBe(1);
|
||||
|
||||
$interval.flush(1000);
|
||||
|
||||
expect(counter).toBe(2);
|
||||
}));
|
||||
|
||||
|
||||
it('should call $apply after each task is executed', inject(function($interval, $rootScope) {
|
||||
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
|
||||
|
||||
$interval(noop, 1000);
|
||||
expect(applySpy).not.toHaveBeenCalled();
|
||||
|
||||
$interval.flush(1000);
|
||||
expect(applySpy).toHaveBeenCalledOnce();
|
||||
|
||||
applySpy.reset();
|
||||
|
||||
$interval(noop, 1000);
|
||||
$interval(noop, 1000);
|
||||
$interval.flush(1000);
|
||||
expect(applySpy.callCount).toBe(3);
|
||||
}));
|
||||
|
||||
|
||||
it('should NOT call $apply if invokeApply is set to false',
|
||||
inject(function($interval, $rootScope) {
|
||||
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
|
||||
|
||||
$interval(noop, 1000, 0, false);
|
||||
expect(applySpy).not.toHaveBeenCalled();
|
||||
|
||||
$interval.flush(2000);
|
||||
expect(applySpy).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it('should allow you to specify the delay time', inject(function($interval) {
|
||||
var counter = 0;
|
||||
$interval(function() { counter++; }, 123);
|
||||
|
||||
expect(counter).toBe(0);
|
||||
|
||||
$interval.flush(122);
|
||||
expect(counter).toBe(0);
|
||||
|
||||
$interval.flush(1);
|
||||
expect(counter).toBe(1);
|
||||
}));
|
||||
|
||||
|
||||
it('should allow you to specify a number of iterations', inject(function($interval) {
|
||||
var counter = 0;
|
||||
$interval(function() {counter++}, 1000, 2);
|
||||
|
||||
$interval.flush(1000);
|
||||
expect(counter).toBe(1);
|
||||
$interval.flush(1000);
|
||||
expect(counter).toBe(2);
|
||||
$interval.flush(1000);
|
||||
expect(counter).toBe(2);
|
||||
}));
|
||||
|
||||
|
||||
describe('flush', function() {
|
||||
it('should move the clock forward by the specified time', inject(function($interval) {
|
||||
var counterA = 0;
|
||||
var counterB = 0;
|
||||
$interval(function() { counterA++; }, 100);
|
||||
$interval(function() { counterB++; }, 401);
|
||||
|
||||
$interval.flush(200);
|
||||
expect(counterA).toEqual(2);
|
||||
|
||||
$interval.flush(201);
|
||||
expect(counterA).toEqual(4);
|
||||
expect(counterB).toEqual(1);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise which will be updated with the count on each iteration',
|
||||
inject(function($interval) {
|
||||
var log = [],
|
||||
promise = $interval(function() { log.push('tick'); }, 1000);
|
||||
|
||||
promise.then(function(value) { log.push('promise success: ' + value); },
|
||||
function(err) { log.push('promise error: ' + err); },
|
||||
function(note) { log.push('promise update: ' + note); });
|
||||
expect(log).toEqual([]);
|
||||
|
||||
$interval.flush(1000);
|
||||
expect(log).toEqual(['tick', 'promise update: 0']);
|
||||
|
||||
$interval.flush(1000);
|
||||
expect(log).toEqual(['tick', 'promise update: 0', 'tick', 'promise update: 1']);
|
||||
}));
|
||||
|
||||
|
||||
it('should return a promise which will be resolved after the specified number of iterations',
|
||||
inject(function($interval) {
|
||||
var log = [],
|
||||
promise = $interval(function() { log.push('tick'); }, 1000, 2);
|
||||
|
||||
promise.then(function(value) { log.push('promise success: ' + value); },
|
||||
function(err) { log.push('promise error: ' + err); },
|
||||
function(note) { log.push('promise update: ' + note); });
|
||||
expect(log).toEqual([]);
|
||||
|
||||
$interval.flush(1000);
|
||||
expect(log).toEqual(['tick', 'promise update: 0']);
|
||||
$interval.flush(1000);
|
||||
|
||||
expect(log).toEqual([
|
||||
'tick', 'promise update: 0', 'tick', 'promise update: 1', 'promise success: 2']);
|
||||
|
||||
}));
|
||||
|
||||
|
||||
describe('exception handling', function() {
|
||||
beforeEach(module(function($exceptionHandlerProvider) {
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
}));
|
||||
|
||||
|
||||
it('should delegate exception to the $exceptionHandler service', inject(
|
||||
function($interval, $exceptionHandler) {
|
||||
$interval(function() { throw "Test Error"; }, 1000);
|
||||
expect($exceptionHandler.errors).toEqual([]);
|
||||
|
||||
$interval.flush(1000);
|
||||
expect($exceptionHandler.errors).toEqual(["Test Error"]);
|
||||
|
||||
$interval.flush(1000);
|
||||
expect($exceptionHandler.errors).toEqual(["Test Error", "Test Error"]);
|
||||
}));
|
||||
|
||||
|
||||
it('should call $apply even if an exception is thrown in callback', inject(
|
||||
function($interval, $rootScope) {
|
||||
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
|
||||
|
||||
$interval(function() { throw "Test Error"; }, 1000);
|
||||
expect(applySpy).not.toHaveBeenCalled();
|
||||
|
||||
$interval.flush(1000);
|
||||
expect(applySpy).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it('should still update the interval promise when an exception is thrown',
|
||||
inject(function($interval) {
|
||||
var log = [],
|
||||
promise = $interval(function() { throw "Some Error"; }, 1000);
|
||||
|
||||
promise.then(function(value) { log.push('promise success: ' + value); },
|
||||
function(err) { log.push('promise error: ' + err); },
|
||||
function(note) { log.push('promise update: ' + note); });
|
||||
$interval.flush(1000);
|
||||
|
||||
expect(log).toEqual(['promise update: 0']);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('cancel', function() {
|
||||
it('should cancel tasks', inject(function($interval) {
|
||||
var task1 = jasmine.createSpy('task1', 1000),
|
||||
task2 = jasmine.createSpy('task2', 1000),
|
||||
task3 = jasmine.createSpy('task3', 1000),
|
||||
promise1, promise3;
|
||||
|
||||
promise1 = $interval(task1, 200);
|
||||
$interval(task2, 1000);
|
||||
promise3 = $interval(task3, 333);
|
||||
|
||||
$interval.cancel(promise3);
|
||||
$interval.cancel(promise1);
|
||||
$interval.flush(1000);
|
||||
|
||||
expect(task1).not.toHaveBeenCalled();
|
||||
expect(task2).toHaveBeenCalledOnce();
|
||||
expect(task3).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it('should cancel the promise', inject(function($interval, $rootScope) {
|
||||
var promise = $interval(noop, 1000),
|
||||
log = [];
|
||||
promise.then(function(value) { log.push('promise success: ' + value); },
|
||||
function(err) { log.push('promise error: ' + err); },
|
||||
function(note) { log.push('promise update: ' + note); });
|
||||
expect(log).toEqual([]);
|
||||
|
||||
$interval.flush(1000);
|
||||
$interval.cancel(promise);
|
||||
$interval.flush(1000);
|
||||
$rootScope.$apply(); // For resolving the promise -
|
||||
// necessary since q uses $rootScope.evalAsync.
|
||||
|
||||
expect(log).toEqual(['promise update: 0', 'promise error: canceled']);
|
||||
}));
|
||||
|
||||
|
||||
it('should return true if a task was successfully canceled', inject(function($interval) {
|
||||
var task1 = jasmine.createSpy('task1'),
|
||||
task2 = jasmine.createSpy('task2'),
|
||||
promise1, promise2;
|
||||
|
||||
promise1 = $interval(task1, 1000, 1);
|
||||
$interval.flush(1000);
|
||||
promise2 = $interval(task2, 1000, 1);
|
||||
|
||||
expect($interval.cancel(promise1)).toBe(false);
|
||||
expect($interval.cancel(promise2)).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it('should not throw a runtime exception when given an undefined promise',
|
||||
inject(function($interval) {
|
||||
expect($interval.cancel()).toBe(false);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('defer', function() {
|
||||
var browser, log;
|
||||
beforeEach(inject(function($browser) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue