Skip to content

Commit

Permalink
Implement asMockFunction, add standard biome config (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
kibertoad authored Jun 23, 2024
1 parent 98bd263 commit 62e4da0
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 80 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

strategy:
matrix:
node-version: [16.x, 18.x, 20.x, 21.x]
node-version: [16.x, 18.x, 20.x, 22.x]

steps:
- name: Checkout Repository
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ Note that this will resolve all non-disabled dependencies within the container,

## Mocking dependencies

Sometimes you may want to intentionally inject objects that do not fully conform to the type definition of an original class. For that you can use `asMockClass` resolver:
Sometimes you may want to intentionally inject objects that do not fully conform to the type definition of an original class. For that you can use `asMockClass` or `asMockFunction` resolvers:

```ts
type DiContainerType = {
Expand Down
53 changes: 20 additions & 33 deletions biome.json
Original file line number Diff line number Diff line change
@@ -1,35 +1,22 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"files": {
"ignore": ["coverage", "dist", "node_modules"]
},
"javascript": {
"formatter": {
"arrowParentheses": "always",
"indentStyle": "space",
"semicolons": "asNeeded",
"trailingComma": "all",
"lineWidth": 100,
"quoteStyle": "single"
}
},
"linter": {
"rules": {
"style": {
"recommended": true,
"noNonNullAssertion": "off"
},
"correctness": {
"recommended": true
},
"suspicious": {
"recommended": true,
"noExplicitAny": "off"
},
"complexity": {
"recommended": true,
"useOptionalChain": "off"
}
}
}
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"extends": ["./node_modules/@kibertoad/biome-config/configs/biome-package.json"],
"overrides": [
{
"include": ["**/*.ts"],
"linter": {
"rules": {
"performance": {
"noBarrelFile": "off"
},
"complexity": {
"useOptionalChain": "off"
},
"suspicious": {
"noExplicitAny": "off"
}
}
}
}
]
}
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export {
AwilixManager,
asMockClass,
asMockFunction,
eagerInject,
asyncInit,
asyncDispose,
Expand Down
9 changes: 9 additions & 0 deletions lib/awilixManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
type Constructor,
type DisposableResolver,
asClass,
asFunction,
} from 'awilix'
import type { FunctionReturning } from 'awilix/lib/container'

declare module 'awilix' {
interface ResolverOptions<T> {
Expand Down Expand Up @@ -34,6 +36,13 @@ export function asMockClass<T = object>(
return asClass(Type as Constructor<T>, opts)
}

export function asMockFunction<T = object>(
fn: FunctionReturning<unknown>,
opts?: BuildResolverOptions<T>,
): BuildResolver<T> & DisposableResolver<T> {
return asFunction(fn as FunctionReturning<T>, opts)
}

export class AwilixManager {
public readonly config: AwilixManagerConfig

Expand Down
71 changes: 36 additions & 35 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
{
"name": "awilix-manager",
"description": "Wrapper over awilix to support more complex use-cases, such as async init and eager injection",
"version": "5.3.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"build:release": "del-cli dist && del-cli coverage && npm run lint && npm run build",
"test": "vitest",
"test:coverage": "npm test -- --coverage",
"lint": "biome check . && tsc --project tsconfig.lint.json --noEmit",
"lint:fix": "biome check --apply .",
"prepublishOnly": "npm run build:release"
},
"peerDependencies": {
"awilix": ">=9.0.0"
},
"devDependencies": {
"@biomejs/biome": "^1.7.3",
"@types/node": "^20.12.13",
"@vitest/coverage-v8": "^1.6.0",
"del-cli": "^5.1.0",
"typescript": "^5.4.5",
"vitest": "^1.6.0"
},
"engines": {
"node": ">=16"
},
"repository": {
"type": "git",
"url": "git://github.com/kibertoad/awilix-manager.git"
},
"keywords": ["init", "async", "eager", "awilix", "di"],
"homepage": "https://github.com/kibertoad/awilix-manager",
"files": ["README.md", "LICENSE", "dist/*"]
"name": "awilix-manager",
"description": "Wrapper over awilix to support more complex use-cases, such as async init and eager injection",
"version": "5.3.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"build:release": "del-cli dist && del-cli coverage && npm run lint && npm run build",
"test": "vitest",
"test:coverage": "npm test -- --coverage",
"lint": "biome check . && tsc --project tsconfig.lint.json --noEmit",
"lint:fix": "biome check --write .",
"prepublishOnly": "npm run build:release"
},
"peerDependencies": {
"awilix": ">=9.0.0"
},
"devDependencies": {
"@biomejs/biome": "^1.8.2",
"@kibertoad/biome-config": "^1.2.0",
"@types/node": "^20.14.8",
"@vitest/coverage-v8": "^1.6.0",
"del-cli": "^5.1.0",
"typescript": "^5.5.2",
"vitest": "^1.6.0"
},
"engines": {
"node": ">=16"
},
"repository": {
"type": "git",
"url": "git://github.com/kibertoad/awilix-manager.git"
},
"keywords": ["init", "async", "eager", "awilix", "di"],
"homepage": "https://github.com/kibertoad/awilix-manager",
"files": ["README.md", "LICENSE", "dist/*"]
}
43 changes: 36 additions & 7 deletions test/awilixManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { Resolver } from 'awilix/lib/resolvers'
import {
AwilixManager,
asMockClass,
asMockFunction,
asyncDispose,
asyncInit,
getByPredicate,
Expand Down Expand Up @@ -112,6 +113,33 @@ describe('asMockClass', () => {
})
})

describe('asMockFunction', () => {
it('Supports passing a mock instance that does not fully implement the real class', () => {
type DiContainerType = {
asyncInitClass: AsyncInitClass
asyncInitClass2: AsyncInitClass
}
const diConfiguration: NameAndRegistrationPair<DiContainerType> = {
asyncInitClass: asClass(AsyncInitClass),
asyncInitClass2: asMockFunction(() => {
return new AsyncDisposeClass()
}),
}

const diContainer = createContainer<DiContainerType>({
injectionMode: 'PROXY',
})

for (const [dependencyKey, dependencyValue] of Object.entries(diConfiguration)) {
diContainer.register(dependencyKey, dependencyValue as Resolver<unknown>)
}

const { asyncInitClass, asyncInitClass2 } = diContainer.cradle
expect(asyncInitClass).toBeInstanceOf(AsyncInitClass)
expect(asyncInitClass2).toBeInstanceOf(AsyncDisposeClass)
})
})

describe('awilixManager', () => {
describe('constructor', () => {
it('throws an error if strictBooleanEnforced is set and undefined is passed', () => {
Expand Down Expand Up @@ -338,7 +366,7 @@ describe('awilixManager', () => {
expect(expectedItemNotFound).toStrictEqual({})
})

it('execute awilixManager.getWithTags on registered dependencies with valid tags', async () => {
it('execute awilixManager.getWithTags on registered dependencies with valid tags', () => {
const diContainer = createContainer({
injectionMode: 'PROXY',
})
Expand Down Expand Up @@ -383,7 +411,7 @@ describe('awilixManager', () => {
})
})

it('does bit execute asyncInit on registered dependencies if disabled', async () => {
it('does not execute asyncInit on registered dependencies if disabled', async () => {
const diContainer = createContainer({
injectionMode: 'PROXY',
})
Expand Down Expand Up @@ -449,7 +477,7 @@ describe('awilixManager', () => {
})
await manager.executeInit()

const { dependency1, dependency2 } = diContainer.cradle
const { dependency1: _1, dependency2: _2 } = diContainer.cradle

expect(isInittedGlobal).toBe(true)
})
Expand Down Expand Up @@ -482,7 +510,7 @@ describe('awilixManager', () => {
})
await manager.executeInit()

const { dependency1, dependency2 } = diContainer.cradle
const { dependency1: _1, dependency2: _2 } = diContainer.cradle

expect(isInittedGlobal).toBe(true)
})
Expand Down Expand Up @@ -534,8 +562,9 @@ describe('awilixManager', () => {
'dependency1',
asClass(AsyncDisposeClass, {
lifetime: 'SINGLETON',
asyncDispose: async (instance) => {
asyncDispose: (instance) => {
instance.isDisposed = true
return Promise.resolve()
},
}),
)
Expand Down Expand Up @@ -669,7 +698,7 @@ describe('awilixManager', () => {

await asyncDispose(diContainer)

const { dependency1, dependency2 } = diContainer.cradle
const { dependency1: _1, dependency2: _2 } = diContainer.cradle

expect(isDisposedGlobal).toBe(true)
})
Expand Down Expand Up @@ -698,7 +727,7 @@ describe('awilixManager', () => {

await asyncDispose(diContainer)

const { dependency1, dependency2 } = diContainer.cradle
const { dependency1: _1, dependency2: _2 } = diContainer.cradle

expect(isDisposedGlobal).toBe(true)
})
Expand Down
6 changes: 3 additions & 3 deletions test/testContext.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { AwilixContainer } from 'awilix'
import type { AwilixContainer, NameAndRegistrationPair } from 'awilix'
import { Lifetime, asClass } from 'awilix'
import type { Resolver } from 'awilix/lib/resolvers'
import '../lib/awilixManager'

export const SINGLETON_CONFIG = { lifetime: Lifetime.SINGLETON }

Expand All @@ -27,7 +27,7 @@ export function registerDependencies(
}
}

type DiConfig = Record<keyof Dependencies, Resolver<any>>
type DiConfig = NameAndRegistrationPair<Dependencies>

export interface Dependencies {
module: SomeModule
Expand Down

0 comments on commit 62e4da0

Please sign in to comment.