Files
TSinjex/src/__tests__/Decorators.spec.ts
Max P. c6e9fbd2a3 feat!: switch to native ESM with NodeNext module resolution and .js import paths
BREAKING CHANGE: Consumers must use ESM-compatible environments.
All import paths now include .js extensions.
CommonJS (require) is no longer supported.
2025-04-02 21:52:36 +02:00

395 lines
13 KiB
TypeScript

/* istanbul ignore file */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Inject } from 'src/decorators/Inject.js';
import { DependencyResolutionError } from 'src/interfaces/Exceptions.js';
import { ForceConstructor } from 'src/types/GenericContructor.js';
import { ITSinjex_, ITSinjex } from '../interfaces/ITSinjex.js';
/**
* Test the Inject decorator.
* @param Container The implementation to test.
* @param inject The Inject decorator to test.
*/
export function test_InjectDecorator(
Container: ITSinjex_,
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
inject: Function,
): void {
describe('Inject Decorator Tests', () => {
let container: ITSinjex;
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(Container as any)['_instance'] = undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(Container as any)['_dependencies'] = undefined;
container = Container.getInstance();
});
it('should inject dependency when necessary is true', () => {
container.register('MockDependencyIdentifier', {
value: 'test-value',
});
class TestClass {
@Inject('MockDependencyIdentifier')
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
}
const instance = new TestClass();
expect(instance.getDependency().value).toBe('test-value');
});
it('should inject dependency and run initializer', () => {
container.register('MockDependencyIdentifier', {
value: 'test-value',
});
class TestClass {
@Inject('MockDependencyIdentifier', (x: string) => {
(x as unknown as { value: string }).value =
'test-value-init';
return x;
})
dependency!: any;
public getDependency() {
return this.dependency;
}
}
const instance = new TestClass();
expect(instance.getDependency().value).toBe('test-value-init');
});
it('should throw an error when necessary is true and the initializer throws an error', () => {
let _error: Error | undefined = undefined;
container.register('InitThrowDependencie', {
value: 'test-value',
});
try {
class TestClass {
@Inject(
'InitThrowDependencie',
() => {
throw new Error('Initializer error');
},
true,
)
dependency!: any;
public getDependency() {
return this.dependency;
}
}
const _instance = new TestClass();
console.log(_instance.getDependency());
} catch (error) {
_error = error;
}
expect(_error).toBeInstanceOf(Error);
});
it('should throw an error when necessary is true and dependency is not found', () => {
let _error: Error | undefined = undefined;
try {
class TestClass {
@Inject('NonExistentDependencyIdentifier')
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
}
const _instance = new TestClass();
console.log(_instance.getDependency());
} catch (error) {
_error = error;
}
expect(_error).toBeInstanceOf(DependencyResolutionError);
});
it('should replace the property with the resolved dependency', () => {
container.register('MockDependencyIdentifier', {
value: 'test-value',
});
class TestClass {
@Inject('MockDependencyIdentifier')
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
public isDependencyTypeofFunction() {
return typeof this._dependency === 'function';
}
}
const instance = new TestClass();
expect(instance.getDependency().value).toBe('test-value');
expect(instance.isDependencyTypeofFunction()).toBe(false);
expect(instance.getDependency().value).toBe('test-value');
});
it('should use a empty initializer when none is provided but true', () => {
container.register(
'MockDependencyIdentifier',
class X {
public value: string = 'test-value';
constructor() {}
},
);
class TestClass {
@Inject('MockDependencyIdentifier', true)
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
}
const instance = new TestClass();
expect(instance.getDependency().value).toBe('test-value');
});
it('should throw an error when the dependency has no instantiation method', () => {
container.register('MockDependencyIdentifier', {
value: 'test-value',
});
class TestClass {
@Inject('MockDependencyIdentifier', true)
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
}
expect(() => {
const instance = new TestClass();
instance.getDependency();
}).toThrow(new RegExp('No instantiation method found for.*'));
});
it('should not throw an error when the dependency has no instantiation method if not necessary', () => {
container.register('MockDependencyIdentifier', {
value: 'test-value',
});
class TestClass {
@Inject('MockDependencyIdentifier', true, false)
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
}
expect(() => {
const instance = new TestClass();
instance.getDependency();
}).not.toThrow(new RegExp('No instantiation method found for.*'));
});
it('should throw an error when the dependency cannot be resolved', () => {
container.register('MockDependencyIdentifier', null);
class TestClass {
@Inject('MockDependencyIdentifier', true)
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
}
expect(() => {
const instance = new TestClass();
instance.getDependency();
}).toThrow(new RegExp('.*could not be resolved.*'));
});
it('should not throw an error when the dependency cannot be resolved if not necessary', () => {
container.register('MockDependencyIdentifier', null);
class TestClass {
@Inject('MockDependencyIdentifier', true, false)
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
}
expect(() => {
const instance = new TestClass();
instance.getDependency();
}).not.toThrow(new RegExp('.*could not be resolved.*'));
});
});
}
export function test_RegisterDecorator(
Container: ITSinjex_,
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
register: Function,
): void {
describe('Register Decorator Tests', () => {
let container: ITSinjex;
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(Container as any)['_instance'] = undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(Container as any)['_dependencies'] = undefined;
container = Container.getInstance();
});
it('should register a dependency', () => {
@register('MockDependencyIdentifier')
class TestClass {
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
}
expect(container.resolve('MockDependencyIdentifier')).toBe(
TestClass,
);
});
});
}
export function test_RegisterInstanceDecorator(
Container: ITSinjex_,
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
registerInstance: Function,
mode: 'instance' | 'standalone' = 'standalone',
): void {
describe('RegisterInstance Decorator Tests', () => {
let container: ITSinjex;
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(Container as any)['_instance'] = undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(Container as any)['_dependencies'] = undefined;
container = Container.getInstance();
});
it('should register an instance of a dependency', () => {
@registerInstance(
'InstanceIdentifier',
mode === 'instance' ? 'instance' : undefined,
)
class TestClass {
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
public mark: string = 'instance';
}
expect(
container.resolve<TestClass>('InstanceIdentifier').mark,
).toBe('instance');
});
it('should register an instance of a dependency an run the init function', () => {
@registerInstance(
'InstanceIdentifier',
(x: ForceConstructor<TestClass>) => {
const instance = new x();
instance.mark = 'init';
return instance;
},
)
class TestClass {
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
public mark: string = 'instance';
}
expect(
container.resolve<TestClass>('InstanceIdentifier').mark,
).toBe('init');
});
it('should register an instance of a dependency and get it on set', () => {
@registerInstance(
'InstanceIdentifier',
mode === 'instance' ? 'instance' : undefined,
)
class TestClass {
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
public mark: string = 'instance';
public test: string = 'test';
}
container.resolve<TestClass>('InstanceIdentifier').test = 'test2';
expect(
container.resolve<TestClass>('InstanceIdentifier').test,
).toBe('test2');
});
it('should register an instance of a dependency an run the init function on set', () => {
@registerInstance(
'InstanceIdentifier',
(x: ForceConstructor<TestClass>) => {
const instance = new x();
instance.mark = 'init';
return instance;
},
)
class TestClass {
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
public mark: string = 'instance';
public test: string = 'test';
}
container.resolve<TestClass>('InstanceIdentifier').test = 'test2';
expect(
container.resolve<TestClass>('InstanceIdentifier').test,
).toBe('test2');
});
});
}