-
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): add injection token factories (#2687)
- Loading branch information
Showing
15 changed files
with
472 additions
and
0 deletions.
There are no files selected for viewing
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,41 @@ | ||
import { faker } from '@faker-js/faker/locale/en_US'; | ||
|
||
import { DaffConfigInjectionToken } from '@daffodil/core'; | ||
|
||
import { createConfigInjectionToken } from './config.factory'; | ||
|
||
interface Config { | ||
field: string; | ||
other: string; | ||
} | ||
|
||
describe('@daffodil/core | createConfigInjectionToken', () => { | ||
let name: string; | ||
let value: number; | ||
let defaultConfig: Config; | ||
|
||
let result: DaffConfigInjectionToken<Config>; | ||
|
||
beforeEach(() => { | ||
name = faker.random.word(); | ||
defaultConfig = { | ||
field: faker.random.word(), | ||
other: faker.random.word(), | ||
}; | ||
result = createConfigInjectionToken(defaultConfig, name); | ||
}); | ||
|
||
it('should return a token', () => { | ||
expect(result.token.toString()).toContain(name); | ||
}); | ||
|
||
it('should return a provider that spreads in passed values with the default', () => { | ||
const val = faker.random.word(); | ||
const res = result.provider({ | ||
field: val, | ||
}); | ||
expect(res.provide).toEqual(result.token); | ||
expect(res.useValue.field).toEqual(val); | ||
expect(res.useValue.other).toEqual(defaultConfig.other); | ||
}); | ||
}); |
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,38 @@ | ||
import { InjectionToken } from '@angular/core'; | ||
|
||
import { DaffConfigInjectionToken } from './config.type'; | ||
import { | ||
TokenDesc, | ||
TokenOptions, | ||
} from './token-constuctor-params.type'; | ||
|
||
/** | ||
* Creates an injection token/provider pair for a DI token that holds a configuration. | ||
* | ||
* See {@link DaffConfigInjectionToken}. | ||
*/ | ||
export const createConfigInjectionToken = <T = unknown>( | ||
defaultConfig: T, | ||
desc: TokenDesc<T>, | ||
options?: Partial<TokenOptions<T>>, | ||
): DaffConfigInjectionToken<T> => { | ||
const token = new InjectionToken<T>( | ||
desc, | ||
{ | ||
factory: () => defaultConfig, | ||
...options, | ||
}, | ||
); | ||
const provider = <R extends T = T>(config: Partial<R>) => ({ | ||
provide: token, | ||
useValue: { | ||
...defaultConfig, | ||
...config, | ||
}, | ||
}); | ||
|
||
return { | ||
token, | ||
provider, | ||
}; | ||
}; |
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,22 @@ | ||
import { | ||
InjectionToken, | ||
ValueProvider, | ||
} from '@angular/core'; | ||
|
||
/** | ||
* A injection token to hold and provide a config value. | ||
*/ | ||
export interface DaffConfigInjectionToken<T = unknown> { | ||
/** | ||
* The injection token. | ||
* Its default value is the default config passed during token creation. | ||
*/ | ||
token: InjectionToken<T>; | ||
|
||
/** | ||
* A helper function to provide a value to the token. | ||
* It will shallow merge the passed config with the default config | ||
* with the passed config keys taking precedence. | ||
*/ | ||
provider: <R extends T = T>(config: Partial<R>) => ValueProvider; | ||
} |
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,71 @@ | ||
import { TestBed } from '@angular/core/testing'; | ||
import { faker } from '@faker-js/faker/locale/en_US'; | ||
|
||
import { DaffMultiInjectionToken } from '@daffodil/core'; | ||
|
||
import { createMultiInjectionToken } from './multi.factory'; | ||
|
||
describe('@daffodil/core | createMultiInjectionToken', () => { | ||
let name: string; | ||
let values: Array<number>; | ||
|
||
let result: DaffMultiInjectionToken<number>; | ||
|
||
beforeEach(() => { | ||
name = faker.random.word(); | ||
values = [ | ||
faker.datatype.number(), | ||
faker.datatype.number(), | ||
]; | ||
result = createMultiInjectionToken(name); | ||
}); | ||
|
||
it('should return a token', () => { | ||
expect(result.token.toString()).toContain(name); | ||
}); | ||
|
||
it('should return a provider', () => { | ||
const res = result.provider(...values); | ||
values.forEach((value, i) => { | ||
expect(res[i].provide).toEqual(result.token); | ||
expect(res[i].useValue).toEqual(value); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('@daffodil/core | createMultiInjectionToken | Integration', () => { | ||
let name: string; | ||
let values: Array<number>; | ||
|
||
let result: DaffMultiInjectionToken<number>; | ||
|
||
beforeEach(() => { | ||
name = faker.random.word(); | ||
values = [ | ||
faker.datatype.number(), | ||
faker.datatype.number(), | ||
]; | ||
|
||
result = createMultiInjectionToken(name); | ||
}); | ||
|
||
describe('when values are provided', () => { | ||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
providers: [ | ||
result.provider(...values), | ||
], | ||
}); | ||
}); | ||
|
||
it('should inject the values', () => { | ||
expect(TestBed.inject(result.token)).toEqual(values); | ||
}); | ||
}); | ||
|
||
describe('when values are not provided', () => { | ||
it('should inject an empty array', () => { | ||
expect(TestBed.inject(result.token)).toEqual([]); | ||
}); | ||
}); | ||
}); |
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,39 @@ | ||
import { InjectionToken } from '@angular/core'; | ||
|
||
import { DaffMultiInjectionToken } from './multi.type'; | ||
import { | ||
TokenDesc, | ||
TokenOptions, | ||
} from './token-constuctor-params.type'; | ||
|
||
// having a single instance of the default factory | ||
// will hopefully reduce memory footprint | ||
const defaultFactory = () => []; | ||
|
||
/** | ||
* Creates an injection token/provider pair for a multi valued DI token. | ||
* | ||
* See {@link DaffMultiInjectionToken}. | ||
*/ | ||
export const createMultiInjectionToken = <T = unknown>( | ||
desc: TokenDesc<Array<T>>, | ||
options?: Partial<TokenOptions<Array<T>>>, | ||
): DaffMultiInjectionToken<T> => { | ||
const token = new InjectionToken<Array<T>>( | ||
desc, | ||
{ | ||
factory: defaultFactory, | ||
...options, | ||
}, | ||
); | ||
const provider = <R extends T = T>(...values: Array<R>) => values.map((value) => ({ | ||
provide: token, | ||
useValue: value, | ||
multi: true, | ||
})); | ||
|
||
return { | ||
token, | ||
provider, | ||
}; | ||
}; |
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,20 @@ | ||
import { | ||
InjectionToken, | ||
ValueProvider, | ||
} from '@angular/core'; | ||
|
||
/** | ||
* A injection token to hold and provide multiple values. | ||
*/ | ||
export interface DaffMultiInjectionToken<T = unknown> { | ||
/** | ||
* The injection token. | ||
* Its default value is an empty array. | ||
*/ | ||
token: InjectionToken<Array<T>>; | ||
|
||
/** | ||
* A helper function to provide values to the token. | ||
*/ | ||
provider: <R extends T = T>(...values: Array<R>) => Array<ValueProvider>; | ||
} |
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,8 @@ | ||
export * from './single.type'; | ||
export * from './single.factory'; | ||
export * from './multi.type'; | ||
export * from './multi.factory'; | ||
export * from './config.type'; | ||
export * from './config.factory'; | ||
export * from './services.type'; | ||
export * from './services.factory'; |
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,97 @@ | ||
import { | ||
Injectable, | ||
Type, | ||
} from '@angular/core'; | ||
import { TestBed } from '@angular/core/testing'; | ||
import { faker } from '@faker-js/faker/locale/en_US'; | ||
|
||
import { DaffServicesInjectionToken } from '@daffodil/core'; | ||
|
||
import { createServicesInjectionToken } from './services.factory'; | ||
|
||
interface TestType { | ||
get(): string; | ||
} | ||
|
||
@Injectable({ | ||
providedIn: 'root', | ||
}) | ||
class Test1 implements TestType { | ||
get() { | ||
return 'test1'; | ||
} | ||
} | ||
|
||
@Injectable({ | ||
providedIn: 'root', | ||
}) | ||
class Test2 implements TestType { | ||
get() { | ||
return 'test2'; | ||
} | ||
} | ||
|
||
describe('@daffodil/core | createServicesInjectionToken', () => { | ||
let name: string; | ||
let values: Array<Type<TestType>>; | ||
|
||
let result: DaffServicesInjectionToken<TestType>; | ||
|
||
beforeEach(() => { | ||
name = faker.random.word(); | ||
values = [ | ||
Test1, | ||
Test2, | ||
]; | ||
result = createServicesInjectionToken(name); | ||
}); | ||
|
||
it('should return a token', () => { | ||
expect(result.token.toString()).toContain(name); | ||
}); | ||
|
||
it('should return a provider', () => { | ||
const res = result.provider(...values); | ||
values.forEach((value, i) => { | ||
expect(res[i].provide).toEqual(result.token); | ||
expect(res[i].useExisting).toEqual(value); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('@daffodil/core | createServicesInjectionToken | Integration', () => { | ||
let name: string; | ||
let values: Array<Type<TestType>>; | ||
|
||
let result: DaffServicesInjectionToken<TestType>; | ||
|
||
beforeEach(() => { | ||
name = faker.random.word(); | ||
values = [ | ||
Test1, | ||
Test2, | ||
]; | ||
result = createServicesInjectionToken(name); | ||
}); | ||
|
||
describe('when values are provided', () => { | ||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
providers: [ | ||
result.provider(...values), | ||
], | ||
}); | ||
}); | ||
|
||
it('should inject the values', () => { | ||
expect(TestBed.inject(result.token)[0].get()).toEqual('test1'); | ||
expect(TestBed.inject(result.token)[1].get()).toEqual('test2'); | ||
}); | ||
}); | ||
|
||
describe('when values are not provided', () => { | ||
it('should inject an empty array', () => { | ||
expect(TestBed.inject(result.token)).toEqual([]); | ||
}); | ||
}); | ||
}); |
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,42 @@ | ||
import { | ||
InjectionToken, | ||
Type, | ||
} from '@angular/core'; | ||
|
||
import { DaffServicesInjectionToken } from './services.type'; | ||
import { | ||
TokenDesc, | ||
TokenOptions, | ||
} from './token-constuctor-params.type'; | ||
|
||
// having a single instance of the default factory | ||
// will hopefully reduce memory footprint | ||
const defaultFactory = () => []; | ||
|
||
/** | ||
* Creates an injection token/provider pair for a DI token that holds services. | ||
* | ||
* See {@link DaffServicesInjectionToken}. | ||
*/ | ||
export const createServicesInjectionToken = <T = unknown>( | ||
desc: TokenDesc<Array<T>>, | ||
options?: Partial<TokenOptions<Array<T>>>, | ||
): DaffServicesInjectionToken<T> => { | ||
const token = new InjectionToken<Array<T>>( | ||
desc, | ||
{ | ||
factory: defaultFactory, | ||
...options, | ||
}, | ||
); | ||
const provider = <R extends T = T>(...classes: Array<Type<R>>) => classes.map((klass) => ({ | ||
provide: token, | ||
useExisting: klass, | ||
multi: true, | ||
})); | ||
|
||
return { | ||
token, | ||
provider, | ||
}; | ||
}; |
Oops, something went wrong.