diff options
Diffstat (limited to 'test/ng/intervalSpec.js')
-rw-r--r-- | test/ng/intervalSpec.js | 270 |
1 files changed, 270 insertions, 0 deletions
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); + })); + }); +}); |