UNPKG

@sigi/di

Version:

Dependencies injection library for sigi framework

341 lines (262 loc) 8.06 kB
import '@abraham/reflection' import { Inject, InjectionToken, Injectable, rootInjector } from '../index' describe('injectable specs', () => { afterEach(() => { rootInjector.reset() }) it('should get single instance', () => { @Injectable() class Single {} const instance = rootInjector.getInstance(Single) expect(instance instanceof Single).toBeTruthy() }) it('should get same instance after add new provider', () => { @Injectable() class Single {} class NewOne {} const instance1 = rootInjector.getInstance(Single) rootInjector.addProvider(NewOne) const instance2 = rootInjector.getInstance(Single) expect(instance1).toBe(instance2) }) it('should get same instance after override providers', () => { @Injectable() class Single {} class NewOne {} class AddedOne {} const injector = rootInjector const addedInjector = injector.createChild([AddedOne, NewOne]) const instance1 = injector.getInstance(Single) const instance2 = addedInjector.getInstance(Single) expect(instance1).toBe(instance2) }) it('should get dependencies', () => { @Injectable() class Dep {} @Injectable() class DepTwo { constructor(public dep: Dep) {} } @Injectable() class Service { constructor( public dep: Dep, public depTwo: DepTwo, ) {} } const injector = rootInjector const service = injector.getInstance(Service) expect(injector.getInstance(Dep) instanceof Dep).toBeTruthy() expect(injector.getInstance(DepTwo) instanceof DepTwo).toBeTruthy() expect(service instanceof Service).toBeTruthy() }) it('should singleton by default', () => { @Injectable() class Dep {} @Injectable() class DepTwo { constructor(public dep: Dep) {} } @Injectable() class Service { constructor( public dep: Dep, public depTwo: DepTwo, ) {} } const injector = rootInjector const service = injector.getInstance(Service) const dep = injector.getInstance(Dep) const depTwo = injector.getInstance(DepTwo) expect(service.dep).toBe(dep) expect(service.depTwo).toBe(depTwo) }) it('should be able to inject by useValue', () => { // eslint-disable-next-line @typescript-eslint/no-empty-function function whatever() {} const token = new InjectionToken<typeof whatever>('whatever') rootInjector.addProvider({ provide: token, useValue: whatever, }) @Injectable() class Service { constructor(@Inject(token) public dep: typeof whatever) {} } const injector = rootInjector const service = injector.getInstance(Service) expect(service instanceof Service).toBeTruthy() expect(service.dep).toBe(whatever) }) it('should be able to replace provide', () => { const rawClientProvide = rootInjector.addProvider({ provide: new InjectionToken('raw-client'), useValue: Object.create(null), }) const queryProvider = rootInjector.addProvider({ provide: new InjectionToken('query'), useFactory: (client: any) => Object.create({ client: client, }), deps: [rawClientProvide.provide], }) @Injectable() class Client { constructor(@Inject(queryProvider.provide) public query: any) {} } @Injectable() class Module { constructor(public client: Client) {} } const childInjector = rootInjector.createChild([ { provide: rawClientProvide.provide, useValue: new Date(), }, ]) const oldM = rootInjector.getInstance(Module) const m = childInjector.getInstance(Module) expect(oldM).not.toBe(m) expect(m.client.query.client instanceof Date).toBeTruthy() }) it('should be able to inject by useFactory', () => { class Dep { constructor(public cacheSize: number) {} } const cacheSize = 5 const token = new InjectionToken<Dep>('whatever') rootInjector.addProvider({ provide: token, useFactory() { return new Dep(cacheSize) }, }) @Injectable() class Service { constructor(@Inject(token) public dep: Dep) {} } const injector = rootInjector const service = injector.getInstance(Service) expect(service.dep instanceof Dep).toBeTruthy() expect(service.dep.cacheSize).toBe(cacheSize) }) it('should be able to resolve deps from useFactory', () => { @Injectable() class DepTwo {} @Injectable() class Dep { constructor( public cacheSize: number, public depTwo: DepTwo, ) {} } const cacheSize = 5 const token = new InjectionToken<Dep>('whatever') rootInjector.addProvider({ provide: token, useFactory(depTwo: DepTwo) { return new Dep(cacheSize, depTwo) }, deps: [DepTwo], }) @Injectable() class Service { constructor(@Inject(token) public dep: Dep) {} } const injector = rootInjector const service = injector.getInstance(Service) const depTwo = injector.getInstance(DepTwo) expect(service.dep instanceof Dep).toBeTruthy() expect(service.dep.cacheSize).toBe(cacheSize) expect(depTwo instanceof DepTwo).toBeTruthy() expect(service.dep.depTwo).toBe(depTwo) }) it('should be able to inject by useClass', () => { @Injectable() class Dep {} const token = new InjectionToken<Dep>('whatever') rootInjector.addProvider({ provide: token, useClass: Dep, }) @Injectable() class Service { constructor(@Inject(token) public dep: Dep) {} } const injector = rootInjector const service = injector.getInstance(Service) expect(service instanceof Service).toBeTruthy() expect(service.dep instanceof Dep).toBeTruthy() }) it('should initialize without cache #1', () => { @Injectable() class Dep {} @Injectable() class Service { constructor(public readonly dep: Dep) {} } const injector = rootInjector const dep = injector.resolveAndInstantiate<Dep>(Dep) const service = injector.getInstance(Service) expect(dep).not.toBe(service.dep) }) it('should initialize without cache #2', () => { @Injectable() class Dep {} const injector = rootInjector const dep1 = injector.resolveAndInstantiate(Dep) const dep2 = injector.resolveAndInstantiate(Dep) expect(dep1).not.toBe(dep2) }) it('should initialize without cache #3', () => { @Injectable() class Dep {} const injector = rootInjector const dep1 = injector.resolveAndInstantiate(Dep) const dep2 = injector.getInstance(Dep) const dep3 = injector.resolveAndInstantiate(Dep) expect(dep1).not.toBe(dep2) expect(dep3).not.toBe(dep1) expect(dep3).not.toBe(dep2) }) it('should resolve and create new injector', () => { class Dep {} const token = new InjectionToken<Dep>('whatever') rootInjector.addProvider({ provide: token, useClass: Dep, }) @Injectable() class Service { constructor(@Inject(token) public dep: Dep) {} } const replacementProvider = { provide: token, useValue: 1, } const newInjector = rootInjector.createChild([replacementProvider]) const service = newInjector.getInstance(Service) const service1 = newInjector.getInstance(Service) expect(service.dep).toBe(1) expect(service).toBe(service1) }) it('should be able to inject provider via InjectableConfigs', () => { @Injectable() class Dep {} const token = new InjectionToken<Dep>('whatever') @Injectable({ providers: [ { useClass: Dep, provide: token, }, ], }) class Service { constructor(@Inject(token) public dep: Dep) {} } expect(rootInjector.getInstance(Service).dep instanceof Dep).toBeTruthy() expect(rootInjector.getInstance(Service).dep).toBe(rootInjector.getInstance(Dep)) }) })