-
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(core): enhance dependency handling and error management
- Adopt a dependencies container approach over the previous dependencies map. - Introduce custom Automock errors for better error clarity. - Refine the `UnitReference` interface for improved type safety. - Utilize `require` with default in the package resolver. - Add integration tests to validate new changes. - Enhance JSDoc comments for better clarity. - Update terminology: Replace "primitive" with "constant value". - Remove redundant warning about undefined dependency values. - Adjust error messages for more accurate feedback on invalid dependencies. - Introduce error handling for invalid dependency identifiers in `.mock()`. BREAKING CHANGE: - Transition from native `Error` to custom Automock errors - Update `UnitReference.get()` to return both `ConstantValue` and `StubbedInstance`.
- Loading branch information
Showing
19 changed files
with
840 additions
and
500 deletions.
There are no files selected for viewing
34 changes: 34 additions & 0 deletions
34
packages/core/__test__/adaper-package-resolving.integration.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { PackageResolver } from '../src/services/package-resolver'; | ||
|
||
describe('Automock Adapter Package Resolving Integration Test', () => { | ||
let packageResolver: PackageResolver; | ||
|
||
describe('Resolving an adapter with default export', () => { | ||
beforeAll(() => { | ||
packageResolver = new PackageResolver( | ||
{ test: './assets/test-adapter' }, | ||
{ resolve: require.resolve, require } | ||
); | ||
}); | ||
|
||
it('should successfully resolve the adapter package', () => { | ||
const adapter = packageResolver.resolveCorrespondingAdapter(); | ||
expect(adapter.inspect({} as never)).toBe('success'); | ||
}); | ||
}); | ||
|
||
describe('Resolving an adapter with no default export', () => { | ||
beforeAll(() => { | ||
packageResolver = new PackageResolver( | ||
{ test: './assets/invalid-adapter' }, | ||
{ resolve: require.resolve, require } | ||
); | ||
}); | ||
|
||
it('should failed resolving the adapter package and throw an error', () => { | ||
expect(() => packageResolver.resolveCorrespondingAdapter()).toThrow( | ||
new Error('Adapter has no default export') | ||
); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import isEqual from 'lodash.isequal'; | ||
import { | ||
AutomockDependenciesAdapter, | ||
ClassInjectable, | ||
IdentifierMetadata, | ||
UndefinedDependency, | ||
WithMetadata, | ||
} from '@automock/common'; | ||
import { Type } from '@automock/types'; | ||
import { normalizeIdentifier } from '../../src/normalize-identifier.stastic'; | ||
|
||
interface Printer { | ||
print(): string; | ||
} | ||
|
||
export class ArbitraryClassOne { | ||
print(): string { | ||
return this.constructor.name; | ||
} | ||
} | ||
|
||
export class ArbitraryClassTwo { | ||
print(): string { | ||
return this.constructor.name; | ||
} | ||
} | ||
|
||
export class ArbitraryClassThree { | ||
print(): string { | ||
return this.constructor.name; | ||
} | ||
} | ||
|
||
export class ArbitraryClassFour { | ||
print(): string { | ||
return this.constructor.name; | ||
} | ||
} | ||
|
||
export class ArbitraryClassFive { | ||
print(): string { | ||
return this.constructor.name; | ||
} | ||
} | ||
|
||
export class ArbitraryClassSix { | ||
print(): string { | ||
return this.constructor.name; | ||
} | ||
} | ||
export class ArbitraryClassSeven { | ||
print(): string { | ||
return this.constructor.name; | ||
} | ||
} | ||
|
||
export class ClassFromToken { | ||
print(): string { | ||
return this.constructor.name; | ||
} | ||
} | ||
|
||
export class StubbedClass { | ||
print(): string { | ||
return 'Stubbed'; | ||
} | ||
} | ||
|
||
/** | ||
* We create the ClassUnderTest with no decorators because the reflection | ||
* is being mocked in this integration test. But what we do want to check | ||
* is that all the class properties have been initiated properly, as well | ||
* as the constructor parameters. The list of injectables from the mocked | ||
* adapter should match exactly the order of the parameters in the constructor, | ||
* as we verify this in the integration test. | ||
*/ | ||
export class ClassUnderTest { | ||
public arbitraryThree: ArbitraryClassThree; | ||
public arbitraryFour: ArbitraryClassFour; | ||
public arbitraryProperty: string; | ||
public withMetadata: StubbedClass; | ||
public withMetadataSecond: StubbedClass; | ||
public arbitrary: StubbedClass; | ||
|
||
public constructor( | ||
private readonly one: Printer, | ||
private readonly two: Printer, | ||
private readonly twoSecond: Printer, | ||
private readonly five: Printer, | ||
private readonly sixWithToken: Printer, | ||
private readonly tokenUndefined: Printer, | ||
private readonly justToken: Printer | ||
) {} | ||
|
||
public getArguments() { | ||
return [ | ||
this.one, | ||
this.two, | ||
this.twoSecond, | ||
this.five, | ||
this.sixWithToken, | ||
this.tokenUndefined, | ||
this.justToken, | ||
]; | ||
} | ||
|
||
public getProperties() { | ||
return Object.keys(this); | ||
} | ||
} | ||
|
||
export const symbolIdentifier = Symbol.for('TOKEN_METADATA'); | ||
|
||
const classInjectables: ClassInjectable[] = [ | ||
{ identifier: ArbitraryClassOne, value: ArbitraryClassOne, type: 'PARAM' }, | ||
// We put the same class twice to test that the mocker, it is not a mistake | ||
{ identifier: ArbitraryClassTwo, value: ArbitraryClassTwo, type: 'PARAM' }, | ||
{ identifier: ArbitraryClassTwo, value: ArbitraryClassTwo, type: 'PARAM' }, | ||
{ identifier: ArbitraryClassFive, value: UndefinedDependency, type: 'PARAM' }, | ||
{ | ||
identifier: 'ArbitraryClassSix', | ||
metadata: { metadataKey: 'value' }, | ||
value: ArbitraryClassSix, | ||
type: 'PARAM', | ||
} as WithMetadata<never>, | ||
{ identifier: 'TOKEN_WITH_UNDEFINED', value: UndefinedDependency, type: 'PARAM' }, | ||
{ identifier: 'TOKEN', value: ClassFromToken, type: 'PARAM' }, | ||
{ | ||
type: 'PROPERTY', | ||
property: { key: 'arbitraryThree' }, | ||
identifier: ArbitraryClassThree, | ||
value: ArbitraryClassThree, | ||
}, | ||
{ | ||
type: 'PROPERTY', | ||
property: { key: 'withMetadata' }, | ||
metadata: { key: 'value' }, | ||
identifier: ArbitraryClassSeven, | ||
value: ArbitraryClassSeven, | ||
} as ClassInjectable, | ||
{ | ||
type: 'PROPERTY', | ||
property: { key: 'withMetadataSecond' }, | ||
metadata: { key: 'value' }, | ||
identifier: symbolIdentifier, | ||
value: ArbitraryClassSeven, | ||
} as ClassInjectable, | ||
{ | ||
type: 'PROPERTY', | ||
property: { key: 'arbitraryFour' }, | ||
identifier: ArbitraryClassFour, | ||
value: ArbitraryClassFour, | ||
}, | ||
{ | ||
type: 'PROPERTY', | ||
property: { key: 'arbitraryProperty' }, | ||
identifier: 'STRING_TOKEN', | ||
value: String, | ||
}, | ||
{ | ||
type: 'PROPERTY', | ||
property: { key: 'arbitrary' }, | ||
identifier: 'ANOTHER_TOKEN', | ||
value: String, | ||
}, | ||
]; | ||
|
||
export const FakeAdapter: AutomockDependenciesAdapter = { | ||
inspect: () => { | ||
return { | ||
list: () => classInjectables, | ||
resolve( | ||
identifier: Type | string, | ||
metadata?: IdentifierMetadata | ||
): ClassInjectable | undefined { | ||
return classInjectables.find((injectable: WithMetadata<never>) => { | ||
const subject = normalizeIdentifier(identifier, metadata); | ||
const toFind = normalizeIdentifier(injectable.identifier, injectable.metadata); | ||
|
||
return isEqual(toFind, subject); | ||
}); | ||
}, | ||
}; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { AutomockDependenciesAdapter, InjectablesRegistry } from '@automock/common'; | ||
|
||
export = { | ||
inspect(): InjectablesRegistry { | ||
return 'not-reachable' as unknown as InjectablesRegistry; | ||
}, | ||
} as AutomockDependenciesAdapter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { AutomockDependenciesAdapter, InjectablesRegistry } from '@automock/common'; | ||
|
||
export default { | ||
inspect(): InjectablesRegistry { | ||
return 'success' as unknown as InjectablesRegistry; | ||
}, | ||
} as AutomockDependenciesAdapter; |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.