1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

fix dock-store.test.ts test by overriding createStorage to not touch file system

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-04-08 10:15:57 -04:00
parent 35e49fe6e6
commit 199f142559
17 changed files with 109 additions and 73 deletions

View File

@ -7,7 +7,7 @@ import { action, observable, reaction, when, makeObservable } from "mobx";
import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store";
import type { Cluster, ClusterApi } from "../../../../common/k8s-api/endpoints"; import type { Cluster, ClusterApi } from "../../../../common/k8s-api/endpoints";
import { getMetricsByNodeNames, type ClusterMetricData } from "../../../../common/k8s-api/endpoints"; import { getMetricsByNodeNames, type ClusterMetricData } from "../../../../common/k8s-api/endpoints";
import type { StorageHelper } from "../../../utils"; import type { StorageLayer } from "../../../utils";
import { autoBind } from "../../../utils"; import { autoBind } from "../../../utils";
import { type IMetricsReqParams, normalizeMetrics } from "../../../../common/k8s-api/endpoints/metrics.api"; import { type IMetricsReqParams, normalizeMetrics } from "../../../../common/k8s-api/endpoints/metrics.api";
import { nodesStore } from "../../+nodes/nodes.store"; import { nodesStore } from "../../+nodes/nodes.store";
@ -28,7 +28,7 @@ export interface ClusterOverviewStorageState {
} }
interface Dependencies { interface Dependencies {
storage: StorageHelper<ClusterOverviewStorageState>; storage: StorageLayer<ClusterOverviewStorageState>;
} }
export class ClusterOverviewStore extends KubeObjectStore<Cluster, ClusterApi> implements ClusterOverviewStorageState { export class ClusterOverviewStore extends KubeObjectStore<Cluster, ClusterApi> implements ClusterOverviewStorageState {

View File

@ -5,7 +5,7 @@
import type { IReactionDisposer } from "mobx"; import type { IReactionDisposer } from "mobx";
import { action, comparer, computed, makeObservable, reaction } from "mobx"; import { action, comparer, computed, makeObservable, reaction } from "mobx";
import type { StorageHelper } from "../../../utils"; import type { StorageLayer } from "../../../utils";
import { autoBind, noop, toggle } from "../../../utils"; import { autoBind, noop, toggle } from "../../../utils";
import type { KubeObjectStoreLoadingParams } from "../../../../common/k8s-api/kube-object.store"; import type { KubeObjectStoreLoadingParams } from "../../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store";
@ -13,7 +13,7 @@ import type { NamespaceApi } from "../../../../common/k8s-api/endpoints/namespac
import { Namespace } from "../../../../common/k8s-api/endpoints/namespaces.api"; import { Namespace } from "../../../../common/k8s-api/endpoints/namespaces.api";
interface Dependencies { interface Dependencies {
storage: StorageHelper<string[] | undefined>; storage: StorageLayer<string[] | undefined>;
} }
export class NamespaceStore extends KubeObjectStore<Namespace, NamespaceApi> { export class NamespaceStore extends KubeObjectStore<Namespace, NamespaceApi> {
@ -70,7 +70,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace, NamespaceApi> {
* @private * @private
* The current value (list of namespaces names) in the storage layer * The current value (list of namespaces names) in the storage layer
*/ */
@computed private get selectedNamespaces(): string[] { @computed private get selectedNamespaces() {
return this.dependencies.storage.get() ?? []; return this.dependencies.storage.get() ?? [];
} }
@ -84,7 +84,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace, NamespaceApi> {
/** /**
* The list of selected namespace names (for filtering) * The list of selected namespace names (for filtering)
*/ */
@computed get contextNamespaces(): string[] { @computed get contextNamespaces() {
if (!this.selectedNamespaces.length) { if (!this.selectedNamespaces.length) {
return this.allowedNamespaces; // show all namespaces when nothing selected return this.allowedNamespaces; // show all namespaces when nothing selected
} }

View File

@ -3,12 +3,11 @@
* 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 directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import hostedClusterIdInjectable from "../../../../common/cluster-store/hosted-cluster-id.injectable";
import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
import type { DockStore, DockTab } from "../dock/store"; import type { DockStore, DockTab } from "../dock/store";
import { TabKind } from "../dock/store"; import { TabKind } from "../dock/store";
import dockStoreInjectable from "../dock/store.injectable"; import dockStoreInjectable from "../dock/store.injectable";
import fse from "fs-extra";
const initialTabs: DockTab[] = [ const initialTabs: DockTab[] = [
{ id: "terminal", kind: TabKind.TERMINAL, title: "Terminal", pinned: false }, { id: "terminal", kind: TabKind.TERMINAL, title: "Terminal", pinned: false },
@ -24,20 +23,11 @@ describe("DockStore", () => {
beforeEach(async () => { beforeEach(async () => {
const di = getDiForUnitTesting({ doGeneralOverrides: true }); const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override( di.override(hostedClusterIdInjectable, () => "some-cluster-id");
directoryForUserDataInjectable,
() => "some-test-suite-specific-directory-for-user-data",
);
await di.runSetups(); await di.runSetups();
dockStore = di.inject(dockStoreInjectable); dockStore = di.inject(dockStoreInjectable);
await dockStore.whenReady;
});
afterEach(() => {
fse.remove("some-test-suite-specific-directory-for-user-data");
dockStore.closeAllTabs();
}); });
it("closes tab and selects one from right", () => { it("closes tab and selects one from right", () => {

View File

@ -4,8 +4,9 @@
*/ */
import { action, observable, reaction } from "mobx"; import { action, observable, reaction } from "mobx";
import type { StorageHelper } from "../../../utils"; import type { StorageLayer } from "../../../utils";
import { autoBind, toJS } from "../../../utils"; import { autoBind, toJS } from "../../../utils";
import type { CreateStorage } from "../../../utils/create-storage/create-storage";
import type { TabId } from "../dock/store"; import type { TabId } from "../dock/store";
export interface DockTabStoreOptions { export interface DockTabStoreOptions {
@ -16,11 +17,11 @@ export interface DockTabStoreOptions {
export type DockTabStorageState<T> = Record<TabId, T>; export type DockTabStorageState<T> = Record<TabId, T>;
interface DockTabStoreDependencies { interface DockTabStoreDependencies {
createStorage: <T>(storageKey: string, options: DockTabStorageState<T>) => StorageHelper<DockTabStorageState<T>>; createStorage: CreateStorage;
} }
export class DockTabStore<T> { export class DockTabStore<T> {
protected storage?: StorageHelper<DockTabStorageState<T>>; protected storage?: StorageLayer<DockTabStorageState<T>>;
private data = observable.map<TabId, T>(); private data = observable.map<TabId, T>();
constructor(protected dependencies: DockTabStoreDependencies, protected options: DockTabStoreOptions) { constructor(protected dependencies: DockTabStoreDependencies, protected options: DockTabStoreOptions) {
@ -41,7 +42,7 @@ export class DockTabStore<T> {
// auto-save to local-storage // auto-save to local-storage
if (storageKey) { if (storageKey) {
const storage = this.storage = this.dependencies.createStorage<T>(storageKey, {}); const storage = this.storage = this.dependencies.createStorage(storageKey, {});
storage.whenReady.then(() => { storage.whenReady.then(() => {
this.data.replace(storage.value); this.data.replace(storage.value);

View File

@ -5,7 +5,7 @@
import * as uuid from "uuid"; import * as uuid from "uuid";
import { action, comparer, computed, makeObservable, observable, reaction, runInAction } from "mobx"; import { action, comparer, computed, makeObservable, observable, reaction, runInAction } from "mobx";
import type { StorageHelper } from "../../../utils"; import type { StorageLayer } from "../../../utils";
import { autoBind } from "../../../utils"; import { autoBind } from "../../../utils";
import throttle from "lodash/throttle"; import throttle from "lodash/throttle";
@ -99,7 +99,7 @@ export interface DockTabCloseEvent {
} }
interface Dependencies { interface Dependencies {
readonly storage: StorageHelper<DockStorageState>; readonly storage: StorageLayer<DockStorageState>;
readonly tabDataClearers: Record<TabKind, (tabId: TabId) => void>; readonly tabDataClearers: Record<TabKind, (tabId: TabId) => void>;
readonly tabDataValidator: Partial<Record<TabKind, (tabId: TabId) => boolean>>; readonly tabDataValidator: Partial<Record<TabKind, (tabId: TabId) => boolean>>;
} }

View File

@ -3,11 +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 { DockTabStorageState } from "../dock-tab-store/dock-tab.store";
import { DockTabStore } from "../dock-tab-store/dock-tab.store"; import { DockTabStore } from "../dock-tab-store/dock-tab.store";
import type { StorageHelper } from "../../../utils";
import type { TabId } from "../dock/store"; import type { TabId } from "../dock/store";
import { logTabDataValidator } from "./log-tab-data.validator"; import { logTabDataValidator } from "./log-tab-data.validator";
import type { CreateStorage } from "../../../utils/create-storage/create-storage";
export interface LogTabOwnerRef { export interface LogTabOwnerRef {
/** /**
@ -58,7 +57,7 @@ export interface LogTabData {
} }
interface Dependencies { interface Dependencies {
createStorage: <T>(storageKey: string, options: DockTabStorageState<T>) => StorageHelper<DockTabStorageState<T>>; createStorage: CreateStorage;
} }
export class LogTabStore extends DockTabStore<LogTabData> { export class LogTabStore extends DockTabStore<LogTabData> {

View File

@ -8,7 +8,7 @@ import "./drawer.scss";
import React from "react"; import React from "react";
import { clipboard } from "electron"; import { clipboard } from "electron";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import type { StorageHelper } from "../../utils"; import type { StorageLayer } from "../../utils";
import { cssNames, noop } from "../../utils"; import { cssNames, noop } from "../../utils";
import { Icon } from "../icon"; import { Icon } from "../icon";
import type { AnimateName } from "../animate"; import type { AnimateName } from "../animate";
@ -63,7 +63,7 @@ export const resizingAnchorProps: Record<DrawerPosition, [ResizeDirection, Resiz
interface Dependencies { interface Dependencies {
history: History; history: History;
drawerStorage: StorageHelper<{ width: number }>; drawerStorage: StorageLayer<{ width: number }>;
} }
class NonInjectedDrawer extends React.Component<DrawerProps & Dependencies & typeof defaultProps, State> { class NonInjectedDrawer extends React.Component<DrawerProps & Dependencies & typeof defaultProps, State> {

View File

@ -10,7 +10,7 @@ import React from "react";
import { computed, makeObservable, untracked } from "mobx"; import { computed, makeObservable, untracked } from "mobx";
import type { ConfirmDialogParams } from "../confirm-dialog"; import type { ConfirmDialogParams } from "../confirm-dialog";
import type { TableCellProps, TableProps, TableRowProps, TableSortCallbacks } from "../table"; import type { TableCellProps, TableProps, TableRowProps, TableSortCallbacks } from "../table";
import type { IClassName, SingleOrMany, StorageHelper } from "../../utils"; import type { IClassName, SingleOrMany, StorageLayer } from "../../utils";
import { autoBind, cssNames, noop } from "../../utils"; import { autoBind, cssNames, noop } from "../../utils";
import type { AddRemoveButtonsProps } from "../add-remove-buttons"; import type { AddRemoveButtonsProps } from "../add-remove-buttons";
import type { ItemObject } from "../../../common/item.store"; import type { ItemObject } from "../../../common/item.store";
@ -65,7 +65,7 @@ export type ItemListStore<I extends ItemObject, PreLoadStores extends boolean> =
}) & ( }) & (
PreLoadStores extends true PreLoadStores extends true
? { ? {
loadAll: (selectedNamespaces: string[]) => Promise<void>; loadAll: (selectedNamespaces: readonly string[]) => Promise<void>;
} }
: { : {
loadAll?: unknown; loadAll?: unknown;
@ -149,9 +149,13 @@ const defaultProps: Partial<ItemListLayoutProps<ItemObject, true>> = {
failedToLoadMessage: "Failed to load items", failedToLoadMessage: "Failed to load items",
}; };
export interface ItemListLayoutStorage {
showFilters: boolean;
}
interface Dependencies { interface Dependencies {
namespaceStore: NamespaceStore; namespaceStore: NamespaceStore;
itemListLayoutStorage: StorageHelper<{ showFilters: boolean }>; itemListLayoutStorage: StorageLayer<ItemListLayoutStorage>;
} }
@observer @observer

View File

@ -7,16 +7,13 @@ import styles from "./main-layout.module.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { StorageHelper } from "../../utils"; import type { StorageLayer } from "../../utils";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import { ErrorBoundary } from "../error-boundary"; import { ErrorBoundary } from "../error-boundary";
import { ResizeDirection, ResizeGrowthDirection, ResizeSide, ResizingAnchor } from "../resizing-anchor"; import { ResizeDirection, ResizeGrowthDirection, ResizeSide, ResizingAnchor } from "../resizing-anchor";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { import type { SidebarStorageState } from "./sidebar-storage/sidebar-storage.injectable";
SidebarStorageState } from "./sidebar-storage/sidebar-storage.injectable"; import sidebarStorageInjectable, { defaultSidebarWidth } from "./sidebar-storage/sidebar-storage.injectable";
import sidebarStorageInjectable, {
defaultSidebarWidth,
} from "./sidebar-storage/sidebar-storage.injectable";
export interface MainLayoutProps { export interface MainLayoutProps {
sidebar: React.ReactNode; sidebar: React.ReactNode;
@ -31,7 +28,7 @@ export interface MainLayoutProps {
*/ */
interface Dependencies { interface Dependencies {
sidebarStorage: StorageHelper<SidebarStorageState>; sidebarStorage: StorageLayer<SidebarStorageState>;
} }
@observer @observer

View File

@ -7,20 +7,18 @@ import "./sidebar-item.scss";
import React from "react"; import React from "react";
import { computed, makeObservable } from "mobx"; import { computed, makeObservable } from "mobx";
import type { StorageHelper } from "../../utils"; import type { StorageLayer } from "../../utils";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { import type { SidebarStorageState } from "./sidebar-storage/sidebar-storage.injectable";
SidebarStorageState,
} from "./sidebar-storage/sidebar-storage.injectable";
import sidebarStorageInjectable from "./sidebar-storage/sidebar-storage.injectable"; import sidebarStorageInjectable from "./sidebar-storage/sidebar-storage.injectable";
import type { HierarchicalSidebarItem } from "./sidebar-items.injectable"; import type { HierarchicalSidebarItem } from "./sidebar-items.injectable";
interface Dependencies { interface Dependencies {
sidebarStorage: StorageHelper<SidebarStorageState>; sidebarStorage: StorageLayer<SidebarStorageState>;
} }
export interface SidebarItemProps { export interface SidebarItemProps {

View File

@ -2,7 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* 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 { StorageHelper } from "../../../utils"; import type { StorageLayer } from "../../../utils";
import type { TableSortParams } from "../table"; import type { TableSortParams } from "../table";
export interface TableStorageModel { export interface TableStorageModel {
@ -12,7 +12,7 @@ export interface TableStorageModel {
} }
interface Dependencies { interface Dependencies {
storage: StorageHelper<TableStorageModel>; storage: StorageLayer<TableStorageModel>;
} }
export class TableModel { export class TableModel {

View File

@ -4,7 +4,7 @@
*/ */
import glob from "glob"; import glob from "glob";
import { memoize, noop } from "lodash/fp"; import { isEqual, isPlainObject, memoize, noop } from "lodash/fp";
import { createContainer } from "@ogre-tools/injectable"; import { createContainer } from "@ogre-tools/injectable";
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import getValueFromRegisteredChannelInjectable from "./app-paths/get-value-from-registered-channel/get-value-from-registered-channel.injectable"; import getValueFromRegisteredChannelInjectable from "./app-paths/get-value-from-registered-channel/get-value-from-registered-channel.injectable";
@ -35,6 +35,10 @@ import { joinPathsFake } from "../common/test-utils/join-paths-fake";
import hotbarStoreInjectable from "../common/hotbar-store.injectable"; import hotbarStoreInjectable from "../common/hotbar-store.injectable";
import terminalSpawningPoolInjectable from "./components/dock/terminal/terminal-spawning-pool.injectable"; import terminalSpawningPoolInjectable from "./components/dock/terminal/terminal-spawning-pool.injectable";
import hostedClusterIdInjectable from "../common/cluster-store/hosted-cluster-id.injectable"; import hostedClusterIdInjectable from "../common/cluster-store/hosted-cluster-id.injectable";
import createStorageInjectable from "./utils/create-storage/create-storage.injectable";
import { observable, toJS } from "mobx";
import type { Draft } from "immer";
import { produce, isDraft } from "immer";
export const getDiForUnitTesting = ( export const getDiForUnitTesting = (
{ doGeneralOverrides } = { doGeneralOverrides: false }, { doGeneralOverrides } = { doGeneralOverrides: false },
@ -59,7 +63,41 @@ export const getDiForUnitTesting = (
di.override(isWindowsInjectable, () => false); di.override(isWindowsInjectable, () => false);
di.override(isLinuxInjectable, () => false); di.override(isLinuxInjectable, () => false);
di.override(terminalSpawningPoolInjectable, () => new HTMLElement()); di.override(terminalSpawningPoolInjectable, () => document.createElement("div"));
di.override(createStorageInjectable, () => function <MockT>(key: string, defaultValue: MockT) {
const srcValue = observable.box(defaultValue);
return {
get: () => srcValue.get(),
isDefaultValue: val => isEqual(val, defaultValue),
merge: (value: Partial<MockT> | ((draft: Draft<MockT>) => void | Partial<MockT>)) => {
const nextValue = produce(toJS(srcValue.get()), (draft) => {
if (typeof value == "function") {
const newValue = value(draft);
// merge returned plain objects from `value-as-callback` usage
// otherwise `draft` can be just modified inside a callback without returning any value (void)
if (newValue && !isDraft(newValue)) {
Object.assign(draft, newValue);
}
} else if (isPlainObject(value)) {
Object.assign(draft, value);
}
return draft;
});
srcValue.set(nextValue);
},
reset: () => srcValue.set(defaultValue),
set: (val: MockT) => srcValue.set(val),
get value() {
return srcValue.get();
},
whenReady: Promise.resolve(),
};
});
di.override(hostedClusterIdInjectable, () => undefined); di.override(hostedClusterIdInjectable, () => undefined);
di.override(getAbsolutePathInjectable, () => getAbsolutePathFake); di.override(getAbsolutePathInjectable, () => getAbsolutePathFake);

View File

@ -5,7 +5,7 @@
import { action, makeObservable, observable, reaction } from "mobx"; import { action, makeObservable, observable, reaction } from "mobx";
import { ItemStore } from "../../../common/item.store"; import { ItemStore } from "../../../common/item.store";
import type { StorageHelper } from "../../utils"; import type { StorageLayer } from "../../utils";
import { autoBind, disposer } from "../../utils"; import { autoBind, disposer } from "../../utils";
import type { ForwardedPort } from "../port-forward-item"; import type { ForwardedPort } from "../port-forward-item";
import { PortForwardItem } from "../port-forward-item"; import { PortForwardItem } from "../port-forward-item";
@ -14,7 +14,7 @@ import { waitUntilFree } from "tcp-port-used";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
interface Dependencies { interface Dependencies {
storage: StorageHelper<ForwardedPort[] | undefined>; storage: StorageLayer<ForwardedPort[] | undefined>;
notifyErrorPortForwarding: (message: string) => void; notifyErrorPortForwarding: (message: string) => void;
} }

View File

@ -6,6 +6,7 @@
import { observable, reaction } from "mobx"; import { observable, reaction } from "mobx";
import { StorageHelper } from "../storageHelper"; import { StorageHelper } from "../storageHelper";
import { delay } from "../../../common/utils/delay"; import { delay } from "../../../common/utils/delay";
import { toJS } from "../../../common/utils";
interface StorageModel { interface StorageModel {
[prop: string]: any /*json-serializable*/; [prop: string]: any /*json-serializable*/;
@ -131,7 +132,7 @@ describe("renderer/utils/StorageHelper", () => {
it("storage.get() is observable", () => { it("storage.get() is observable", () => {
expect(storageHelper.get()).toEqual(defaultValue); expect(storageHelper.get()).toEqual(defaultValue);
reaction(() => storageHelper.toJSON(), change => { reaction(() => toJS(storageHelper.get()), change => {
observedChanges.push(change); observedChanges.push(change);
}); });

View File

@ -15,20 +15,19 @@ import hostedClusterIdInjectable from "../../../common/cluster-store/hosted-clus
const createStorageInjectable = getInjectable({ const createStorageInjectable = getInjectable({
id: "create-storage", id: "create-storage",
instantiate: (di) => instantiate: (di) => createStorage({
createStorage({ storage: observable({
storage: observable({ initialized: false,
initialized: false, loaded: false,
loaded: false, data: {},
data: {} as Record<string /*key*/, any>, // json-serializable
}),
readJsonFile: di.inject(readJsonFileInjectable),
writeJsonFile: di.inject(writeJsonFileInjectable),
logger: di.inject(loggerInjectable),
directoryForLensLocalStorage: di.inject(directoryForLensLocalStorageInjectable),
getAbsolutePath: di.inject(getAbsolutePathInjectable),
hostedClusterId: di.inject(hostedClusterIdInjectable),
}), }),
readJsonFile: di.inject(readJsonFileInjectable),
writeJsonFile: di.inject(writeJsonFileInjectable),
logger: di.inject(loggerInjectable),
directoryForLensLocalStorage: di.inject(directoryForLensLocalStorageInjectable),
getAbsolutePath: di.inject(getAbsolutePathInjectable),
hostedClusterId: di.inject(hostedClusterIdInjectable),
}),
}); });
export default createStorageInjectable; export default createStorageInjectable;

View File

@ -6,6 +6,7 @@
// Keeps window.localStorage state in external JSON-files. // Keeps window.localStorage state in external JSON-files.
// Because app creates random port between restarts => storage session wiped out each time. // Because app creates random port between restarts => storage session wiped out each time.
import { comparer, reaction, toJS, when } from "mobx"; import { comparer, reaction, toJS, when } from "mobx";
import type { StorageLayer } from "../storageHelper";
import { StorageHelper } from "../storageHelper"; import { StorageHelper } from "../storageHelper";
import { isTestEnv } from "../../../common/vars"; import { isTestEnv } from "../../../common/vars";
import type { JsonObject, JsonValue } from "type-fest"; import type { JsonObject, JsonValue } from "type-fest";
@ -22,6 +23,8 @@ interface Dependencies {
hostedClusterId: string | undefined; hostedClusterId: string | undefined;
} }
export type CreateStorage = <T>(key: string, defaultValue: T) => StorageLayer<T>;
/** /**
* Creates a helper for saving data under the "key" intended for window.localStorage * Creates a helper for saving data under the "key" intended for window.localStorage
*/ */
@ -33,7 +36,7 @@ export const createStorage = ({
readJsonFile, readJsonFile,
writeJsonFile, writeJsonFile,
hostedClusterId, hostedClusterId,
}: Dependencies) => <T>(key: string, defaultValue: T) => { }: Dependencies): CreateStorage => (key, defaultValue) => {
const { logPrefix } = StorageHelper; const { logPrefix } = StorageHelper;
if (!storage.initialized) { if (!storage.initialized) {
@ -78,7 +81,7 @@ export const createStorage = ({
.catch(error => logger.error(`${logPrefix} Failed to initialize storage: ${error}`)); .catch(error => logger.error(`${logPrefix} Failed to initialize storage: ${error}`));
} }
return new StorageHelper<T>(key, { return new StorageHelper(key, {
autoInit: true, autoInit: true,
defaultValue, defaultValue,
storage: { storage: {

View File

@ -24,7 +24,17 @@ export interface StorageHelperOptions<T> {
defaultValue: T; defaultValue: T;
} }
export class StorageHelper<T> { export interface StorageLayer<T> {
isDefaultValue(val: T): boolean;
get(): T;
readonly value: T;
readonly whenReady: Promise<void>;
set(value: T): void;
reset(): void;
merge(value: Partial<T> | ((draft: Draft<T>) => Partial<T> | void)): void;
}
export class StorageHelper<T> implements StorageLayer<T> {
static logPrefix = "[StorageHelper]:"; static logPrefix = "[StorageHelper]:";
readonly storage: StorageAdapter<T>; readonly storage: StorageAdapter<T>;
@ -140,7 +150,7 @@ export class StorageHelper<T> {
@action @action
merge(value: Partial<T> | ((draft: Draft<T>) => Partial<T> | void)) { merge(value: Partial<T> | ((draft: Draft<T>) => Partial<T> | void)) {
const nextValue = produce<T>(this.toJSON(), (draft: Draft<T>) => { const nextValue = produce<T>(toJS(this.get()), (draft) => {
if (typeof value == "function") { if (typeof value == "function") {
const newValue = value(draft); const newValue = value(draft);
@ -159,8 +169,4 @@ export class StorageHelper<T> {
this.set(nextValue); this.set(nextValue);
} }
toJSON(): T {
return toJS(this.get());
}
} }