From c0cc56dba2bdc4a5385de2c39e95b74c7548ba2b Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Tue, 17 May 2022 13:33:39 +0300 Subject: [PATCH] Fix merge conflicts Co-authored-by: Mikko Aspiala Signed-off-by: Janne Savolainen --- .../navigation-to-helm-charts.test.ts.snap | 4 +- .../preferences/closing-preferences.test.tsx | 13 - ...igation-to-application-preferences.test.ts | 15 -- .../navigation-to-editor-preferences.test.ts | 16 -- ...to-extension-specific-preferences.test.tsx | 15 -- ...vigation-to-kubernetes-preferences.test.ts | 17 -- .../navigation-to-proxy-preferences.test.ts | 15 -- ...vigation-to-telemetry-preferences.test.tsx | 15 -- ...navigation-to-terminal-preferences.test.ts | 15 -- .../navigation-using-application-menu.test.ts | 17 -- src/common/__tests__/hotbar-store.test.ts | 4 +- .../catalog-category-registry.injectable.ts | 14 - .../has-category-for-entity.injectable.ts | 1 + src/common/cluster/cluster.ts | 4 +- src/common/k8s-api/__tests__/kube-api.test.ts | 8 +- src/common/runnable/run-many-for.test.ts | 8 +- src/common/runnable/run-many-for.ts | 48 ++-- src/common/runnable/run-many-sync-for.ts | 44 +-- .../legacy-global-di-for-extension-api.ts | 8 +- .../get-electron-app-path.test.ts | 2 +- .../kubeconfig-sync/manager.injectable.ts | 2 +- .../sync-weblinks.injectable.ts | 2 +- .../catalog-sync-to-renderer.injectable.ts | 2 +- .../catalog/entity-registry.injectable.ts | 5 +- .../cluster-detectors/detector-registry.ts | 2 +- src/main/cluster-manager.injectable.ts | 2 +- src/main/cluster-manager.ts | 4 +- .../create-context-handler.injectable.ts | 2 +- .../setup-ipc-main-handlers.injectable.ts | 5 +- .../setup-ipc-main-handlers.ts | 7 +- src/main/getDiForUnitTesting.ts | 7 +- src/main/helm/helm-repo-manager.injectable.ts | 75 +++--- .../ipc/ask-user-for-file-paths.injectable.ts | 36 +++ src/main/ipc/dialog.ts | 15 -- .../create-kubeconfig-manager.injectable.ts | 2 +- src/main/lens-proxy/lens-proxy.ts | 5 +- src/main/router/router.test.ts | 4 +- src/main/router/router.ts | 2 +- .../get-metric-providers-route.injectable.ts | 5 +- .../terminal-shell-env-modify.injectable.ts | 2 +- .../create-lens-window.injectable.ts | 8 +- .../initialize-extensions.injectable.ts | 2 +- .../start-kube-config-sync.injectable.ts | 2 +- .../stop-kube-config-sync.injectable.ts | 2 +- .../setup-hotbar-store.injectable.ts | 2 +- .../runnables/setup-lens-proxy.injectable.ts | 2 +- .../api/setup-on-api-errors.injectable.ts | 14 +- .../release-details/release-details.tsx | 2 +- ...ts => cluster-frame-handler.injectable.ts} | 8 +- ...lens-views.ts => cluster-frame-handler.ts} | 0 .../cluster-manager/cluster-view.tsx | 6 +- .../__tests__/delete-cluster-dialog.test.tsx | 254 +++++++++--------- src/renderer/components/menu/menu-actions.tsx | 1 + .../test-utils/get-application-builder.tsx | 14 +- .../create-cluster.injectable.ts | 7 +- src/renderer/getDiForUnitTesting.tsx | 25 +- .../observable-history.injectable.ts | 1 + ...extension-route-registrator.injectable.tsx | 4 +- src/test-utils/override-ipc-bridge.ts | 4 +- 59 files changed, 358 insertions(+), 469 deletions(-) delete mode 100644 src/common/catalog/catalog-category-registry.injectable.ts create mode 100644 src/main/ipc/ask-user-for-file-paths.injectable.ts delete mode 100644 src/main/ipc/dialog.ts rename src/renderer/components/cluster-manager/{lens-views.injectable.ts => cluster-frame-handler.injectable.ts} (56%) rename src/renderer/components/cluster-manager/{lens-views.ts => cluster-frame-handler.ts} (100%) diff --git a/src/behaviours/helm-charts/__snapshots__/navigation-to-helm-charts.test.ts.snap b/src/behaviours/helm-charts/__snapshots__/navigation-to-helm-charts.test.ts.snap index 92b483ba08..4f1555049d 100644 --- a/src/behaviours/helm-charts/__snapshots__/navigation-to-helm-charts.test.ts.snap +++ b/src/behaviours/helm-charts/__snapshots__/navigation-to-helm-charts.test.ts.snap @@ -340,7 +340,7 @@ exports[`helm-charts - navigation to Helm charts when navigating to Helm charts class="items box grow flex column" >
{ let applicationBuilder: ApplicationBuilder; @@ -39,16 +36,6 @@ describe("preferences - closing-preferences", () => { rendererDi.register(testFrontPageRouteComponentInjectable); rendererDi.register(testNavigationItemInjectable); - const userStoreStub = { - extensionRegistryUrl: { customUrl: "some-custom-url" }, - } as unknown as UserStore; - - rendererDi.override(userStoreInjectable, () => userStoreStub); - rendererDi.override(ipcRendererInjectable, () => ({ - on: jest.fn(), - invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge - } as never)); - rendererDi.override(navigateToFrontPageInjectable, (di) => { const navigateToRoute = di.inject(navigateToRouteInjectionToken); const testFrontPage = di.inject(testFrontPageRouteInjectable); diff --git a/src/behaviours/preferences/navigation-to-application-preferences.test.ts b/src/behaviours/preferences/navigation-to-application-preferences.test.ts index 868f593c58..c91f091323 100644 --- a/src/behaviours/preferences/navigation-to-application-preferences.test.ts +++ b/src/behaviours/preferences/navigation-to-application-preferences.test.ts @@ -5,28 +5,13 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import type { UserStore } from "../../common/user-store"; import navigateToProxyPreferencesInjectable from "../../common/front-end-routing/routes/preferences/proxy/navigate-to-proxy-preferences.injectable"; -import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable"; describe("preferences - navigation to application preferences", () => { let applicationBuilder: ApplicationBuilder; beforeEach(() => { applicationBuilder = getApplicationBuilder(); - - applicationBuilder.beforeApplicationStart(({ rendererDi }) => { - const userStoreStub = { - extensionRegistryUrl: { customUrl: "some-custom-url" }, - } as unknown as UserStore; - - rendererDi.override(userStoreInjectable, () => userStoreStub); - rendererDi.override(ipcRendererInjectable, () => ({ - on: jest.fn(), - invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge - } as never)); - }); }); describe("given in some child page of preferences, when rendered", () => { diff --git a/src/behaviours/preferences/navigation-to-editor-preferences.test.ts b/src/behaviours/preferences/navigation-to-editor-preferences.test.ts index 3803a4a6fc..1b74b94eb4 100644 --- a/src/behaviours/preferences/navigation-to-editor-preferences.test.ts +++ b/src/behaviours/preferences/navigation-to-editor-preferences.test.ts @@ -5,28 +5,12 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import type { UserStore } from "../../common/user-store"; -import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable"; describe("preferences - navigation to editor preferences", () => { let applicationBuilder: ApplicationBuilder; beforeEach(() => { applicationBuilder = getApplicationBuilder(); - - applicationBuilder.beforeApplicationStart(({ rendererDi }) => { - const userStoreStub = { - extensionRegistryUrl: { customUrl: "some-custom-url" }, - editorConfiguration: { minimap: {}, tabSize: 42, fontSize: 42 }, - } as unknown as UserStore; - - rendererDi.override(userStoreInjectable, () => userStoreStub); - rendererDi.override(ipcRendererInjectable, () => ({ - on: jest.fn(), - invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge - } as never)); - }); }); describe("given in preferences, when rendered", () => { diff --git a/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx b/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx index 0cdaa0422a..f44fb9bf20 100644 --- a/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx +++ b/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx @@ -5,30 +5,15 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import type { UserStore } from "../../common/user-store"; import React from "react"; import type { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake"; import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake"; -import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable"; describe("preferences - navigation to extension specific preferences", () => { let applicationBuilder: ApplicationBuilder; beforeEach(() => { applicationBuilder = getApplicationBuilder(); - - applicationBuilder.beforeApplicationStart(({ rendererDi }) => { - const userStoreStub = { - extensionRegistryUrl: { customUrl: "some-custom-url" }, - } as unknown as UserStore; - - rendererDi.override(userStoreInjectable, () => userStoreStub); - rendererDi.override(ipcRendererInjectable, () => ({ - on: jest.fn(), - invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge - } as never)); - }); }); describe("given in preferences, when rendered", () => { diff --git a/src/behaviours/preferences/navigation-to-kubernetes-preferences.test.ts b/src/behaviours/preferences/navigation-to-kubernetes-preferences.test.ts index d511268d0b..e404e1f489 100644 --- a/src/behaviours/preferences/navigation-to-kubernetes-preferences.test.ts +++ b/src/behaviours/preferences/navigation-to-kubernetes-preferences.test.ts @@ -5,29 +5,12 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import type { UserStore } from "../../common/user-store"; -import { observable } from "mobx"; -import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable"; describe("preferences - navigation to kubernetes preferences", () => { let applicationBuilder: ApplicationBuilder; beforeEach(() => { applicationBuilder = getApplicationBuilder(); - - applicationBuilder.beforeApplicationStart(({ rendererDi }) => { - const userStoreStub = { - extensionRegistryUrl: { customUrl: "some-custom-url" }, - syncKubeconfigEntries: observable.map(), - } as unknown as UserStore; - - rendererDi.override(userStoreInjectable, () => userStoreStub); - rendererDi.override(ipcRendererInjectable, () => ({ - on: jest.fn(), - invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge - } as never)); - }); }); describe("given in preferences, when rendered", () => { diff --git a/src/behaviours/preferences/navigation-to-proxy-preferences.test.ts b/src/behaviours/preferences/navigation-to-proxy-preferences.test.ts index 5e52d2232c..3d2c8d6d2c 100644 --- a/src/behaviours/preferences/navigation-to-proxy-preferences.test.ts +++ b/src/behaviours/preferences/navigation-to-proxy-preferences.test.ts @@ -5,27 +5,12 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import type { UserStore } from "../../common/user-store"; -import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable"; describe("preferences - navigation to proxy preferences", () => { let applicationBuilder: ApplicationBuilder; beforeEach(() => { applicationBuilder = getApplicationBuilder(); - - applicationBuilder.beforeApplicationStart(({ rendererDi }) => { - const userStoreStub = { - extensionRegistryUrl: { customUrl: "some-custom-url" }, - } as unknown as UserStore; - - rendererDi.override(userStoreInjectable, () => userStoreStub); - rendererDi.override(ipcRendererInjectable, () => ({ - on: jest.fn(), - invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge - } as never)); - }); }); describe("given in preferences, when rendered", () => { diff --git a/src/behaviours/preferences/navigation-to-telemetry-preferences.test.tsx b/src/behaviours/preferences/navigation-to-telemetry-preferences.test.tsx index 56b4c00762..f16c2d809b 100644 --- a/src/behaviours/preferences/navigation-to-telemetry-preferences.test.tsx +++ b/src/behaviours/preferences/navigation-to-telemetry-preferences.test.tsx @@ -8,29 +8,14 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import type { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake"; import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake"; -import type { UserStore } from "../../common/user-store"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; import navigateToTelemetryPreferencesInjectable from "../../common/front-end-routing/routes/preferences/telemetry/navigate-to-telemetry-preferences.injectable"; import sentryDnsUrlInjectable from "../../renderer/components/+preferences/sentry-dns-url.injectable"; -import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable"; describe("preferences - navigation to telemetry preferences", () => { let applicationBuilder: ApplicationBuilder; beforeEach(() => { applicationBuilder = getApplicationBuilder(); - - applicationBuilder.beforeApplicationStart(({ rendererDi }) => { - const userStoreStub = { - extensionRegistryUrl: { customUrl: "some-custom-url" }, - } as unknown as UserStore; - - rendererDi.override(userStoreInjectable, () => userStoreStub); - rendererDi.override(ipcRendererInjectable, () => ({ - on: jest.fn(), - invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge - } as never)); - }); }); describe("given in preferences, when rendered", () => { diff --git a/src/behaviours/preferences/navigation-to-terminal-preferences.test.ts b/src/behaviours/preferences/navigation-to-terminal-preferences.test.ts index 19226bfa2a..5d7e1e08fb 100644 --- a/src/behaviours/preferences/navigation-to-terminal-preferences.test.ts +++ b/src/behaviours/preferences/navigation-to-terminal-preferences.test.ts @@ -5,11 +5,7 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import type { UserStore } from "../../common/user-store"; -import { observable } from "mobx"; import defaultShellInjectable from "../../renderer/components/+preferences/default-shell.injectable"; -import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable"; describe("preferences - navigation to terminal preferences", () => { let applicationBuilder: ApplicationBuilder; @@ -18,18 +14,7 @@ describe("preferences - navigation to terminal preferences", () => { applicationBuilder = getApplicationBuilder(); applicationBuilder.beforeApplicationStart(({ rendererDi }) => { - const userStoreStub = { - extensionRegistryUrl: { customUrl: "some-custom-url" }, - syncKubeconfigEntries: observable.map(), - terminalConfig: { fontSize: 42 }, - } as unknown as UserStore; - - rendererDi.override(userStoreInjectable, () => userStoreStub); rendererDi.override(defaultShellInjectable, () => "some-default-shell"); - rendererDi.override(ipcRendererInjectable, () => ({ - on: jest.fn(), - invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge - } as never)); }); }); diff --git a/src/behaviours/preferences/navigation-using-application-menu.test.ts b/src/behaviours/preferences/navigation-using-application-menu.test.ts index 8da90092d9..e12aa85a44 100644 --- a/src/behaviours/preferences/navigation-using-application-menu.test.ts +++ b/src/behaviours/preferences/navigation-using-application-menu.test.ts @@ -6,10 +6,6 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable"; -import type { UserStore } from "../../common/user-store"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable"; describe("preferences - navigation using application menu", () => { let applicationBuilder: ApplicationBuilder; @@ -17,19 +13,6 @@ describe("preferences - navigation using application menu", () => { beforeEach(async () => { applicationBuilder = getApplicationBuilder(); - applicationBuilder.beforeApplicationStart(({ rendererDi, mainDi }) => { - mainDi.override(isAutoUpdateEnabledInjectable, () => () => false); - - const userStoreStub = { - extensionRegistryUrl: { customUrl: "some-custom-url" }, - } as unknown as UserStore; - - rendererDi.override(userStoreInjectable, () => userStoreStub); - rendererDi.override(ipcRendererInjectable, () => ({ - on: jest.fn(), - invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge - } as never)); - }); rendered = await applicationBuilder.render(); }); diff --git a/src/common/__tests__/hotbar-store.test.ts b/src/common/__tests__/hotbar-store.test.ts index df1957cb18..cc8a4dc8fa 100644 --- a/src/common/__tests__/hotbar-store.test.ts +++ b/src/common/__tests__/hotbar-store.test.ts @@ -284,7 +284,7 @@ describe("HotbarStore", () => { }); describe("given data from 5.0.0-beta.3 and version being 5.0.0-beta.10", () => { - beforeEach(async () => { + beforeEach(() => { const configurationToBeMigrated = { "some-directory-for-user-data": { "lens-hotbar-store.json": JSON.stringify({ @@ -351,8 +351,6 @@ describe("HotbarStore", () => { di.override(appVersionInjectable, () => "5.0.0-beta.10"); - await di.runSetups(); - hotbarStore = di.inject(hotbarStoreInjectable); hotbarStore.load(); diff --git a/src/common/catalog/catalog-category-registry.injectable.ts b/src/common/catalog/catalog-category-registry.injectable.ts deleted file mode 100644 index 5bf320aaa6..0000000000 --- a/src/common/catalog/catalog-category-registry.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { catalogCategoryRegistry } from "./catalog-category-registry"; - -const catalogCategoryRegistryInjectable = getInjectable({ - id: "catalog-category-registry", - instantiate: () => catalogCategoryRegistry, - causesSideEffects: true, -}); - -export default catalogCategoryRegistryInjectable; diff --git a/src/common/catalog/has-category-for-entity.injectable.ts b/src/common/catalog/has-category-for-entity.injectable.ts index 97084ec96d..cff7d720c1 100644 --- a/src/common/catalog/has-category-for-entity.injectable.ts +++ b/src/common/catalog/has-category-for-entity.injectable.ts @@ -10,6 +10,7 @@ export type HasCategoryForEntity = (data: CatalogEntityData & CatalogEntityKindD const hasCategoryForEntityInjectable = getInjectable({ id: "has-category-for-entity", + instantiate: (di): HasCategoryForEntity => { const registry = di.inject(catalogCategoryRegistryInjectable); diff --git a/src/common/cluster/cluster.ts b/src/common/cluster/cluster.ts index 92b7ca7e10..1652e18ef1 100644 --- a/src/common/cluster/cluster.ts +++ b/src/common/cluster/cluster.ts @@ -30,8 +30,8 @@ export interface ClusterDependencies { readonly directoryForKubeConfigs: string; readonly logger: Logger; readonly detectorRegistry: DetectorRegistry; - createKubeconfigManager: (cluster: Cluster) => KubeconfigManager | undefined; - createContextHandler: (cluster: Cluster) => ClusterContextHandler | undefined; + createKubeconfigManager: (cluster: Cluster) => KubeconfigManager; + createContextHandler: (cluster: Cluster) => ClusterContextHandler; createKubectl: (clusterVersion: string) => Kubectl; createAuthorizationReview: (config: KubeConfig) => CanI; createListNamespaces: (config: KubeConfig) => ListNamespaces; diff --git a/src/common/k8s-api/__tests__/kube-api.test.ts b/src/common/k8s-api/__tests__/kube-api.test.ts index 05c9e22e09..9efa23bdaf 100644 --- a/src/common/k8s-api/__tests__/kube-api.test.ts +++ b/src/common/k8s-api/__tests__/kube-api.test.ts @@ -23,11 +23,9 @@ const mockFetch = fetch as FetchMock; describe("forRemoteCluster", () => { let apiManager: jest.Mocked; - beforeEach(async () => { + beforeEach(() => { const di = getDiForUnitTesting({ doGeneralOverrides: true }); - await di.runSetups(); - apiManager = new ApiManager() as jest.Mocked; di.override(apiManagerInjectable, () => apiManager); @@ -87,11 +85,9 @@ describe("KubeApi", () => { let request: KubeJsonApi; let apiManager: jest.Mocked; - beforeEach(async () => { + beforeEach(() => { const di = getDiForUnitTesting({ doGeneralOverrides: true }); - await di.runSetups(); - request = new KubeJsonApi({ serverAddress: `http://127.0.0.1:9999`, apiBase: "/api-kube", diff --git a/src/common/runnable/run-many-for.test.ts b/src/common/runnable/run-many-for.test.ts index e8473ddca1..997b913b25 100644 --- a/src/common/runnable/run-many-for.test.ts +++ b/src/common/runnable/run-many-for.test.ts @@ -45,7 +45,7 @@ describe("runManyFor", () => { const runMany = runManyFor(rootDi)(someInjectionTokenForRunnables); - actualPromise = runMany(); + actualPromise = runMany() as Promise; }); it("runs all runnables at the same time", () => { @@ -66,7 +66,7 @@ describe("runManyFor", () => { }); describe("given hierarchy that is three levels deep, when running many", () => { - let runMock: AsyncFnMock<(arg: string) => Promise>; + let runMock: AsyncFnMock<(...args: unknown[]) => Promise>; let actualPromise: Promise; beforeEach(() => { @@ -110,7 +110,7 @@ describe("runManyFor", () => { const runMany = runManyFor(di)(someInjectionTokenForRunnables); - actualPromise = runMany(); + actualPromise = runMany() as Promise; }); it("runs first level runnables", () => { @@ -173,7 +173,7 @@ describe("runManyFor", () => { }); describe("when running many with parameter", () => { - let runMock: AsyncFnMock<(arg: string, arg2: string) => Promise>; + let runMock: AsyncFnMock<(...args: unknown[]) => Promise>; beforeEach(() => { const rootDi = createContainer(); diff --git a/src/common/runnable/run-many-for.ts b/src/common/runnable/run-many-for.ts index 1a1ffa335e..93fd596810 100644 --- a/src/common/runnable/run-many-for.ts +++ b/src/common/runnable/run-many-for.ts @@ -10,35 +10,37 @@ import type { import { filter, map } from "lodash/fp"; export interface Runnable { - run: (parameter: TParameter) => Promise | void; + run: Run; runAfter?: this; } -export const runManyFor = - (di: DiContainerForInjection) => - >( - injectionToken: InjectionToken, +type Run = (parameter: Param) => Promise | void; + +export type RunMany = ( + injectionToken: InjectionToken, void> +) => Run; + +export function runManyFor(di: DiContainerForInjection): RunMany { + return (injectionToken) => async (parameter) => { + const allRunnables = di.injectMany(injectionToken); + + const recursedRun = async ( + runAfterRunnable: Runnable | undefined = undefined, ) => - async (parameter: Parameters[0]) => { - const allRunnables = di.injectMany(injectionToken); + await pipeline( + allRunnables, - const recursedRun = async (runAfterRunnable: Runnable = undefined) => - await pipeline( - allRunnables, + filter((runnable) => runnable.runAfter === runAfterRunnable), - filter((runnable) => runnable.runAfter === runAfterRunnable), - - map(async (runnable) => { - await runnable.run(parameter); - - await recursedRun(runnable); - }), - - promises => Promise.all(promises), - ); - - await recursedRun(); - }; + map(async (runnable) => { + await runnable.run(parameter); + await recursedRun(runnable); + }), + (promises) => Promise.all(promises), + ); + await recursedRun(); + }; +} diff --git a/src/common/runnable/run-many-sync-for.ts b/src/common/runnable/run-many-sync-for.ts index dab29ea229..32c2f608b0 100644 --- a/src/common/runnable/run-many-sync-for.ts +++ b/src/common/runnable/run-many-sync-for.ts @@ -8,32 +8,38 @@ import type { InjectionToken, } from "@ogre-tools/injectable"; import { filter, map } from "lodash/fp"; +import type { Runnable } from "./run-many-for"; export interface RunnableSync { - run: (parameter: TParameter) => void; + run: RunSync; runAfter?: this; } -export const runManySyncFor = - (di: DiContainerForInjection) => - >( - injectionToken: InjectionToken, +type RunSync = (parameter: Param) => void; + +export type RunManySync = ( + injectionToken: InjectionToken, void> +) => RunSync; + +export function runManySyncFor(di: DiContainerForInjection): RunManySync { + return (injectionToken) => async (parameter) => { + const allRunnables = di.injectMany(injectionToken); + + const recursedRun = ( + runAfterRunnable: RunnableSync | undefined = undefined, ) => - (parameter: Parameters[0]) => { - const allRunnables = di.injectMany(injectionToken); + pipeline( + allRunnables, - const recursedRun = (runAfterRunnable: RunnableSync = undefined) => - pipeline( - allRunnables, + filter((runnable) => runnable.runAfter === runAfterRunnable), - filter((runnable) => runnable.runAfter === runAfterRunnable), + map((runnable) => { + runnable.run(parameter); - map((runnable) => { - runnable.run(parameter); + recursedRun(runnable); + }), + ); - recursedRun(runnable); - }), - ); - - recursedRun(); - }; + recursedRun(); + }; +} diff --git a/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts b/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts index 74842daed9..4d7d9ca8f5 100644 --- a/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts +++ b/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts @@ -33,5 +33,11 @@ export const getLegacyGlobalDiForExtensionApi = () => { }; export function getEnvironmentSpecificLegacyGlobalDiForExtensionApi(environment: Environments) { - return legacyGlobalDis.get(environment); + const di = legacyGlobalDis.get(environment); + + if (!di) { + throw new Error("Tried to get DI container using legacy globals in environment which doesn't exist"); + } + + return di; } diff --git a/src/main/app-paths/get-electron-app-path/get-electron-app-path.test.ts b/src/main/app-paths/get-electron-app-path/get-electron-app-path.test.ts index 394ce1c784..6cb937f45d 100644 --- a/src/main/app-paths/get-electron-app-path/get-electron-app-path.test.ts +++ b/src/main/app-paths/get-electron-app-path/get-electron-app-path.test.ts @@ -35,7 +35,7 @@ describe("get-electron-app-path", () => { di.override(registerChannelInjectable, () => () => undefined); di.override(joinPathsInjectable, () => joinPathsFake); - getElectronAppPath = di.inject(getElectronAppPathInjectable); + getElectronAppPath = di.inject(getElectronAppPathInjectable) as (name: string) => string; }); it("given app path exists, when called, returns app path", () => { diff --git a/src/main/catalog-sources/kubeconfig-sync/manager.injectable.ts b/src/main/catalog-sources/kubeconfig-sync/manager.injectable.ts index 8663b482dd..f95fa0fb17 100644 --- a/src/main/catalog-sources/kubeconfig-sync/manager.injectable.ts +++ b/src/main/catalog-sources/kubeconfig-sync/manager.injectable.ts @@ -7,7 +7,7 @@ import directoryForKubeConfigsInjectable from "../../../common/app-paths/directo import { KubeconfigSyncManager } from "./manager"; import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token"; import clusterManagerInjectable from "../../cluster-manager.injectable"; -import catalogEntityRegistryInjectable from "../../catalog/catalog-entity-registry.injectable"; +import catalogEntityRegistryInjectable from "../../catalog/entity-registry.injectable"; const kubeconfigSyncManagerInjectable = getInjectable({ id: "kubeconfig-sync-manager", diff --git a/src/main/catalog-sources/sync-weblinks.injectable.ts b/src/main/catalog-sources/sync-weblinks.injectable.ts index f789427842..836418781d 100644 --- a/src/main/catalog-sources/sync-weblinks.injectable.ts +++ b/src/main/catalog-sources/sync-weblinks.injectable.ts @@ -5,7 +5,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { syncWeblinks } from "./weblinks"; import weblinkStoreInjectable from "../../common/weblink-store.injectable"; -import catalogEntityRegistryInjectable from "../catalog/catalog-entity-registry.injectable"; +import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable"; const syncWeblinksInjectable = getInjectable({ id: "sync-weblinks", diff --git a/src/main/catalog-sync-to-renderer/catalog-sync-to-renderer.injectable.ts b/src/main/catalog-sync-to-renderer/catalog-sync-to-renderer.injectable.ts index 37daf0b265..37dfd2f7fd 100644 --- a/src/main/catalog-sync-to-renderer/catalog-sync-to-renderer.injectable.ts +++ b/src/main/catalog-sync-to-renderer/catalog-sync-to-renderer.injectable.ts @@ -3,9 +3,9 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import catalogEntityRegistryInjectable from "../catalog/catalog-entity-registry.injectable"; import { startCatalogSyncToRenderer } from "../catalog-pusher"; import { getStartableStoppable } from "../../common/utils/get-startable-stoppable"; +import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable"; const catalogSyncToRendererInjectable = getInjectable({ id: "catalog-sync-to-renderer", diff --git a/src/main/catalog/entity-registry.injectable.ts b/src/main/catalog/entity-registry.injectable.ts index b044453116..003ed3d62e 100644 --- a/src/main/catalog/entity-registry.injectable.ts +++ b/src/main/catalog/entity-registry.injectable.ts @@ -4,14 +4,13 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import hasCategoryForEntityInjectable from "../../common/catalog/has-category-for-entity.injectable"; -import { CatalogEntityRegistry } from "./catalog-entity-registry"; -import catalogCategoryRegistryInjectable from "../../common/catalog/catalog-category-registry.injectable"; +import { CatalogEntityRegistry } from "./entity-registry"; const catalogEntityRegistryInjectable = getInjectable({ id: "catalog-entity-registry", + instantiate: (di) => new CatalogEntityRegistry({ hasCategoryForEntity: di.inject(hasCategoryForEntityInjectable), - categoryRegistryRegistry: di.inject(catalogCategoryRegistryInjectable); }), }); diff --git a/src/main/cluster-detectors/detector-registry.ts b/src/main/cluster-detectors/detector-registry.ts index e46ff6517b..395d819850 100644 --- a/src/main/cluster-detectors/detector-registry.ts +++ b/src/main/cluster-detectors/detector-registry.ts @@ -13,7 +13,7 @@ interface Dependencies { k8sRequest: K8sRequest; } -export type DetectorConstructor = new (cluster: Cluster) => BaseClusterDetector; +export type DetectorConstructor = new (cluster: Cluster, k8sRequest: K8sRequest) => BaseClusterDetector; export class DetectorRegistry { constructor(private dependencies: Dependencies) {} diff --git a/src/main/cluster-manager.injectable.ts b/src/main/cluster-manager.injectable.ts index 2d0c91bbdc..2b55f0e854 100644 --- a/src/main/cluster-manager.injectable.ts +++ b/src/main/cluster-manager.injectable.ts @@ -5,7 +5,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { ClusterManager } from "./cluster-manager"; import clusterStoreInjectable from "../common/cluster-store/cluster-store.injectable"; -import catalogEntityRegistryInjectable from "./catalog/catalog-entity-registry.injectable"; +import catalogEntityRegistryInjectable from "./catalog/entity-registry.injectable"; const clusterManagerInjectable = getInjectable({ id: "cluster-manager", diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index 6d0ad2af97..787f2a1618 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -10,13 +10,13 @@ import type { Cluster } from "../common/cluster/cluster"; import logger from "./logger"; import { apiKubePrefix } from "../common/vars"; import { getClusterIdFromHost, isErrnoException } from "../common/utils"; -import type { catalogEntityRegistry } from "./catalog"; import type { KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster"; import { isKubernetesCluster, KubernetesCluster, LensKubernetesClusterStatus } from "../common/catalog-entities/kubernetes-cluster"; import { ipcMainOn } from "../common/ipc"; import { once } from "lodash"; import type { ClusterStore } from "../common/cluster-store/cluster-store"; import type { ClusterId } from "../common/cluster-types"; +import type { CatalogEntityRegistry } from "./catalog"; const logPrefix = "[CLUSTER-MANAGER]:"; @@ -261,7 +261,7 @@ export class ClusterManager { }); } - getClusterForRequest(req: http.IncomingMessage): Cluster | undefined { + getClusterForRequest = (req: http.IncomingMessage): Cluster | undefined => { if (!req.headers.host) { return undefined; } diff --git a/src/main/context-handler/create-context-handler.injectable.ts b/src/main/context-handler/create-context-handler.injectable.ts index 6042ec48f7..8185bd7c80 100644 --- a/src/main/context-handler/create-context-handler.injectable.ts +++ b/src/main/context-handler/create-context-handler.injectable.ts @@ -19,7 +19,7 @@ const createContextHandlerInjectable = getInjectable({ const createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable); const prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable); - return (cluster: Cluster): ClusterContextHandler | undefined => { + return (cluster: Cluster): ClusterContextHandler => { const clusterUrl = new URLParse(cluster.apiUrl); const dependencies = { diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts index b25eb86fbb..24f90f01b3 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts @@ -9,10 +9,11 @@ import loggerInjectable from "../../../../common/logger.injectable"; import clusterManagerInjectable from "../../../cluster-manager.injectable"; import applicationMenuItemsInjectable from "../../../menu/application-menu-items.injectable"; import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path.injectable"; -import catalogEntityRegistryInjectable from "../../../catalog/catalog-entity-registry.injectable"; import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import { onLoadOfApplicationInjectionToken } from "../../../start-main-application/runnable-tokens/on-load-of-application-injection-token"; import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable"; +import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.injectable"; +import askUserForFilePathsInjectable from "../../../ipc/ask-user-for-file-paths.injectable"; const setupIpcMainHandlersInjectable = getInjectable({ id: "setup-ipc-main-handlers", @@ -30,6 +31,7 @@ const setupIpcMainHandlersInjectable = getInjectable({ const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); const clusterStore = di.inject(clusterStoreInjectable); const operatingSystemTheme = di.inject(operatingSystemThemeInjectable); + const askUserForFilePaths = di.inject(askUserForFilePathsInjectable); return { run: () => { @@ -43,6 +45,7 @@ const setupIpcMainHandlersInjectable = getInjectable({ catalogEntityRegistry, clusterStore, operatingSystemTheme, + askUserForFilePaths, }); }, }; diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts index bac1fc66da..9342696c77 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts @@ -21,9 +21,9 @@ import type { MenuItemOpts } from "../../../menu/application-menu-items.injectab import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window"; import { handleWindowAction, onLocationChange } from "../../../ipc/window"; import { openFilePickingDialogChannel } from "../../../../common/ipc/dialog"; -import { showOpenDialog } from "../../../ipc/dialog"; import { getNativeThemeChannel } from "../../../../common/ipc/native-theme"; import type { Theme } from "../../../theme/operating-system-theme-state.injectable"; +import type { AskUserForFilePaths } from "../../../ipc/ask-user-for-file-paths.injectable"; interface Dependencies { directoryForLensLocalStorage: string; @@ -33,9 +33,10 @@ interface Dependencies { catalogEntityRegistry: CatalogEntityRegistry; clusterStore: ClusterStore; operatingSystemTheme: IComputedValue; + askUserForFilePaths: AskUserForFilePaths; } -export const setupIpcMainHandlers = ({ applicationMenuItems, directoryForLensLocalStorage, getAbsolutePath, clusterManager, catalogEntityRegistry, clusterStore, operatingSystemTheme }: Dependencies) => { +export const setupIpcMainHandlers = ({ applicationMenuItems, directoryForLensLocalStorage, getAbsolutePath, clusterManager, catalogEntityRegistry, clusterStore, operatingSystemTheme, askUserForFilePaths }: Dependencies) => { ipcMainHandle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => { return ClusterStore.getInstance() .getById(clusterId) @@ -149,7 +150,7 @@ export const setupIpcMainHandlers = ({ applicationMenuItems, directoryForLensLoc ipcMainOn(windowLocationChangedChannel, () => onLocationChange()); - ipcMainHandle(openFilePickingDialogChannel, (event, opts) => showOpenDialog(opts)); + ipcMainHandle(openFilePickingDialogChannel, (event, opts) => askUserForFilePaths(opts)); ipcMainHandle(broadcastMainChannel, (event, channel, ...args) => broadcastMessage(channel, ...args)); diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts index 264185bd18..efd065905c 100644 --- a/src/main/getDiForUnitTesting.ts +++ b/src/main/getDiForUnitTesting.ts @@ -31,7 +31,6 @@ import joinPathsInjectable from "../common/path/join-paths.injectable"; import { joinPathsFake } from "../common/test-utils/join-paths-fake"; import hotbarStoreInjectable from "../common/hotbars/store.injectable"; import type { GetDiForUnitTestingOptions } from "../test-utils/get-dis-for-unit-testing"; -import hotbarStoreInjectable from "../common/hotbar-store.injectable"; import isAutoUpdateEnabledInjectable from "./is-auto-update-enabled.injectable"; import appEventBusInjectable from "../common/app-event-bus/app-event-bus.injectable"; import { EventEmitter } from "../common/event-emitter"; @@ -41,8 +40,6 @@ import initializeExtensionsInjectable from "./start-main-application/runnables/i import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.injectable"; import registerFileProtocolInjectable from "./electron-app/features/register-file-protocol.injectable"; import environmentVariablesInjectable from "../common/utils/environment-variables.injectable"; -import { CatalogCategoryRegistry } from "../common/catalog"; -import catalogCategoryRegistryInjectable from "../common/catalog/catalog-category-registry.injectable"; import setupIpcMainHandlersInjectable from "./electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable"; import setupLensProxyInjectable from "./start-main-application/runnables/setup-lens-proxy.injectable"; import setupRunnablesForAfterRootFrameIsReadyInjectable from "./start-main-application/runnables/setup-runnables-for-after-root-frame-is-ready.injectable"; @@ -125,8 +122,6 @@ export function getDiForUnitTesting(opts: GetDiForUnitTestingOptions = {}) { di.override(trayInjectable, () => ({ start: () => {}, stop: () => {} })); di.override(applicationMenuInjectable, () => ({ start: () => {}, stop: () => {} })); - di.override(catalogCategoryRegistryInjectable, () => new CatalogCategoryRegistry()); - // TODO: Remove usages of globally exported appEventBus to get rid of this di.override(appEventBusInjectable, () => new EventEmitter<[AppEvent]>()); @@ -229,7 +224,7 @@ const overrideElectronFeatures = (di: DiContainer) => { close: () => {}, send: (arg) => { - const sendFake = di.inject(sendToChannelInElectronBrowserWindowInjectable); + const sendFake = di.inject(sendToChannelInElectronBrowserWindowInjectable) as any; sendFake(null, arg); }, diff --git a/src/main/helm/helm-repo-manager.injectable.ts b/src/main/helm/helm-repo-manager.injectable.ts index fa6075e438..9a12a3ca71 100644 --- a/src/main/helm/helm-repo-manager.injectable.ts +++ b/src/main/helm/helm-repo-manager.injectable.ts @@ -12,10 +12,14 @@ import logger from "../logger"; import { execHelm } from "./exec"; import type { HelmEnv, HelmRepo, HelmRepoConfig } from "./helm-repo-manager"; -class HelmRepoManager { - protected repos: HelmRepo[]; - protected helmEnv: HelmEnv; - protected didUpdateOnce: boolean; +interface EnsuredHelmRepoManagerData { + helmEnv: HelmEnv; + didUpdateOnce: boolean; +} + +export class HelmRepoManager { + protected helmEnv?: HelmEnv; + protected didUpdateOnce?: boolean; public async loadAvailableRepos(): Promise { const res = await customRequestPromise({ @@ -25,13 +29,13 @@ class HelmRepoManager { timeout: 10000, }); - return orderBy(res.body as HelmRepo[], (repo) => repo.name); + return orderBy(res.body as HelmRepo[], repo => repo.name); } - private async ensureInitialized() { + private async ensureInitialized(): Promise { this.helmEnv ??= await this.parseHelmEnv(); - const repos = await this.list(); + const repos = await this.list(this.helmEnv); if (repos.length === 0) { await this.addRepo({ @@ -44,12 +48,17 @@ class HelmRepoManager { await this.update(); this.didUpdateOnce = true; } + + return { + didUpdateOnce: this.didUpdateOnce, + helmEnv: this.helmEnv, + }; } protected async parseHelmEnv() { const output = await execHelm(["env"]); const lines = output.split(/\r?\n/); // split by new line feed - const env: HelmEnv = {}; + const env: Partial> = {}; lines.forEach((line: string) => { const [key, value] = line.split("="); @@ -59,21 +68,18 @@ class HelmRepoManager { } }); - return env; + return env as HelmEnv; } - public async repo(name: string): Promise { + public async repo(name: string): Promise { const repos = await this.repositories(); - return repos.find((repo) => repo.name === name); + return repos.find(repo => repo.name === name); } - private async list(): Promise { + private async list(helmEnv: HelmEnv): Promise { try { - const rawConfig = await readFile( - this.helmEnv.HELM_REPOSITORY_CONFIG, - "utf8", - ); + const rawConfig = await readFile(helmEnv.HELM_REPOSITORY_CONFIG, "utf8"); const parsedConfig = yaml.load(rawConfig) as HelmRepoConfig; if (typeof parsedConfig === "object" && parsedConfig) { @@ -88,13 +94,13 @@ class HelmRepoManager { public async repositories(): Promise { try { - await this.ensureInitialized(); + const { helmEnv } = await this.ensureInitialized(); - const repos = await this.list(); + const repos = await this.list(helmEnv); - return repos.map((repo) => ({ + return repos.map(repo => ({ ...repo, - cacheFilePath: `${this.helmEnv.HELM_REPOSITORY_CACHE}/${repo.name}-index.yaml`, + cacheFilePath: `${helmEnv.HELM_REPOSITORY_CACHE}/${repo.name}-index.yaml`, })); } catch (error) { logger.error(`[HELM]: repositories listing error`, error); @@ -104,21 +110,20 @@ class HelmRepoManager { } public async update() { - return execHelm(["repo", "update"]); + return execHelm([ + "repo", + "update", + ]); } - public async addRepo({ - name, - url, - insecureSkipTlsVerify, - username, - password, - caFile, - keyFile, - certFile, - }: HelmRepo) { + public async addRepo({ name, url, insecureSkipTlsVerify, username, password, caFile, keyFile, certFile }: HelmRepo) { logger.info(`[HELM]: adding repo ${name} from ${url}`); - const args = ["repo", "add", name, url]; + const args = [ + "repo", + "add", + name, + url, + ]; if (insecureSkipTlsVerify) { args.push("--insecure-skip-tls-verify"); @@ -150,7 +155,11 @@ class HelmRepoManager { public async removeRepo({ name, url }: HelmRepo): Promise { logger.info(`[HELM]: removing repo ${name} (${url})`); - return execHelm(["repo", "remove", name]); + return execHelm([ + "repo", + "remove", + name, + ]); } } diff --git a/src/main/ipc/ask-user-for-file-paths.injectable.ts b/src/main/ipc/ask-user-for-file-paths.injectable.ts new file mode 100644 index 0000000000..9fc1ac1471 --- /dev/null +++ b/src/main/ipc/ask-user-for-file-paths.injectable.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { OpenDialogOptions } from "electron"; +import { dialog } from "electron"; +import { getInjectable } from "@ogre-tools/injectable"; +import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable"; + +// TODO: Replace leaking electron with abstraction +export type AskUserForFilePaths = ( + dialogOptions: OpenDialogOptions +) => Promise<{ canceled: boolean; filePaths: string[] }>; + +const askUserForFilePathsInjectable = getInjectable({ + id: "ask-user-for-file-paths", + + instantiate: (di): AskUserForFilePaths => { + const showApplicationWindow = di.inject(showApplicationWindowInjectable); + + return async (dialogOptions) => { + await showApplicationWindow(); + + const { canceled, filePaths } = await dialog.showOpenDialog( + dialogOptions, + ); + + return { canceled, filePaths }; + }; + }, + + causesSideEffects: true, +}); + +export default askUserForFilePathsInjectable; diff --git a/src/main/ipc/dialog.ts b/src/main/ipc/dialog.ts deleted file mode 100644 index 78dd4b69a9..0000000000 --- a/src/main/ipc/dialog.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { OpenDialogOptions } from "electron"; -import { dialog } from "electron"; -import { WindowManager } from "../window-manager"; - -export async function showOpenDialog(dialogOptions: OpenDialogOptions): Promise<{ canceled: boolean; filePaths: string[] }> { - const window = await WindowManager.getInstance().ensureMainWindow(); - const { canceled, filePaths } = await dialog.showOpenDialog(window, dialogOptions); - - return { canceled, filePaths }; -} diff --git a/src/main/kubeconfig-manager/create-kubeconfig-manager.injectable.ts b/src/main/kubeconfig-manager/create-kubeconfig-manager.injectable.ts index 24a2bca616..e1f365f098 100644 --- a/src/main/kubeconfig-manager/create-kubeconfig-manager.injectable.ts +++ b/src/main/kubeconfig-manager/create-kubeconfig-manager.injectable.ts @@ -14,7 +14,7 @@ export interface KubeConfigManagerInstantiationParameter { cluster: Cluster; } -export type CreateKubeconfigManager = (cluster: Cluster) => KubeconfigManager | undefined; +export type CreateKubeconfigManager = (cluster: Cluster) => KubeconfigManager; const createKubeconfigManagerInjectable = getInjectable({ id: "create-kubeconfig-manager", diff --git a/src/main/lens-proxy/lens-proxy.ts b/src/main/lens-proxy/lens-proxy.ts index 0828055147..571cbea7b4 100644 --- a/src/main/lens-proxy/lens-proxy.ts +++ b/src/main/lens-proxy/lens-proxy.ts @@ -7,7 +7,7 @@ import net from "net"; import type http from "http"; import spdy from "spdy"; import type httpProxy from "http-proxy"; -import { apiPrefix, apiKubePrefix } from "../common/vars"; +import { apiPrefix, apiKubePrefix } from "../../common/vars"; import type { Router } from "../router/router"; import type { ClusterContextHandler } from "../context-handler/context-handler"; import logger from "../logger"; @@ -59,12 +59,9 @@ const disallowedPorts = new Set([ ]); export class LensProxy { - protected origin: string; protected proxyServer: http.Server; protected closed = false; protected retryCounters = new Map(); - protected getClusterForRequest: GetClusterForRequest; - constructor(private dependencies: Dependencies) { this.configureProxy(dependencies.proxy); diff --git a/src/main/router/router.test.ts b/src/main/router/router.test.ts index 2993518b3a..20b8022efc 100644 --- a/src/main/router/router.test.ts +++ b/src/main/router/router.test.ts @@ -15,10 +15,12 @@ import parseRequestInjectable from "./parse-request.injectable"; import { contentTypes } from "./router-content-types"; import mockFs from "mock-fs"; import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import type { Route } from "./route"; +import type { SetRequired } from "type-fest"; describe("router", () => { let router: Router; - let routeHandlerMock: AsyncFnMock>; + let routeHandlerMock: AsyncFnMock<() => any>; beforeEach(async () => { routeHandlerMock = asyncFn(); diff --git a/src/main/router/router.ts b/src/main/router/router.ts index 0138d14629..98ce50bac3 100644 --- a/src/main/router/router.ts +++ b/src/main/router/router.ts @@ -8,8 +8,8 @@ import type http from "http"; import { toPairs } from "lodash/fp"; import type { Cluster } from "../../common/cluster/cluster"; import { contentTypes } from "./router-content-types"; -import type { ServerIncomingMessage } from "../lens-proxy"; import type { LensApiRequest, LensApiResult, Route } from "./route"; +import type { ServerIncomingMessage } from "../lens-proxy/lens-proxy"; export interface RouterRequestOpts { req: http.IncomingMessage; diff --git a/src/main/routes/metrics/get-metric-providers-route.injectable.ts b/src/main/routes/metrics/get-metric-providers-route.injectable.ts index c42856541a..22b2fbf11b 100644 --- a/src/main/routes/metrics/get-metric-providers-route.injectable.ts +++ b/src/main/routes/metrics/get-metric-providers-route.injectable.ts @@ -5,14 +5,13 @@ import { apiPrefix } from "../../../common/vars"; import { getRouteInjectable } from "../../router/router.injectable"; -import { PrometheusProviderRegistry } from "../../prometheus"; import { route } from "../../router/route"; import prometheusProviderRegistryInjectable from "../../prometheus/prometheus-provider-registry.injectable"; const getMetricProvidersRouteInjectable = getRouteInjectable({ id: "get-metric-providers-route", - instantiate: () => { + instantiate: (di) => { const prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable); return route({ @@ -26,7 +25,7 @@ const getMetricProvidersRouteInjectable = getRouteInjectable({ ({ name, id, isConfigurable }) => ({ name, id, isConfigurable }), ), })); - } + }, }); export default getMetricProvidersRouteInjectable; diff --git a/src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts b/src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts index ce87c8959d..7d8cb37a1c 100644 --- a/src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts +++ b/src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts @@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable"; import { terminalShellEnvModify } from "./terminal-shell-env-modifiers"; -import catalogEntityRegistryInjectable from "../../catalog/catalog-entity-registry.injectable"; +import catalogEntityRegistryInjectable from "../../catalog/entity-registry.injectable"; const terminalShellEnvModifyInjectable = getInjectable({ id: "terminal-shell-env-modify", diff --git a/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts b/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts index e37a91eb5e..aafd0050a4 100644 --- a/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts +++ b/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts @@ -34,7 +34,7 @@ const createLensWindowInjectable = getInjectable({ instantiate: (di) => (configuration: LensWindowConfiguration) => { - let browserWindow: LensWindow = null; + let browserWindow: LensWindow | undefined; const createElectronWindow = di.inject(createElectronWindowForInjectable)( { @@ -52,14 +52,14 @@ const createLensWindowInjectable = getInjectable({ beforeOpen: configuration.beforeOpen, onClose: () => { - browserWindow = null; + browserWindow = undefined; }, }, ); return { get visible() { - return !!this.browserWindow; + return !!browserWindow; }, show: async () => { @@ -72,7 +72,7 @@ const createLensWindowInjectable = getInjectable({ close: () => { browserWindow?.close(); - browserWindow = null; + browserWindow = undefined; }, send: async (args: SendToViewArgs) => { diff --git a/src/main/start-main-application/runnables/initialize-extensions.injectable.ts b/src/main/start-main-application/runnables/initialize-extensions.injectable.ts index 1038a4c189..899d684abf 100644 --- a/src/main/start-main-application/runnables/initialize-extensions.injectable.ts +++ b/src/main/start-main-application/runnables/initialize-extensions.injectable.ts @@ -44,7 +44,7 @@ const initializeExtensionsInjectable = getInjectable({ }); extensionLoader.initExtensions(extensions); - } catch (error) { + } catch (error: any) { showErrorPopup( "Lens Error", `Could not load extensions${ diff --git a/src/main/start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable.ts b/src/main/start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable.ts index 8e27ce47ab..d6d964c7df 100644 --- a/src/main/start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable.ts +++ b/src/main/start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable.ts @@ -5,8 +5,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import { afterRootFrameIsReadyInjectionToken } from "../../runnable-tokens/after-root-frame-is-ready-injection-token"; import directoryForKubeConfigsInjectable from "../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import kubeconfigSyncManagerInjectable from "../../../catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.injectable"; import ensureDirInjectable from "../../../../common/fs/ensure-dir.injectable"; +import kubeconfigSyncManagerInjectable from "../../../catalog-sources/kubeconfig-sync/manager.injectable"; const startKubeConfigSyncInjectable = getInjectable({ id: "start-kubeconfig-sync", diff --git a/src/main/start-main-application/runnables/kube-config-sync/stop-kube-config-sync.injectable.ts b/src/main/start-main-application/runnables/kube-config-sync/stop-kube-config-sync.injectable.ts index 80e7ca915b..2613ab77ba 100644 --- a/src/main/start-main-application/runnables/kube-config-sync/stop-kube-config-sync.injectable.ts +++ b/src/main/start-main-application/runnables/kube-config-sync/stop-kube-config-sync.injectable.ts @@ -3,8 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import kubeconfigSyncManagerInjectable from "../../../catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.injectable"; import { beforeQuitOfFrontEndInjectionToken } from "../../runnable-tokens/before-quit-of-front-end-injection-token"; +import kubeconfigSyncManagerInjectable from "../../../catalog-sources/kubeconfig-sync/manager.injectable"; const stopKubeConfigSyncInjectable = getInjectable({ id: "stop-kube-config-sync", diff --git a/src/main/start-main-application/runnables/setup-hotbar-store.injectable.ts b/src/main/start-main-application/runnables/setup-hotbar-store.injectable.ts index ed35272e40..372c339e5e 100644 --- a/src/main/start-main-application/runnables/setup-hotbar-store.injectable.ts +++ b/src/main/start-main-application/runnables/setup-hotbar-store.injectable.ts @@ -3,9 +3,9 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import hotbarStoreInjectable from "../../../common/hotbar-store.injectable"; import setupSyncingOfGeneralCatalogEntitiesInjectable from "./setup-syncing-of-general-catalog-entities.injectable"; import { onLoadOfApplicationInjectionToken } from "../runnable-tokens/on-load-of-application-injection-token"; +import hotbarStoreInjectable from "../../../common/hotbars/store.injectable"; const setupHotbarStoreInjectable = getInjectable({ id: "setup-hotbar-store", diff --git a/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts b/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts index 9e3546fbd8..8b90bcf5e3 100644 --- a/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts +++ b/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts @@ -28,7 +28,7 @@ const setupLensProxyInjectable = getInjectable({ try { logger.info("🔌 Starting LensProxy"); await lensProxy.listen(); // lensProxy.port available - } catch (error) { + } catch (error: any) { showErrorPopup("Lens Error", `Could not start proxy: ${error?.message || "unknown error"}`); return exitApp(); diff --git a/src/renderer/api/setup-on-api-errors.injectable.ts b/src/renderer/api/setup-on-api-errors.injectable.ts index 3ea5117594..859b333586 100644 --- a/src/renderer/api/setup-on-api-errors.injectable.ts +++ b/src/renderer/api/setup-on-api-errors.injectable.ts @@ -5,13 +5,19 @@ import { getInjectable } from "@ogre-tools/injectable"; import { apiBase } from "../../common/k8s-api"; import { onApiError } from "./on-api-error"; +import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token"; const setupOnApiErrorListenersInjectable = getInjectable({ id: "setup-on-api-error-listeners", - setup: () => { - apiBase?.onError.addListener(onApiError); - }, - instantiate: () => undefined, + + instantiate: () => ({ + run: () => { + apiBase?.onError.addListener(onApiError); + }, + }), + + injectionToken: beforeFrameStartsInjectionToken, + causesSideEffects: true, }); export default setupOnApiErrorListenersInjectable; diff --git a/src/renderer/components/+helm-releases/release-details/release-details.tsx b/src/renderer/components/+helm-releases/release-details/release-details.tsx index 557fd00a70..ee01070fbf 100644 --- a/src/renderer/components/+helm-releases/release-details/release-details.tsx +++ b/src/renderer/components/+helm-releases/release-details/release-details.tsx @@ -105,7 +105,7 @@ class NonInjectedReleaseDetails extends Component { const { hideDetails, createUpgradeChartTab } = this.props; - this.props.createUpgradeChartTab(this.release); + createUpgradeChartTab(release); hideDetails(); }; diff --git a/src/renderer/components/cluster-manager/lens-views.injectable.ts b/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts similarity index 56% rename from src/renderer/components/cluster-manager/lens-views.injectable.ts rename to src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts index 18c30725fb..19611f3117 100644 --- a/src/renderer/components/cluster-manager/lens-views.injectable.ts +++ b/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts @@ -3,11 +3,11 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { ClusterFrameHandler } from "./lens-views"; +import { ClusterFrameHandler } from "./cluster-frame-handler"; -const clusterFramesInjectable = getInjectable({ - id: "cluster-frames", +const clusterFrameHandlerInjectable = getInjectable({ + id: "cluster-frame-handler", instantiate: () => new ClusterFrameHandler(), }); -export default clusterFramesInjectable; +export default clusterFrameHandlerInjectable; diff --git a/src/renderer/components/cluster-manager/lens-views.ts b/src/renderer/components/cluster-manager/cluster-frame-handler.ts similarity index 100% rename from src/renderer/components/cluster-manager/lens-views.ts rename to src/renderer/components/cluster-manager/cluster-frame-handler.ts diff --git a/src/renderer/components/cluster-manager/cluster-view.tsx b/src/renderer/components/cluster-manager/cluster-view.tsx index 15dc5ee9e1..1eba3195a9 100644 --- a/src/renderer/components/cluster-manager/cluster-view.tsx +++ b/src/renderer/components/cluster-manager/cluster-view.tsx @@ -9,7 +9,7 @@ import type { IComputedValue } from "mobx"; import { computed, makeObservable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { ClusterStatus } from "./cluster-status"; -import type { ClusterFrameHandler } from "./lens-views"; +import type { ClusterFrameHandler } from "./cluster-frame-handler"; import type { Cluster } from "../../../common/cluster/cluster"; import { ClusterStore } from "../../../common/cluster-store/cluster-store"; import { requestClusterActivation } from "../../ipc"; @@ -17,7 +17,7 @@ import { withInjectables } from "@ogre-tools/injectable-react"; import type { NavigateToCatalog } from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; import clusterViewRouteParametersInjectable from "./cluster-view-route-parameters.injectable"; -import clusterFramesInjectable from "./lens-views.injectable"; +import clusterFrameHandlerInjectable from "./cluster-frame-handler.injectable"; import type { CatalogEntityRegistry } from "../../api/catalog/entity/registry"; import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable"; @@ -107,7 +107,7 @@ export const ClusterView = withInjectables(NonInjectedClusterView, getProps: (di) => ({ clusterId: di.inject(clusterViewRouteParametersInjectable).clusterId, navigateToCatalog: di.inject(navigateToCatalogInjectable), - clusterFrames: di.inject(clusterFramesInjectable), + clusterFrames: di.inject(clusterFrameHandlerInjectable), entityRegistry: di.inject(catalogEntityRegistryInjectable), }), }); diff --git a/src/renderer/components/delete-cluster-dialog/__tests__/delete-cluster-dialog.test.tsx b/src/renderer/components/delete-cluster-dialog/__tests__/delete-cluster-dialog.test.tsx index 4c4c7b2112..a2a0e6a301 100644 --- a/src/renderer/components/delete-cluster-dialog/__tests__/delete-cluster-dialog.test.tsx +++ b/src/renderer/components/delete-cluster-dialog/__tests__/delete-cluster-dialog.test.tsx @@ -4,28 +4,20 @@ */ import "@testing-library/jest-dom/extend-expect"; import { KubeConfig } from "@kubernetes/client-node"; -import { fireEvent, screen } from "@testing-library/react"; -import type { RenderResult } from "@testing-library/react"; import { fireEvent } from "@testing-library/react"; +import type { RenderResult } from "@testing-library/react"; import mockFs from "mock-fs"; import React from "react"; import * as selectEvent from "react-select-event"; import type { Cluster } from "../../../../common/cluster/cluster"; import { DeleteClusterDialog } from "../view"; -import { DeleteClusterDialog } from "../delete-cluster-dialog"; import type { ClusterModel } from "../../../../common/cluster-types"; import { createClusterInjectionToken } from "../../../../common/cluster/create-cluster-injection-token"; import createContextHandlerInjectable from "../../../../main/context-handler/create-context-handler.injectable"; -import type { DiRender } from "../../test-utils/renderFor"; -import { renderFor } from "../../test-utils/renderFor"; import type { OpenDeleteClusterDialog } from "../open.injectable"; import openDeleteClusterDialogInjectable from "../open.injectable"; import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable"; import createKubeconfigManagerInjectable from "../../../../main/kubeconfig-manager/create-kubeconfig-manager.injectable"; -import ipcRendererInjectable from "../../../app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable"; -import deleteClusterDialogModelInjectable from "../delete-cluster-dialog-model/delete-cluster-dialog-model.injectable"; -import type { DeleteClusterDialogModel } from "../delete-cluster-dialog-model/delete-cluster-dialog-model"; -import hotbarStoreInjectable from "../../../../common/hotbar-store.injectable"; import type { ApplicationBuilder } from "../../test-utils/get-application-builder"; import { getApplicationBuilder } from "../../test-utils/get-application-builder"; import { routeInjectionToken } from "../../../../common/front-end-routing/route-injection-token"; @@ -33,6 +25,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; import { routeSpecificComponentInjectionToken } from "../../../routes/route-specific-component-injection-token"; import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token"; +import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable"; jest.mock("electron", () => ({ app: { @@ -50,33 +43,29 @@ jest.mock("electron", () => ({ }, })); -const kubeconfig = ` +const multiClusterConfig = ` apiVersion: v1 clusters: - cluster: server: https://localhost - name: test + name: some-current-context-cluster - cluster: server: http://localhost - name: other-cluster + name: some-non-current-context-cluster contexts: - context: - cluster: test - user: test - name: test + cluster: some-current-context-cluster + user: some-user + name: some-current-context - context: - cluster: test - user: test - name: test2 -- context: - cluster: other-cluster - user: test - name: other-context -current-context: other-context + cluster: some-non-current-context-cluster + user: some-user + name: some-non-current-context +current-context: some-current-context kind: Config preferences: {} users: -- name: test +- name: some-user user: token: kubeconfig-user-q4lm4:xxxyyyy `; @@ -86,17 +75,17 @@ apiVersion: v1 clusters: - cluster: server: http://localhost - name: other-cluster + name: some-cluster contexts: - context: - cluster: other-cluster - user: test - name: other-context -current-context: other-context + cluster: some-cluster + user: some-user + name: some-context +current-context: some-context kind: Config preferences: {} users: -- name: test +- name: some-user user: token: kubeconfig-user-q4lm4:xxxyyyy `; @@ -107,46 +96,22 @@ describe("", () => { let applicationBuilder: ApplicationBuilder; let createCluster: (model: ClusterModel) => Cluster; let openDeleteClusterDialog: OpenDeleteClusterDialog; - let render: DiRender; beforeEach(async () => { applicationBuilder = getApplicationBuilder(); applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => { - mainDi.override(createContextHandlerInjectable, () => () => undefined); - mainDi.override(createKubeconfigManagerInjectable, () => () => undefined); + mainDi.override(createContextHandlerInjectable, () => () => undefined as any); + mainDi.override(createKubeconfigManagerInjectable, () => () => undefined as any); rendererDi.override(hotbarStoreInjectable, () => ({})); rendererDi.override(storesAndApisCanBeCreatedInjectable, () => true); - rendererDi.override(ipcRendererInjectable, () => ({ - on: jest.fn(), - invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge - } as never)); - - const testRouteInjectable = getInjectable({ - id: "some-test-route", - - instantiate: () => ({ - path: "/some-test-path", - clusterFrame: false, - isEnabled: computed(() => true), - }), - - injectionToken: routeInjectionToken, - }); - - const testRouteComponent = getInjectable({ - id: "some-test-component", - - instantiate: (di) => ({ - route: di.inject(testRouteInjectable), - Component: () => , - }), - - injectionToken: routeSpecificComponentInjectionToken, - }); }); + const { rendererDi } = applicationBuilder.dis; + + rendererDi.register(testRouteInjectable, testRouteComponentInjectable); + applicationBuilder.beforeRender(({ rendererDi }) => { const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken); const testRoute = rendererDi.inject(testRouteInjectable); @@ -154,70 +119,100 @@ describe("", () => { navigateToRoute(testRoute); }); - rendererDi.register(testRouteInjectable, testRouteComponent); - mockFs(); - openDeleteClusterDialog = rendererDi.inject(openDeleteClusterDialogInjectable); - createCluster = mainDi.inject(createClusterInjectionToken); + applicationBuilder.beforeRender(({ rendererDi }) => { + openDeleteClusterDialog = rendererDi.inject(openDeleteClusterDialogInjectable); + createCluster = rendererDi.inject(createClusterInjectionToken); + }); }); afterEach(() => { mockFs.restore(); }); + it("shows context switcher when deleting current cluster", async () => { + const mockOpts = { + "temp-kube-config": multiClusterConfig, + }; + + mockFs(mockOpts); + + config = new KubeConfig(); + config.loadFromString(multiClusterConfig); + + applicationBuilder.beforeRender(({ rendererDi }) => { + const createCluster = rendererDi.inject(createClusterInjectionToken); + + const cluster = createCluster({ + id: "some-current-context-cluster", + contextName: "some-current-context", + preferences: { + clusterName: "some-current-context-cluster", + }, + kubeConfigPath: "./temp-kube-config", + }); + + openDeleteClusterDialog({ cluster, config }); + }); + + const rendered = await applicationBuilder.render(); + + const { getByText } = rendered; + + const menu = getByText("Select new context..."); + + expect(menu).toBeInTheDocument(); + selectEvent.openMenu(menu); + + expect(getByText("some-current-context")).toBeInTheDocument(); + expect(getByText("some-non-current-context")).toBeInTheDocument(); + }); + + describe("Kubeconfig with different clusters", () => { let rendered: RenderResult; - let createCluster: (model: ClusterModel) => Cluster; - let deleteClusterDialogModel: DeleteClusterDialogModel; beforeEach(async () => { const mockOpts = { - "temp-kube-config": kubeconfig, + "temp-kube-config": multiClusterConfig, }; mockFs(mockOpts); config = new KubeConfig(); - config.loadFromString(kubeconfig); + config.loadFromString(multiClusterConfig); rendered = await applicationBuilder.render(); - - deleteClusterDialogModel = applicationBuilder.dis.rendererDi.inject(deleteClusterDialogModelInjectable); - createCluster = applicationBuilder.dis.mainDi.inject(createClusterInjectionToken); - }); - - afterEach(() => { - mockFs.restore(); }); it("renders w/o errors", () => { expect(rendered.container).toBeInstanceOf(HTMLElement); }); - it("shows warning when deleting non-current-context cluster", async () => { + it("shows warning when deleting non-current-context cluster", () => { const cluster = createCluster({ - id: "test", - contextName: "test", + id: "some-non-current-context-cluster", + contextName: "some-non-current-context", preferences: { clusterName: "minikube", }, kubeConfigPath: "./temp-kube-config", }); - deleteClusterDialogModel.open({ cluster, config }); + openDeleteClusterDialog({ cluster, config }); const message = "The contents of kubeconfig file will be changed!"; expect(rendered.getByText(message)).toBeInstanceOf(HTMLElement); }); - it("shows warning when deleting current-context cluster", async () => { + it("shows warning when deleting current-context cluster", () => { const cluster = createCluster({ - id: "other-cluster", - contextName: "other-context", + id: "some-current-context-cluster", + contextName: "some-current-context", preferences: { - clusterName: "other-cluster", + clusterName: "some-current-context-cluster", }, kubeConfigPath: "./temp-kube-config", }); @@ -227,42 +222,17 @@ describe("", () => { expect(rendered.getByTestId("current-context-warning")).toBeInstanceOf(HTMLElement); }); - it("shows context switcher when deleting current cluster", async () => { + it("shows context switcher after checkbox click", () => { const cluster = createCluster({ - id: "other-cluster", - contextName: "other-context", + id: "some-current-context-cluster", + contextName: "some-current-context", preferences: { - clusterName: "other-cluster", + clusterName: "some-current-context-cluster", }, kubeConfigPath: "./temp-kube-config", }); openDeleteClusterDialog({ cluster, config }); - render(); - - const { getByText } = rendered; - - const menu = getByText("Select new context..."); - - expect(menu).toBeInTheDocument(); - selectEvent.openMenu(menu); - - expect(getByText("test")).toBeInTheDocument(); - expect(getByText("test2")).toBeInTheDocument(); - }); - - it("shows context switcher after checkbox click", async () => { - const cluster = createCluster({ - id: "some-cluster", - contextName: "test", - preferences: { - clusterName: "test", - }, - kubeConfigPath: "./temp-kube-config", - }); - - openDeleteClusterDialog({ cluster, config }); - render(); const { getByText, getByTestId } = rendered; const link = getByTestId("context-switch"); @@ -270,28 +240,30 @@ describe("", () => { expect(link).toBeInstanceOf(HTMLElement); fireEvent.click(link); - const menu = await screen.findByText("Select new context..."); + const menu = getByText("Select new context..."); expect(menu).toBeInTheDocument(); selectEvent.openMenu(menu); - expect(await screen.findByText("test")).toBeInTheDocument(); - expect(await screen.findByText("test2")).toBeInTheDocument(); + expect(getByText("some-current-context")).toBeInTheDocument(); + expect(getByText("some-non-current-context")).toBeInTheDocument(); }); - it("shows warning for internal kubeconfig cluster", async () => { + it("given cluster in internal kubeconfig, when deleting cluster outside of current context, shows warning for internal kubeconfig cluster", () => { const cluster = createCluster({ - id: "some-cluster", - contextName: "test", + id: "some-non-current-context-cluster", + contextName: "some-non-current-context", + preferences: { - clusterName: "test", + clusterName: "some-non-current-context-cluster", }, + kubeConfigPath: "./temp-kube-config", }); const spy = jest.spyOn(cluster, "isInLocalKubeconfig").mockImplementation(() => true); - deleteClusterDialogModel.open({ cluster, config }); + openDeleteClusterDialog({ cluster, config }); expect(rendered.getByTestId("internal-kubeconfig-warning")).toBeInstanceOf(HTMLElement); @@ -301,8 +273,6 @@ describe("", () => { describe("Kubeconfig with single cluster", () => { let rendered: RenderResult; - let createCluster: (model: ClusterModel) => Cluster; - let deleteClusterDialogModel: DeleteClusterDialogModel; beforeEach(async () => { const mockOpts = { @@ -315,28 +285,44 @@ describe("", () => { config.loadFromString(singleClusterConfig); rendered = await applicationBuilder.render(); - - deleteClusterDialogModel = applicationBuilder.dis.rendererDi.inject(deleteClusterDialogModelInjectable); - createCluster = applicationBuilder.dis.mainDi.inject(createClusterInjectionToken); }); - afterEach(() => { - mockFs.restore(); - }); - - it("shows warning if no other contexts left", async () => { + it("shows warning if no other contexts left", () => { const cluster = createCluster({ - id: "other-cluster", - contextName: "other-context", + id: "some-cluster", + contextName: "some-context", preferences: { - clusterName: "other-cluster", + clusterName: "some-cluster", }, kubeConfigPath: "./temp-kube-config", }); - deleteClusterDialogModel.open({ cluster, config }); + openDeleteClusterDialog({ cluster, config }); expect(rendered.getByTestId("no-more-contexts-warning")).toBeInstanceOf(HTMLElement); }); }); }); + +const testRouteInjectable = getInjectable({ + id: "some-test-route", + + instantiate: () => ({ + path: "/some-test-path", + clusterFrame: false, + isEnabled: computed(() => true), + }), + + injectionToken: routeInjectionToken, +}); + +const testRouteComponentInjectable = getInjectable({ + id: "some-test-component", + + instantiate: (di) => ({ + route: di.inject(testRouteInjectable), + Component: () => , + }), + + injectionToken: routeSpecificComponentInjectionToken, +}); diff --git a/src/renderer/components/menu/menu-actions.tsx b/src/renderer/components/menu/menu-actions.tsx index 03beb5d40d..aa44d3634f 100644 --- a/src/renderer/components/menu/menu-actions.tsx +++ b/src/renderer/components/menu/menu-actions.tsx @@ -50,6 +50,7 @@ class NonInjectedMenuActions extends React.Component void | Promise; @@ -235,9 +235,11 @@ export const getApplicationBuilder = () => { const clusterStub = { accessibleNamespaces: [], - } as Cluster; + } as unknown as Cluster; - const namespaceStoreStub = {} as NamespaceStore; + const namespaceStoreStub = { + contextNamespaces: [], + } as unknown as NamespaceStore; const clusterFrameContextFake = new ClusterFrameContext( clusterStub, @@ -321,7 +323,7 @@ export const getApplicationBuilder = () => { await startFrame(); const render = renderFor(rendererDi); - const history = rendererDi.inject(observableHistoryInjectable); + const history = rendererDi.inject(historyInjectable) as any; const currentRouteComponent = rendererDi.inject(currentRouteComponentInjectable); for (const callback of beforeRenderCallbacks) { diff --git a/src/renderer/create-cluster/create-cluster.injectable.ts b/src/renderer/create-cluster/create-cluster.injectable.ts index 196a3b4ce3..688662a965 100644 --- a/src/renderer/create-cluster/create-cluster.injectable.ts +++ b/src/renderer/create-cluster/create-cluster.injectable.ts @@ -18,12 +18,13 @@ const createClusterInjectable = getInjectable({ logger: di.inject(loggerInjectable), // TODO: Dismantle wrong abstraction - createKubeconfigManager: () => undefined, + // Note: "as never" to get around strictness in unnatural scenario + createKubeconfigManager: () => undefined as never, createKubectl: () => { throw new Error("Tried to access back-end feature in front-end.");}, - createContextHandler: () => undefined, + createContextHandler: () => undefined as never, createAuthorizationReview: () => { throw new Error("Tried to access back-end feature in front-end."); }, createListNamespaces: () => { throw new Error("Tried to access back-end feature in front-end."); }, - detectorRegistry: undefined, + detectorRegistry: undefined as never, createVersionDetector: () => { throw new Error("Tried to access back-end feature in front-end."); }, }; diff --git a/src/renderer/getDiForUnitTesting.tsx b/src/renderer/getDiForUnitTesting.tsx index f1c63b4448..22ac104e88 100644 --- a/src/renderer/getDiForUnitTesting.tsx +++ b/src/renderer/getDiForUnitTesting.tsx @@ -34,11 +34,15 @@ import terminalSpawningPoolInjectable from "./components/dock/terminal/terminal- import hostedClusterIdInjectable from "../common/cluster-store/hosted-cluster-id.injectable"; import type { GetDiForUnitTestingOptions } from "../test-utils/get-dis-for-unit-testing"; import historyInjectable from "./navigation/history.injectable"; -import themeStoreInjectable from "./theme-store.injectable"; -import apiManagerInjectable from "./components/kube-object-menu/dependencies/api-manager.injectable"; import { ApiManager } from "../common/k8s-api/api-manager"; import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.injectable"; import broadcastMessageInjectable from "../common/ipc/broadcast-message.injectable"; +import apiManagerInjectable from "../common/k8s-api/api-manager/manager.injectable"; +import ipcRendererInjectable + from "./app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable"; +import type { IpcRenderer } from "electron"; +import setupOnApiErrorListenersInjectable from "./api/setup-on-api-errors.injectable"; +import { observable } from "mobx"; export const getDiForUnitTesting = (opts: GetDiForUnitTestingOptions = {}) => { const { @@ -75,6 +79,11 @@ export const getDiForUnitTesting = (opts: GetDiForUnitTestingOptions = {}) => { di.override(lensResourcesDirInjectable, () => "/irrelevant"); + di.override(ipcRendererInjectable, () => ({ + invoke: () => {}, + on: () => {}, + }) as unknown as IpcRenderer); + di.override(broadcastMessageInjectable, () => () => { throw new Error("Tried to broadcast message over IPC without explicit override."); }); @@ -89,11 +98,17 @@ export const getDiForUnitTesting = (opts: GetDiForUnitTestingOptions = {}) => { // eslint-disable-next-line unused-imports/no-unused-vars-ts di.override(clusterStoreInjectable, () => ({ getById: (id): Cluster => ({}) as Cluster }) as ClusterStore); + di.override(setupOnApiErrorListenersInjectable, () => ({ run: () => {} })); + di.override( userStoreInjectable, () => ({ isTableColumnHidden: () => false, + extensionRegistryUrl: { customUrl: "some-custom-url" }, + syncKubeconfigEntries: observable.map(), + terminalConfig: { fontSize: 42 }, + editorConfiguration: { minimap: {}, tabSize: 42, fontSize: 42 }, } as unknown as UserStore), ); @@ -113,12 +128,6 @@ export const getDiForUnitTesting = (opts: GetDiForUnitTestingOptions = {}) => { info: noop, silly: noop, })); - - di.override(themeStoreInjectable, () => ({ - activeTheme: { - type: "some-active-theme-type", - }, - })); } return di; diff --git a/src/renderer/navigation/observable-history.injectable.ts b/src/renderer/navigation/observable-history.injectable.ts index d3fd5bffba..81d9df2dd1 100644 --- a/src/renderer/navigation/observable-history.injectable.ts +++ b/src/renderer/navigation/observable-history.injectable.ts @@ -10,6 +10,7 @@ import historyInjectable from "./history.injectable"; const observableHistoryInjectable = getInjectable({ id: "observable-history", + instantiate: (di) => { const history = di.inject(historyInjectable); const logger = di.inject(loggerInjectable); diff --git a/src/renderer/routes/extension-route-registrator.injectable.tsx b/src/renderer/routes/extension-route-registrator.injectable.tsx index 883874fa8a..e3ce5cbd6d 100644 --- a/src/renderer/routes/extension-route-registrator.injectable.tsx +++ b/src/renderer/routes/extension-route-registrator.injectable.tsx @@ -2,7 +2,7 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DiContainerForInstantiate } from "@ogre-tools/injectable"; +import type { DiContainerForInjection } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable"; import type { LensRendererExtension } from "../../extensions/lens-renderer-extension"; @@ -46,7 +46,7 @@ export default extensionRouteRegistratorInjectable; const toRouteInjectableFor = ( - di: DiContainerForInstantiate, + di: DiContainerForInjection, extension: LensRendererExtension, extensionInstallationCount: number, ) => diff --git a/src/test-utils/override-ipc-bridge.ts b/src/test-utils/override-ipc-bridge.ts index 375151d382..a914cc66b6 100644 --- a/src/test-utils/override-ipc-bridge.ts +++ b/src/test-utils/override-ipc-bridge.ts @@ -87,8 +87,8 @@ export const overrideIpcBridge = ({ mainDi.override( sendToChannelInElectronBrowserWindowInjectable, () => - (browserWindow, { channel: channelName, data }: SendToViewArgs) => { - const handles = rendererIpcFakeHandles.get(channelName); + (browserWindow, { channel: channelName, data = [] }: SendToViewArgs) => { + const handles = rendererIpcFakeHandles.get(channelName) || []; if (isEmpty(handles)) { throw new Error(