UNPKG

@joist/di

Version:

Dependency Injection for Vanilla JS classes

303 lines • 11.8 kB
import { __esDecorate, __runInitializers } from "tslib"; import { assert } from "chai"; import { inject } from "./inject.js"; import { injectable } from "./injectable.js"; import { Injector } from "./injector.js"; import { StaticToken } from "./provider.js"; it("should create a new instance of a single provider", () => { class A { } const app = new Injector(); assert(app.inject(A) instanceof A); assert.equal(app.inject(A), app.inject(A)); }); it("should inject providers in the correct order", () => { class A { } class B { } let MyService = (() => { let _classDecorators = [injectable()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; var MyService = class { static { _classThis = this; } static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); MyService = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); __runInitializers(_classThis, _classExtraInitializers); } a = inject(A); b = inject(B); }; return MyService = _classThis; })(); const app = new Injector(); const instance = app.inject(MyService); assert(instance.a() instanceof A); assert(instance.b() instanceof B); }); it("should create a new instance of a provider that has a full dep tree", () => { class A { } let B = (() => { let _classDecorators = [injectable()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; var B = class { static { _classThis = this; } static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); B = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); __runInitializers(_classThis, _classExtraInitializers); } a = inject(A); }; return B = _classThis; })(); let C = (() => { let _classDecorators = [injectable()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; var C = class { static { _classThis = this; } static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); C = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); __runInitializers(_classThis, _classExtraInitializers); } b = inject(B); }; return C = _classThis; })(); let D = (() => { let _classDecorators = [injectable()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; var D = class { static { _classThis = this; } static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); D = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); __runInitializers(_classThis, _classExtraInitializers); } c = inject(C); }; return D = _classThis; })(); let E = (() => { let _classDecorators = [injectable()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; var E = class { static { _classThis = this; } static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); E = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); __runInitializers(_classThis, _classExtraInitializers); } d = inject(D); }; return E = _classThis; })(); const app = new Injector(); const instance = app.inject(E); assert(instance.d().c().b().a() instanceof A); }); it("should override a provider if explicitly instructed", () => { class A { } let B = (() => { let _classDecorators = [injectable()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; var B = class { static { _classThis = this; } static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); B = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); __runInitializers(_classThis, _classExtraInitializers); } a = inject(A); }; return B = _classThis; })(); class AltA extends A { } const app = new Injector({ providers: [[A, { use: AltA }]], }); assert(app.inject(B).a() instanceof AltA); }); it("should return an existing instance from a parent injector", () => { class A { } const parent = new Injector(); const app = new Injector({ parent, }); assert.equal(parent.inject(A), app.inject(A)); }); it("should use a factory if provided", () => { class Service { hello() { return "world"; } } const injector = new Injector({ providers: [ [ Service, { factory() { return { hello() { return "world"; }, }; }, }, ], ], }); assert.equal(injector.inject(Service).hello(), "world"); }); it("should throw an error if provider is missing both factory and use", () => { class Service { hello() { return "world"; } } const injector = new Injector({ providers: [[Service, {}]], }); assert.throws(() => injector.inject(Service), "Provider for Service found but is missing either 'use' or 'factory'"); }); it("should pass factories and instance of the injector", async () => { class Service { hello() { return "world"; } } let factoryInjector = null; const injector = new Injector({ providers: [ [ Service, { factory(i) { factoryInjector = i; }, }, ], ], }); injector.inject(Service); assert.equal(factoryInjector, injector); }); it("should create an instance from a StaticToken factory", () => { const TOKEN = new StaticToken("test", () => "Hello World"); const injector = new Injector(); const res = injector.inject(TOKEN); assert.equal(res, "Hello World"); }); it("should create an instance from an async StaticToken factory", async () => { const TOKEN = new StaticToken("test", async () => "Hello World"); const injector = new Injector(); const res = await injector.inject(TOKEN); assert.equal(res, "Hello World"); }); it("should allow static token to be overridden", () => { const TOKEN = new StaticToken("test"); const injector = new Injector({ providers: [ [ TOKEN, { factory() { return "Hello World"; }, }, ], ], }); const res = injector.inject(TOKEN); assert.equal(res, "Hello World"); }); it("should allow you to get ALL available instances in a particular injector chain", () => { const TOKEN = new StaticToken("TOKEN"); const injector = new Injector({ providers: [[TOKEN, { factory: () => "first" }]], parent: new Injector({ providers: [[TOKEN, { factory: () => "second" }]], parent: new Injector({ providers: [[TOKEN, { factory: () => "third" }]], parent: new Injector({ providers: [[TOKEN, { factory: () => "fourth" }]], }), }), }), }); const res = injector.injectAll(TOKEN); assert.deepEqual(res, ["first", "second", "third", "fourth"]); }); it("should respect skipParent option when injecting", () => { class Service { value = "child"; } const parent = new Injector({ providers: [ [ Service, { use: class extends Service { value = "parent"; }, }, ], ], }); const child = new Injector({ parent }); assert.equal(child.inject(Service).value, "parent"); assert.equal(child.inject(Service, { skipParent: true }).value, "child"); }); it("should handle skipParent with static tokens", () => { const TOKEN = new StaticToken("test", () => "child-value"); const parent = new Injector({ providers: [[TOKEN, { factory: () => "parent-value" }]], }); const child = new Injector({ parent }); assert.equal(child.inject(TOKEN), "parent-value"); assert.equal(child.inject(TOKEN, { skipParent: true }), "child-value"); }); it("should handle StaticToken with null/undefined factory", () => { const TOKEN = new StaticToken("test"); const injector = new Injector(); assert.throws(() => injector.inject(TOKEN), 'Provider not found for "test"'); }); it("should handle StaticToken factory throwing errors", () => { const TOKEN = new StaticToken("test", () => { throw new Error("Factory error"); }); const injector = new Injector(); assert.throws(() => injector.inject(TOKEN), "Factory error"); }); //# sourceMappingURL=injector.test.js.map