aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xangularFiles.js1
-rwxr-xr-xsrc/AngularPublic.js1
-rw-r--r--src/ng/interval.js90
-rw-r--r--src/ngMock/angular-mocks.js114
-rw-r--r--test/ng/intervalSpec.js270
-rw-r--r--test/ng/timeoutSpec.js14
-rw-r--r--test/ngMock/angular-mocksSpec.js235
7 files changed, 725 insertions, 0 deletions
diff --git a/angularFiles.js b/angularFiles.js
index cd23aca0..946cff45 100755
--- a/angularFiles.js
+++ b/angularFiles.js
@@ -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',
diff --git a/src/AngularPublic.js b/src/AngularPublic.js
index b225fc85..9bd7fd7d 100755
--- a/src/AngularPublic.js
+++ b/src/AngularPublic.js
@@ -114,6 +114,7 @@ function publishExternalAPI(angular){
$exceptionHandler: $ExceptionHandlerProvider,
$filter: $FilterProvider,
$interpolate: $InterpolateProvider,
+ $interval: $IntervalProvider,
$http: $HttpProvider,
$httpBackend: $HttpBackendProvider,
$location: $LocationProvider,
diff --git a/src/ng/interval.js b/src/ng/interval.js
new file mode 100644
index 00000000..e612f3e4
--- /dev/null
+++ b/src/ng/interval.js
@@ -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;
+ }];
+}
diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js
index 9bc80ff3..11f6f045 100644
--- a/src/ngMock/angular-mocks.js
+++ b/src/ngMock/angular-mocks.js
@@ -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) {
diff --git a/test/ng/intervalSpec.js b/test/ng/intervalSpec.js
new file mode 100644
index 00000000..6999f750
--- /dev/null
+++ b/test/ng/intervalSpec.js
@@ -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);
+ }));
+ });
+});
diff --git a/test/ng/timeoutSpec.js b/test/ng/timeoutSpec.js
index 8de63bec..97c8448e 100644
--- a/test/ng/timeoutSpec.js
+++ b/test/ng/timeoutSpec.js
@@ -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'),
diff --git a/test/ngMock/angular-mocksSpec.js b/test/ngMock/angular-mocksSpec.js
index 1a4290e0..851f7803 100644
--- a/test/ngMock/angular-mocksSpec.js
+++ b/test/ngMock/angular-mocksSpec.js
@@ -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) {