Skip to content

Commit

Permalink
Kludge "isEnabledForCluster" work again for kube object menu items
Browse files Browse the repository at this point in the history
Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
  • Loading branch information
jansav committed Jul 11, 2022
1 parent 392b2ef commit 6d0bda5
Show file tree
Hide file tree
Showing 14 changed files with 1,639 additions and 147 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder";
import type { KubernetesCluster } from "../../../../common/catalog-entities";
import { getApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder";
import { getExtensionFakeFor } from "../../../../renderer/components/test-utils/get-extension-fake";
import { getInjectable } from "@ogre-tools/injectable";
import { frontEndRouteInjectionToken } from "../../../../common/front-end-routing/front-end-route-injection-token";
import { computed } from "mobx";
import React from "react";
import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token";
import { routeSpecificComponentInjectionToken } from "../../../../renderer/routes/route-specific-component-injection-token";
import { KubeObject } from "../../../../common/k8s-api/kube-object";
import extensionShouldBeEnabledForClusterFrameInjectable from "../../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
import { KubeObjectMenu } from "../../../../renderer/components/kube-object-menu";
import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";

describe("disable kube object menu items when cluster is not relevant", () => {
let builder: ApplicationBuilder;
let rendered: RenderResult;
let isEnabledForClusterMock: AsyncFnMock<
(cluster: KubernetesCluster) => Promise<boolean>
>;

beforeEach(async () => {
builder = getApplicationBuilder();

builder.beforeApplicationStart(({ mainDi }) => {
mainDi.override(apiManagerInjectable, () => ({}));
});

const rendererDi = builder.dis.rendererDi;

rendererDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable);

rendererDi.register(testRouteInjectable, testRouteComponentInjectable);

builder.setEnvironmentToClusterFrame();

const getExtensionFake = getExtensionFakeFor(builder);

isEnabledForClusterMock = asyncFn();

const testExtension = getExtensionFake({
id: "test-extension-id",
name: "test-extension",

rendererOptions: {
isEnabledForCluster: isEnabledForClusterMock,

kubeObjectMenuItems: [
{
kind: "some-kind",
apiVersions: ["some-api-version"],
components: {
MenuItem: () => (
<div data-testid="some-test-id">Some menu item</div>
),
},
},
],
},
});

rendered = await builder.render();

const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken);
const testRoute = rendererDi.inject(testRouteInjectable);

navigateToRoute(testRoute);

builder.extensions.enable(testExtension);
});

describe("given not yet known if extension should be enabled for the cluster, when navigating", () => {
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});

it("does not show the kube object menu item", () => {
const actual = rendered.queryByTestId("some-test-id");

expect(actual).not.toBeInTheDocument();
});
});

describe("given extension shouldn't be enabled for the cluster, when navigating", () => {
beforeEach(async () => {
await isEnabledForClusterMock.resolve(false);
});

it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});

it("does not show the kube object menu item", () => {
const actual = rendered.queryByTestId("some-test-id");

expect(actual).not.toBeInTheDocument();
});
});

describe("given extension should be enabled for the cluster, when navigating", () => {
beforeEach(async () => {
await isEnabledForClusterMock.resolve(true);
});

it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});

it("shows the kube object menu item", () => {
const actual = rendered.getByTestId("some-test-id");

expect(actual).toBeInTheDocument();
});
});
});

const testRouteInjectable = getInjectable({
id: "test-route",

instantiate: () => ({
path: "/test-route",
clusterFrame: true,
isEnabled: computed(() => true),
}),

injectionToken: frontEndRouteInjectionToken,
});

const testRouteComponentInjectable = getInjectable({
id: "test-route-component",

instantiate: (di) => ({
route: di.inject(testRouteInjectable),

Component: () => (
<KubeObjectMenu
toolbar={true}
object={getKubeObjectStub("some-kind", "some-api-version")}
/>
),
}),

injectionToken: routeSpecificComponentInjectionToken,
});

const getKubeObjectStub = (kind: string, apiVersion: string) =>
KubeObject.create({
apiVersion,
kind,
metadata: {
uid: "some-uid",
name: "some-name",
resourceVersion: "some-resource-version",
namespace: "some-namespace",
selfLink: "",
},
});

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { IComputedValue } from "mobx";
import type { KubeObjectMenuProps } from "./kube-object-menu";
import type { KubeObject } from "../../../common/k8s-api/kube-object";
import type React from "react";

export type KubeObjectMenuItemComponent = React.ElementType<
KubeObjectMenuProps<KubeObject>
>;

export interface KubeObjectMenuItem {
kind: string;
apiVersions: string[];
enabled: IComputedValue<boolean>;
Component: KubeObjectMenuItemComponent;
orderNumber: number;
}

export const kubeObjectMenuItemInjectionToken = getInjectionToken<KubeObjectMenuItem>({
id: "kube-object-menu-item-injection-token",
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* 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 { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
import getRandomIdInjectable from "../../../common/utils/get-random-id.injectable";
import extensionShouldBeEnabledForClusterFrameInjectable from "../../extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
import { kubeObjectMenuItemInjectionToken } from "./kube-object-menu-item-injection-token";
import { computed } from "mobx";

const kubeObjectMenuItemRegistratorInjectable = getInjectable({
id: "kube-object-menu-item-registrator",

instantiate: (di) => {
const getRandomId = di.inject(getRandomIdInjectable);

const getExtensionShouldBeEnabledForClusterFrame = (
extension: LensRendererExtension,
) =>
di.inject(extensionShouldBeEnabledForClusterFrameInjectable, extension);

return (ext) => {
const extension = ext as LensRendererExtension;

const extensionShouldBeEnabledForClusterFrame =
getExtensionShouldBeEnabledForClusterFrame(extension);

return extension.kubeObjectMenuItems.map((registration) => {
const id = `kube-object-menu-item-registration-from-${
extension.sanitizedExtensionId
}-${getRandomId()}`;

return getInjectable({
id,

instantiate: () => ({
kind: registration.kind,
apiVersions: registration.apiVersions,
Component: registration.components.MenuItem,

enabled: computed(() =>
extensionShouldBeEnabledForClusterFrame.value.get(),
),

orderNumber: 100,
}),

injectionToken: kubeObjectMenuItemInjectionToken,
});
});
};
},

injectionToken: extensionRegistratorInjectionToken,
});

export default kubeObjectMenuItemRegistratorInjectable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";

import type { KubeObject } from "../../../common/k8s-api/kube-object";
import { computed } from "mobx";
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
import { kubeObjectMenuItemInjectionToken } from "./kube-object-menu-item-injection-token";
import { filter, map, sortBy } from "lodash/fp";
import { pipeline } from "@ogre-tools/fp";

const kubeObjectMenuItemsInjectable = getInjectable({
id: "kube-object-menu-items",

instantiate: (di, kubeObject: KubeObject) => {
const computedInjectMany = di.inject(computedInjectManyInjectable);
const menuItems = computedInjectMany(kubeObjectMenuItemInjectionToken);

return computed(() =>
pipeline(
menuItems.get(),

filter(
(item) =>
item.kind === kubeObject?.kind &&
item.apiVersions.includes(kubeObject?.apiVersion) &&
item.enabled.get(),
),

sortBy((item) => item.orderNumber),
map((item) => item.Component),
),
);
},

lifecycle: lifecycleEnum.keyedSingleton({
getInstanceKey: (di, kubeObject: KubeObject) => kubeObject?.getId(),
}),
});

export default kubeObjectMenuItemsInjectable;
Loading

0 comments on commit 6d0bda5

Please sign in to comment.