import {describe, ddescribe, it, iit, expect, beforeEach} from 'angular2/test_lib'; import {Injector, Inject, InjectLazy, bind} from 'angular2/di'; class Engine { } class BrokenEngine { constructor() { throw "Broken Engine"; } } class DashboardSoftware { } class Dashboard { constructor(software: DashboardSoftware) {} } class TurboEngine extends Engine { } class Car { engine:Engine; constructor(engine:Engine) { this.engine = engine; } } class CarWithLazyEngine { engineFactory; constructor(@InjectLazy(Engine) engineFactory) { this.engineFactory = engineFactory; } } class CarWithDashboard { engine:Engine; dashboard:Dashboard; constructor(engine:Engine, dashboard:Dashboard) { this.engine = engine; this.dashboard = dashboard; } } class SportsCar extends Car { engine:Engine; constructor(engine:Engine) { super(engine); } } class CarWithInject { engine:Engine; constructor(@Inject(TurboEngine) engine:Engine) { this.engine = engine; } } class CyclicEngine { constructor(car:Car) {} } class NoAnnotations { constructor(secretDependency) {} } export function main() { describe('injector', function () { it('should instantiate a class without dependencies', function () { var injector = new Injector([Engine]); var engine = injector.get(Engine); expect(engine).toBeAnInstanceOf(Engine); }); it('should resolve dependencies based on type information', function () { var injector = new Injector([Engine, Car]); var car = injector.get(Car); expect(car).toBeAnInstanceOf(Car); expect(car.engine).toBeAnInstanceOf(Engine); }); it('should resolve dependencies based on @Inject annotation', function () { var injector = new Injector([TurboEngine, Engine, CarWithInject]); var car = injector.get(CarWithInject); expect(car).toBeAnInstanceOf(CarWithInject); expect(car.engine).toBeAnInstanceOf(TurboEngine); }); it('should throw when no type and not @Inject', function () { expect(() => new Injector([NoAnnotations])).toThrowError( 'Cannot resolve all parameters for NoAnnotations'); }); it('should cache instances', function () { var injector = new Injector([Engine]); var e1 = injector.get(Engine); var e2 = injector.get(Engine); expect(e1).toBe(e2); }); it('should bind to a value', function () { var injector = new Injector([ bind(Engine).toValue("fake engine") ]); var engine = injector.get(Engine); expect(engine).toEqual("fake engine"); }); it('should bind to a factory', function () { function sportsCarFactory(e:Engine) { return new SportsCar(e); } var injector = new Injector([ Engine, bind(Car).toFactory(sportsCarFactory) ]); var car = injector.get(Car); expect(car).toBeAnInstanceOf(SportsCar); expect(car.engine).toBeAnInstanceOf(Engine); }); it('should support overriding factory dependencies', function () { var injector = new Injector([ Engine, bind(Car).toFactory((e) => new SportsCar(e), [Engine]) ]); var car = injector.get(Car); expect(car).toBeAnInstanceOf(SportsCar); expect(car.engine).toBeAnInstanceOf(Engine); }); it("should flatten passed-in bindings", function () { var injector = new Injector([ [[Engine, Car]] ]); var car = injector.get(Car); expect(car).toBeAnInstanceOf(Car); }); it("should use the last binding "+ "when there are mutliple bindings for same token", function () { var injector = new Injector([ bind(Engine).toClass(Engine), bind(Engine).toClass(TurboEngine) ]); expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine); }); it('should use non-type tokens', function () { var injector = new Injector([ bind('token').toValue('value') ]); expect(injector.get('token')).toEqual('value'); }); it('should throw when given invalid bindings', function () { expect(() => new Injector(["blah"])).toThrowError('Invalid binding blah'); expect(() => new Injector([bind("blah")])).toThrowError('Invalid binding blah'); }); it('should provide itself', function () { var parent = new Injector([]); var child = parent.createChild([]); expect(child.get(Injector)).toBe(child); }); it('should throw when no provider defined', function () { var injector = new Injector([]); expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!'); }); it('should show the full path when no provider', function () { var injector = new Injector([CarWithDashboard, Engine, Dashboard]); expect(() => injector.get(CarWithDashboard)). toThrowError('No provider for DashboardSoftware! (CarWithDashboard -> Dashboard -> DashboardSoftware)'); }); it('should throw when trying to instantiate a cyclic dependency', function () { var injector = new Injector([ Car, bind(Engine).toClass(CyclicEngine) ]); expect(() => injector.get(Car)) .toThrowError('Cannot instantiate cyclic dependency! (Car -> Engine -> Car)'); expect(() => injector.asyncGet(Car)) .toThrowError('Cannot instantiate cyclic dependency! (Car -> Engine -> Car)'); }); it('should show the full path when error happens in a constructor', function () { var injector = new Injector([ Car, bind(Engine).toClass(BrokenEngine) ]); try { injector.get(Car); throw "Must throw"; } catch (e) { expect(e.message).toContain("Error during instantiation of Engine! (Car -> Engine)"); } }); it('should instantiate an object after a failed attempt', function () { var isBroken = true; var injector = new Injector([ Car, bind(Engine).toFactory(() => isBroken ? new BrokenEngine() : new Engine()) ]); expect(() => injector.get(Car)).toThrowError(new RegExp("Error")); isBroken = false; expect(injector.get(Car)).toBeAnInstanceOf(Car); }); describe("default bindings", function () { it("should be used when no matching binding found", function () { var injector = new Injector([], {defaultBindings: true}); var car = injector.get(Car); expect(car).toBeAnInstanceOf(Car); }); it("should use the matching binding when it is available", function () { var injector = new Injector([ bind(Car).toClass(SportsCar) ], {defaultBindings: true}); var car = injector.get(Car); expect(car).toBeAnInstanceOf(SportsCar); }); }); describe("child", function () { it('should load instances from parent injector', function () { var parent = new Injector([Engine]); var child = parent.createChild([]); var engineFromParent = parent.get(Engine); var engineFromChild = child.get(Engine); expect(engineFromChild).toBe(engineFromParent); }); it('should create new instance in a child injector', function () { var parent = new Injector([Engine]); var child = parent.createChild([ bind(Engine).toClass(TurboEngine) ]); var engineFromParent = parent.get(Engine); var engineFromChild = child.get(Engine); expect(engineFromParent).not.toBe(engineFromChild); expect(engineFromChild).toBeAnInstanceOf(TurboEngine); }); it("should create child injectors without default bindings", function () { var parent = new Injector([], {defaultBindings: true}); var child = parent.createChild([]); //child delegates to parent the creation of Car var childCar = child.get(Car); var parentCar = parent.get(Car); expect(childCar).toBe(parentCar); }); }); describe("lazy", function () { it("should create dependencies lazily", function () { var injector = new Injector([ Engine, CarWithLazyEngine ]); var car = injector.get(CarWithLazyEngine); expect(car.engineFactory()).toBeAnInstanceOf(Engine); }); it("should cache instance created lazily", function () { var injector = new Injector([ Engine, CarWithLazyEngine ]); var car = injector.get(CarWithLazyEngine); var e1 = car.engineFactory(); var e2 = car.engineFactory(); expect(e1).toBe(e2); }); }); }); }