diff --git a/src/renderer/components/+preferences/__tests__/preferences.test.tsx b/src/renderer/components/+preferences/__tests__/preferences.test.tsx
index c4fc53d73f..9008a5e131 100644
--- a/src/renderer/components/+preferences/__tests__/preferences.test.tsx
+++ b/src/renderer/components/+preferences/__tests__/preferences.test.tsx
@@ -29,6 +29,46 @@ import { AppPreferenceRegistry } from "../../../../extensions/registries";
import { computed } from "mobx";
import { MemoryRouter } from "react-router-dom";
import "@testing-library/jest-dom/extend-expect";
+import { LensExtension } from "../../../../extensions/lens-extension";
+
+const extension = {
+ id: "/absolute/path/test",
+ manifest: {
+ name: "@k8slens/test",
+ version: "1.2.3",
+ },
+ absolutePath: "/absolute/path",
+ manifestPath: "/symlinked/path/package.json",
+ isBundled: false,
+ isEnabled: true,
+ isCompatible: true,
+}
+
+const crdExtension = {
+ id: "/absolute/path/crd",
+ manifest: {
+ name: "@k8slens/crd-example",
+ version: "1.2.3",
+ },
+ absolutePath: "/absolute/path/crd",
+ manifestPath: "/symlinked/path/package.json",
+ isBundled: false,
+ isEnabled: true,
+ isCompatible: true,
+}
+
+const sampleExtension = {
+ id: "/absolute/path/sample",
+ manifest: {
+ name: "@k8slens/sample",
+ version: "1.2.3",
+ },
+ absolutePath: "/absolute/path/sample",
+ manifestPath: "/symlinked/path/package.json",
+ isBundled: false,
+ isEnabled: true,
+ isCompatible: true,
+}
describe("Preferences", () => {
let di: ConfigurableDependencyInjectionContainer;
@@ -39,25 +79,86 @@ describe("Preferences", () => {
render = renderFor(di);
AppPreferenceRegistry.createInstance();
+ AppPreferenceRegistry.getInstance().add([
+ {
+ components: {
+ Input: () =>
input
,
+ Hint: () => hint
+ },
+ extensionId: "@k8slens/test",
+ id: "example-preferences",
+ title: "Example Preferences",
+ }
+ ], new LensExtension(extension));
+ AppPreferenceRegistry.getInstance().add([
+ {
+ components: {
+ Input: () => crd input
,
+ Hint: () => crd hint
+ },
+ extensionId: "@k8slens/crd-example",
+ title: "Example Preferences",
+ }
+ ], new LensExtension(crdExtension));
+ AppPreferenceRegistry.getInstance().add([
+ {
+ components: {
+ Input: () => sample input
,
+ Hint: () => sample hint
+ },
+ extensionId: "@k8slens/crd-example",
+ title: "Extension with duplicated name",
+ }
+ ], new LensExtension(crdExtension))
+ });
+
+ afterEach(() => {
+ AppPreferenceRegistry.resetInstance();
});
it("renders w/o errors", () => {
- di.override(userExtensionsInjectable, () => {
- return computed(() => [] as any);
- });
-
const { container } = render();
expect(container).toBeInstanceOf(HTMLElement);
});
- it("doesn't render extension settings tabs if no extensions found", () => {
- di.override(userExtensionsInjectable, () => {
- return computed(() => [] as any);
+ describe("Extension custom settings", () => {
+ it("doesn't render custom settings tabs if no extensions found", () => {
+ const { queryByTestId } = render();
+
+ expect(queryByTestId("custom-settings")).not.toBeInTheDocument();
});
- const { queryByTestId } = render();
+ it("renders custom settings tabs if registered extensions found", () => {
+ di.override(userExtensionsInjectable, () => {
+ return computed(() => [extension]);
+ });
- expect(queryByTestId("custom-settings")).not.toBeInTheDocument();
+ const { getByTestId } = render();
+
+ expect(getByTestId("custom-settings")).toBeInTheDocument();
+ })
+
+ it("renders tabs for each extension having custom settings", () => {
+ di.override(userExtensionsInjectable, () => {
+ return computed(() => [extension, crdExtension, sampleExtension]);
+ });
+
+ const { getByText, queryByText } = render();
+
+ expect(getByText("@k8slens/test")).toBeInTheDocument();
+ expect(getByText("@k8slens/crd-example")).toBeInTheDocument();
+ expect(queryByText("@k8slens/sample")).not.toBeInTheDocument();
+ });
+
+ it("renders extension tab only once", () => {
+ di.override(userExtensionsInjectable, () => {
+ return computed(() => [crdExtension]);
+ });
+
+ const { getAllByText } = render();
+
+ expect(getAllByText("@k8slens/crd-example").length).toBe(1);
+ })
});
});
diff --git a/src/renderer/components/+preferences/extension-settings-page.tsx b/src/renderer/components/+preferences/extension-settings-page.tsx
index 38836da0b0..469a2daddc 100644
--- a/src/renderer/components/+preferences/extension-settings-page.tsx
+++ b/src/renderer/components/+preferences/extension-settings-page.tsx
@@ -19,7 +19,6 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-import { observer } from "mobx-react";
import React from "react";
import { matchPath, RouteComponentProps } from "react-router";
import { extensionSettingsRoute } from "../../../common/routes";
@@ -29,13 +28,13 @@ import { ExtensionSettings } from "./extension-settings";
interface Props extends RouteComponentProps<{ extensionId?: string }> {
}
-export const ExtensionSettingsPage = observer((props: Props) => {
+export const ExtensionSettingsPage = (props: Props) => {
// https://github.com/remix-run/react-router/issues/5870#issuecomment-394194338
const match = matchPath<{ extensionId: string }>(props.history.location.pathname, {
path: extensionSettingsRoute.path,
exact: true,
});
- const extensionId = decodeURIComponent(match.params.extensionId);
+ const extensionId = decodeURIComponent(match?.params?.extensionId);
const settings = AppPreferenceRegistry.getInstance().getItems();
const currentSettings = settings.filter(setting => setting.extensionId == extensionId);
@@ -57,4 +56,4 @@ export const ExtensionSettingsPage = observer((props: Props) => {
{renderContent()}
);
-});
+};
diff --git a/src/renderer/components/+preferences/preferences.tsx b/src/renderer/components/+preferences/preferences.tsx
index d1bd6b9eed..1f8a023891 100644
--- a/src/renderer/components/+preferences/preferences.tsx
+++ b/src/renderer/components/+preferences/preferences.tsx
@@ -48,13 +48,13 @@ import { Kubernetes } from "./kubernetes";
import { Editor } from "./editor";
import { LensProxy } from "./proxy";
import { Telemetry } from "./telemetry";
-import { Extensions } from "./extensions";
import { sentryDsn } from "../../../common/vars";
import { withInjectables } from "@ogre-tools/injectable-react";
import type { InstalledExtension } from "../../../extensions/extension-discovery";
import userExtensionsInjectable from "../+extensions/user-extensions/user-extensions.injectable";
import { ExtensionSettingsPage } from "./extension-settings-page";
import { Icon } from "../icon";
+import { uniqBy } from "lodash";
interface Dependencies {
userExtensions: IComputedValue;
@@ -70,11 +70,11 @@ class Preferences extends React.Component {
}
renderNavigation() {
- const preferenceRegistries = AppPreferenceRegistry.getInstance().getItems();
+ const preferenceRegistries = uniqBy(AppPreferenceRegistry.getInstance().getItems(), "extensionId");
const telemetryExtensions = preferenceRegistries.filter(e => e.showInPreferencesTab == "telemetry");
const currentLocation = navigation.location.pathname;
const isActive = (route: RouteProps) => !!matchPath(currentLocation, { path: route.path, exact: route.exact });
- const extensions = this.props.userExtensions.get().filter(extension =>
+ const extensionsWithSettings = this.props.userExtensions.get().filter(extension =>
preferenceRegistries.some(registry => registry.extensionId.includes(extension.manifest.name) && !registry.showInPreferencesTab),
);
@@ -93,12 +93,12 @@ class Preferences extends React.Component {
{preferenceRegistries.filter(e => !e.showInPreferencesTab).length > 0 &&
}
- {extensions.length > 0 && (
+ {extensionsWithSettings.length > 0 && (
Custom settings
- {extensions.map(extension => (
+ {extensionsWithSettings.map(extension => (
{
-