@navios/di 0.4.1 → 0.4.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.
@@ -0,0 +1,173 @@
1
+ import { beforeEach, describe, expect, it } from 'vitest'
2
+
3
+ import { Injectable } from '../../decorators/injectable.decorator.mjs'
4
+ import { InjectionToken } from '../../injection-token.mjs'
5
+ import { inject } from '../../injector.mjs'
6
+ import { TestContainer } from '../test-container.mjs'
7
+
8
+ describe('TestContainer', () => {
9
+ let container: TestContainer
10
+
11
+ beforeEach(() => {
12
+ container = new TestContainer()
13
+ })
14
+
15
+ describe('clear method', () => {
16
+ it('should clear all instances from the container', () => {
17
+ // This test verifies that the clear method exists and can be called
18
+ expect(() => container.clear()).not.toThrow()
19
+ })
20
+ })
21
+
22
+ describe('bind method', () => {
23
+ it('should create a TestBindingBuilder', () => {
24
+ const token = InjectionToken.create<string>('test-token')
25
+ const builder = container.bind(token)
26
+ expect(builder).toBeDefined()
27
+ expect(builder).toHaveProperty('toValue')
28
+ expect(builder).toHaveProperty('toClass')
29
+ })
30
+
31
+ it('should allow chaining bind operations', () => {
32
+ const token = InjectionToken.create<string>('test-token')
33
+ const result = container.bind(token).toValue('test-value')
34
+ expect(result).toBe(container)
35
+ })
36
+
37
+ it('should bind value and retrieve it correctly', async () => {
38
+ const token = InjectionToken.create<string>('test-token')
39
+ const testValue = 'test-value'
40
+
41
+ container.bind(token).toValue(testValue)
42
+
43
+ const retrievedValue = await container.get(token)
44
+ expect(retrievedValue).toBe(testValue)
45
+ })
46
+
47
+ it('should bind class and retrieve instance correctly', async () => {
48
+ @Injectable()
49
+ class TestService {
50
+ getValue() {
51
+ return 'test-service-value'
52
+ }
53
+ }
54
+
55
+ const token = InjectionToken.create<TestService>('test-service')
56
+
57
+ container.bind(token).toClass(TestService)
58
+
59
+ const instance = await container.get(token)
60
+ expect(instance).toBeInstanceOf(TestService)
61
+ expect(instance.getValue()).toBe('test-service-value')
62
+ })
63
+ })
64
+
65
+ describe('bindValue method', () => {
66
+ it('should bind a value to a token', () => {
67
+ const token = InjectionToken.create<string>('test-token')
68
+ const result = container.bindValue(token, 'test-value')
69
+ expect(result).toBe(container)
70
+ })
71
+
72
+ it('should bind value and retrieve it correctly', async () => {
73
+ const token = InjectionToken.create<number>('number-token')
74
+ const testValue = 42
75
+
76
+ container.bindValue(token, testValue)
77
+
78
+ const retrievedValue = await container.get(token)
79
+ expect(retrievedValue).toBe(testValue)
80
+ })
81
+
82
+ it('should bind object value and retrieve it correctly', async () => {
83
+ interface TestConfig {
84
+ apiUrl: string
85
+ timeout: number
86
+ }
87
+
88
+ const token = InjectionToken.create<TestConfig>('config-token')
89
+ const testConfig: TestConfig = {
90
+ apiUrl: 'https://api.example.com',
91
+ timeout: 5000,
92
+ }
93
+
94
+ container.bindValue(token, testConfig)
95
+
96
+ const retrievedConfig = await container.get(token)
97
+ expect(retrievedConfig).toEqual(testConfig)
98
+ expect(retrievedConfig.apiUrl).toBe('https://api.example.com')
99
+ expect(retrievedConfig.timeout).toBe(5000)
100
+ })
101
+ })
102
+
103
+ describe('bindClass method', () => {
104
+ it('should bind a class to a token', () => {
105
+ class TestClass {}
106
+ const token = InjectionToken.create<TestClass>('test-token')
107
+ const result = container.bindClass(token, TestClass)
108
+ expect(result).toBe(container)
109
+ })
110
+
111
+ it('should bind class and retrieve instance correctly', async () => {
112
+ @Injectable()
113
+ class UserService {
114
+ private users: string[] = ['alice', 'bob']
115
+
116
+ getUsers() {
117
+ return this.users
118
+ }
119
+
120
+ addUser(user: string) {
121
+ this.users.push(user)
122
+ }
123
+ }
124
+
125
+ const token = InjectionToken.create<UserService>('user-service')
126
+
127
+ container.bindClass(token, UserService)
128
+
129
+ const instance = await container.get(token)
130
+ expect(instance).toBeInstanceOf(UserService)
131
+ expect(instance.getUsers()).toEqual(['alice', 'bob'])
132
+
133
+ instance.addUser('charlie')
134
+ expect(instance.getUsers()).toEqual(['alice', 'bob', 'charlie'])
135
+ })
136
+
137
+ it('should bind class with dependencies and retrieve instance correctly', async () => {
138
+ @Injectable()
139
+ class DatabaseService {
140
+ connect() {
141
+ return 'connected'
142
+ }
143
+ }
144
+
145
+ @Injectable()
146
+ class UserRepository {
147
+ db = inject(DatabaseService)
148
+
149
+ findUser(id: string) {
150
+ return `User ${id} from ${this.db.connect()}`
151
+ }
152
+ }
153
+
154
+ const dbToken = InjectionToken.create<DatabaseService>('db-service')
155
+ const userRepoToken = InjectionToken.create<UserRepository>('user-repo')
156
+
157
+ container.bindClass(dbToken, DatabaseService)
158
+ container.bindClass(userRepoToken, UserRepository)
159
+
160
+ const userRepo = await container.get(userRepoToken)
161
+ expect(userRepo).toBeInstanceOf(UserRepository)
162
+ expect(userRepo.findUser('123')).toBe('User 123 from connected')
163
+ })
164
+ })
165
+
166
+ describe('createChild method', () => {
167
+ it('should create a new TestContainer instance', () => {
168
+ const child = container.createChild()
169
+ expect(child).toBeInstanceOf(TestContainer)
170
+ expect(child).not.toBe(container)
171
+ })
172
+ })
173
+ })
@@ -0,0 +1 @@
1
+ export * from './test-container.mjs'
@@ -0,0 +1,120 @@
1
+ import type { ClassType, InjectionToken } from '../injection-token.mjs'
2
+ import type { Registry } from '../registry.mjs'
3
+ import type { Injectors } from '../utils/index.mjs'
4
+
5
+ import { Container } from '../container.mjs'
6
+ import { Injectable } from '../decorators/injectable.decorator.mjs'
7
+ import { InjectableScope, InjectableType } from '../enums/index.mjs'
8
+ import { globalRegistry } from '../registry.mjs'
9
+ import { getInjectableToken } from '../utils/index.mjs'
10
+
11
+ /**
12
+ * A binding builder for the TestContainer that allows chaining binding operations.
13
+ */
14
+ export class TestBindingBuilder<T> {
15
+ constructor(
16
+ private readonly container: TestContainer,
17
+ private readonly token: InjectionToken<T, any>,
18
+ ) {}
19
+
20
+ /**
21
+ * Binds the token to a specific value.
22
+ * This is useful for testing with mock values or constants.
23
+ * @param value The value to bind to the token
24
+ */
25
+ toValue(value: T): TestContainer {
26
+ const instanceName = this.container
27
+ .getServiceLocator()
28
+ .getInstanceIdentifier(this.token)
29
+ this.container
30
+ .getServiceLocator()
31
+ .getManager()
32
+ .storeCreatedHolder(
33
+ instanceName,
34
+ value,
35
+ InjectableType.Class,
36
+ InjectableScope.Singleton,
37
+ )
38
+ return this.container
39
+ }
40
+
41
+ /**
42
+ * Binds the token to a class constructor.
43
+ * @param target The class constructor to bind to
44
+ */
45
+ toClass(target: ClassType): TestContainer {
46
+ this.container['registry'].set(
47
+ this.token,
48
+ InjectableScope.Singleton,
49
+ target,
50
+ InjectableType.Class,
51
+ )
52
+ return this.container
53
+ }
54
+ }
55
+
56
+ /**
57
+ * TestContainer extends the base Container with additional methods useful for testing.
58
+ * It provides a simplified API for binding values and classes during test setup.
59
+ */
60
+ @Injectable()
61
+ export class TestContainer extends Container {
62
+ constructor(
63
+ registry: Registry = globalRegistry,
64
+ logger: Console | null = null,
65
+ injectors: Injectors = undefined as any,
66
+ ) {
67
+ super(registry, logger, injectors)
68
+ }
69
+
70
+ /**
71
+ * Creates a binding builder for the given token.
72
+ * This allows chaining binding operations like bind(Token).toValue(value).
73
+ * @param token The injection token to bind
74
+ * @returns A TestBindingBuilder for chaining binding operations
75
+ */
76
+ bind<T>(token: ClassType): TestBindingBuilder<T>
77
+ bind<T>(token: InjectionToken<T, any>): TestBindingBuilder<T>
78
+ bind(token: any): TestBindingBuilder<any> {
79
+ let realToken = token
80
+ if (typeof token === 'function') {
81
+ realToken = getInjectableToken(token)
82
+ }
83
+ return new TestBindingBuilder(this, realToken)
84
+ }
85
+
86
+ /**
87
+ * Binds a value directly to a token.
88
+ * This is a convenience method equivalent to bind(token).toValue(value).
89
+ * @param token The injection token to bind
90
+ * @param value The value to bind to the token
91
+ * @returns The TestContainer instance for chaining
92
+ */
93
+ bindValue<T>(token: ClassType, value: T): TestContainer
94
+ bindValue<T>(token: InjectionToken<T, any>, value: T): TestContainer
95
+ bindValue(token: any, value: any): TestContainer {
96
+ return this.bind(token).toValue(value)
97
+ }
98
+
99
+ /**
100
+ * Binds a class to a token.
101
+ * This is a convenience method equivalent to bind(token).toClass(target).
102
+ * @param token The injection token to bind
103
+ * @param target The class constructor to bind to
104
+ * @returns The TestContainer instance for chaining
105
+ */
106
+ bindClass(token: ClassType, target: ClassType): TestContainer
107
+ bindClass<T>(token: InjectionToken<T, any>, target: ClassType): TestContainer
108
+ bindClass(token: any, target: any): TestContainer {
109
+ return this.bind(token).toClass(target)
110
+ }
111
+
112
+ /**
113
+ * Creates a new TestContainer instance with the same configuration.
114
+ * This is useful for creating isolated test containers.
115
+ * @returns A new TestContainer instance
116
+ */
117
+ createChild(): TestContainer {
118
+ return new TestContainer(this.registry, this.logger, this.injectors)
119
+ }
120
+ }
package/tsup.config.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { defineConfig } from 'tsup'
2
2
 
3
3
  export default defineConfig({
4
- entry: ['src/index.mts'],
4
+ entry: ['src/index.mts', 'src/testing/index.mts'],
5
5
  outDir: 'lib',
6
6
  format: ['esm', 'cjs'],
7
7
  clean: true,