Skip to content

Commit

Permalink
feat(core): add singleton injection token factory (#3261)
Browse files Browse the repository at this point in the history
  • Loading branch information
griest024 authored Oct 14, 2024
1 parent c3ea0bf commit a454214
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 0 deletions.
2 changes: 2 additions & 0 deletions libs/core/src/injection-tokens/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ export * from './config.type';
export * from './config.factory';
export * from './services.type';
export * from './services.factory';
export * from './singleton.type';
export * from './singleton.factory';
73 changes: 73 additions & 0 deletions libs/core/src/injection-tokens/singleton.factory.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
Injectable,
Type,
} from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { faker } from '@faker-js/faker/locale/en_US';

import { DaffSingletonInjectionToken } from '@daffodil/core';

import { createSingletonInjectionToken } from './singleton.factory';

interface TestType {
get(): string;
}

@Injectable({
providedIn: 'root',
})
class Test1 implements TestType {
get() {
return 'test1';
}
}

describe('@daffodil/core | createSingletonInjectionToken', () => {
let name: string;
let value: Type<TestType>;

let result: DaffSingletonInjectionToken<TestType>;

beforeEach(() => {
name = faker.random.word();
value = Test1;
result = createSingletonInjectionToken(name);
});

it('should return a token', () => {
expect(result.token.toString()).toContain(name);
});

it('should return a provider', () => {
const res = result.provider(value);
expect(res.provide).toEqual(result.token);
expect(res.useExisting).toEqual(value);
});
});

describe('@daffodil/core | createSingletonInjectionToken | Integration', () => {
let name: string;
let value: Type<TestType>;

let result: DaffSingletonInjectionToken<TestType>;

beforeEach(() => {
name = faker.random.word();
value = Test1;
result = createSingletonInjectionToken(name);
});

describe('when value are provided', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
result.provider(value),
],
});
});

it('should inject the value', () => {
expect(TestBed.inject(result.token).get()).toEqual('test1');
});
});
});
34 changes: 34 additions & 0 deletions libs/core/src/injection-tokens/singleton.factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
InjectionToken,
Type,
} from '@angular/core';

import { DaffSingletonInjectionToken } from './singleton.type';
import {
TokenDesc,
TokenOptions,
} from './token-constuctor-params.type';

/**
* Creates an injection token/provider pair for a DI token that holds a singleton service.
*
* See {@link DaffSingletonInjectionToken}.
*/
export const createSingletonInjectionToken = <T = unknown>(
desc: TokenDesc<T>,
options?: TokenOptions<T>,
): DaffSingletonInjectionToken<T> => {
const token = new InjectionToken<T>(
desc,
options,
);
const provider = <R extends T = T>(klass: Type<R>) => ({
provide: token,
useExisting: klass,
});

return {
token,
provider,
};
};
21 changes: 21 additions & 0 deletions libs/core/src/injection-tokens/singleton.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {
ExistingProvider,
InjectionToken,
Type,
} from '@angular/core';

/**
* A injection token to hold and provide a singleton service.
*/
export interface DaffSingletonInjectionToken<T = unknown> {
/**
* The injection token.
* Its default value is an empty array.
*/
token: InjectionToken<T>;

/**
* A helper function to provide the service class to the token.
*/
provider: <R extends T = T>(klass: Type<R>) => ExistingProvider;
}

0 comments on commit a454214

Please sign in to comment.