@tsed/di 8.0.1 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/package.json +10 -9
  2. package/src/common/constants/constants.ts +5 -0
  3. package/src/common/decorators/autoInjectable.spec.ts +135 -0
  4. package/src/common/decorators/autoInjectable.ts +59 -0
  5. package/src/common/decorators/configuration.spec.ts +45 -0
  6. package/src/common/decorators/configuration.ts +34 -0
  7. package/src/common/decorators/constant.spec.ts +67 -0
  8. package/src/common/decorators/constant.ts +72 -0
  9. package/src/common/decorators/controller.spec.ts +43 -0
  10. package/src/common/decorators/controller.ts +64 -0
  11. package/src/common/decorators/inject.spec.ts +298 -0
  12. package/src/common/decorators/inject.ts +144 -0
  13. package/src/common/decorators/injectable.spec.ts +43 -0
  14. package/src/common/decorators/injectable.ts +30 -0
  15. package/src/common/decorators/intercept.spec.ts +103 -0
  16. package/src/common/decorators/intercept.ts +77 -0
  17. package/src/common/decorators/interceptor.ts +17 -0
  18. package/src/common/decorators/lazyInject.spec.ts +53 -0
  19. package/src/common/decorators/lazyInject.ts +91 -0
  20. package/src/common/decorators/module.ts +52 -0
  21. package/src/common/decorators/opts.ts +51 -0
  22. package/src/common/decorators/overrideProvider.spec.ts +30 -0
  23. package/src/common/decorators/overrideProvider.ts +15 -0
  24. package/src/common/decorators/scope.spec.ts +25 -0
  25. package/src/common/decorators/scope.ts +13 -0
  26. package/src/common/decorators/service.ts +14 -0
  27. package/src/common/decorators/useOpts.spec.ts +90 -0
  28. package/src/common/decorators/useOpts.ts +61 -0
  29. package/src/common/decorators/value.spec.ts +59 -0
  30. package/src/common/decorators/value.ts +60 -0
  31. package/src/common/domain/Container.spec.ts +85 -0
  32. package/src/common/domain/Container.ts +80 -0
  33. package/src/common/domain/ControllerProvider.spec.ts +55 -0
  34. package/src/common/domain/ControllerProvider.ts +48 -0
  35. package/src/common/domain/InjectablePropertyType.ts +7 -0
  36. package/src/common/domain/LocalsContainer.spec.ts +20 -0
  37. package/src/common/domain/LocalsContainer.ts +11 -0
  38. package/src/common/domain/Provider.spec.ts +107 -0
  39. package/src/common/domain/Provider.ts +205 -0
  40. package/src/common/domain/ProviderScope.ts +5 -0
  41. package/src/common/domain/ProviderType.ts +8 -0
  42. package/src/common/errors/InjectionError.spec.ts +17 -0
  43. package/src/common/errors/InjectionError.ts +68 -0
  44. package/src/common/errors/InvalidPropertyTokenError.ts +13 -0
  45. package/src/common/fn/configuration.spec.ts +42 -0
  46. package/src/common/fn/configuration.ts +22 -0
  47. package/src/common/fn/constant.spec.ts +37 -0
  48. package/src/common/fn/constant.ts +7 -0
  49. package/src/common/fn/inject.spec.ts +54 -0
  50. package/src/common/fn/inject.ts +29 -0
  51. package/src/common/fn/injectMany.ts +16 -0
  52. package/src/common/fn/injectable.spec.ts +87 -0
  53. package/src/common/fn/injectable.ts +15 -0
  54. package/src/common/fn/injector.ts +26 -0
  55. package/src/common/fn/lazyInject.spec.ts +22 -0
  56. package/src/common/fn/lazyInject.ts +36 -0
  57. package/src/common/fn/localsContainer.ts +55 -0
  58. package/src/common/fn/refValue.spec.ts +47 -0
  59. package/src/common/fn/refValue.ts +33 -0
  60. package/src/common/index.ts +63 -0
  61. package/src/common/integration/async-factory.spec.ts +60 -0
  62. package/src/common/integration/di.spec.ts +153 -0
  63. package/src/common/integration/imports.spec.ts +39 -0
  64. package/src/common/integration/request.spec.ts +79 -0
  65. package/src/common/integration/singleton.spec.ts +125 -0
  66. package/src/common/interfaces/DIConfigurationOptions.ts +29 -0
  67. package/src/common/interfaces/DILogger.ts +13 -0
  68. package/src/common/interfaces/DILoggerOptions.ts +54 -0
  69. package/src/common/interfaces/ImportTokenProviderOpts.ts +29 -0
  70. package/src/common/interfaces/InterceptorContext.ts +13 -0
  71. package/src/common/interfaces/InterceptorMethods.ts +5 -0
  72. package/src/common/interfaces/InvokeOptions.ts +28 -0
  73. package/src/common/interfaces/OnDestroy.ts +3 -0
  74. package/src/common/interfaces/OnInit.ts +3 -0
  75. package/src/common/interfaces/ProviderOpts.ts +57 -0
  76. package/src/common/interfaces/RegistrySettings.ts +11 -0
  77. package/src/common/interfaces/ResolvedInvokeOptions.ts +14 -0
  78. package/src/common/interfaces/TokenProvider.ts +3 -0
  79. package/src/common/interfaces/TokenRoute.ts +6 -0
  80. package/src/common/registries/GlobalProviders.spec.ts +94 -0
  81. package/src/common/registries/GlobalProviders.ts +131 -0
  82. package/src/common/registries/ProviderRegistry.spec.ts +36 -0
  83. package/src/common/registries/ProviderRegistry.ts +32 -0
  84. package/src/common/services/DIConfiguration.spec.ts +60 -0
  85. package/src/common/services/DIConfiguration.ts +140 -0
  86. package/src/common/services/DILogger.spec.ts +22 -0
  87. package/src/common/services/DILogger.ts +8 -0
  88. package/src/common/services/InjectorService.spec.ts +793 -0
  89. package/src/common/services/InjectorService.ts +601 -0
  90. package/src/common/utils/colors.spec.ts +9 -0
  91. package/src/common/utils/colors.ts +11 -0
  92. package/src/common/utils/createContainer.spec.ts +18 -0
  93. package/src/common/utils/createContainer.ts +14 -0
  94. package/src/common/utils/discoverHooks.ts +15 -0
  95. package/src/common/utils/getConfiguration.spec.ts +40 -0
  96. package/src/common/utils/getConfiguration.ts +20 -0
  97. package/src/common/utils/getConstructorDependencies.ts +9 -0
  98. package/src/common/utils/providerBuilder.ts +81 -0
  99. package/src/index.ts +2 -0
  100. package/src/node/decorators/injectContext.spec.ts +60 -0
  101. package/src/node/decorators/injectContext.ts +29 -0
  102. package/src/node/domain/ContextLogger.spec.ts +277 -0
  103. package/src/node/domain/ContextLogger.ts +137 -0
  104. package/src/node/domain/DIContext.spec.ts +103 -0
  105. package/src/node/domain/DIContext.ts +115 -0
  106. package/src/node/fn/context.ts +24 -0
  107. package/src/node/fn/logger.spec.ts +46 -0
  108. package/src/node/fn/logger.ts +17 -0
  109. package/src/node/index.ts +16 -0
  110. package/src/node/integration/async-factory-invoke.spec.ts +58 -0
  111. package/src/node/integration/inject-context.spec.ts +29 -0
  112. package/src/node/integration/invoke-oninit.spec.ts +25 -0
  113. package/src/node/interfaces/AlterRunInContext.ts +3 -0
  114. package/src/node/services/DILogger.spec.ts +24 -0
  115. package/src/node/services/DILogger.ts +6 -0
  116. package/src/node/services/DITest.spec.ts +109 -0
  117. package/src/node/services/DITest.ts +119 -0
  118. package/src/node/utils/asyncHookContext.spec.ts +147 -0
  119. package/src/node/utils/asyncHookContext.ts +48 -0
  120. package/src/node/utils/attachLogger.spec.ts +14 -0
  121. package/src/node/utils/attachLogger.ts +7 -0
  122. package/src/node/utils/setLoggerConfiguration.spec.ts +43 -0
  123. package/src/node/utils/setLoggerConfiguration.ts +7 -0
  124. package/src/node/utils/setLoggerFormat.ts +33 -0
  125. package/src/node/utils/setLoggerLevel.ts +12 -0
@@ -0,0 +1,793 @@
1
+ import {$emit} from "@tsed/hooks";
2
+
3
+ import {Configuration} from "../decorators/configuration.js";
4
+ import {Inject} from "../decorators/inject.js";
5
+ import {Injectable} from "../decorators/injectable.js";
6
+ import {Container} from "../domain/Container.js";
7
+ import {LocalsContainer} from "../domain/LocalsContainer.js";
8
+ import {Provider} from "../domain/Provider.js";
9
+ import {ProviderScope} from "../domain/ProviderScope.js";
10
+ import {ProviderType} from "../domain/ProviderType.js";
11
+ import {inject} from "../fn/inject.js";
12
+ import {destroyInjector, injector} from "../fn/injector.js";
13
+ import {registerProvider} from "../registries/ProviderRegistry.js";
14
+ import {InjectorService} from "./InjectorService.js";
15
+
16
+ vi.mock("@tsed/hooks", async (importOriginal) => {
17
+ const mod = await importOriginal<typeof import("@tsed/hooks")>();
18
+
19
+ return {
20
+ ...mod,
21
+ $emit: vi.fn()
22
+ };
23
+ });
24
+
25
+ class Test {
26
+ @Inject()
27
+ prop: InjectorService;
28
+
29
+ value: any;
30
+ constant: any;
31
+
32
+ constructor() {}
33
+
34
+ test3(o: any) {
35
+ return o + " called ";
36
+ }
37
+ }
38
+
39
+ describe("InjectorService", () => {
40
+ afterEach(() => destroyInjector());
41
+
42
+ describe("has()", () => {
43
+ it("should return true", () => {
44
+ expect(injector().has(InjectorService)).toBe(true);
45
+ });
46
+
47
+ it("should return false", () => {
48
+ expect(injector().has(Test)).toBe(false);
49
+ });
50
+ });
51
+ describe("get()", () => {
52
+ it("should return element", () => {
53
+ expect(injector().get(InjectorService)).toBeInstanceOf(InjectorService);
54
+ });
55
+
56
+ it("should return Test", () => {
57
+ expect(injector().get(Test)).toBeInstanceOf(Test);
58
+ });
59
+ });
60
+ describe("getMany()", () => {
61
+ it("should return all instance", () => {
62
+ injector().addProvider("token", {
63
+ type: ProviderType.VALUE,
64
+ useValue: 1
65
+ });
66
+
67
+ expect(!!injector().getMany(ProviderType.VALUE).length).toEqual(true);
68
+
69
+ injector().delete("token");
70
+ });
71
+ });
72
+ describe("toArray()", () => {
73
+ it("should return instances", () => {
74
+ expect(injector().toArray()).toBeInstanceOf(Array);
75
+ });
76
+ });
77
+ describe("invoke()", () => {
78
+ describe("when we call invoke with rebuild options (SINGLETON)", () => {
79
+ it("should invoke the provider from container", async () => {
80
+ // GIVEN
81
+ const token = class Test {};
82
+
83
+ const provider = new Provider<any>(token);
84
+ provider.scope = ProviderScope.SINGLETON;
85
+ provider.deps = [InjectorService];
86
+ provider.alias = "alias";
87
+
88
+ const container = new Container();
89
+ container.set(token, provider);
90
+
91
+ await injector().load(container);
92
+
93
+ vi.spyOn(injector() as any, "invokeToken");
94
+ vi.spyOn(injector() as any, "resolve");
95
+ vi.spyOn(injector(), "getProvider");
96
+
97
+ const locals = new LocalsContainer();
98
+
99
+ // WHEN
100
+
101
+ const result1: any = inject(token, {locals});
102
+ const result2: any = inject(token, {locals, rebuild: true});
103
+
104
+ // THEN
105
+ expect(result1 !== result2).toEqual(true);
106
+ expect(injector().getProvider).toHaveBeenCalledWith(token);
107
+ expect(injector().get("alias")).toBeInstanceOf(token);
108
+
109
+ expect((injector() as any).invokeToken).toHaveBeenCalledWith(token, {locals, rebuild: true});
110
+ expect(injector().resolve).toHaveBeenCalledWith(InjectorService, {
111
+ locals,
112
+ parent: token
113
+ });
114
+ });
115
+ });
116
+ describe("when provider is a REQUEST", () => {
117
+ it("should invoke a request from local container", async () => {
118
+ // GIVEN
119
+ const token = class Test {};
120
+
121
+ const provider = new Provider<any>(token);
122
+ provider.scope = ProviderScope.REQUEST;
123
+
124
+ const container = new Container();
125
+ container.set(token, provider);
126
+
127
+ await injector().load(container);
128
+
129
+ vi.spyOn(injector() as any, "resolve");
130
+ vi.spyOn(injector(), "get");
131
+ vi.spyOn(injector(), "getProvider");
132
+
133
+ const locals = new LocalsContainer(); // LocalContainer for the first request
134
+ const locals2 = new LocalsContainer(); // LocalContainer for the second request
135
+
136
+ // WHEN REQ1
137
+ const result1: any = inject(token, {locals});
138
+ const result2: any = inject(token, {locals});
139
+
140
+ // WHEN REQ2
141
+ const result3: any = inject(token, {locals: locals2});
142
+
143
+ // THEN
144
+ expect(result1).toEqual(result2);
145
+ expect(result2 !== result3).toEqual(true);
146
+
147
+ expect(injector().getProvider).toHaveBeenCalledWith(token);
148
+ expect((injector() as any).resolve).toHaveBeenCalledWith(token, {locals});
149
+ expect(locals.get(token)).toEqual(result1);
150
+ expect(locals2.get(token)).toEqual(result3);
151
+
152
+ expect(injector().get).not.toHaveBeenCalled();
153
+ });
154
+ });
155
+ describe("when provider is a INSTANCE", () => {
156
+ it("should invoke a new instance", async () => {
157
+ // GIVEN
158
+ const token = class Test {};
159
+
160
+ const provider = new Provider<any>(token);
161
+ provider.scope = ProviderScope.INSTANCE;
162
+
163
+ const container = new Container();
164
+ container.set(token, provider);
165
+
166
+ await injector().load(container);
167
+
168
+ vi.spyOn(injector() as any, "resolve");
169
+ vi.spyOn(injector(), "get");
170
+ vi.spyOn(injector(), "getProvider");
171
+
172
+ const locals = new LocalsContainer(); // LocalContainer for the first request
173
+
174
+ // WHEN REQ1
175
+ const result1: any = inject(token, {locals});
176
+ const result2: any = inject(token, {locals});
177
+
178
+ // THEN
179
+ expect(result1 !== result2).toEqual(true);
180
+
181
+ expect(injector().getProvider).toHaveBeenCalledWith(token);
182
+ expect((injector() as any).resolve).toHaveBeenCalledWith(token, {locals});
183
+ expect(locals.has(token)).toEqual(false);
184
+ expect(injector().get).not.toHaveBeenCalled();
185
+
186
+ await injector().destroy();
187
+ });
188
+ });
189
+ describe("when provider is a SINGLETON", () => {
190
+ it("should invoke the provider from container", () => {
191
+ // GIVEN
192
+ const token = class Test {};
193
+ const provider = new Provider<any>(token);
194
+ provider.scope = ProviderScope.SINGLETON;
195
+
196
+ injector().set(token, provider);
197
+
198
+ // WHEN
199
+ const result: any = inject(token);
200
+
201
+ // THEN
202
+ expect(result).toBeInstanceOf(token);
203
+ expect($emit).toHaveBeenCalledWith("$beforeInvoke", token, [expect.any(Object)]);
204
+ expect($emit).toHaveBeenCalledWith("$beforeInvoke:provider", [expect.any(Object)]);
205
+ expect($emit).toHaveBeenCalledWith("$afterInvoke", token, [result, expect.any(Object)]);
206
+ });
207
+ it("should invoke the provider from container (2)", async () => {
208
+ // GIVEN
209
+ const token = class Test {};
210
+
211
+ const provider = new Provider<any>(token);
212
+ provider.scope = ProviderScope.SINGLETON;
213
+
214
+ const container = new Container();
215
+ container.set(token, provider);
216
+
217
+ await injector().load(container);
218
+
219
+ vi.spyOn(injector() as any, "invokeToken");
220
+ vi.spyOn(injector(), "getProvider");
221
+
222
+ const locals = new LocalsContainer();
223
+
224
+ // WHEN
225
+ const result1: any = inject(token, {locals});
226
+ const result2: any = inject(token, {locals});
227
+
228
+ // THEN
229
+ expect(result1).toEqual(result2);
230
+ expect((injector() as any).invokeToken).not.toHaveBeenCalled();
231
+ });
232
+ });
233
+ describe("when provider is a Value (useValue)", () => {
234
+ it("should invoke the provider from container (1)", async () => {
235
+ // GIVEN
236
+ const token = Symbol.for("TokenValue1");
237
+
238
+ const provider = new Provider<any>(token);
239
+ provider.scope = ProviderScope.SINGLETON;
240
+ provider.useValue = "TEST";
241
+
242
+ const container = new Container();
243
+ container.set(token, provider);
244
+
245
+ await injector().load(container);
246
+
247
+ // WHEN
248
+ const result: any = inject(token);
249
+
250
+ // THEN
251
+ expect(result).toEqual("TEST");
252
+ });
253
+ it("should invoke the provider from container (2)", async () => {
254
+ // GIVEN
255
+ const token = Symbol.for("TokenValue2");
256
+
257
+ const provider = new Provider<any>(token);
258
+ provider.scope = ProviderScope.SINGLETON;
259
+ provider.useValue = () => "TEST";
260
+
261
+ const container = new Container();
262
+ container.set(token, provider);
263
+
264
+ await injector().load(container);
265
+
266
+ // WHEN
267
+ const result: any = inject(token);
268
+
269
+ // THEN
270
+ expect(result).toEqual("TEST");
271
+ });
272
+ it("should invoke the provider from container with falsy value", async () => {
273
+ // GIVEN
274
+ const token = Symbol.for("TokenValue3");
275
+
276
+ const provider = new Provider<any>(token);
277
+ provider.scope = ProviderScope.SINGLETON;
278
+ provider.useValue = null;
279
+
280
+ const container = new Container();
281
+ container.set(token, provider);
282
+
283
+ await injector().load(container);
284
+
285
+ // WHEN
286
+ const result: any = inject(token);
287
+
288
+ // THEN
289
+ expect(result).toEqual(null);
290
+ });
291
+ });
292
+ describe("when provider is a Factory (useFactory)", () => {
293
+ it("should invoke the provider from container", async () => {
294
+ // GIVEN
295
+ const token = Symbol.for("TokenFactory");
296
+
297
+ const provider = new Provider<any>(token);
298
+ provider.scope = ProviderScope.SINGLETON;
299
+ provider.useFactory = () => ({factory: "factory"});
300
+
301
+ const container = new Container();
302
+ container.set(token, provider);
303
+
304
+ await injector().load(container);
305
+
306
+ // WHEN
307
+ const result: any = inject(token);
308
+
309
+ // THEN
310
+ expect(result).toEqual({factory: "factory"});
311
+ });
312
+ });
313
+ describe("when provider is an AsyncFactory (useAsyncFactory)", () => {
314
+ it("should invoke the provider from container", async () => {
315
+ // GIVEN
316
+ const tokenChild = Symbol.for("TokenChildFactory");
317
+ const providerChild = new Provider<any>(tokenChild);
318
+ providerChild.useAsyncFactory = (dep: any) => Promise.resolve("test async");
319
+
320
+ const token = Symbol.for("TokenFactory");
321
+ const provider = new Provider<any>(token);
322
+ provider.deps = [tokenChild];
323
+ provider.useAsyncFactory = (dep: any) => Promise.resolve({factory: dep + " factory"});
324
+ provider.hooks = {
325
+ $onDestroy: () => {}
326
+ };
327
+
328
+ const tokenSync = Symbol.for("TokenSyncFactory");
329
+ const providerSync = new Provider<any>(tokenSync);
330
+ providerSync.deps = [token];
331
+ providerSync.hooks = {$onDestroy: vi.fn(), $onInit: vi.fn()};
332
+ providerSync.useFactory = (asyncInstance: any) => asyncInstance.factory;
333
+
334
+ const container = new Container();
335
+ container.set(tokenChild, providerChild);
336
+ container.set(token, provider);
337
+ container.set(tokenSync, providerSync);
338
+
339
+ await injector().load(container);
340
+
341
+ // WHEN
342
+ const result: any = inject(token);
343
+ const result2: any = inject(tokenSync);
344
+
345
+ // THEN
346
+ expect(result).toEqual({factory: "test async factory"});
347
+ expect(result2).toEqual("test async factory");
348
+
349
+ await injector().emit("$onInit");
350
+
351
+ expect(providerSync.hooks.$onInit).toHaveBeenCalledWith("test async factory");
352
+ });
353
+ it("should invoke the provider from container with nested async factory", async () => {
354
+ // GIVEN
355
+ const tokenChild = Symbol.for("TokenChildFactory");
356
+ const providerChild = new Provider<any>(tokenChild);
357
+ providerChild.useAsyncFactory = (dep: any) => Promise.resolve("test async");
358
+
359
+ const token = Symbol.for("TokenFactory");
360
+ const provider = new Provider<any>(token);
361
+ provider.deps = [tokenChild];
362
+ provider.useAsyncFactory = (dep: any) => Promise.resolve({factory: dep + " factory"});
363
+
364
+ const token2 = Symbol.for("TokenFactory2");
365
+ const provider2 = new Provider<any>(token2);
366
+ provider2.deps = [token];
367
+ provider2.useAsyncFactory = (dep: any) => {
368
+ return Promise.resolve({factory: dep.factory + " factory2"});
369
+ };
370
+
371
+ const container = new Container();
372
+ container.set(tokenChild, providerChild);
373
+ container.set(token, provider);
374
+ container.set(token2, provider2);
375
+
376
+ await injector().load(container);
377
+
378
+ // WHEN
379
+ const result: any = inject(token2);
380
+
381
+ // THEN
382
+ expect(result).toEqual({factory: "test async factory factory2"});
383
+ });
384
+ });
385
+ describe("when provider is an unknown provider", () => {
386
+ it("should invoke the class from given parameter", () => {
387
+ // GIVEN
388
+ const token = class {};
389
+
390
+ // WHEN
391
+ const result: any = inject(token);
392
+
393
+ // THEN
394
+ expect(result).toBeInstanceOf(token);
395
+ });
396
+ });
397
+ describe("when one of dependencies is undefined", () => {
398
+ it("should throw InjectionError > UndefinedTokenError", () => {
399
+ // GIVEN
400
+ const token2 = class Ctrl {
401
+ constructor() {}
402
+ };
403
+ const token3 = class Test {
404
+ constructor(test: any) {}
405
+ };
406
+
407
+ const provider2 = new Provider<any>(token2);
408
+ provider2.scope = ProviderScope.SINGLETON;
409
+ provider2.type = ProviderType.CONTROLLER;
410
+ provider2.useClass = token2;
411
+
412
+ const provider3 = new Provider<any>(token3);
413
+ provider3.scope = ProviderScope.SINGLETON;
414
+ provider3.deps = [undefined] as never;
415
+
416
+ injector().set(token2, provider2);
417
+ injector().set(token3, provider3);
418
+
419
+ // WHEN
420
+ let actualError;
421
+ try {
422
+ inject(token3);
423
+ } catch (er) {
424
+ actualError = er;
425
+ }
426
+
427
+ // THEN
428
+ expect(actualError.message).toContain(
429
+ "Injection failed on Test\nOrigin: Unable to inject dependency. Given token is undefined. Could mean a circular dependency problem. Try to use @Inject(() => Token) to solve it."
430
+ );
431
+ });
432
+ it("should throw InjectionError > Object", () => {
433
+ // GIVEN
434
+ const token2 = class Ctrl {
435
+ constructor() {}
436
+ };
437
+ const token3 = class Test {
438
+ constructor(test: Object) {}
439
+ };
440
+
441
+ const provider2 = new Provider<any>(token2);
442
+ provider2.scope = ProviderScope.SINGLETON;
443
+ provider2.type = ProviderType.CONTROLLER;
444
+ provider2.useClass = token2;
445
+
446
+ const provider3 = new Provider<any>(token3);
447
+ provider3.scope = ProviderScope.SINGLETON;
448
+ provider3.deps = [Object];
449
+
450
+ injector().set(token2, provider2);
451
+ injector().set(token3, provider3);
452
+
453
+ // WHEN
454
+ let actualError;
455
+ try {
456
+ inject(token3);
457
+ } catch (er) {
458
+ actualError = er;
459
+ }
460
+
461
+ // THEN
462
+ expect(actualError.message).toContain("Injection failed on Test\nOrigin: Unable to inject dependency.");
463
+ });
464
+ it("should try to inject string token (optional)", () => {
465
+ // GIVEN
466
+
467
+ // WHEN
468
+ const result = inject("token.not.found");
469
+
470
+ // THEN
471
+ expect(result).toEqual(undefined);
472
+ });
473
+ });
474
+ describe("when error occur", () => {
475
+ it("should throw InjectionError", () => {
476
+ // GIVEN
477
+ const token1 = Symbol.for("TokenValue");
478
+ const token2 = Symbol.for("TokenFactory");
479
+ const token3 = class Test {
480
+ constructor(dep: any) {}
481
+ };
482
+
483
+ const provider1 = new Provider<any>(token1);
484
+ provider1.scope = ProviderScope.SINGLETON;
485
+ provider1.useValue = () => undefined; // should throw error because instance is undefined
486
+
487
+ const provider2 = new Provider<any>(token2);
488
+ provider2.scope = ProviderScope.SINGLETON;
489
+ provider2.deps = [token1];
490
+ provider2.useFactory = () => ({});
491
+
492
+ const provider3 = new Provider<any>(token3);
493
+ provider3.scope = ProviderScope.SINGLETON;
494
+ provider3.deps = [token2];
495
+
496
+ injector().set(token1, provider1);
497
+ injector().set(token2, provider2);
498
+ injector().set(token3, provider3);
499
+
500
+ // WHEN
501
+ let actualError;
502
+ try {
503
+ inject(token3);
504
+ } catch (er) {
505
+ actualError = er;
506
+ }
507
+
508
+ // THEN
509
+ expect(actualError.message).toEqual(
510
+ "Injection failed on Test > TokenFactory > TokenValue\nOrigin: Unable to create new instance from undefined value. Check your provider declaration for TokenValue"
511
+ );
512
+ });
513
+ });
514
+ describe("when provider has Provider as dependencies", () => {
515
+ it("should inject Provider", () => {
516
+ // GIVEN
517
+
518
+ const token = Symbol.for("TokenProvider1");
519
+ injector().add(token, {
520
+ deps: [Provider],
521
+ configuration: {
522
+ test: "test"
523
+ },
524
+ useFactory(provider: any) {
525
+ return {to: provider};
526
+ }
527
+ });
528
+
529
+ // WHEN
530
+ const instance: any = inject(token)!;
531
+
532
+ // THEN
533
+ expect(instance).toEqual({to: injector().getProvider(token)});
534
+ });
535
+ });
536
+ describe("when provider has Configuration as dependencies", () => {
537
+ it("should inject Provider", () => {
538
+ // GIVEN
539
+
540
+ const token = Symbol.for("TokenProvider1");
541
+ injector().add(token, {
542
+ deps: [Configuration],
543
+ useFactory(settings: any) {
544
+ return {to: settings};
545
+ }
546
+ });
547
+
548
+ // WHEN
549
+ const instance: any = inject(token)!;
550
+
551
+ // THEN
552
+ expect(instance).toEqual({to: injector().settings});
553
+ });
554
+ });
555
+ });
556
+ describe("resolveConfiguration()", () => {
557
+ it("should load configuration from each providers", () => {
558
+ // GIVEN
559
+
560
+ injector().settings.set({
561
+ scopes: {
562
+ [ProviderType.VALUE]: ProviderScope.SINGLETON
563
+ }
564
+ });
565
+
566
+ expect(injector().settings.get("scopes")).toEqual({
567
+ [ProviderType.VALUE]: ProviderScope.SINGLETON
568
+ });
569
+
570
+ injector().add(Symbol.for("TOKEN1"), {
571
+ configuration: {
572
+ custom: "config",
573
+ scopes: {
574
+ provider_custom: ProviderScope.SINGLETON
575
+ }
576
+ }
577
+ });
578
+
579
+ injector().add(Symbol.for("TOKEN2"), {
580
+ configuration: {
581
+ scopes: {
582
+ provider_custom_2: ProviderScope.SINGLETON
583
+ }
584
+ }
585
+ });
586
+
587
+ // WHEN
588
+ injector().resolveConfiguration();
589
+ // should load only once the configuration
590
+ injector().resolveConfiguration();
591
+
592
+ // THEN
593
+ expect(injector().settings.get<string>("custom")).toEqual("config");
594
+ expect(injector().settings.get<any>("scopes")).toEqual({
595
+ provider_custom_2: "singleton",
596
+ provider_custom: "singleton",
597
+ value: "singleton"
598
+ });
599
+ });
600
+ it("should load configuration from each providers (with resolvers)", () => {
601
+ // GIVEN
602
+
603
+ injector().add(Symbol.for("TOKEN1"), {
604
+ configuration: {
605
+ custom: {
606
+ config: "1"
607
+ }
608
+ }
609
+ });
610
+
611
+ injector().add(Symbol.for("TOKEN2"), {
612
+ configuration: {
613
+ custom: {
614
+ config2: "1"
615
+ }
616
+ }
617
+ });
618
+
619
+ // WHEN
620
+ injector().resolveConfiguration();
621
+
622
+ // THEN
623
+ expect(injector().settings.get("custom")).toEqual({config: "1", config2: "1"});
624
+ });
625
+ });
626
+ describe("alter()", () => {
627
+ it("should alter value", () => {
628
+ @Injectable()
629
+ class Test {
630
+ $alterValue(value: any) {
631
+ return "alteredValue";
632
+ }
633
+ }
634
+
635
+ vi.spyOn(Test.prototype, "$alterValue");
636
+
637
+ // GIVEN
638
+
639
+ inject<Test>(Test);
640
+
641
+ const service = injector().get<Test>(Test)!;
642
+
643
+ const value = injector().alter("$alterValue", "value");
644
+
645
+ expect(service.$alterValue).toHaveBeenCalledWith("value");
646
+ expect(value).toEqual("alteredValue");
647
+ });
648
+ it("should alter value (factory)", () => {
649
+ registerProvider({
650
+ token: "TOKEN",
651
+ useFactory: () => {
652
+ return {};
653
+ },
654
+ hooks: {
655
+ $alterValue(instance: any, value: any) {
656
+ return "alteredValue";
657
+ }
658
+ }
659
+ });
660
+
661
+ // GIVEN
662
+
663
+ inject<any>("TOKEN");
664
+
665
+ const value = injector().alter("$alterValue", "value");
666
+
667
+ expect(value).toEqual("alteredValue");
668
+ });
669
+ });
670
+ describe("alterAsync()", () => {
671
+ it("should alter value", async () => {
672
+ @Injectable()
673
+ class Test {
674
+ $alterValue(value: any) {
675
+ return Promise.resolve(value + ":alteredValue");
676
+ }
677
+ }
678
+
679
+ vi.spyOn(Test.prototype, "$alterValue");
680
+
681
+ // GIVEN
682
+
683
+ inject<Test>(Test);
684
+
685
+ const value = await injector().alterAsync("$alterValue", "value");
686
+
687
+ expect(value).toEqual("value:alteredValue");
688
+ });
689
+ });
690
+ describe("imports", () => {
691
+ it("should load all provider and override by configuration a provider (use)", async () => {
692
+ @Injectable()
693
+ class TestService {
694
+ get() {
695
+ return "hello";
696
+ }
697
+ }
698
+
699
+ injector().settings.set("imports", [
700
+ {
701
+ token: TestService,
702
+ use: {
703
+ get: vi.fn().mockReturnValue("world")
704
+ }
705
+ }
706
+ ]);
707
+
708
+ await injector().load();
709
+
710
+ const result = injector().get<TestService>(TestService)!.get();
711
+ expect(result).toEqual("world");
712
+ });
713
+ it("should load all provider and override by configuration a provider (useClass)", async () => {
714
+ @Injectable()
715
+ class TestService {
716
+ get() {
717
+ return "hello";
718
+ }
719
+ }
720
+
721
+ @Injectable()
722
+ class FsTestService {
723
+ get() {
724
+ return "fs";
725
+ }
726
+ }
727
+
728
+ injector().settings.set("imports", [
729
+ {
730
+ token: TestService,
731
+ useClass: FsTestService
732
+ }
733
+ ]);
734
+
735
+ await injector().load();
736
+
737
+ const result = injector().get<TestService>(TestService)!.get();
738
+ expect(result).toEqual("fs");
739
+ });
740
+ it("should load all provider and override by configuration a provider (useFactory)", async () => {
741
+ @Injectable()
742
+ class TestService {
743
+ get() {
744
+ return "hello";
745
+ }
746
+ }
747
+
748
+ injector().settings.set("imports", [
749
+ {
750
+ token: TestService,
751
+ useFactory: () => {
752
+ return {
753
+ get() {
754
+ return "world";
755
+ }
756
+ };
757
+ }
758
+ }
759
+ ]);
760
+
761
+ await injector().load();
762
+
763
+ const result = injector().get<TestService>(TestService)!.get();
764
+ expect(result).toEqual("world");
765
+ });
766
+ it("should load all provider and override by configuration a provider (useAsyncFactory)", async () => {
767
+ @Injectable()
768
+ class TestService {
769
+ get() {
770
+ return "hello";
771
+ }
772
+ }
773
+
774
+ injector().settings.set("imports", [
775
+ {
776
+ token: TestService,
777
+ useAsyncFactory: () => {
778
+ return Promise.resolve({
779
+ get() {
780
+ return "world";
781
+ }
782
+ });
783
+ }
784
+ }
785
+ ]);
786
+
787
+ await injector().load();
788
+
789
+ const result = injector().get<TestService>(TestService)!.get();
790
+ expect(result).toEqual("world");
791
+ });
792
+ });
793
+ });