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

fix some tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2021-05-20 10:01:42 -04:00
parent e0a9fff62f
commit f25dc4a00b
20 changed files with 244 additions and 108 deletions

View File

@ -33,15 +33,6 @@ module.exports = {
} }
}, },
overrides: [ overrides: [
{
files: [
"extensions/**/*.ts",
"extensions/**/*.tsx",
],
rules: {
"import/no-unresolved": "off", // warns on @k8slens/extensions
}
},
{ {
files: [ files: [
"**/*.js" "**/*.js"
@ -229,6 +220,15 @@ module.exports = {
{ "blankLine": "any", "prev": ["const", "let", "var"], "next": ["const", "let", "var"]}, { "blankLine": "any", "prev": ["const", "let", "var"], "next": ["const", "let", "var"]},
] ]
}, },
} },
{
files: [
"extensions/**/*.ts",
"extensions/**/*.tsx",
],
rules: {
"import/no-unresolved": "off", // warns on @k8slens/extensions
}
},
] ]
}; };

View File

@ -22,6 +22,7 @@
import { SearchStore } from "../search-store"; import { SearchStore } from "../search-store";
import { Console } from "console"; import { Console } from "console";
import { stdout, stderr } from "process"; import { stdout, stderr } from "process";
import { DockStore } from "../../renderer/components/dock";
jest.mock("electron", () => ({ jest.mock("electron", () => ({
app: { app: {
@ -31,7 +32,6 @@ jest.mock("electron", () => ({
console = new Console(stdout, stderr); console = new Console(stdout, stderr);
let searchStore: SearchStore = null;
const logs = [ const logs = [
"1:M 30 Oct 2020 16:17:41.553 # Connection with replica 172.17.0.12:6379 lost", "1:M 30 Oct 2020 16:17:41.553 # Connection with replica 172.17.0.12:6379 lost",
"1:M 30 Oct 2020 16:17:41.623 * Replica 172.17.0.12:6379 asks for synchronization", "1:M 30 Oct 2020 16:17:41.623 * Replica 172.17.0.12:6379 asks for synchronization",
@ -39,16 +39,26 @@ const logs = [
]; ];
describe("search store tests", () => { describe("search store tests", () => {
beforeEach(async () => { beforeEach(() => {
searchStore = new SearchStore(); DockStore.createInstance();
SearchStore.createInstance();
}); });
afterEach(() => {
DockStore.resetInstance();
SearchStore.resetInstance();
})
it("does nothing with empty search query", () => { it("does nothing with empty search query", () => {
const searchStore = SearchStore.getInstance();
searchStore.onSearch([], ""); searchStore.onSearch([], "");
expect(searchStore.occurrences).toEqual([]); expect(searchStore.occurrences).toEqual([]);
}); });
it("doesn't break if no text provided", () => { it("doesn't break if no text provided", () => {
const searchStore = SearchStore.getInstance();
searchStore.onSearch(null, "replica"); searchStore.onSearch(null, "replica");
expect(searchStore.occurrences).toEqual([]); expect(searchStore.occurrences).toEqual([]);
@ -57,33 +67,45 @@ describe("search store tests", () => {
}); });
it("find 3 occurrences across 3 lines", () => { it("find 3 occurrences across 3 lines", () => {
const searchStore = SearchStore.getInstance();
searchStore.onSearch(logs, "172"); searchStore.onSearch(logs, "172");
expect(searchStore.occurrences).toEqual([0, 1, 2]); expect(searchStore.occurrences).toEqual([0, 1, 2]);
}); });
it("find occurrences within 1 line (case-insensitive)", () => { it("find occurrences within 1 line (case-insensitive)", () => {
const searchStore = SearchStore.getInstance();
searchStore.onSearch(logs, "Starting"); searchStore.onSearch(logs, "Starting");
expect(searchStore.occurrences).toEqual([2, 2]); expect(searchStore.occurrences).toEqual([2, 2]);
}); });
it("sets overlay index equal to first occurrence", () => { it("sets overlay index equal to first occurrence", () => {
const searchStore = SearchStore.getInstance();
searchStore.onSearch(logs, "Replica"); searchStore.onSearch(logs, "Replica");
expect(searchStore.activeOverlayIndex).toBe(0); expect(searchStore.activeOverlayIndex).toBe(0);
}); });
it("set overlay index to next occurrence", () => { it("set overlay index to next occurrence", () => {
const searchStore = SearchStore.getInstance();
searchStore.onSearch(logs, "172"); searchStore.onSearch(logs, "172");
searchStore.setNextOverlayActive(); searchStore.setNextOverlayActive();
expect(searchStore.activeOverlayIndex).toBe(1); expect(searchStore.activeOverlayIndex).toBe(1);
}); });
it("sets overlay to last occurrence", () => { it("sets overlay to last occurrence", () => {
const searchStore = SearchStore.getInstance();
searchStore.onSearch(logs, "172"); searchStore.onSearch(logs, "172");
searchStore.setPrevOverlayActive(); searchStore.setPrevOverlayActive();
expect(searchStore.activeOverlayIndex).toBe(2); expect(searchStore.activeOverlayIndex).toBe(2);
}); });
it("gets line index where overlay is located", () => { it("gets line index where overlay is located", () => {
const searchStore = SearchStore.getInstance();
searchStore.onSearch(logs, "synchronization"); searchStore.onSearch(logs, "synchronization");
expect(searchStore.activeOverlayLine).toBe(1); expect(searchStore.activeOverlayLine).toBe(1);
}); });
@ -95,12 +117,16 @@ describe("search store tests", () => {
}); });
it("gets active find number", () => { it("gets active find number", () => {
const searchStore = SearchStore.getInstance();
searchStore.onSearch(logs, "172"); searchStore.onSearch(logs, "172");
searchStore.setNextOverlayActive(); searchStore.setNextOverlayActive();
expect(searchStore.activeFind).toBe(2); expect(searchStore.activeFind).toBe(2);
}); });
it("gets total finds number", () => { it("gets total finds number", () => {
const searchStore = SearchStore.getInstance();
searchStore.onSearch(logs, "Starting"); searchStore.onSearch(logs, "Starting");
expect(searchStore.totalFinds).toBe(2); expect(searchStore.totalFinds).toBe(2);
}); });

View File

@ -20,10 +20,10 @@
*/ */
import { action, computed, observable,reaction } from "mobx"; import { action, computed, observable,reaction } from "mobx";
import { dockStore } from "../renderer/components/dock/dock.store"; import { DockStore } from "../renderer/components/dock";
import { autobind } from "../renderer/utils"; import { autobind, Singleton } from "../renderer/utils";
export class SearchStore { export class SearchStore extends Singleton {
/** /**
* An utility methods escaping user string to safely pass it into new Regex(variable) * An utility methods escaping user string to safely pass it into new Regex(variable)
* @param value Unescaped string * @param value Unescaped string
@ -54,8 +54,10 @@ export class SearchStore {
@observable activeOverlayIndex = -1; @observable activeOverlayIndex = -1;
constructor() { constructor() {
reaction(() => dockStore.selectedTabId, () => { super();
searchStore.reset();
reaction(() => DockStore.getInstance().selectedTabId, () => {
this.reset();
}); });
} }
@ -173,5 +175,3 @@ export class SearchStore {
this.occurrences = []; this.occurrences = [];
} }
} }
export const searchStore = new SearchStore;

View File

@ -19,10 +19,23 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { Cluster } from "../../../main/cluster";
import { ApiManager } from "../api-manager";
import { CustomResourceDefinition } from "../endpoints"; import { CustomResourceDefinition } from "../endpoints";
import type { IKubeObjectMetadata } from "../kube-object"; import type { IKubeObjectMetadata } from "../kube-object";
describe("Crds", () => { describe("Crds", () => {
beforeEach(() => {
ApiManager.createInstance(new Cluster({
id: "foo",
kubeConfigPath: "/bar",
}));
});
afterEach(() => {
ApiManager.resetInstance();
});
describe("getVersion", () => { describe("getVersion", () => {
it("should get the first version name from the list of versions", () => { it("should get the first version name from the list of versions", () => {
const crd = new CustomResourceDefinition({ const crd = new CustomResourceDefinition({

View File

@ -19,10 +19,23 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { Cluster } from "../../../main/cluster";
import { ApiManager } from "../api-manager";
import { KubeApi } from "../kube-api"; import { KubeApi } from "../kube-api";
import { KubeObject } from "../kube-object"; import { KubeObject } from "../kube-object";
describe("KubeApi", () => { describe("KubeApi", () => {
beforeEach(() => {
ApiManager.createInstance(new Cluster({
id: "foo",
kubeConfigPath: "/bar",
}));
});
afterEach(() => {
ApiManager.resetInstance();
});
it("uses url from apiBase if apiBase contains the resource", async () => { it("uses url from apiBase if apiBase contains the resource", async () => {
(fetch as any).mockResponse(async (request: any) => { (fetch as any).mockResponse(async (request: any) => {
if (request.url === "/api-kube/apis/networking.k8s.io/v1") { if (request.url === "/api-kube/apis/networking.k8s.io/v1") {

View File

@ -19,6 +19,8 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { Cluster } from "../../../main/cluster";
import { ApiManager } from "../api-manager";
import { Pod } from "../endpoints"; import { Pod } from "../endpoints";
interface GetDummyPodOptions { interface GetDummyPodOptions {
@ -164,6 +166,17 @@ function getDummyPod(opts: GetDummyPodOptions = getDummyPodDefaultOptions()): Po
} }
describe("Pods", () => { describe("Pods", () => {
beforeEach(() => {
ApiManager.createInstance(new Cluster({
id: "foo",
kubeConfigPath: "/bar",
}));
});
afterEach(() => {
ApiManager.resetInstance();
});
const podTests = []; const podTests = [];
for (let r = 0; r < 3; r += 1) { for (let r = 0; r < 3; r += 1) {

View File

@ -92,14 +92,14 @@ export class ApiManager extends Singleton {
} }
@action @action
registerStore<Store extends KubeObjectStore<KubeObject>>(storeConstructor: KubeObjectStoreConstructor<Store>, apis?: KubeApi[]): this { registerStore<Store extends KubeObjectStore<KubeObject>>(storeConstructor: KubeObjectStoreConstructor<Store>, apis?: KubeApi[]): Store {
const store = new storeConstructor(this.cluster); const store = new storeConstructor(this.cluster);
for (const api of apis ?? [store.api]) { for (const api of apis ?? [store.api]) {
this.stores.set(api.apiBase, store); this.stores.set(api.apiBase, store);
} }
return this; return store;
} }
getStore<Store extends KubeObjectStore<KubeObject>>(api: string | KubeApi): Store | undefined { getStore<Store extends KubeObjectStore<KubeObject>>(api: string | KubeApi): Store | undefined {

View File

@ -24,8 +24,10 @@ import { render, waitFor, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect"; import "@testing-library/jest-dom/extend-expect";
import { DeploymentScaleDialog } from "./deployment-scale-dialog"; import { DeploymentScaleDialog } from "./deployment-scale-dialog";
jest.mock("../../api/endpoints"); jest.mock("../../api/endpoints/deployment.api");
import { Deployment, deploymentApi } from "../../api/endpoints"; import { Deployment, deploymentApi } from "../../api/endpoints/deployment.api";
import { ApiManager } from "../../api/api-manager";
import { Cluster } from "../../../main/cluster";
const dummyDeployment: Deployment = { const dummyDeployment: Deployment = {
apiVersion: "v1", apiVersion: "v1",
@ -116,6 +118,16 @@ const dummyDeployment: Deployment = {
}; };
describe("<DeploymentScaleDialog />", () => { describe("<DeploymentScaleDialog />", () => {
beforeEach(() => {
ApiManager.createInstance(new Cluster({
id: "foo",
kubeConfigPath: "/bar",
}));
});
afterEach(() => {
ApiManager.resetInstance();
});
it("renders w/o errors", () => { it("renders w/o errors", () => {
const { container } = render(<DeploymentScaleDialog />); const { container } = render(<DeploymentScaleDialog />);

View File

@ -26,6 +26,8 @@ import { ReplicaSetScaleDialog } from "./replicaset-scale-dialog";
import { render, waitFor, fireEvent } from "@testing-library/react"; import { render, waitFor, fireEvent } from "@testing-library/react";
import React from "react"; import React from "react";
import { ReplicaSet, replicaSetApi } from "../../api/endpoints/replica-set.api"; import { ReplicaSet, replicaSetApi } from "../../api/endpoints/replica-set.api";
import { Cluster } from "../../../main/cluster";
import { ApiManager } from "../../api/api-manager";
const dummyReplicaSet: ReplicaSet = { const dummyReplicaSet: ReplicaSet = {
apiVersion: "v1", apiVersion: "v1",
@ -111,6 +113,17 @@ const dummyReplicaSet: ReplicaSet = {
}; };
describe("<ReplicaSetScaleDialog />", () => { describe("<ReplicaSetScaleDialog />", () => {
beforeEach(() => {
ApiManager.createInstance(new Cluster({
id: "foo",
kubeConfigPath: "/bar",
}));
});
afterEach(() => {
ApiManager.resetInstance();
});
it("renders w/o errors", () => { it("renders w/o errors", () => {
const { container } = render(<ReplicaSetScaleDialog/>); const { container } = render(<ReplicaSetScaleDialog/>);

View File

@ -26,6 +26,8 @@ import { StatefulSet, statefulSetApi } from "../../api/endpoints";
import { StatefulSetScaleDialog } from "./statefulset-scale-dialog"; import { StatefulSetScaleDialog } from "./statefulset-scale-dialog";
import { render, waitFor, fireEvent } from "@testing-library/react"; import { render, waitFor, fireEvent } from "@testing-library/react";
import React from "react"; import React from "react";
import { Cluster } from "../../../main/cluster";
import { ApiManager } from "../../api/api-manager";
const dummyStatefulSet: StatefulSet = { const dummyStatefulSet: StatefulSet = {
apiVersion: "v1", apiVersion: "v1",
@ -121,6 +123,17 @@ const dummyStatefulSet: StatefulSet = {
}; };
describe("<StatefulSetScaleDialog />", () => { describe("<StatefulSetScaleDialog />", () => {
beforeEach(() => {
ApiManager.createInstance(new Cluster({
id: "foo",
kubeConfigPath: "/bar",
}));
});
afterEach(() => {
ApiManager.resetInstance();
});
it("renders w/o errors", () => { it("renders w/o errors", () => {
const { container } = render(<StatefulSetScaleDialog/>); const { container } = render(<StatefulSetScaleDialog/>);

View File

@ -24,7 +24,7 @@ import { render, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect"; import "@testing-library/jest-dom/extend-expect";
import { DockTabs } from "../dock-tabs"; import { DockTabs } from "../dock-tabs";
import { dockStore, IDockTab, TabKind } from "../dock.store"; import { DockStore, TabKind } from "../dock.store";
jest.mock("electron", () => ({ jest.mock("electron", () => ({
app: { app: {
@ -36,8 +36,8 @@ const onChangeTab = jest.fn();
const getComponent = () => ( const getComponent = () => (
<DockTabs <DockTabs
tabs={dockStore.tabs} tabs={DockStore.getInstance().tabs}
selectedTab={dockStore.selectedTab} selectedTab={DockStore.getInstance().selectedTab}
autoFocus={true} autoFocus={true}
onChangeTab={onChangeTab} onChangeTab={onChangeTab}
/> />
@ -59,27 +59,22 @@ Object.defineProperty(window, "matchMedia", {
const renderTabs = () => render(getComponent()); const renderTabs = () => render(getComponent());
const getTabKinds = () => dockStore.tabs.map(tab => tab.kind); const getTabKinds = () => DockStore.getInstance().tabs.map(tab => tab.kind);
describe("<DockTabs />", () => { describe("<DockTabs />", () => {
beforeEach(() => { beforeEach(() => {
const terminalTab: IDockTab = { id: "terminal1", kind: TabKind.TERMINAL, title: "Terminal" }; DockStore.createInstance().tabs.push(
const createResourceTab: IDockTab = { id: "create", kind: TabKind.CREATE_RESOURCE, title: "Create resource" }; { id: "terminal1", kind: TabKind.TERMINAL, title: "Terminal" },
const editResourceTab: IDockTab = { id: "edit", kind: TabKind.EDIT_RESOURCE, title: "Edit resource" }; { id: "create", kind: TabKind.CREATE_RESOURCE, title: "Create resource" },
const installChartTab: IDockTab = { id: "install", kind: TabKind.INSTALL_CHART, title: "Install chart" }; { id: "edit", kind: TabKind.EDIT_RESOURCE, title: "Edit resource" },
const logsTab: IDockTab = { id: "logs", kind: TabKind.POD_LOGS, title: "Logs" }; { id: "install", kind: TabKind.INSTALL_CHART, title: "Install chart" },
{ id: "logs", kind: TabKind.POD_LOGS, title: "Logs" },
dockStore.tabs.push(
terminalTab,
createResourceTab,
editResourceTab,
installChartTab,
logsTab
); );
}); });
afterEach(() => { afterEach(() => {
dockStore.reset(); DockStore.getInstance().reset();
DockStore.resetInstance();
}); });
it("renders w/o errors", () => { it("renders w/o errors", () => {
@ -174,7 +169,7 @@ describe("<DockTabs />", () => {
}); });
it("disables 'Close All' & 'Close Other' items if only 1 tab available", () => { it("disables 'Close All' & 'Close Other' items if only 1 tab available", () => {
dockStore.tabs = [{ DockStore.getInstance().tabs = [{
id: "terminal", kind: TabKind.TERMINAL, title: "Terminal" id: "terminal", kind: TabKind.TERMINAL, title: "Terminal"
}]; }];
const { container, getByText } = renderTabs(); const { container, getByText } = renderTabs();
@ -189,7 +184,7 @@ describe("<DockTabs />", () => {
}); });
it("disables 'Close To The Right' item if last tab clicked", () => { it("disables 'Close To The Right' item if last tab clicked", () => {
dockStore.tabs = [ DockStore.getInstance().tabs = [
{ id: "terminal", kind: TabKind.TERMINAL, title: "Terminal" }, { id: "terminal", kind: TabKind.TERMINAL, title: "Terminal" },
{ id: "logs", kind: TabKind.POD_LOGS, title: "Pod Logs" }, { id: "logs", kind: TabKind.POD_LOGS, title: "Pod Logs" },
]; ];

View File

@ -30,6 +30,8 @@ import type { LogTabData } from "../log-tab.store";
import { dockerPod, deploymentPod1 } from "./pod.mock"; import { dockerPod, deploymentPod1 } from "./pod.mock";
import { ThemeStore } from "../../../theme.store"; import { ThemeStore } from "../../../theme.store";
import { UserStore } from "../../../../common/user-store"; import { UserStore } from "../../../../common/user-store";
import { Cluster } from "../../../../main/cluster";
import { ApiManager } from "../../../api/api-manager";
jest.mock("electron", () => ({ jest.mock("electron", () => ({
app: { app: {
@ -70,6 +72,17 @@ const getFewPodsTabData = (): LogTabData => {
}; };
describe("<LogResourceSelector />", () => { describe("<LogResourceSelector />", () => {
beforeEach(() => {
ApiManager.createInstance(new Cluster({
id: "foo",
kubeConfigPath: "/bar",
}));
});
afterEach(() => {
ApiManager.resetInstance();
});
beforeEach(() => { beforeEach(() => {
UserStore.createInstance(); UserStore.createInstance();
ThemeStore.createInstance(); ThemeStore.createInstance();

View File

@ -19,11 +19,13 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import _ from "lodash";
import { PodsStore } from "../../+workloads-pods/pods.store"; import { PodsStore } from "../../+workloads-pods/pods.store";
import { Cluster } from "../../../../main/cluster"; import { Cluster } from "../../../../main/cluster";
import { Pod } from "../../../api/endpoints"; import { ApiManager } from "../../../api/api-manager";
import { dockStore } from "../dock.store"; import { Pod, podsApi } from "../../../api/endpoints";
import { logTabStore } from "../log-tab.store"; import { DockStore } from "../dock.store";
import { LogTabStore } from "../log-tab.store";
import { deploymentPod1, deploymentPod2, deploymentPod3, dockerPod } from "./pod.mock"; import { deploymentPod1, deploymentPod2, deploymentPod3, dockerPod } from "./pod.mock";
jest.mock("electron", () => ({ jest.mock("electron", () => ({
@ -32,22 +34,31 @@ jest.mock("electron", () => ({
}, },
})); }));
const podsStore = new PodsStore(new Cluster({
id: "foo",
kubeConfigPath: "/foo/bar",
}));
podsStore.items.push(new Pod(dockerPod));
podsStore.items.push(new Pod(deploymentPod1));
podsStore.items.push(new Pod(deploymentPod2));
describe("log tab store", () => { describe("log tab store", () => {
beforeEach(() => {
const store = ApiManager
.createInstance(new Cluster({
id: "foo",
kubeConfigPath: "/bar",
}))
.registerStore(PodsStore);
DockStore.createInstance();
LogTabStore.createInstance();
store.items.push(new Pod(dockerPod));
store.items.push(new Pod(deploymentPod1));
store.items.push(new Pod(deploymentPod2));
});
afterEach(() => { afterEach(() => {
logTabStore.reset(); ApiManager.resetInstance();
dockStore.reset(); LogTabStore.resetInstance();
DockStore.resetInstance();
}); });
it("creates log tab without sibling pods", () => { it("creates log tab without sibling pods", () => {
const logTabStore = LogTabStore.getInstance();
const dockStore = DockStore.getInstance();
const selectedPod = new Pod(dockerPod); const selectedPod = new Pod(dockerPod);
const selectedContainer = selectedPod.getAllContainers()[0]; const selectedContainer = selectedPod.getAllContainers()[0];
@ -66,6 +77,8 @@ describe("log tab store", () => {
}); });
it("creates log tab with sibling pods", () => { it("creates log tab with sibling pods", () => {
const logTabStore = LogTabStore.getInstance();
const dockStore = DockStore.getInstance();
const selectedPod = new Pod(deploymentPod1); const selectedPod = new Pod(deploymentPod1);
const siblingPod = new Pod(deploymentPod2); const siblingPod = new Pod(deploymentPod2);
const selectedContainer = selectedPod.getInitContainers()[0]; const selectedContainer = selectedPod.getInitContainers()[0];
@ -85,6 +98,9 @@ describe("log tab store", () => {
}); });
it("removes item from pods list if pod deleted from store", () => { it("removes item from pods list if pod deleted from store", () => {
const logTabStore = LogTabStore.getInstance();
const dockStore = DockStore.getInstance();
const podsStore = ApiManager.getInstance().getStore(podsApi);
const selectedPod = new Pod(deploymentPod1); const selectedPod = new Pod(deploymentPod1);
const selectedContainer = selectedPod.getInitContainers()[0]; const selectedContainer = selectedPod.getInitContainers()[0];
@ -105,6 +121,9 @@ describe("log tab store", () => {
}); });
it("adds item into pods list if new sibling pod added to store", () => { it("adds item into pods list if new sibling pod added to store", () => {
const logTabStore = LogTabStore.getInstance();
const dockStore = DockStore.getInstance();
const podsStore = ApiManager.getInstance().getStore(podsApi);
const selectedPod = new Pod(deploymentPod1); const selectedPod = new Pod(deploymentPod1);
const selectedContainer = selectedPod.getInitContainers()[0]; const selectedContainer = selectedPod.getInitContainers()[0];
@ -125,6 +144,9 @@ describe("log tab store", () => {
}); });
it("closes tab if no pods left in store", () => { it("closes tab if no pods left in store", () => {
const logTabStore = LogTabStore.getInstance();
const dockStore = DockStore.getInstance();
const podsStore = ApiManager.getInstance().getStore(podsApi);
const selectedPod = new Pod(deploymentPod1); const selectedPod = new Pod(deploymentPod1);
const selectedContainer = selectedPod.getInitContainers()[0]; const selectedContainer = selectedPod.getInitContainers()[0];

View File

@ -20,8 +20,8 @@
*/ */
import { autorun, observable, reaction, toJS } from "mobx"; import { autorun, observable, reaction, toJS } from "mobx";
import { autobind, createStorage, StorageHelper } from "../../utils"; import { autobind, createStorage, Singleton, StorageHelper } from "../../utils";
import { dockStore, TabId } from "./dock.store"; import { DockStore, TabId } from "./dock.store";
export interface DockTabStoreOptions { export interface DockTabStoreOptions {
autoInit?: boolean; // load data from storage when `storageKey` is provided and bind events, default: true autoInit?: boolean; // load data from storage when `storageKey` is provided and bind events, default: true
@ -31,11 +31,13 @@ export interface DockTabStoreOptions {
export type DockTabStorageState<T> = Record<TabId, T>; export type DockTabStorageState<T> = Record<TabId, T>;
@autobind() @autobind()
export class DockTabStore<T> { export class DockTabStore<T> extends Singleton {
protected storage?: StorageHelper<DockTabStorageState<T>>; protected storage?: StorageHelper<DockTabStorageState<T>>;
protected data = observable.map<TabId, T>(); protected data = observable.map<TabId, T>();
constructor(protected options: DockTabStoreOptions = {}) { constructor(protected options: DockTabStoreOptions = {}) {
super();
this.options = { this.options = {
autoInit: true, autoInit: true,
...this.options, ...this.options,
@ -60,7 +62,7 @@ export class DockTabStore<T> {
// clear data for closed tabs // clear data for closed tabs
autorun(() => { autorun(() => {
const currentTabs = dockStore.tabs.map(tab => tab.id); const currentTabs = DockStore.getInstance().tabs.map(tab => tab.id);
Array.from(this.data.keys()).forEach(tabId => { Array.from(this.data.keys()).forEach(tabId => {
if (!currentTabs.includes(tabId)) { if (!currentTabs.includes(tabId)) {

View File

@ -24,7 +24,7 @@ import "./dock-tab.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { autobind, cssNames, prevDefault } from "../../utils"; import { autobind, cssNames, prevDefault } from "../../utils";
import { dockStore, IDockTab } from "./dock.store"; import { DockStore, IDockTab } from "./dock.store";
import { Tab, TabProps } from "../tabs"; import { Tab, TabProps } from "../tabs";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { Menu, MenuItem } from "../menu"; import { Menu, MenuItem } from "../menu";
@ -44,11 +44,11 @@ export class DockTab extends React.Component<DockTabProps> {
@autobind() @autobind()
close() { close() {
dockStore.closeTab(this.tabId); DockStore.getInstance().closeTab(this.tabId);
} }
renderMenu() { renderMenu() {
const { closeTab, closeAllTabs, closeOtherTabs, closeTabsToTheRight, tabs, getTabIndex } = dockStore; const { closeTab, closeAllTabs, closeOtherTabs, closeTabsToTheRight, tabs, getTabIndex } = DockStore.getInstance();
const closeAllDisabled = tabs.length === 1; const closeAllDisabled = tabs.length === 1;
const closeOtherDisabled = tabs.length === 1; const closeOtherDisabled = tabs.length === 1;
const closeRightDisabled = getTabIndex(this.tabId) === tabs.length - 1; const closeRightDisabled = getTabIndex(this.tabId) === tabs.length - 1;

View File

@ -21,7 +21,7 @@
import MD5 from "crypto-js/md5"; import MD5 from "crypto-js/md5";
import { action, computed, IReactionOptions, observable, reaction } from "mobx"; import { action, computed, IReactionOptions, observable, reaction } from "mobx";
import { autobind, createStorage } from "../../utils"; import { autobind, createStorage, Singleton } from "../../utils";
import throttle from "lodash/throttle"; import throttle from "lodash/throttle";
export type TabId = string; export type TabId = string;
@ -50,7 +50,7 @@ export interface DockStorageState {
} }
@autobind() @autobind()
export class DockStore implements DockStorageState { export class DockStore extends Singleton implements DockStorageState {
readonly minHeight = 100; readonly minHeight = 100;
@observable fullSize = false; @observable fullSize = false;
@ -102,6 +102,7 @@ export class DockStore implements DockStorageState {
} }
constructor() { constructor() {
super();
this.init(); this.init();
} }
@ -267,5 +268,3 @@ export class DockStore implements DockStorageState {
this.storage?.reset(); this.storage?.reset();
} }
} }
export const dockStore = new DockStore();

View File

@ -32,7 +32,7 @@ import { ResizeDirection, ResizingAnchor } from "../resizing-anchor";
import { CreateResource } from "./create-resource"; import { CreateResource } from "./create-resource";
import { createResourceTab } from "./create-resource.store"; import { createResourceTab } from "./create-resource.store";
import { DockTabs } from "./dock-tabs"; import { DockTabs } from "./dock-tabs";
import { dockStore, IDockTab, TabKind } from "./dock.store"; import { DockStore, IDockTab, TabKind } from "./dock.store";
import { EditResource } from "./edit-resource"; import { EditResource } from "./edit-resource";
import { InstallChart } from "./install-chart"; import { InstallChart } from "./install-chart";
import { Logs } from "./logs"; import { Logs } from "./logs";
@ -47,7 +47,7 @@ interface Props {
@observer @observer
export class Dock extends React.Component<Props> { export class Dock extends React.Component<Props> {
onKeydown = (evt: React.KeyboardEvent<HTMLElement>) => { onKeydown = (evt: React.KeyboardEvent<HTMLElement>) => {
const { close, closeTab, selectedTab } = dockStore; const { close, closeTab, selectedTab } = DockStore.getInstance();
if (!selectedTab) return; if (!selectedTab) return;
const { code, ctrlKey, shiftKey } = evt.nativeEvent; const { code, ctrlKey, shiftKey } = evt.nativeEvent;
@ -63,7 +63,7 @@ export class Dock extends React.Component<Props> {
}; };
onChangeTab = (tab: IDockTab) => { onChangeTab = (tab: IDockTab) => {
const { open, selectTab } = dockStore; const { open, selectTab } = DockStore.getInstance();
open(); open();
selectTab(tab.id); selectTab(tab.id);
@ -87,7 +87,7 @@ export class Dock extends React.Component<Props> {
} }
renderTabContent() { renderTabContent() {
const { isOpen, height, selectedTab } = dockStore; const { isOpen, height, selectedTab } = DockStore.getInstance();
if (!isOpen || !selectedTab) return null; if (!isOpen || !selectedTab) return null;
@ -100,6 +100,7 @@ export class Dock extends React.Component<Props> {
render() { render() {
const { className } = this.props; const { className } = this.props;
const dockStore = DockStore.getInstance();
const { isOpen, toggle, tabs, toggleFillSize, selectedTab, hasTabs, fullSize } = dockStore; const { isOpen, toggle, tabs, toggleFillSize, selectedTab, hasTabs, fullSize } = dockStore;
return ( return (

View File

@ -20,3 +20,5 @@
*/ */
export * from "./dock"; export * from "./dock";
export * from "./dock.store";
export * from "./dock-tab.store";

View File

@ -27,7 +27,7 @@ import { ApiManager } from "../../api/api-manager";
import { IPodContainer, Pod, podsApi } from "../../api/endpoints"; import { IPodContainer, Pod, podsApi } from "../../api/endpoints";
import type { WorkloadKubeObject } from "../../api/workload-kube-object"; import type { WorkloadKubeObject } from "../../api/workload-kube-object";
import { DockTabStore } from "./dock-tab.store"; import { DockTabStore } from "./dock-tab.store";
import { dockStore, IDockTab, TabKind } from "./dock.store"; import { DockStore, IDockTab, TabKind } from "./dock.store";
export interface LogTabData { export interface LogTabData {
pods: Pod[]; pods: Pod[];
@ -92,11 +92,11 @@ export class LogTabStore extends DockTabStore<LogTabData> {
renameTab(tabId: string) { renameTab(tabId: string) {
const { selectedPod } = this.getData(tabId); const { selectedPod } = this.getData(tabId);
dockStore.renameTab(tabId, `Pod ${selectedPod.metadata.name}`); DockStore.getInstance().renameTab(tabId, `Pod ${selectedPod.metadata.name}`);
} }
private createDockTab(tabParams: Partial<IDockTab>) { private createDockTab(tabParams: Partial<IDockTab>) {
dockStore.createTab({ DockStore.getInstance().createTab({
kind: TabKind.POD_LOGS, kind: TabKind.POD_LOGS,
...tabParams ...tabParams
}, false); }, false);
@ -138,8 +138,6 @@ export class LogTabStore extends DockTabStore<LogTabData> {
private closeTab(tabId: string) { private closeTab(tabId: string) {
this.clearData(tabId); this.clearData(tabId);
dockStore.closeTab(tabId); DockStore.getInstance().closeTab(tabId);
} }
} }
export const logTabStore = new LogTabStore();

View File

@ -52,34 +52,35 @@ import { ReplicaSetStore } from "../components/+workloads-replicasets";
import { StatefulSetStore } from "../components/+workloads-statefulsets"; import { StatefulSetStore } from "../components/+workloads-statefulsets";
export function initApiManagerStores() { export function initApiManagerStores() {
ApiManager.getInstance() const am = ApiManager.getInstance();
.registerStore(HpaStore)
.registerStore(LimitRangesStore) am.registerStore(HpaStore)
.registerStore(ConfigMapsStore) am.registerStore(LimitRangesStore)
.registerStore(PodDisruptionBudgetsStore) am.registerStore(ConfigMapsStore)
.registerStore(ResourceQuotasStore) am.registerStore(PodDisruptionBudgetsStore)
.registerStore(SecretsStore) am.registerStore(ResourceQuotasStore)
.registerStore(CrdStore) am.registerStore(SecretsStore)
.registerStore(EventStore) am.registerStore(CrdStore)
.registerStore(NamespaceStore) am.registerStore(EventStore)
.registerStore(EndpointStore) am.registerStore(NamespaceStore)
.registerStore(IngressStore) am.registerStore(EndpointStore)
.registerStore(NetworkPolicyStore) am.registerStore(IngressStore)
.registerStore(ServiceStore) am.registerStore(NetworkPolicyStore)
.registerStore(NodesStore) am.registerStore(ServiceStore)
.registerStore(PodSecurityPoliciesStore) am.registerStore(NodesStore)
.registerStore(StorageClassStore) am.registerStore(PodSecurityPoliciesStore)
.registerStore(PersistentVolumeClaimStore) am.registerStore(StorageClassStore)
.registerStore(PersistentVolumesStore) am.registerStore(PersistentVolumeClaimStore)
.registerStore(ServiceAccountsStore) am.registerStore(PersistentVolumesStore)
.registerStore(CronJobStore) am.registerStore(ServiceAccountsStore)
.registerStore(DaemonSetStore) am.registerStore(CronJobStore)
.registerStore(DeploymentStore) am.registerStore(DaemonSetStore)
.registerStore(JobStore) am.registerStore(DeploymentStore)
.registerStore(PodsStore) am.registerStore(JobStore)
.registerStore(ReplicaSetStore) am.registerStore(PodsStore)
.registerStore(StatefulSetStore) am.registerStore(ReplicaSetStore)
.registerStore(RolesStore, [roleApi, clusterRoleApi]) am.registerStore(StatefulSetStore)
.registerStore(RoleBindingsStore, [roleBindingApi, clusterRoleBindingApi]) am.registerStore(RolesStore, [roleApi, clusterRoleApi])
.registerStore(ClusterObjectStore); am.registerStore(RoleBindingsStore, [roleBindingApi, clusterRoleBindingApi])
am.registerStore(ClusterObjectStore);
} }