decorator-dependency-injection 1.0.3 → 1.0.4

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.
@@ -1,309 +0,0 @@
1
- import {Factory, Inject, resetMocks, Singleton} from '../index.js'
2
-
3
- describe('Injection via fields', () => {
4
- @Singleton()
5
- class TestSingleton {
6
- static calls = 0
7
-
8
- constructor() {
9
- TestSingleton.calls++
10
- }
11
- }
12
-
13
- it('should inject singleton', () => {
14
- class TestInjection {
15
- @Inject(TestSingleton) testSingleton
16
-
17
- constructor() {
18
- expect(this.testSingleton).toBeInstanceOf(TestSingleton)
19
- expect(TestSingleton.calls).toBe(1)
20
- }
21
- }
22
-
23
- class TestInjection2 {
24
- @Inject(TestSingleton) testSingleton
25
-
26
- constructor() {
27
- expect(this.testSingleton).toBeInstanceOf(TestSingleton)
28
- expect(TestSingleton.calls).toBe(1)
29
- }
30
- }
31
-
32
- new TestInjection()
33
- new TestInjection2()
34
- })
35
-
36
- @Factory()
37
- class TestFactory {
38
- static calls = 0
39
- params
40
-
41
- @Inject(TestSingleton) testSingleton
42
-
43
- constructor(...params) {
44
- TestFactory.calls++
45
- this.params = params
46
- }
47
- }
48
-
49
- afterEach(() => {
50
- TestFactory.calls = 0
51
- TestSingleton.calls = 0
52
- resetMocks()
53
- })
54
-
55
- it('should inject factory', () => {
56
- class TestInjectionFactory {
57
- @Inject(TestFactory) testFactory
58
-
59
- constructor() {
60
- expect(this.testFactory).toBeInstanceOf(TestFactory)
61
- expect(TestFactory.calls).toBe(1)
62
- }
63
- }
64
-
65
- class TestInjectionFactory2 {
66
- @Inject(TestFactory) testFactory
67
-
68
- constructor() {
69
- expect(this.testFactory).toBeInstanceOf(TestFactory)
70
- expect(TestFactory.calls).toBe(2)
71
- }
72
- }
73
-
74
- const result = new TestInjectionFactory()
75
- new TestInjectionFactory2()
76
- expect(result.testFactory.testSingleton).toBeInstanceOf(TestSingleton)
77
- })
78
-
79
- it('should inject factory with parameters', () => {
80
- class TestInjectionFactoryParams {
81
- @Inject(TestFactory, 'param1', 'param2') testFactory
82
-
83
- constructor() {
84
- expect(this.testFactory).toBeInstanceOf(TestFactory)
85
- expect(this.testFactory.params).toEqual(['param1', 'param2'])
86
- }
87
- }
88
-
89
- new TestInjectionFactoryParams()
90
- })
91
-
92
- it('should cache factory instance on repeated accesses', () => {
93
- class TestRepeatedFactoryAccess {
94
- @Inject(TestFactory) testFactory
95
-
96
- constructor() {
97
- const instance1 = this.testFactory
98
- const instance2 = this.testFactory
99
- expect(instance1).toBe(instance2)
100
- }
101
- }
102
-
103
- new TestRepeatedFactoryAccess()
104
- })
105
-
106
- it('should create distinct factory instances for different fields in the same object', () => {
107
- class TestMultipleFactoryInjection {
108
- @Inject(TestFactory) testFactory1
109
- @Inject(TestFactory) testFactory2
110
-
111
- constructor() {
112
- // Access both properties to trigger initialization.
113
- const one = this.testFactory1
114
- const two = this.testFactory2
115
- expect(one).not.toBe(two)
116
- }
117
- }
118
-
119
- new TestMultipleFactoryInjection()
120
- })
121
-
122
- it('should inject the same singleton instance for different fields in the same object', () => {
123
- class TestMultipleSingletonInjection {
124
- @Inject(TestSingleton) testSingleton1
125
- @Inject(TestSingleton) testSingleton2
126
-
127
- constructor() {
128
- // Access both properties to trigger initialization.
129
- const one = this.testSingleton1
130
- const two = this.testSingleton2
131
- expect(one).toBe(two)
132
- }
133
- }
134
-
135
- new TestMultipleSingletonInjection()
136
- })
137
-
138
- @Singleton('named')
139
- class NamedSingleton {
140
- static calls = 0
141
-
142
- constructor() {
143
- NamedSingleton.calls++
144
- }
145
- }
146
-
147
- it('should inject named singleton', () => {
148
- class TestInjectionNamedSingleton {
149
- @Inject('named') namedSingleton
150
-
151
- constructor() {
152
- expect(this.namedSingleton).toBeInstanceOf(NamedSingleton)
153
- expect(NamedSingleton.calls).toBe(1)
154
- }
155
- }
156
-
157
- class TestInjectionNamedSingleton2 {
158
- @Inject('named') namedSingleton
159
-
160
- constructor() {
161
- expect(this.namedSingleton).toBeInstanceOf(NamedSingleton)
162
- expect(NamedSingleton.calls).toBe(1)
163
- }
164
- }
165
-
166
- new TestInjectionNamedSingleton()
167
- new TestInjectionNamedSingleton2()
168
- })
169
-
170
- @Factory('named2')
171
- class NamedFactory {
172
- static calls = 0
173
- params
174
-
175
- constructor(...params) {
176
- NamedFactory.calls++
177
- this.params = params
178
- }
179
- }
180
-
181
- it('should inject named factory', () => {
182
- class TestInjectionNamedFactory {
183
- @Inject('named2') namedFactory
184
-
185
- constructor() {
186
- expect(this.namedFactory).toBeInstanceOf(NamedFactory)
187
- expect(NamedFactory.calls).toBe(1)
188
- }
189
- }
190
-
191
- class TestInjectionNamedFactory2 {
192
- @Inject('named2') namedFactory
193
-
194
- constructor() {
195
- expect(this.namedFactory).toBeInstanceOf(NamedFactory)
196
- expect(NamedFactory.calls).toBe(2)
197
- }
198
- }
199
-
200
- const result = new TestInjectionNamedFactory()
201
- new TestInjectionNamedFactory2()
202
- expect(result.namedFactory.params).toEqual([])
203
- })
204
-
205
- it('should cache named factory instance on repeated accesses', () => {
206
- class TestRepeatedNamedFactoryAccess {
207
- @Inject('named2') namedFactory
208
-
209
- constructor() {
210
- const instance1 = this.namedFactory
211
- const instance2 = this.namedFactory
212
- expect(instance1).toBe(instance2)
213
- }
214
- }
215
-
216
- new TestRepeatedNamedFactoryAccess()
217
- })
218
-
219
- it('should throw if @Inject is applied to a method', () => {
220
- expect(() => {
221
- // noinspection JSUnusedLocalSymbols
222
- class BadInjection {
223
- @Inject('something')
224
- someMethod() {
225
- }
226
- }
227
- }).toThrow()
228
- })
229
-
230
- it('should handle circular dependencies gracefully', () => {
231
- @Singleton()
232
- class A {
233
- @Inject('B') b
234
- }
235
-
236
- @Singleton('B')
237
- class B {
238
- @Inject(A) a
239
- }
240
-
241
- expect(() => new A()).toThrow(/Circular dependency detected.*@InjectLazy/)
242
- })
243
-
244
- it('should throw if decorator is used on non-class object', () => {
245
- expect(() => {
246
- const obj = {}
247
- Inject('something')(obj, 'field')
248
- }).toThrow()
249
- })
250
-
251
- it('should throw a helpful error for eager circular dependencies', () => {
252
- @Factory()
253
- class A2 {
254
- @Inject('B2') b
255
- }
256
-
257
- @Singleton('B2')
258
- class B2 {
259
- @Inject(A2) a
260
- }
261
-
262
- expect(() => new A2()).toThrow(/Circular dependency detected.*@InjectLazy/)
263
- })
264
-
265
- it('should inject into symbol-named fields', () => {
266
- const sym = Symbol('sym')
267
-
268
- @Singleton()
269
- class S {
270
- }
271
-
272
- class Test {
273
- @Inject(S) [sym]
274
- }
275
-
276
- const t = new Test()
277
- expect(t[sym]).toBeInstanceOf(S)
278
- })
279
-
280
- it('should not leak injected properties to prototype', () => {
281
- @Singleton()
282
- class S {
283
- }
284
-
285
- class Test {
286
- @Inject(S) dep
287
- }
288
-
289
- // noinspection JSUnusedLocalSymbols
290
- const t = new Test()
291
- expect(Object.prototype.hasOwnProperty.call(Test.prototype, 'dep')).toBe(false)
292
- })
293
-
294
- it('should handle undefined/null/complex params in factory', () => {
295
- @Factory()
296
- class F {
297
- constructor(...params) {
298
- this.params = params
299
- }
300
- }
301
-
302
- class Test {
303
- @Inject(F, undefined, null, {a: 1}) dep
304
- }
305
-
306
- const t = new Test()
307
- expect(t.dep.params).toEqual([undefined, null, {a: 1}])
308
- })
309
- })
@@ -1,249 +0,0 @@
1
- import {Factory, InjectLazy, resetMocks, Singleton} from '../index.js'
2
-
3
- describe('Lazy Injection via fields', () => {
4
- @Singleton()
5
- class TestLazySingleton {
6
- static calls = 0
7
-
8
- constructor() {
9
- TestLazySingleton.calls++
10
- }
11
- }
12
-
13
- it('should lazily inject singleton', () => {
14
- class TestLazySingletonInjection {
15
- @InjectLazy(TestLazySingleton) testSingleton
16
-
17
- constructor() {
18
- }
19
- }
20
-
21
- const instance = new TestLazySingletonInjection()
22
- expect(TestLazySingleton.calls).toBe(0) // Not constructed until access
23
- const first = instance.testSingleton
24
- expect(first).toBeInstanceOf(TestLazySingleton)
25
- expect(TestLazySingleton.calls).toBe(1)
26
- // Repeated access returns the same instance.
27
- const second = instance.testSingleton
28
- expect(first).toBe(second)
29
- })
30
-
31
- @Factory()
32
- class TestLazyFactory {
33
- static calls = 0
34
- params
35
-
36
- constructor(...params) {
37
- TestLazyFactory.calls++
38
- this.params = params
39
- }
40
- }
41
-
42
- afterEach(() => {
43
- TestLazyFactory.calls = 0
44
- TestLazySingleton.calls = 0
45
- resetMocks()
46
- })
47
-
48
- it('should lazily inject factory with caching on first access per field', () => {
49
- class TestLazyFactoryInjection {
50
- @InjectLazy(TestLazyFactory) testFactory
51
-
52
- constructor() {
53
- }
54
- }
55
-
56
- const inst = new TestLazyFactoryInjection()
57
- expect(TestLazyFactory.calls).toBe(0)
58
- const first = inst.testFactory
59
- expect(first).toBeInstanceOf(TestLazyFactory)
60
- expect(TestLazyFactory.calls).toBe(1)
61
- expect(inst.testFactory).toBe(first)
62
- })
63
-
64
- it('should create distinct factory instances for different fields', () => {
65
- class TestMultipleLazyFactoryInjection {
66
- @InjectLazy(TestLazyFactory) testFactory1
67
- @InjectLazy(TestLazyFactory) testFactory2
68
-
69
- constructor() {
70
- }
71
- }
72
-
73
- const instance = new TestMultipleLazyFactoryInjection()
74
- expect(TestLazyFactory.calls).toBe(0)
75
- const one = instance.testFactory1
76
- const two = instance.testFactory2
77
- expect(one).toBeInstanceOf(TestLazyFactory)
78
- expect(two).toBeInstanceOf(TestLazyFactory)
79
- expect(one).not.toBe(two)
80
- })
81
-
82
- it('should pass constructor parameters when using lazy injection', () => {
83
- class TestLazyFactoryParamsInjection {
84
- @InjectLazy(TestLazyFactory, 'param1', 'param2') testFactory
85
-
86
- constructor() {
87
- }
88
- }
89
-
90
- TestLazyFactory.calls = 0
91
- const instance = new TestLazyFactoryParamsInjection()
92
- expect(TestLazyFactory.calls).toBe(0)
93
- const factoryInst = instance.testFactory
94
- expect(factoryInst).toBeInstanceOf(TestLazyFactory)
95
- expect(factoryInst.params).toEqual(['param1', 'param2'])
96
- })
97
-
98
- it('should not initialize dependency if property is never accessed', () => {
99
- class TestNeverAccess {
100
- @InjectLazy(TestLazySingleton) testSingleton
101
-
102
- constructor() {
103
- }
104
- }
105
-
106
- TestLazySingleton.calls = 0
107
- new TestNeverAccess() // Do not access testSingleton.
108
- expect(TestLazySingleton.calls).toBe(0)
109
- })
110
-
111
- @Singleton('lazyNamedSingleton')
112
- class NamedLazySingleton {
113
- static calls = 0
114
-
115
- constructor() {
116
- NamedLazySingleton.calls++
117
- }
118
- }
119
-
120
- it('should lazily inject named singleton', () => {
121
- class TestLazyNamedSingletonInjection {
122
- @InjectLazy('lazyNamedSingleton') namedSingleton
123
-
124
- constructor() {
125
- }
126
- }
127
-
128
- const instance = new TestLazyNamedSingletonInjection()
129
- expect(NamedLazySingleton.calls).toBe(0)
130
- const first = instance.namedSingleton
131
- expect(first).toBeInstanceOf(NamedLazySingleton)
132
- expect(NamedLazySingleton.calls).toBe(1)
133
- expect(instance.namedSingleton).toBe(first)
134
- })
135
-
136
- @Factory('lazyNamedFactory')
137
- class NamedLazyFactory {
138
- static calls = 0
139
- params
140
-
141
- constructor(...params) {
142
- NamedLazyFactory.calls++
143
- this.params = params
144
- }
145
- }
146
-
147
- it('should lazily inject named factory', () => {
148
- class TestLazyNamedFactoryInjection {
149
- @InjectLazy('lazyNamedFactory') namedFactory
150
-
151
- constructor() {
152
- }
153
- }
154
-
155
- NamedLazyFactory.calls = 0
156
- const instance = new TestLazyNamedFactoryInjection()
157
- expect(NamedLazyFactory.calls).toBe(0)
158
- const first = instance.namedFactory
159
- expect(first).toBeInstanceOf(NamedLazyFactory)
160
- expect(NamedLazyFactory.calls).toBe(1)
161
- expect(instance.namedFactory).toBe(first)
162
- })
163
-
164
- it('should not expose any internal caching artifacts', () => {
165
- class TestLazyEnum {
166
- @InjectLazy(TestLazySingleton) lazyProp
167
-
168
- constructor() {
169
- }
170
- }
171
-
172
- const inst = new TestLazyEnum()
173
- // noinspection JSUnusedLocalSymbols: Force lazy initialization.
174
- const _ = inst.lazyProp
175
- const keys = Object.keys(inst)
176
- expect(keys).toContain('lazyProp')
177
- expect(keys.length).toBe(1)
178
- const symbols = Object.getOwnPropertySymbols(inst)
179
- expect(symbols.length).toBe(0)
180
- })
181
-
182
- it('should throw if @InjectLazy is applied to a method', () => {
183
- expect(() => {
184
- // noinspection JSUnusedLocalSymbols
185
- class BadLazy {
186
- @InjectLazy('something')
187
- someMethod() {
188
- }
189
- }
190
- }).toThrow()
191
- })
192
-
193
- it('should handle circular dependencies (lazy)', () => {
194
- @Singleton()
195
- class A {
196
- @InjectLazy('B') b
197
- }
198
-
199
- @Singleton('B')
200
- class B {
201
- @InjectLazy(A) a
202
- }
203
-
204
- expect(() => new A()).not.toThrow()
205
- })
206
-
207
- it('should inject into symbol-named fields (lazy)', () => {
208
- const sym = Symbol('sym')
209
-
210
- @Singleton()
211
- class S {
212
- }
213
-
214
- class Test {
215
- @InjectLazy(S) [sym]
216
- }
217
-
218
- const t = new Test()
219
- expect(t[sym]).toBeInstanceOf(S)
220
- })
221
-
222
- it('should not leak lazy injected properties to prototype', () => {
223
- @Singleton()
224
- class S {
225
- }
226
-
227
- class Test {
228
- @InjectLazy(S) dep
229
- }
230
-
231
- // noinspection JSUnusedLocalSymbols
232
- const t = new Test()
233
- expect(Object.prototype.hasOwnProperty.call(Test.prototype, 'dep')).toBe(false)
234
- })
235
-
236
- it('should allow circular dependencies with lazy injection', () => {
237
- @Singleton()
238
- class A1 {
239
- @InjectLazy('B1') b
240
- }
241
-
242
- @Factory('B1')
243
- class B1 {
244
- @InjectLazy(A1) a
245
- }
246
-
247
- expect(() => new A1()).not.toThrow()
248
- })
249
- })