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

Get tests to pass

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-06-20 16:08:12 -04:00
parent eb3b260cf5
commit 450ac378e1
15 changed files with 242 additions and 39 deletions

View File

@ -27,6 +27,7 @@ import assert from "assert";
import type { Logger } from "../logger";
import type { ReadFileSync } from "../fs/read-file-sync.injectable";
import type { EmitClusterConnectionUpdate } from "../../main/cluster/emit-connection-update.injectable";
import type { CreateKubectl } from "../../main/kubectl/create-kubectl.injectable";
export interface ClusterDependencies {
readonly directoryForKubeConfigs: string;
@ -34,7 +35,7 @@ export interface ClusterDependencies {
readonly detectorRegistry: DetectorRegistry;
createKubeconfigManager: (cluster: Cluster) => KubeconfigManager;
createContextHandler: (cluster: Cluster) => ClusterContextHandler;
createKubectl: (clusterVersion: string) => Kubectl;
createKubectl: CreateKubectl;
createAuthorizationReview: (config: KubeConfig) => CanI;
createListNamespaces: (config: KubeConfig) => ListNamespaces;
createVersionDetector: (cluster: Cluster) => VersionDetector;

View File

@ -0,0 +1,22 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ClusterId } from "../cluster-types";
import type { Cluster } from "./cluster";
import { getInjectable } from "@ogre-tools/injectable";
import clusterStoreInjectable from "../cluster-store/cluster-store.injectable";
export type GetClusterById = (id: ClusterId) => Cluster | undefined;
const getClusterByIdInjectable = getInjectable({
id: "get-cluster-by-id",
instantiate: (di): GetClusterById => {
const store = di.inject(clusterStoreInjectable);
return (id) => store.getById(id);
},
});
export default getClusterByIdInjectable;

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { ClusterId } from "../cluster-types";
import type { MessageChannel } from "../utils/channel/message-channel-injection-token";
export type SetVisibleClusterMessage = {
action: "set";
clusterId: ClusterId;
} | {
action: "clear";
};
export type SetVisibleClusterChannel = MessageChannel<SetVisibleClusterMessage>;
const setVisibleClusterChannelInjectable = getInjectable({
id: "set-visible-cluster-channel",
instantiate: (): SetVisibleClusterChannel => ({
id: "set-visible-cluster-channel",
}),
});
export default setVisibleClusterChannelInjectable;

View File

@ -4,7 +4,6 @@
*/
export const clusterSetFrameIdHandler = "cluster:set-frame-id";
export const clusterVisibilityHandler = "cluster:visibility";
export const clusterRefreshHandler = "cluster:refresh";
export const clusterDisconnectHandler = "cluster:disconnect";
export const clusterDeleteHandler = "cluster:delete";

View File

@ -67,3 +67,51 @@ exports[`cluster connection status when navigating to cluster connection renders
</div>
</body>
`;
exports[`cluster connection status when navigating to cluster connection when a connection update has been broadcast for first cluster when navigating to a different cluster renders 1`] = `
<body>
<div>
<div
class="StatusBar"
>
<div
class="leftSide"
/>
<div
class="rightSide"
/>
</div>
<div
class="ClusterView flex column align-center"
>
<div
class="status flex column box center align-center justify-center box center"
data-testid="cluster-status"
>
<div
class="flex items-center column gaps"
>
<h2>
minikube-2
</h2>
<div
class="Spinner spinner"
/>
<pre
class="kube-auth-out"
>
<p>
Connecting
</p>
</pre>
<pre />
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;

View File

@ -4,6 +4,8 @@
*/
import type { RenderResult } from "@testing-library/react";
import type { ObservableMap } from "mobx";
import { observable } from "mobx";
import type { ClusterStore } from "../../common/cluster-store/cluster-store";
import clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable";
import type { ClusterId } from "../../common/cluster-types";
@ -12,13 +14,20 @@ import type { NavigateToClusterView } from "../../common/front-end-routing/route
import navigateToClusterViewInjectable from "../../common/front-end-routing/routes/cluster-view/navigate-to-cluster-view.injectable";
import type { ReadFileSync } from "../../common/fs/read-file-sync.injectable";
import readFileSyncInjectable from "../../common/fs/read-file-sync.injectable";
import clusterManagerInjectable from "../../main/cluster-manager.injectable";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import createClusterInjectable from "../../renderer/create-cluster/create-cluster.injectable";
import createClusterInjectable from "../../main/create-cluster/create-cluster.injectable";
import createContextHandlerInjectable from "../../main/context-handler/create-context-handler.injectable";
import createKubeconfigManagerInjectable from "../../main/kubeconfig-manager/create-kubeconfig-manager.injectable";
import createKubectlInjectable from "../../main/kubectl/create-kubectl.injectable";
import type { KubeconfigManager } from "../../main/kubeconfig-manager/kubeconfig-manager";
import type { Kubectl } from "../../main/kubectl/kubectl";
import type { ContextHandler } from "../../main/context-handler/context-handler";
describe("cluster connection status", () => {
let clusterStore: ClusterStore;
let clusters: Map<ClusterId, Cluster>;
let clusters: ObservableMap<ClusterId, Cluster>;
let cluster: Cluster;
let cluster2: Cluster;
let applicationBuilder: ApplicationBuilder;
@ -64,8 +73,14 @@ describe("cluster connection status", () => {
};
applicationBuilder.dis.rendererDi.override(readFileSyncInjectable, () => readFileSyncMock);
applicationBuilder.dis.mainDi.override(readFileSyncInjectable, () => readFileSyncMock);
applicationBuilder.dis.mainDi.override(clusterManagerInjectable, () => ({}));
applicationBuilder.dis.mainDi.override(createKubeconfigManagerInjectable, () => () => ({} as KubeconfigManager));
applicationBuilder.dis.mainDi.override(createKubectlInjectable, () => () => ({} as Kubectl));
applicationBuilder.dis.mainDi.override(createContextHandlerInjectable, () => () => ({} as ContextHandler));
applicationBuilder.beforeRender(() => {
applicationBuilder.beforeApplicationStart(() => {
clusters = observable.map();
clusterStore = ({
clusters,
get clustersList() {
@ -76,10 +91,12 @@ describe("cluster connection status", () => {
applicationBuilder.dis.mainDi.override(clusterStoreInjectable, () => clusterStore);
applicationBuilder.dis.rendererDi.override(clusterStoreInjectable, () => clusterStore);
});
applicationBuilder.beforeRender(() => {
navigateToClusterView = applicationBuilder.dis.rendererDi.inject(navigateToClusterViewInjectable);
const createCluster = applicationBuilder.dis.rendererDi.inject(createClusterInjectable);
const createCluster = applicationBuilder.dis.mainDi.inject(createClusterInjectable);
cluster = createCluster({
contextName: "minikube",
@ -92,14 +109,14 @@ describe("cluster connection status", () => {
cluster2 = createCluster({
contextName: "minikube-2",
id: "some-cluster-id",
id: "some-cluster-id-2",
kubeConfigPath: "/some/file/path",
}, {
clusterServerUrl: "https://localhost:1234",
});
cluster2.activate = jest.fn(); // override for test
clusters = new Map([
clusters.replace([
[cluster.id, cluster],
[cluster2.id, cluster2],
]);

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { SetVisibleClusterChannel } from "../../common/cluster/set-visible-channel.injectable";
import setVisibleClusterChannelInjectable from "../../common/cluster/set-visible-channel.injectable";
import { messageChannelListenerInjectionToken } from "../../common/utils/channel/message-channel-listener-injection-token";
import type { MessageChannelListener } from "../../common/utils/channel/message-channel-listener-injection-token";
import clusterManagerInjectable from "../cluster-manager.injectable";
const setVisibileClusterListenerInjectable = getInjectable({
id: "set-visible-cluster-listener",
instantiate: (di) => {
const channel = di.inject(setVisibleClusterChannelInjectable);
const clusterManager = di.inject(clusterManagerInjectable);
return {
channel,
handler: (message) => {
switch (message.action) {
case "clear":
clusterManager.visibleCluster = undefined;
break;
case "set":
clusterManager.visibleCluster = message.clusterId;
break;
}
},
} as MessageChannelListener<SetVisibleClusterChannel>;
},
injectionToken: messageChannelListenerInjectionToken,
});
export default setVisibileClusterListenerInjectable;

View File

@ -5,7 +5,7 @@
import type { IpcMainInvokeEvent } from "electron";
import { BrowserWindow, Menu } from "electron";
import { clusterFrameMap } from "../../../../common/cluster-frames";
import { clusterSetFrameIdHandler, clusterVisibilityHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterDeleteHandler, clusterSetDeletingHandler, clusterClearDeletingHandler } from "../../../../common/ipc/cluster";
import { clusterSetFrameIdHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterDeleteHandler, clusterSetDeletingHandler, clusterClearDeletingHandler } from "../../../../common/ipc/cluster";
import type { ClusterId } from "../../../../common/cluster-types";
import { ClusterStore } from "../../../../common/cluster-store/cluster-store";
import { appEventBus } from "../../../../common/app-event-bus/event-bus";
@ -48,10 +48,6 @@ export const setupIpcMainHandlers = ({ applicationMenuItems, directoryForLensLoc
}
});
ipcMainOn(clusterVisibilityHandler, (event, clusterId?: ClusterId) => {
clusterManager.visibleCluster = clusterId;
});
ipcMainHandle(clusterRefreshHandler, (event, clusterId: ClusterId) => {
return ClusterStore.getInstance()
.getById(clusterId)

View File

@ -13,10 +13,12 @@ import kubectlBinaryNameInjectable from "./binary-name.injectable";
import bundledKubectlBinaryPathInjectable from "./bundled-binary-path.injectable";
import baseBundledBinariesDirectoryInjectable from "../../common/vars/base-bundled-binaries-dir.injectable";
export type CreateKubectl = (clusterVersion: string) => Kubectl;
const createKubectlInjectable = getInjectable({
id: "create-kubectl",
instantiate: (di) => {
instantiate: (di): CreateKubectl => {
const dependencies: KubectlDependencies = {
userStore: di.inject(userStoreInjectable),
directoryForKubectlBinaries: di.inject(directoryForKubectlBinariesInjectable),
@ -27,7 +29,7 @@ const createKubectlInjectable = getInjectable({
baseBundeledBinariesDirectory: di.inject(baseBundledBinariesDirectoryInjectable),
};
return (clusterVersion: string) => new Kubectl(dependencies, clusterVersion);
return (clusterVersion) => new Kubectl(dependencies, clusterVersion);
},
});

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { SetVisibleClusterChannel } from "../../common/cluster/set-visible-channel.injectable";
import setVisibleClusterChannelInjectable from "../../common/cluster/set-visible-channel.injectable";
import type { EmitChannelMessage } from "../../common/utils/channel/message-to-channel-injection-token";
import messageToChannelInjectable from "../utils/channel/message-to-channel.injectable";
export type SendSetVisibleCluster = EmitChannelMessage<SetVisibleClusterChannel>;
const sendSetVisibleClusterInjectable = getInjectable({
id: "send-set-visible-cluster",
instantiate: (di): SendSetVisibleCluster => {
const channel = di.inject(setVisibleClusterChannelInjectable);
const messageToChannel = di.inject(messageToChannelInjectable);
return (message) => messageToChannel(channel, message);
},
});
export default sendSetVisibleClusterInjectable;

View File

@ -3,11 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import getClusterByIdInjectable from "../../../common/cluster/get-by-id.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import sendSetVisibleClusterInjectable from "../../cluster/send-set-visible.injectable";
import { ClusterFrameHandler } from "./cluster-frame-handler";
import clusterFrameParentElementInjectable from "./parent-element.injectable";
const clusterFrameHandlerInjectable = getInjectable({
id: "cluster-frame-handler",
instantiate: () => new ClusterFrameHandler(),
instantiate: (di) => new ClusterFrameHandler({
getClusterById: di.inject(getClusterByIdInjectable),
sendSetVisibleCluster: di.inject(sendSetVisibleClusterInjectable),
parentElem: di.inject(clusterFrameParentElementInjectable),
logger: di.inject(loggerInjectable),
}),
});
export default clusterFrameHandlerInjectable;

View File

@ -4,24 +4,30 @@
*/
import { action, makeObservable, observable, when } from "mobx";
import logger from "../../../main/logger";
import { clusterVisibilityHandler } from "../../../common/ipc/cluster";
import { ClusterStore } from "../../../common/cluster-store/cluster-store";
import type { ClusterId } from "../../../common/cluster-types";
import type { Disposer } from "../../utils";
import { getClusterFrameUrl, onceDefined } from "../../utils";
import { ipcRenderer } from "electron";
import assert from "assert";
import type { GetClusterById } from "../../../common/cluster/get-by-id.injectable";
import type { SendSetVisibleCluster } from "../../cluster/send-set-visible.injectable";
import type { Logger } from "../../../common/logger";
export interface LensView {
isLoaded: boolean;
frame: HTMLIFrameElement;
}
export interface ClusterFrameHandlerDependencies {
getClusterById: GetClusterById;
sendSetVisibleCluster: SendSetVisibleCluster;
readonly parentElem: HTMLElement;
readonly logger: Logger;
}
export class ClusterFrameHandler {
private readonly views = observable.map<string, LensView>();
constructor() {
constructor(protected readonly dependencies: ClusterFrameHandlerDependencies) {
makeObservable(this);
}
@ -31,36 +37,33 @@ export class ClusterFrameHandler {
@action
public initView(clusterId: ClusterId) {
const cluster = ClusterStore.getInstance().getById(clusterId);
const parentElem = document.getElementById("lens-views");
assert(parentElem, "DOM with #lens-views must be present");
const cluster = this.dependencies.getClusterById(clusterId);
if (!cluster || this.views.has(clusterId)) {
return;
}
logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`);
this.dependencies.logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`);
const iframe = document.createElement("iframe");
iframe.id = `cluster-frame-${cluster.id}`;
iframe.name = cluster.contextName;
iframe.setAttribute("src", getClusterFrameUrl(clusterId));
iframe.addEventListener("load", action(() => {
logger.info(`[LENS-VIEW]: frame for clusterId=${clusterId} has loaded`);
this.dependencies.logger.info(`[LENS-VIEW]: frame for clusterId=${clusterId} has loaded`);
const view = this.views.get(clusterId);
assert(view, `view for ${clusterId} MUST still exist here`);
view.isLoaded = true;
}), { once: true });
this.views.set(clusterId, { frame: iframe, isLoaded: false });
parentElem.appendChild(iframe);
this.dependencies.parentElem.appendChild(iframe);
logger.info(`[LENS-VIEW]: waiting cluster to be ready, clusterId=${clusterId}`);
this.dependencies.logger.info(`[LENS-VIEW]: waiting cluster to be ready, clusterId=${clusterId}`);
const dispose = when(
() => cluster.ready,
() => logger.info(`[LENS-VIEW]: cluster is ready, clusterId=${clusterId}`),
() => this.dependencies.logger.info(`[LENS-VIEW]: cluster is ready, clusterId=${clusterId}`),
);
when(
@ -69,14 +72,14 @@ export class ClusterFrameHandler {
() => {
when(
() => {
const cluster = ClusterStore.getInstance().getById(clusterId);
const cluster = this.dependencies.getClusterById(clusterId);
return Boolean(!cluster || (cluster.disconnected && this.views.get(clusterId)?.isLoaded));
},
() => {
logger.info(`[LENS-VIEW]: remove dashboard, clusterId=${clusterId}`);
this.dependencies.logger.info(`[LENS-VIEW]: remove dashboard, clusterId=${clusterId}`);
this.views.delete(clusterId);
parentElem.removeChild(iframe);
this.dependencies.parentElem.removeChild(iframe);
dispose();
},
);
@ -90,15 +93,15 @@ export class ClusterFrameHandler {
// Clear the previous when ASAP
this.prevVisibleClusterChange?.();
logger.info(`[LENS-VIEW]: refreshing iframe views, visible cluster id=${clusterId}`);
ipcRenderer.send(clusterVisibilityHandler);
this.dependencies.logger.info(`[LENS-VIEW]: refreshing iframe views, visible cluster id=${clusterId}`);
this.dependencies.sendSetVisibleCluster({ action: "clear" });
for (const { frame: view } of this.views.values()) {
view.classList.add("hidden");
}
const cluster = clusterId
? ClusterStore.getInstance().getById(clusterId)
? this.dependencies.getClusterById(clusterId)
: undefined;
if (cluster && clusterId) {
@ -113,10 +116,10 @@ export class ClusterFrameHandler {
return undefined;
},
(view: LensView) => {
logger.info(`[LENS-VIEW]: cluster id=${clusterId} should now be visible`);
this.dependencies.logger.info(`[LENS-VIEW]: cluster id=${clusterId} should now be visible`);
view.frame.classList.remove("hidden");
view.frame.focus();
ipcRenderer.send(clusterVisibilityHandler, clusterId);
this.dependencies.sendSetVisibleCluster({ action: "set", clusterId });
},
);
}

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
const clusterFrameParentElementInjectable = getInjectable({
id: "cluster-frame-parent-element",
instantiate: () => {
const elem = document.getElementById("#lens-view");
assert(elem, "DOM with #lens-views must be present");
return elem;
},
causesSideEffects: true,
});
export default clusterFrameParentElementInjectable;

View File

@ -65,6 +65,7 @@ import setupSystemCaInjectable from "./frames/root-frame/setup-system-ca.injecta
import extensionShouldBeEnabledForClusterFrameInjectable from "./extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
import { asyncComputed } from "@ogre-tools/injectable-react";
import forceUpdateModalRootFrameComponentInjectable from "./application-update/force-update-modal/force-update-modal-root-frame-component.injectable";
import clusterFrameParentElementInjectable from "./components/cluster-manager/parent-element.injectable";
import legacyOnChannelListenInjectable from "./ipc/legacy-channel-listen.injectable";
import getEntitySettingCommandsInjectable from "./components/command-palette/registered-commands/get-entity-setting-commands.injectable";
import storageSaveDelayInjectable from "./utils/create-storage/storage-save-delay.injectable";
@ -108,6 +109,7 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {})
di.override(terminalSpawningPoolInjectable, () => document.createElement("div"));
di.override(hostedClusterIdInjectable, () => undefined);
di.override(clusterFrameParentElementInjectable, () => document.createElement("div"));
di.override(getAbsolutePathInjectable, () => getAbsolutePathFake);
di.override(joinPathsInjectable, () => joinPathsFake);

View File

@ -19,7 +19,6 @@ const routePathParametersInjectable = getInjectable({
return <Param>(route: Route<Param>): IComputedValue<Partial<Param>> => (
getOrInsertWith(pathParametersCache, route, () => computed(() => (
console.log(currentPath.get()),
matchPath(currentPath.get(), {
path: route.path,
exact: true,