'use strict'; describe('injector', function() { var providers; var injector; beforeEach(module(function($provide) { providers = function(name, factory, annotations) { $provide.factory(name, extend(factory, annotations||{})); }; })); beforeEach(inject(function($injector){ injector = $injector; })); it("should return same instance from calling provider", function() { var instance = {}, original = instance; providers('instance', function() { return instance; }); expect(injector.get('instance')).toEqual(instance); instance = 'deleted'; expect(injector.get('instance')).toEqual(original); }); it('should inject providers', function() { providers('a', function() {return 'Mi';}); providers('b', function(mi) {return mi+'sko';}, {$inject:['a']}); expect(injector.get('b')).toEqual('Misko'); }); it('should resolve dependency graph and instantiate all services just once', function() { var log = []; // s1 // / | \ // / s2 \ // / / | \ \ // /s3 < s4 > s5 // // // s6 providers('s1', function() { log.push('s1'); }, {$inject: ['s2', 's5', 's6']}); providers('s2', function() { log.push('s2'); }, {$inject: ['s3', 's4', 's5']}); providers('s3', function() { log.push('s3'); }, {$inject: ['s6']}); providers('s4', function() { log.push('s4'); }, {$inject: ['s3', 's5']}); providers('s5', function() { log.push('s5'); }); providers('s6', function() { log.push('s6'); }); injector.get('s1'); expect(log).toEqual(['s6', 's3', 's5', 's4', 's2', 's1']); }); it('should provide useful message if no provider', function() { expect(function() { injector.get('idontexist'); }).toThrow("Unknown provider: idontexistProvider <- idontexist"); }); it('should proved path to the missing provider', function() { providers('a', function(idontexist) {return 1;}); providers('b', function(a) {return 2;}); expect(function() { injector.get('b'); }).toThrow("Unknown provider: idontexistProvider <- idontexist <- a <- b"); }); describe('invoke', function() { var args; beforeEach(function() { args = null; providers('a', function() {return 1;}); providers('b', function() {return 2;}); }); function fn(a, b, c, d) { args = [this, a, b, c, d]; return a + b + c + d; } it('should call function', function() { fn.$inject = ['a', 'b', 'c', 'd']; injector.invoke(fn, {name:"this"}, {c:3, d:4}); expect(args).toEqual([{name:'this'}, 1, 2, 3, 4]); }); it('should treat array as annotations', function() { injector.invoke(['a', 'b', 'c', 'd', fn], {name:"this"}, {c:3, d:4}); expect(args).toEqual([{name:'this'}, 1, 2, 3, 4]); }); it('should invoke the passed-in fn with all of the dependencies as arguments', function() { providers('c', function() {return 3;}); providers('d', function() {return 4;}); expect(injector.invoke(['a', 'b', 'c', 'd', fn])).toEqual(10); }); it('should fail with errors if not function or array', function() { expect(function() { injector.invoke({}); }).toThrow("Argument 'fn' is not a function, got Object"); expect(function() { injector.invoke(['a', 123], {}); }).toThrow("Argument 'fn' is not a function, got number"); }); }); describe('annotation', function() { it('should return $inject', function() { function fn() {} fn.$inject = ['a']; expect(inferInjectionArgs(fn)).toBe(fn.$inject); expect(inferInjectionArgs(function() {})).toEqual([]); expect(inferInjectionArgs(function () {})).toEqual([]); expect(inferInjectionArgs(function () {})).toEqual([]); expect(inferInjectionArgs(function /* */ () {})).toEqual([]); }); it('should create $inject', function() { // keep the multi-line to make sure we can handle it function $f_n0 /* */( $a, // x, <-- looks like an arg but it is a comment b_, /* z, <-- looks like an arg but it is a multi-line comment function (a, b) {} */ _c, /* {some type} */ d) { extraParans();} expect(inferInjectionArgs($f_n0)).toEqual(['$a', 'b_', '_c', 'd']); expect($f_n0.$inject).toEqual(['$a', 'b_', '_c', 'd']); }); it('should handle no arg functions', function() { function $f_n0() {} expect(inferInjectionArgs($f_n0)).toEqual([]); expect($f_n0.$inject).toEqual([]); }); it('should handle args with both $ and _', function() { function $f_n0($a_) {} expect(inferInjectionArgs($f_n0)).toEqual(['$a_']); expect($f_n0.$inject).toEqual(['$a_']); }); it('should throw on non function arg', function() { expect(function() { inferInjectionArgs({}); }).toThrow(); }); }); it('should have $injector', function() { var $injector = createInjector(); expect($injector.get('$injector')).toBe($injector); }); it('should define module', function() { var log = ''; var injector = createInjector([function($provide) { $provide.value('value', 'value;'); $provide.factory('fn', valueFn('function;')); $provide.service('service', function() { this.$get = valueFn('service;'); }); }, function(valueProvider, fnProvider, serviceProvider) { log += valueProvider.$get() + fnProvider.$get() + serviceProvider.$get(); }]).invoke(function(value, fn, service) { log += '->' + value + fn + service; }); expect(log).toEqual('value;function;service;->value;function;service;'); }); describe('module', function() { it('should provide $injector even when no module is requested', function() { var $provide, $injector = createInjector([ angular.extend(function(p) { $provide = p; }, {$inject: ['$provide']}) ]); expect($injector.get('$injector')).toBe($injector); }); it('should load multiple function modules and infer inject them', function() { var a = 'junk'; var $injector = createInjector([ function() { a = 'A'; // reset to prove we ran }, function($provide) { $provide.value('a', a); }, angular.extend(function(p, serviceA) { p.value('b', serviceA.$get() + 'B' ); }, {$inject:['$provide', 'aProvider']}), ['$provide', 'bProvider', function(p, serviceB) { p.value('c', serviceB.$get() + 'C'); }] ]); expect($injector.get('a')).toEqual('A'); expect($injector.get('b')).toEqual('AB'); expect($injector.get('c')).toEqual('ABC'); }); it('should run symbolic modules', function() { angularModule('myModule', []).value('a', 'abc'); var $injector = createInjector(['myModule']); expect($injector.get('a')).toEqual('abc'); }); it('should error on invalid module name', function() { expect(function() { createInjector(['IDontExist'], {}); }).toThrow("No module: IDontExist"); }); it('should load dependant modules only once', function() { var log = ''; angular.module('a', [], function(){ log += 'a'; }); angular.module('b', ['a'], function(){ log += 'b'; }); angular.module('c', ['a', 'b'], function(){ log += 'c'; }); createInjector(['c', 'c']); expect(log).toEqual('abc'); }); it('should execute runBlocks after injector creation', function() { var log = ''; angular.module('a', [], function(){ log += 'a'; }).run(function() { log += 'A'; }); angular.module('b', ['a'], function(){ log += 'b'; }).run(function() { log += 'B'; }); createInjector([ 'b', valueFn(function() { log += 'C'; }), [valueFn(function() { log += 'D'; })] ]); expect(log).toEqual('abABCD'); });
'use strict'; describe('$timeout', function() { beforeEach(module(provideLog)); it('should delegate functions to $browser.defer', inject(function($timeout, $browser) { var counter = 0; $timeout(function() { counter++; }); expect(counter).toBe(0); $browser.defer.flush(); expect(counter).toBe(1); expect(function() {$browser.defer.flush();}).toThrow('No deferred tasks to be flushed'); expect(counter).toBe(1); })); it('should call $apply after each callback is executed', inject(function($timeout, $rootScope) { var applySpy = spyOn($rootScope, '$apply').andCallThrough(); $timeout(function() {}); expect(applySpy).not.toHaveBeenCalled(); $timeout.flush(); expect(applySpy).toHaveBeenCalledOnce(); applySpy.reset(); $timeout(function() {}); $timeout(function() {}); $timeout.flush(); expect(applySpy.callCount).toBe(2); })); it('should NOT call $apply if skipApply is set to true', inject(function($timeout, $rootScope) { var applySpy = spyOn($rootScope, '$apply').andCallThrough(); $timeout(function() {}, 12, false); expect(applySpy).not.toHaveBeenCalled(); $timeout.flush(); expect(applySpy).not.toHaveBeenCalled(); })); it('should allow you to specify the delay time', inject(function($timeout, $browser) { var defer = spyOn($browser, 'defer'); $timeout(noop, 123); expect(defer.callCount).toEqual(1); expect(defer.mostRecentCall.args[1]).toEqual(123); })); it('should return a promise which will be resolved with return value of the timeout callback', inject(function($timeout, log) { var promise = $timeout(function() { log('timeout'); return 'buba'; }); promise.then(function(value) { log('promise success: ' + value); }, log.fn('promise error')); expect(log).toEqual([]); $timeout.flush(); expect(log).toEqual(['timeout', 'promise success: buba']); })); describe('exception handling', function() { beforeEach(module(function($exceptionHandlerProvider) { $exceptionHandlerProvider.mode('log'); })); it('should delegate exception to the $exceptionHandler service', inject( function($timeout, $exceptionHandler) { $timeout(function() {throw "Test Error";}); expect($exceptionHandler.errors).toEqual([]); $timeout.flush(); expect($exceptionHandler.errors).toEqual(["Test Error"]); })); it('should call $apply even if an exception is thrown in callback', inject( function($timeout, $rootScope) { var applySpy = spyOn($rootScope, '$apply').andCallThrough(); $timeout(function() {throw "Test Error";}); expect(applySpy).not.toHaveBeenCalled(); $timeout.flush(); expect(applySpy).toHaveBeenCalled(); })); it('should reject the timeout promise when an exception is thrown in the timeout callback', inject(function($timeout, log) { var promise = $timeout(function() { throw "Some Error"; }); promise.then(log.fn('success'), function(reason) { log('error: ' + reason); }); $timeout.flush(); expect(log).toEqual('error: Some Error'); })); }); describe('cancel', function() { it('should cancel tasks', inject(function($timeout) { var task1 = jasmine.createSpy('task1'), task2 = jasmine.createSpy('task2'), task3 = jasmine.createSpy('task3'), promise1, promise3; promise1 = $timeout(task1); $timeout(task2); promise3 = $timeout(task3, 333); $timeout.cancel(promise3); $timeout.cancel(promise1); $timeout.flush(); expect(task1).not.toHaveBeenCalled(); expect(task2).toHaveBeenCalledOnce(); expect(task3).not.toHaveBeenCalled(); })); it('should return true if a task was successfully canceled', inject(function($timeout) { var task1 = jasmine.createSpy('task1'), task2 = jasmine.createSpy('task2'), promise1, promise2; promise1 = $timeout(task1); $timeout.flush(); promise2 = $timeout(task2); expect($timeout.cancel(promise1)).toBe(false); expect($timeout.cancel(promise2)).toBe(true); })); it('should not throw a runtime exception when given an undefined promise', inject(function($timeout) { expect($timeout.cancel()).toBe(false); })); }); });