mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
chore: Introduce competition for InitializableState
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
82fdcf69c0
commit
72a01cdc17
@ -5,11 +5,14 @@
|
|||||||
|
|
||||||
import type { AsyncFnMock } from "@async-fn/jest";
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
import asyncFn from "@async-fn/jest";
|
import asyncFn from "@async-fn/jest";
|
||||||
import type { DiContainer, Injectable } from "@ogre-tools/injectable";
|
import type { Runnable } from "@k8slens/run-many";
|
||||||
|
import { runManyFor } from "@k8slens/run-many";
|
||||||
|
import type { DiContainer, Injectable, InjectionToken } from "@ogre-tools/injectable";
|
||||||
|
import { createContainer, getInjectionToken } from "@ogre-tools/injectable";
|
||||||
import { runInAction } from "mobx";
|
import { runInAction } from "mobx";
|
||||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||||
import type { InitializableState } from "./create";
|
import type { ImplInitializableInjectionTokensArgs, Initializable, InitializableState } from "./create";
|
||||||
import { createInitializableState } from "./create";
|
import { getInjectablesForInitializable, getInitializable, createInitializableState } from "./create";
|
||||||
|
|
||||||
describe("InitializableState tests", () => {
|
describe("InitializableState tests", () => {
|
||||||
let di: DiContainer;
|
let di: DiContainer;
|
||||||
@ -75,3 +78,72 @@ describe("InitializableState tests", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("InitializableTokens technical tests", () => {
|
||||||
|
let di: DiContainer;
|
||||||
|
let initializableToken: Initializable<number>;
|
||||||
|
let phase: InjectionToken<Runnable<void>, void>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
di = createContainer("irrelevant");
|
||||||
|
|
||||||
|
initializableToken = getInitializable("some-root-id");
|
||||||
|
phase = getInjectionToken({ id: "some-runnable-phase" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws given attempting to inject the state token", () => {
|
||||||
|
expect(() => di.inject(initializableToken.stateToken)).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
`"Tried to inject non-registered injectable "irrelevant" -> "some-root-id-state-token"."`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given some implementation for initializableToken is registered", () => {
|
||||||
|
let mockInit: AsyncFnMock<ImplInitializableInjectionTokensArgs<number>["init"]>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockInit = asyncFn();
|
||||||
|
|
||||||
|
const { initializationInjectable, stateInjectable } = getInjectablesForInitializable({
|
||||||
|
init: mockInit,
|
||||||
|
phase,
|
||||||
|
token: initializableToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
di.register(initializationInjectable, stateInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws given attempting to inject the state token", () => {
|
||||||
|
expect(() => di.inject(initializableToken.stateToken)).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
`"Tried to inject "some-root-id" before initialization was complete"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given the phase is started to be run", () => {
|
||||||
|
let runManyPromise: Promise<void>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
runManyPromise = runManyFor(di)(phase)();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws given attempting to inject the state token", () => {
|
||||||
|
expect(() => di.inject(initializableToken.stateToken)).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
`"Tried to inject "some-root-id" before initialization was complete"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when initialization is complete", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await mockInit.resolve(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("initializes the state", () => {
|
||||||
|
expect(di.inject(initializableToken.stateToken)).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows the runMany to complete", async () => {
|
||||||
|
await runManyPromise;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -3,8 +3,10 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { Runnable } from "@k8slens/run-many";
|
||||||
import type { DiContainerForInjection, Injectable, InjectionToken } from "@ogre-tools/injectable";
|
import type { DiContainerForInjection, Injectable, InjectionToken } from "@ogre-tools/injectable";
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectionToken, getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
export interface CreateInitializableStateArgs<T> {
|
export interface CreateInitializableStateArgs<T> {
|
||||||
id: string;
|
id: string;
|
||||||
@ -21,6 +23,67 @@ type InitializableStateValue<T> =
|
|||||||
| { set: false }
|
| { set: false }
|
||||||
| { set: true; value: T } ;
|
| { set: true; value: T } ;
|
||||||
|
|
||||||
|
export interface Initializable<T> {
|
||||||
|
readonly rootId: string;
|
||||||
|
readonly stateToken: InjectionToken<T, void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getInitializable = <T>(rootId: string): Initializable<T> => ({
|
||||||
|
rootId,
|
||||||
|
stateToken: getInjectionToken<T>({
|
||||||
|
id: `${rootId}-state-token`,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
type InitState<T> = { set: true; value: T } | { set: false };
|
||||||
|
|
||||||
|
export interface ImplInitializableInjectionTokensArgs<T> {
|
||||||
|
token: Initializable<T>;
|
||||||
|
init: (di: DiContainerForInjection) => T | Promise<T>;
|
||||||
|
phase: InjectionToken<Runnable<void>, void>;
|
||||||
|
runAfter?: Runnable<void>["runAfter"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getInjectablesForInitializable = <T>({
|
||||||
|
init,
|
||||||
|
phase,
|
||||||
|
token: {
|
||||||
|
rootId,
|
||||||
|
stateToken,
|
||||||
|
},
|
||||||
|
runAfter,
|
||||||
|
}: ImplInitializableInjectionTokensArgs<T>) => {
|
||||||
|
let state: InitState<T> = { set: false };
|
||||||
|
|
||||||
|
const stateInjectable = getInjectable({
|
||||||
|
id: `${rootId}-state`,
|
||||||
|
instantiate: () => {
|
||||||
|
assert(state.set, `Tried to inject "${rootId}" before initialization was complete`);
|
||||||
|
|
||||||
|
return state.value;
|
||||||
|
},
|
||||||
|
injectionToken: stateToken,
|
||||||
|
});
|
||||||
|
const initializationInjectable = getInjectable({
|
||||||
|
id: `${rootId}-initialization`,
|
||||||
|
instantiate: (di) => ({
|
||||||
|
run: async () => {
|
||||||
|
state = {
|
||||||
|
set: true,
|
||||||
|
value: await init(di),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
runAfter,
|
||||||
|
}),
|
||||||
|
injectionToken: phase,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
stateInjectable,
|
||||||
|
initializationInjectable,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export function createInitializableState<T>(args: CreateInitializableStateArgs<T>): Injectable<InitializableState<T>, unknown, void> {
|
export function createInitializableState<T>(args: CreateInitializableStateArgs<T>): Injectable<InitializableState<T>, unknown, void> {
|
||||||
const { id, init, injectionToken } = args;
|
const { id, init, injectionToken } = args;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user