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

Consolidate code for application window

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen 2022-05-02 12:03:46 +03:00
parent 5ac9b2aa87
commit 55d1471c10
No known key found for this signature in database
GPG Key ID: 8C6CFB2FFFE8F68A
68 changed files with 730 additions and 387 deletions

View File

@ -35,8 +35,8 @@ describe("add-cluster - navigation using application menu", () => {
}); });
describe("when navigating to add cluster using application menu", () => { describe("when navigating to add cluster using application menu", () => {
beforeEach(() => { beforeEach(async () => {
applicationBuilder.applicationMenu.click("file.add-cluster"); await applicationBuilder.applicationMenu.click("file.add-cluster");
}); });
it("renders", () => { it("renders", () => {

View File

@ -46,8 +46,8 @@ describe("extensions - navigation using application menu", () => {
}); });
describe("when navigating to extensions using application menu", () => { describe("when navigating to extensions using application menu", () => {
beforeEach(() => { beforeEach(async () => {
applicationBuilder.applicationMenu.click("root.extensions"); await applicationBuilder.applicationMenu.click("root.extensions");
}); });
it("focuses the window", () => { it("focuses the window", () => {

View File

@ -18,7 +18,6 @@ describe("preferences - navigation using application menu", () => {
beforeEach(async () => { beforeEach(async () => {
applicationBuilder = getApplicationBuilder(); applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeSetups(({ rendererDi, mainDi }) => { applicationBuilder.beforeSetups(({ rendererDi, mainDi }) => {
mainDi.override(isAutoUpdateEnabledInjectable, () => () => false); mainDi.override(isAutoUpdateEnabledInjectable, () => () => false);
@ -47,8 +46,8 @@ describe("preferences - navigation using application menu", () => {
}); });
describe("when navigating to preferences using application menu", () => { describe("when navigating to preferences using application menu", () => {
beforeEach(() => { beforeEach(async () => {
applicationBuilder.applicationMenu.click("root.preferences"); await applicationBuilder.applicationMenu.click("root.preferences");
}); });
it("renders", () => { it("renders", () => {

View File

@ -31,8 +31,8 @@ describe("welcome - navigation using application menu", () => {
}); });
describe("when navigating to welcome using application menu", () => { describe("when navigating to welcome using application menu", () => {
beforeEach(() => { beforeEach(async () => {
applicationBuilder.applicationMenu.click("help.welcome"); await applicationBuilder.applicationMenu.click("help.welcome");
}); });
it("renders", () => { it("renders", () => {

View File

@ -14,17 +14,13 @@ import { stdout, stderr } from "process";
import getCustomKubeConfigDirectoryInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; import getCustomKubeConfigDirectoryInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import clusterStoreInjectable from "../cluster-store/cluster-store.injectable"; import clusterStoreInjectable from "../cluster-store/cluster-store.injectable";
import type { ClusterModel } from "../cluster-types"; import type { ClusterModel } from "../cluster-types";
import type { import type { DiContainer } from "@ogre-tools/injectable";
DiContainer,
} from "@ogre-tools/injectable";
import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token"; import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token";
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable"; import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable";
import { runSetups } from "../setupable-injection-token/run-setups"; import directoryForTempInjectable from "../app-paths/directory-for-temp/directory-for-temp.injectable";
console = new Console(stdout, stderr); console = new Console(stdout, stderr);
@ -86,12 +82,12 @@ describe("cluster-store", () => {
mockFs(); mockFs();
mainDi.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); mainDi.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
mainDi.override(directoryForTempInjectable, () => "some-temp-directory");
mainDi.permitSideEffects(getConfigurationFileModelInjectable); mainDi.permitSideEffects(getConfigurationFileModelInjectable);
mainDi.permitSideEffects(appVersionInjectable); mainDi.permitSideEffects(appVersionInjectable);
mainDi.permitSideEffects(clusterStoreInjectable); mainDi.permitSideEffects(clusterStoreInjectable);
// @ts-ignore
mainDi.unoverride(clusterStoreInjectable); mainDi.unoverride(clusterStoreInjectable);
}); });
@ -115,8 +111,6 @@ describe("cluster-store", () => {
mockFs(mockOpts); mockFs(mockOpts);
await runSetups(mainDi);
createCluster = mainDi.inject(createClusterInjectionToken); createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable); clusterStore = mainDi.inject(clusterStoreInjectable);
@ -239,8 +233,6 @@ describe("cluster-store", () => {
mockFs(mockOpts); mockFs(mockOpts);
await runSetups(mainDi);
createCluster = mainDi.inject(createClusterInjectionToken); createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable); clusterStore = mainDi.inject(clusterStoreInjectable);
@ -325,8 +317,6 @@ users:
mockFs(mockOpts); mockFs(mockOpts);
await runSetups(mainDi);
createCluster = mainDi.inject(createClusterInjectionToken); createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable); clusterStore = mainDi.inject(clusterStoreInjectable);
@ -371,8 +361,6 @@ users:
mockFs(mockOpts); mockFs(mockOpts);
await runSetups(mainDi);
createCluster = mainDi.inject(createClusterInjectionToken); createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable); clusterStore = mainDi.inject(clusterStoreInjectable);

View File

@ -12,11 +12,10 @@ import getConfigurationFileModelInjectable from "../get-configuration-file-model
import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable"; import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable";
import type { DiContainer } from "@ogre-tools/injectable"; import type { DiContainer } from "@ogre-tools/injectable";
import hotbarStoreInjectable from "../hotbar-store.injectable"; import hotbarStoreInjectable from "../hotbar-store.injectable";
import { HotbarStore } from "../hotbar-store"; import type { HotbarStore } from "../hotbar-store";
import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
import { runSetups } from "../setupable-injection-token/run-setups";
import catalogEntityRegistryInjectable from "../../main/catalog/catalog-entity-registry.injectable"; import catalogEntityRegistryInjectable from "../../main/catalog/catalog-entity-registry.injectable";
import directoryForUserDataInjectable
from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKindData): CatalogEntity { function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKindData): CatalogEntity {
return { return {
@ -82,6 +81,9 @@ describe("HotbarStore", () => {
di.permitSideEffects(getConfigurationFileModelInjectable); di.permitSideEffects(getConfigurationFileModelInjectable);
di.permitSideEffects(appVersionInjectable); di.permitSideEffects(appVersionInjectable);
di.permitSideEffects(hotbarStoreInjectable);
di.unoverride(hotbarStoreInjectable);
di.override(catalogEntityRegistryInjectable, () => ({ di.override(catalogEntityRegistryInjectable, () => ({
addComputedSource: (id, source) => {}, addComputedSource: (id, source) => {},
items: [ items: [
@ -135,13 +137,7 @@ describe("HotbarStore", () => {
], ],
})); }));
di.override(hotbarStoreInjectable, () => { di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
HotbarStore.resetInstance();
return HotbarStore.createInstance({
catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable),
});
});
}); });
afterEach(() => { afterEach(() => {
@ -152,9 +148,9 @@ describe("HotbarStore", () => {
beforeEach(async () => { beforeEach(async () => {
mockFs(); mockFs();
await runSetups(di);
hotbarStore = di.inject(hotbarStoreInjectable); hotbarStore = di.inject(hotbarStoreInjectable);
hotbarStore.load();
}); });
describe("load", () => { describe("load", () => {
@ -320,7 +316,7 @@ describe("HotbarStore", () => {
describe("given pre beta-5 configurations", () => { describe("given pre beta-5 configurations", () => {
beforeEach(async () => { beforeEach(async () => {
const configurationToBeMigrated = { const configurationToBeMigrated = {
"some-electron-app-path-for-user-data": { "some-directory-for-user-data": {
"lens-hotbar-store.json": JSON.stringify({ "lens-hotbar-store.json": JSON.stringify({
__internal__: { __internal__: {
migrations: { migrations: {
@ -383,9 +379,9 @@ describe("HotbarStore", () => {
mockFs(configurationToBeMigrated); mockFs(configurationToBeMigrated);
await runSetups(di);
hotbarStore = di.inject(hotbarStoreInjectable); hotbarStore = di.inject(hotbarStoreInjectable);
hotbarStore.load();
}); });
it("allows to retrieve a hotbar", () => { it("allows to retrieve a hotbar", () => {

View File

@ -2,27 +2,27 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import type { DiContainer } from "@ogre-tools/injectable";
import type { AppPaths } from "./app-path-injection-token"; import type { AppPaths } from "./app-path-injection-token";
import { appPathsInjectionToken } from "./app-path-injection-token"; import { appPathsInjectionToken } from "./app-path-injection-token";
import getElectronAppPathInjectable from "../../main/app-paths/get-electron-app-path/get-electron-app-path.injectable"; import getElectronAppPathInjectable from "../../main/app-paths/get-electron-app-path/get-electron-app-path.injectable";
import { getDisForUnitTesting } from "../../test-utils/get-dis-for-unit-testing";
import type { PathName } from "./app-path-names"; import type { PathName } from "./app-path-names";
import setElectronAppPathInjectable from "../../main/app-paths/set-electron-app-path/set-electron-app-path.injectable"; import setElectronAppPathInjectable from "../../main/app-paths/set-electron-app-path/set-electron-app-path.injectable";
import appNameInjectable from "../../main/app-paths/app-name/app-name.injectable"; import appNameInjectable from "../../main/app-paths/app-name/app-name.injectable";
import directoryForIntegrationTestingInjectable from "../../main/app-paths/directory-for-integration-testing/directory-for-integration-testing.injectable"; import directoryForIntegrationTestingInjectable from "../../main/app-paths/directory-for-integration-testing/directory-for-integration-testing.injectable";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import type { DiContainer } from "@ogre-tools/injectable";
describe("app-paths", () => { describe("app-paths", () => {
let mainDi: DiContainer; let applicationBuilder: ApplicationBuilder;
let rendererDi: DiContainer; let rendererDi: DiContainer;
let runSetups: () => Promise<void[]>; let mainDi: DiContainer;
beforeEach(() => { beforeEach(() => {
const dis = getDisForUnitTesting({ doGeneralOverrides: true }); applicationBuilder = getApplicationBuilder();
mainDi = dis.mainDi; rendererDi = applicationBuilder.dis.rendererDi;
rendererDi = dis.rendererDi; mainDi = applicationBuilder.dis.mainDi;
runSetups = dis.runSetups;
const defaultAppPathsStub: AppPaths = { const defaultAppPathsStub: AppPaths = {
appData: "some-app-data", appData: "some-app-data",
@ -43,27 +43,29 @@ describe("app-paths", () => {
userData: "some-irrelevant-user-data", userData: "some-irrelevant-user-data",
}; };
mainDi.override( applicationBuilder.beforeSetups(({ mainDi }) => {
getElectronAppPathInjectable, mainDi.override(
() => getElectronAppPathInjectable,
(key: PathName): string | null => () =>
defaultAppPathsStub[key], (key: PathName): string | null =>
); defaultAppPathsStub[key],
);
mainDi.override( mainDi.override(
setElectronAppPathInjectable, setElectronAppPathInjectable,
() => () =>
(key: PathName, path: string): void => { (key: PathName, path: string): void => {
defaultAppPathsStub[key] = path; defaultAppPathsStub[key] = path;
}, },
); );
mainDi.override(appNameInjectable, () => "some-app-name"); mainDi.override(appNameInjectable, () => "some-app-name");
});
}); });
describe("normally", () => { describe("normally", () => {
beforeEach(async () => { beforeEach(async () => {
await runSetups(); await applicationBuilder.render();
}); });
it("given in renderer, when injecting app paths, returns application specific app paths", () => { it("given in renderer, when injecting app paths, returns application specific app paths", () => {
@ -115,12 +117,14 @@ describe("app-paths", () => {
describe("when running integration tests", () => { describe("when running integration tests", () => {
beforeEach(async () => { beforeEach(async () => {
mainDi.override( applicationBuilder.beforeSetups(({ mainDi }) => {
directoryForIntegrationTestingInjectable, mainDi.override(
() => "some-integration-testing-app-data", directoryForIntegrationTestingInjectable,
); () => "some-integration-testing-app-data",
);
});
await runSetups(); await applicationBuilder.render();
}); });
it("given in renderer, when injecting path for app data, has integration specific app data path", () => { it("given in renderer, when injecting path for app data, has integration specific app data path", () => {

View File

@ -0,0 +1,14 @@
/**
* 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 { broadcastMessage } from "./ipc";
const broadcastMessageInjectable = getInjectable({
id: "broadcast-message",
instantiate: () => broadcastMessage,
// causesSideEffects: true,
});
export default broadcastMessageInjectable;

View File

@ -1,21 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DiContainer } from "@ogre-tools/injectable";
import { beforeElectronIsReadyInjectionToken } from "../../main/start-main-application/before-electron-is-ready/before-electron-is-ready-injection-token";
import { afterApplicationIsReadyInjectionToken } from "../../main/start-main-application/after-application-is-ready/after-application-is-ready-injection-token";
export const runSetups = async (di: DiContainer) => {
await Promise.all(
di
.injectMany(beforeElectronIsReadyInjectionToken)
.map((setupable) => setupable.run()),
);
await Promise.all(
di
.injectMany(afterApplicationIsReadyInjectionToken)
.map((setupable) => setupable.run()),
);
};

View File

@ -9,8 +9,8 @@ import { stdout, stderr } from "process";
import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable"; import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable";
import { runInAction } from "mobx"; import { runInAction } from "mobx";
import updateExtensionsStateInjectable from "../extension-loader/update-extensions-state/update-extensions-state.injectable"; import updateExtensionsStateInjectable from "../extension-loader/update-extensions-state/update-extensions-state.injectable";
import { getDisForUnitTesting } from "../../test-utils/get-dis-for-unit-testing";
import mockFs from "mock-fs"; import mockFs from "mock-fs";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
console = new Console(stdout, stderr); console = new Console(stdout, stderr);
@ -110,15 +110,15 @@ describe("ExtensionLoader", () => {
let updateExtensionStateMock: jest.Mock; let updateExtensionStateMock: jest.Mock;
beforeEach(() => { beforeEach(() => {
const dis = getDisForUnitTesting({ doGeneralOverrides: true }); const di = getDiForUnitTesting({ doGeneralOverrides: true });
mockFs(); mockFs();
updateExtensionStateMock = jest.fn(); updateExtensionStateMock = jest.fn();
dis.mainDi.override(updateExtensionsStateInjectable, () => updateExtensionStateMock); di.override(updateExtensionsStateInjectable, () => updateExtensionStateMock);
extensionLoader = dis.mainDi.inject(extensionLoaderInjectable); extensionLoader = di.inject(extensionLoaderInjectable);
}); });
afterEach(() => { afterEach(() => {

View File

@ -2,7 +2,8 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { runSetups } from "../../common/setupable-injection-token/run-setups"; import directoryForTempInjectable
from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
const logger = { const logger = {
silly: jest.fn(), silly: jest.fn(),
@ -44,6 +45,8 @@ import { createClusterInjectionToken } from "../../common/cluster/create-cluster
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable"; import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable"; import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable"; import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
import directoryForUserDataInjectable
from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
console = new Console(process.stdout, process.stderr); // fix mockFS console = new Console(process.stdout, process.stderr); // fix mockFS
@ -80,14 +83,14 @@ describe("create clusters", () => {
}), }),
}); });
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
di.override(directoryForTempInjectable, () => "some-directory-for-temp");
di.override(authorizationReviewInjectable, () => () => () => Promise.resolve(true)); di.override(authorizationReviewInjectable, () => () => () => Promise.resolve(true));
di.override(listNamespacesInjectable, () => () => () => Promise.resolve([ "default" ])); di.override(listNamespacesInjectable, () => () => () => Promise.resolve([ "default" ]));
di.override(createContextHandlerInjectable, () => () => { di.override(createContextHandlerInjectable, () => () => {
throw new Error("you should never come here"); throw new Error("you should never come here");
}); });
await runSetups(di);
createCluster = di.inject(createClusterInjectionToken); createCluster = di.inject(createClusterInjectionToken);
jest.spyOn(Kubectl.prototype, "ensureKubectl").mockReturnValue(Promise.resolve(true)); jest.spyOn(Kubectl.prototype, "ensureKubectl").mockReturnValue(Promise.resolve(true));

View File

@ -57,7 +57,10 @@ import path from "path";
import spawnInjectable from "../child-process/spawn.injectable"; import spawnInjectable from "../child-process/spawn.injectable";
import getConfigurationFileModelInjectable from "../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import getConfigurationFileModelInjectable from "../../common/get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.injectable"; import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.injectable";
import { runSetups } from "../../common/setupable-injection-token/run-setups"; import directoryForUserDataInjectable
from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import directoryForTempInjectable
from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
console = new Console(stdout, stderr); console = new Console(stdout, stderr);
@ -100,6 +103,9 @@ describe("kube auth proxy tests", () => {
const di = getDiForUnitTesting({ doGeneralOverrides: true }); const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
di.override(directoryForTempInjectable, () => "some-directory-for-temp");
di.override(spawnInjectable, () => mockSpawn); di.override(spawnInjectable, () => mockSpawn);
di.permitSideEffects(getConfigurationFileModelInjectable); di.permitSideEffects(getConfigurationFileModelInjectable);
@ -107,8 +113,6 @@ describe("kube auth proxy tests", () => {
mockFs(mockMinikubeConfig); mockFs(mockMinikubeConfig);
await runSetups(di);
createCluster = di.inject(createClusterInjectionToken); createCluster = di.inject(createClusterInjectionToken);
createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable); createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable);

View File

@ -2,8 +2,13 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { runSetups } from "../../common/setupable-injection-token/run-setups"; import directoryForUserDataInjectable
from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
const logger = { const logger = {
silly: jest.fn(), silly: jest.fn(),
debug: jest.fn(), debug: jest.fn(),
@ -57,6 +62,8 @@ describe("kubeconfig manager tests", () => {
di = getDiForUnitTesting({ doGeneralOverrides: true }); di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForTempInjectable, () => "some-directory-for-temp"); di.override(directoryForTempInjectable, () => "some-directory-for-temp");
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
mockFs({ mockFs({
"minikube-config.yml": JSON.stringify({ "minikube-config.yml": JSON.stringify({
@ -86,8 +93,6 @@ describe("kubeconfig manager tests", () => {
throw new Error("you should never come here"); throw new Error("you should never come here");
}); });
await runSetups(di);
const createCluster = di.inject(createClusterInjectionToken); const createCluster = di.inject(createClusterInjectionToken);
createKubeconfigManager = di.inject(createKubeconfigManagerInjectable); createKubeconfigManager = di.inject(createKubeconfigManagerInjectable);

View File

@ -17,8 +17,9 @@ import directoryForKubeConfigsInjectable from "../../../common/app-paths/directo
import { ClusterStore } from "../../../common/cluster-store/cluster-store"; import { ClusterStore } from "../../../common/cluster-store/cluster-store";
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable"; import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable";
import { runSetups } from "../../../common/setupable-injection-token/run-setups";
import clusterManagerInjectable from "../../cluster-manager.injectable"; import clusterManagerInjectable from "../../cluster-manager.injectable";
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import directoryForTempInjectable from "../../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
jest.mock("electron", () => ({ jest.mock("electron", () => ({
app: { app: {
@ -44,6 +45,9 @@ describe("kubeconfig-sync.source tests", () => {
mockFs(); mockFs();
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
di.override(directoryForTempInjectable, () => "some-directory-for-temp");
di.override(clusterStoreInjectable, () => di.override(clusterStoreInjectable, () =>
ClusterStore.createInstance({ createCluster: () => null }), ClusterStore.createInstance({ createCluster: () => null }),
); );
@ -51,8 +55,6 @@ describe("kubeconfig-sync.source tests", () => {
di.permitSideEffects(getConfigurationFileModelInjectable); di.permitSideEffects(getConfigurationFileModelInjectable);
di.permitSideEffects(appVersionInjectable); di.permitSideEffects(appVersionInjectable);
await runSetups(di);
computeDiff = computeDiffFor({ computeDiff = computeDiffFor({
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable), directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
createCluster: di.inject(createClusterInjectionToken), createCluster: di.inject(createClusterInjectionToken),

View File

@ -10,7 +10,8 @@ import commandLineArgumentsInjectable from "../../utils/command-line-arguments.i
import { pipeline } from "@ogre-tools/fp"; import { pipeline } from "@ogre-tools/fp";
import { find, startsWith, toLower, map } from "lodash/fp"; import { find, startsWith, toLower, map } from "lodash/fp";
import { whenApplicationIsLoadingInjectionToken } from "../../start-main-application/when-application-is-loading/when-application-is-loading-injection-token"; import { whenApplicationIsLoadingInjectionToken } from "../../start-main-application/when-application-is-loading/when-application-is-loading-injection-token";
import applicationWindowInjectable from "../../start-main-application/lens-window/application-window/application-window.injectable"; import showApplicationWindowInjectable
from "../../start-main-application/lens-window/show-application-window.injectable";
const setupDeepLinkingInjectable = getInjectable({ const setupDeepLinkingInjectable = getInjectable({
id: "setup-deep-linking", id: "setup-deep-linking",
@ -19,7 +20,7 @@ const setupDeepLinkingInjectable = getInjectable({
const app = di.inject(electronAppInjectable); const app = di.inject(electronAppInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const openDeepLinkForUrl = di.inject(openDeepLinkInjectable); const openDeepLinkForUrl = di.inject(openDeepLinkInjectable);
const applicationWindow = di.inject(applicationWindowInjectable); const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const firstInstanceCommandLineArguments = di.inject( const firstInstanceCommandLineArguments = di.inject(
commandLineArgumentsInjectable, commandLineArgumentsInjectable,
@ -53,7 +54,7 @@ const setupDeepLinkingInjectable = getInjectable({
async (_, secondInstanceCommandLineArguments) => { async (_, secondInstanceCommandLineArguments) => {
const url = getDeepLinkUrl(secondInstanceCommandLineArguments); const url = getDeepLinkUrl(secondInstanceCommandLineArguments);
await applicationWindow.show(); await showApplicationWindow();
if (url) { if (url) {
await openDeepLinkForUrl(url); await openDeepLinkForUrl(url);

View File

@ -6,14 +6,14 @@ import { getInjectable } from "@ogre-tools/injectable";
import electronAppInjectable from "../electron-app.injectable"; import electronAppInjectable from "../electron-app.injectable";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../../start-main-application/when-application-is-loading/when-application-is-loading-injection-token"; import { whenApplicationIsLoadingInjectionToken } from "../../start-main-application/when-application-is-loading/when-application-is-loading-injection-token";
import applicationWindowInjectable from "../../start-main-application/lens-window/application-window/application-window.injectable"; import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable";
const setupMainWindowVisibilityAfterActivationInjectable = getInjectable({ const setupMainWindowVisibilityAfterActivationInjectable = getInjectable({
id: "setup-main-window-visibility-after-activation", id: "setup-main-window-visibility-after-activation",
instantiate: (di) => { instantiate: (di) => {
const app = di.inject(electronAppInjectable); const app = di.inject(electronAppInjectable);
const applicationWindow = di.inject(applicationWindowInjectable); const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
return { return {
@ -22,7 +22,7 @@ const setupMainWindowVisibilityAfterActivationInjectable = getInjectable({
logger.info("APP:ACTIVATE", { hasVisibleWindows: windowIsVisible }); logger.info("APP:ACTIVATE", { hasVisibleWindows: windowIsVisible });
if (!windowIsVisible) { if (!windowIsVisible) {
await applicationWindow.show(); await showApplicationWindow();
} }
}); });
}, },

View File

@ -0,0 +1,31 @@
/**
* 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 { beforeElectronIsReadyInjectionToken } from "../../start-main-application/before-electron-is-ready/before-electron-is-ready-injection-token";
import electronAppInjectable from "../electron-app.injectable";
import { runManyFor } from "../../../common/runnable/run-many-for";
import { afterWindowIsOpenedInjectionToken } from "../../start-main-application/after-window-is-opened/after-window-is-opened-injection-token";
const setupRunnablesAfterWindowIsOpenedInjectable = getInjectable({
id: "setup-runnables-after-window-is-opened",
instantiate: (di) => {
const afterWindowIsOpened = runManyFor(di)(afterWindowIsOpenedInjectionToken);
return {
run: () => {
const app = di.inject(electronAppInjectable);
app.on("browser-window-created", async () => {
await afterWindowIsOpened();
});
},
};
},
injectionToken: beforeElectronIsReadyInjectionToken,
});
export default setupRunnablesAfterWindowIsOpenedInjectable;

View File

@ -9,7 +9,7 @@ import { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/
import electronAppInjectable from "../electron-app.injectable"; import electronAppInjectable from "../electron-app.injectable";
import isIntegrationTestingInjectable from "../../../common/vars/is-integration-testing.injectable"; import isIntegrationTestingInjectable from "../../../common/vars/is-integration-testing.injectable";
import autoUpdaterInjectable from "../features/auto-updater.injectable"; import autoUpdaterInjectable from "../features/auto-updater.injectable";
import { runManySyncFor } from "../../start-main-application/run-many-sync-for"; import { runManySyncFor } from "../../../common/runnable/run-many-sync-for";
const setupRunnablesBeforeClosingOfApplicationInjectable = getInjectable({ const setupRunnablesBeforeClosingOfApplicationInjectable = getInjectable({
id: "setup-closing-of-application", id: "setup-closing-of-application",

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { beforeQuitOfFrontEndInjectionToken } from "../../start-main-application/before-quit-of-front-end/before-quit-of-front-end-injection-token";
import electronAppInjectable from "../electron-app.injectable";
import { lensWindowInjectionToken } from "../../start-main-application/lens-window/application-window/lens-window-injection-token";
import { pipeline } from "@ogre-tools/fp";
import { filter, isEmpty } from "lodash/fp";
const hideDockForLastClosedWindowInjectable = getInjectable({
id: "hide-dock-when-there-are-no-windows",
instantiate: (di) => {
const app = di.inject(electronAppInjectable);
const getLensWindows = () => di.injectMany(lensWindowInjectionToken);
return {
run: () => {
const visibleWindows = pipeline(
getLensWindows(),
filter(window => !!window.visible),
);
if (isEmpty(visibleWindows)) {
app.dock?.hide();
}
},
};
},
injectionToken: beforeQuitOfFrontEndInjectionToken,
});
export default hideDockForLastClosedWindowInjectable;

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import electronAppInjectable from "../electron-app.injectable";
import { afterWindowIsOpenedInjectionToken } from "../../start-main-application/after-window-is-opened/after-window-is-opened-injection-token";
const showDockForFirstOpenedWindowInjectable = getInjectable({
id: "show-dock-for-first-opened-window",
instantiate: (di) => {
const app = di.inject(electronAppInjectable);
return {
run: () => {
app.dock?.show();
},
};
},
injectionToken: afterWindowIsOpenedInjectionToken,
});
export default showDockForFirstOpenedWindowInjectable;

View File

@ -0,0 +1,14 @@
/**
* 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 electronAppInjectable from "../electron-app.injectable";
const waitForElectronToBeReadyInjectable = getInjectable({
id: "wait-for-electron-to-be-ready",
instantiate: (di) => () => di.inject(electronAppInjectable).whenReady(),
});
export default waitForElectronToBeReadyInjectable;

View File

@ -72,8 +72,21 @@ import showMessagePopupInjectable from "./electron-app/features/show-message-pop
import clusterFramesInjectable from "../common/cluster-frames.injectable"; import clusterFramesInjectable from "../common/cluster-frames.injectable";
import type { ClusterFrameInfo } from "../common/cluster-frames"; import type { ClusterFrameInfo } from "../common/cluster-frames";
import { observable } from "mobx"; import { observable } from "mobx";
import createBrowserWindowInjectable from "./start-main-application/lens-window/application-window/create-browser-window.injectable"; // import createBrowserWindowInjectable from "./start-main-application/lens-window/application-window/create-browser-window.injectable";
import type { BrowserWindow } from "electron"; import waitForElectronToBeReadyInjectable from "./electron-app/features/wait-for-electron-to-be-ready.injectable";
import setupListenerForCurrentClusterFrameInjectable
from "./start-main-application/lens-window/current-cluster-frame/setup-listener-for-current-cluster-frame.injectable";
import ipcMainInjectable from "./app-paths/register-channel/ipc-main/ipc-main.injectable";
import createElectronWindowForInjectable
from "./start-main-application/lens-window/application-window/create-electron-window-for.injectable";
import setupRunnablesAfterWindowIsOpenedInjectable
from "./electron-app/before-application-is-ready/setup-runnables-after-window-is-opened.injectable";
import sendToChannelInElectronBrowserWindowInjectable
from "./start-main-application/lens-window/application-window/send-to-channel-in-electron-browser-window.injectable";
export const getDiForUnitTesting = ( export const getDiForUnitTesting = (
{ doGeneralOverrides } = { doGeneralOverrides: false }, { doGeneralOverrides } = { doGeneralOverrides: false },
@ -180,6 +193,8 @@ const overrideRunnablesHavingSideEffects = (di: DiContainer) => {
setupShellInjectable, setupShellInjectable,
setupSyncingOfWeblinksInjectable, setupSyncingOfWeblinksInjectable,
setupSystemCaInjectable, setupSystemCaInjectable,
setupListenerForCurrentClusterFrameInjectable,
setupRunnablesAfterWindowIsOpenedInjectable,
].forEach((injectable) => { ].forEach((injectable) => {
di.override(injectable, () => ({ run: () => {} })); di.override(injectable, () => ({ run: () => {} }));
}); });
@ -211,11 +226,20 @@ const overrideElectronFeatures = (di: DiContainer) => {
di.override(disableHardwareAccelerationInjectable, () => () => {}); di.override(disableHardwareAccelerationInjectable, () => () => {});
di.override(shouldStartHiddenInjectable, () => true); di.override(shouldStartHiddenInjectable, () => true);
di.override(showMessagePopupInjectable, () => () => {}); di.override(showMessagePopupInjectable, () => () => {});
di.override(waitForElectronToBeReadyInjectable, () => () => Promise.resolve());
di.override(ipcMainInjectable, () => ({}));
di.override(createBrowserWindowInjectable, () => async () => ({ di.override(createElectronWindowForInjectable, () => () => async () => ({
show: () => {}, show: () => {},
hide: () => {},
}) as unknown as BrowserWindow); close: () => {},
send: (arg) => {
const sendFake = di.inject(sendToChannelInElectronBrowserWindowInjectable);
sendFake(null, arg);
},
}));
di.override( di.override(
getElectronAppPathInjectable, getElectronAppPathInjectable,

View File

@ -24,6 +24,8 @@ import { computed } from "mobx";
import showAboutInjectable from "./show-about.injectable"; import showAboutInjectable from "./show-about.injectable";
import applicationWindowInjectable from "../start-main-application/lens-window/application-window/application-window.injectable"; import applicationWindowInjectable from "../start-main-application/lens-window/application-window/application-window.injectable";
import reloadWindowInjectable from "../start-main-application/lens-window/reload-window.injectable"; import reloadWindowInjectable from "../start-main-application/lens-window/reload-window.injectable";
import showApplicationWindowInjectable
from "../start-main-application/lens-window/show-application-window.injectable";
function ignoreIf(check: boolean, menuItems: MenuItemConstructorOptions[]) { function ignoreIf(check: boolean, menuItems: MenuItemConstructorOptions[]) {
return check ? [] : menuItems; return check ? [] : menuItems;
@ -44,6 +46,7 @@ const applicationMenuItemsInjectable = getInjectable({
const electronMenuItems = di.inject(electronMenuItemsInjectable); const electronMenuItems = di.inject(electronMenuItemsInjectable);
const showAbout = di.inject(showAboutInjectable); const showAbout = di.inject(showAboutInjectable);
const applicationWindow = di.inject(applicationWindowInjectable); const applicationWindow = di.inject(applicationWindowInjectable);
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const reloadApplicationWindow = di.inject(reloadWindowInjectable, applicationWindow); const reloadApplicationWindow = di.inject(reloadWindowInjectable, applicationWindow);
return computed((): MenuItemOpts[] => { return computed((): MenuItemOpts[] => {
@ -76,7 +79,7 @@ const applicationMenuItemsInjectable = getInjectable({
{ {
label: "Check for updates", label: "Check for updates",
click() { click() {
checkForUpdates().then(() => applicationWindow.show()); checkForUpdates().then(() => showApplicationWindow());
}, },
}, },
]), ]),
@ -294,7 +297,7 @@ const applicationMenuItemsInjectable = getInjectable({
label: "Check for updates", label: "Check for updates",
click() { click() {
checkForUpdates().then(() => checkForUpdates().then(() =>
applicationWindow.show(), showApplicationWindow(),
); );
}, },
}, },

View File

@ -13,7 +13,7 @@ const navigateToRouteInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const navigateToUrl = di.inject(navigateToUrlInjectionToken); const navigateToUrl = di.inject(navigateToUrlInjectionToken);
return (route, options) => { return async (route, options) => {
const url = buildURL(route.path, { const url = buildURL(route.path, {
// TODO: enhance typing // TODO: enhance typing
params: options?.parameters as any, params: options?.parameters as any,
@ -21,7 +21,7 @@ const navigateToRouteInjectable = getInjectable({
fragment: options?.fragment, fragment: options?.fragment,
}); });
navigateToUrl(url, options); await navigateToUrl(url, options);
}; };
}, },

View File

@ -12,8 +12,8 @@ const navigateToUrlInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const navigate = di.inject(navigateInjectable); const navigate = di.inject(navigateInjectable);
return (url) => { return async (url) => {
navigate(url); await navigate(url);
}; };
}, },

View File

@ -18,7 +18,9 @@ import lensProtocolRouterMainInjectable from "../lens-protocol-router-main/lens-
import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable"; import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable";
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable"; import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable";
import { runSetups } from "../../../common/setupable-injection-token/run-setups"; import directoryForUserDataInjectable
from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
jest.mock("../../../common/ipc"); jest.mock("../../../common/ipc");
@ -47,7 +49,7 @@ describe("protocol router tests", () => {
di.permitSideEffects(getConfigurationFileModelInjectable); di.permitSideEffects(getConfigurationFileModelInjectable);
di.permitSideEffects(appVersionInjectable); di.permitSideEffects(appVersionInjectable);
await runSetups(di); di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
extensionLoader = di.inject(extensionLoaderInjectable); extensionLoader = di.inject(extensionLoaderInjectable);

View File

@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable"; import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable";
import { LensProtocolRouterMain } from "./lens-protocol-router-main"; import { LensProtocolRouterMain } from "./lens-protocol-router-main";
import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable"; import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable";
import applicationWindowInjectable from "../../start-main-application/lens-window/application-window/application-window.injectable"; import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable";
const lensProtocolRouterMainInjectable = getInjectable({ const lensProtocolRouterMainInjectable = getInjectable({
id: "lens-protocol-router-main", id: "lens-protocol-router-main",
@ -15,7 +15,7 @@ const lensProtocolRouterMainInjectable = getInjectable({
new LensProtocolRouterMain({ new LensProtocolRouterMain({
extensionLoader: di.inject(extensionLoaderInjectable), extensionLoader: di.inject(extensionLoaderInjectable),
extensionsStore: di.inject(extensionsStoreInjectable), extensionsStore: di.inject(extensionsStoreInjectable),
applicationWindow: di.inject(applicationWindowInjectable), showApplicationWindow: di.inject(showApplicationWindowInjectable),
}), }),
}); });

View File

@ -14,9 +14,6 @@ import { ProtocolHandlerInvalid } from "../../../common/protocol-handler";
import { disposer, noop } from "../../../common/utils"; import { disposer, noop } from "../../../common/utils";
import type { ExtensionLoader } from "../../../extensions/extension-loader"; import type { ExtensionLoader } from "../../../extensions/extension-loader";
import type { ExtensionsStore } from "../../../extensions/extensions-store/extensions-store"; import type { ExtensionsStore } from "../../../extensions/extensions-store/extensions-store";
import type {
LensWindow,
} from "../../start-main-application/lens-window/application-window/lens-window-injection-token";
export interface FallbackHandler { export interface FallbackHandler {
(name: string): Promise<boolean>; (name: string): Promise<boolean>;
@ -42,7 +39,7 @@ function checkHost<Query>(url: URLParse<Query>): boolean {
interface Dependencies { interface Dependencies {
extensionLoader: ExtensionLoader; extensionLoader: ExtensionLoader;
extensionsStore: ExtensionsStore; extensionsStore: ExtensionsStore;
applicationWindow: LensWindow; showApplicationWindow: () => Promise<void>;
} }
export class LensProtocolRouterMain extends proto.LensProtocolRouter { export class LensProtocolRouterMain extends proto.LensProtocolRouter {
@ -79,7 +76,7 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
throw new proto.RoutingError(proto.RoutingErrorType.INVALID_PROTOCOL, url); throw new proto.RoutingError(proto.RoutingErrorType.INVALID_PROTOCOL, url);
} }
this.dependencies.applicationWindow.show().catch(noop); this.dependencies.showApplicationWindow().catch(noop);
const routeInternally = checkHost(url); const routeInternally = checkHost(url);
logger.info(`${proto.LensProtocolRouter.LoggingPrefix}: routing ${url.toString()}`); logger.info(`${proto.LensProtocolRouter.LoggingPrefix}: routing ${url.toString()}`);

View File

@ -14,7 +14,9 @@ import asyncFn from "@async-fn/jest";
import parseRequestInjectable from "./parse-request.injectable"; import parseRequestInjectable from "./parse-request.injectable";
import { contentTypes } from "./router-content-types"; import { contentTypes } from "./router-content-types";
import mockFs from "mock-fs"; import mockFs from "mock-fs";
import { runSetups } from "../../common/setupable-injection-token/run-setups"; import directoryForUserDataInjectable
from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
describe("router", () => { describe("router", () => {
let router: Router; let router: Router;
@ -28,6 +30,7 @@ describe("router", () => {
mockFs(); mockFs();
di.override(parseRequestInjectable, () => () => Promise.resolve({ payload: "some-payload" })); di.override(parseRequestInjectable, () => () => Promise.resolve({ payload: "some-payload" }));
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
const injectable = getInjectable({ const injectable = getInjectable({
id: "some-route", id: "some-route",
@ -43,8 +46,6 @@ describe("router", () => {
di.register(injectable); di.register(injectable);
await runSetups(di);
router = di.inject(routerInjectable); router = di.inject(routerInjectable);
}); });

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../run-many-for"; import type { Runnable } from "../../../common/runnable/run-many-for";
export const afterApplicationIsLoadedInjectionToken = getInjectionToken<Runnable>({ export const afterApplicationIsLoadedInjectionToken = getInjectionToken<Runnable>({
id: "after-application-is-loaded", id: "after-application-is-loaded",

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../run-many-for"; import type { Runnable } from "../../../common/runnable/run-many-for";
export const afterRootFrameIsReadyInjectionToken = getInjectionToken<Runnable>({ export const afterRootFrameIsReadyInjectionToken = getInjectionToken<Runnable>({
id: "after-root-frame-is-ready", id: "after-root-frame-is-ready",

View File

@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../../../common/runnable/run-many-for";
export const afterWindowIsOpenedInjectionToken = getInjectionToken<Runnable>({
id: "after-window-is-opened",
});

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../run-many-for"; import type { Runnable } from "../../../common/runnable/run-many-for";
export const beforeApplicationIsLoadingInjectionToken = getInjectionToken<Runnable>({ export const beforeApplicationIsLoadingInjectionToken = getInjectionToken<Runnable>({
id: "before-application-is-loading", id: "before-application-is-loading",

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { RunnableSync } from "../run-many-sync-for"; import type { RunnableSync } from "../../../common/runnable/run-many-sync-for";
export const beforeElectronIsReadyInjectionToken = export const beforeElectronIsReadyInjectionToken =
getInjectionToken<RunnableSync>({ getInjectionToken<RunnableSync>({

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { RunnableSync } from "../run-many-sync-for"; import type { RunnableSync } from "../../../common/runnable/run-many-sync-for";
export const beforeQuitOfBackEndInjectionToken = export const beforeQuitOfBackEndInjectionToken =
getInjectionToken<RunnableSync>({ getInjectionToken<RunnableSync>({

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { RunnableSync } from "../run-many-sync-for"; import type { RunnableSync } from "../../../common/runnable/run-many-sync-for";
export const beforeQuitOfFrontEndInjectionToken = export const beforeQuitOfFrontEndInjectionToken =
getInjectionToken<RunnableSync>({ getInjectionToken<RunnableSync>({

View File

@ -3,86 +3,28 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { BrowserWindow } from "electron"; import { lensWindowInjectionToken } from "../application-window/lens-window-injection-token";
import type { import createLensWindowInjectable from "../application-window/create-lens-window.injectable";
SendToViewArgs,
} from "../application-window/lens-window-injection-token";
import {
lensWindowInjectionToken,
} from "../application-window/lens-window-injection-token";
const applicationIsLoadingWindowInjectable = getInjectable({ const applicationIsLoadingWindowInjectable = getInjectable({
id: "application-is-loading-window", id: "application-is-loading-window",
instantiate: () => { instantiate: (di) => {
let loadingWindow: BrowserWindow; const createLensWindow = di.inject(createLensWindowInjectable);
const hideWindow = () => { return createLensWindow({
loadingWindow?.hide(); id: "splash",
}; title: "Loading",
getContentUrl: () => "static://splash.html",
return { defaultWidth: 500,
show: async () => { defaultHeight: 300,
if (!loadingWindow) { resizable: false,
loadingWindow = await createLoadingWindow(); windowFrameUtilitiesAreShown: false,
} centered: true,
});
loadingWindow.show();
},
hide: hideWindow,
close: () => {
hideWindow();
loadingWindow = null;
},
send: async ({ channel, frameInfo, data = [] }: SendToViewArgs) => {
if (!loadingWindow) {
loadingWindow = await createLoadingWindow();
}
if (frameInfo) {
loadingWindow.webContents.sendToFrame(
[frameInfo.processId, frameInfo.frameId],
channel,
...data,
);
} else {
loadingWindow.webContents.send(channel, ...data);
}
},
};
}, },
injectionToken: lensWindowInjectionToken, injectionToken: lensWindowInjectionToken,
causesSideEffects: true,
}); });
export default applicationIsLoadingWindowInjectable; export default applicationIsLoadingWindowInjectable;
const createLoadingWindow = async () => {
const loadingWindow = new BrowserWindow({
width: 500,
height: 300,
backgroundColor: "#1e2124",
center: true,
frame: false,
resizable: false,
show: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
nodeIntegrationInSubFrames: true,
nativeWindowOpen: true,
},
});
await loadingWindow.loadURL("static://splash.html");
return loadingWindow;
};

View File

@ -2,20 +2,31 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import windowStateKeeper from "electron-window-state"; import windowStateKeeper from "electron-window-state";
interface WindowStateConfiguration {
id: string;
defaultHeight: number;
defaultWidth: number;
}
const applicationWindowStateInjectable = getInjectable({ const applicationWindowStateInjectable = getInjectable({
id: "application-window-state", id: "application-window-state",
instantiate: () => { instantiate: (
console.log("asdasd"); di,
{ id, defaultHeight, defaultWidth }: WindowStateConfiguration,
) =>
windowStateKeeper({
defaultHeight,
defaultWidth,
file: `window-state-for-${id}.json`,
}),
return windowStateKeeper({ lifecycle: lifecycleEnum.keyedSingleton({
defaultHeight: 900, getInstanceKey: (di, { id }: WindowStateConfiguration) => id,
defaultWidth: 1440, }),
});
},
}); });
export default applicationWindowStateInjectable; export default applicationWindowStateInjectable;

View File

@ -3,56 +3,63 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import createBrowserWindowInjectable from "./create-browser-window.injectable"; import { lensWindowInjectionToken } from "./lens-window-injection-token";
import type { import createLensWindowInjectable from "./create-lens-window.injectable";
SendToViewArgs } from "./lens-window-injection-token"; import lensProxyPortNumberStateInjectable from "../../../lens-proxy-port-number-state.injectable";
import { import isMacInjectable from "../../../../common/vars/is-mac.injectable";
lensWindowInjectionToken, import appNameInjectable from "../../../app-paths/app-name/app-name.injectable";
} from "./lens-window-injection-token"; import appEventBusInjectable from "../../../../common/app-event-bus/app-event-bus.injectable";
import type { BrowserWindow } from "electron"; import { delay } from "../../../../common/utils";
import sendToChannelInElectronBrowserWindowInjectable from "./send-to-channel-in-electron-browser-window.injectable"; import { bundledExtensionsLoaded } from "../../../../common/ipc/extension-handling";
import ipcMainInjectable from "../../../app-paths/register-channel/ipc-main/ipc-main.injectable";
const applicationWindowInjectable = getInjectable({ const applicationWindowInjectable = getInjectable({
id: "application-window", id: "application-window",
instantiate: (di) => { instantiate: (di) => {
const createBrowserWindow = di.inject(createBrowserWindowInjectable); const createLensWindow = di.inject(createLensWindowInjectable);
const isMac = di.inject(isMacInjectable);
const applicationName = di.inject(appNameInjectable);
const appEventBus = di.inject(appEventBusInjectable);
const ipcMain = di.inject(ipcMainInjectable);
let browserWindow: BrowserWindow = null; const lensProxyPortNumberState = di.inject(
lensProxyPortNumberStateInjectable,
const hideWindow = () => {
browserWindow?.hide();
};
const sendToChannelInLensWindow = di.inject(
sendToChannelInElectronBrowserWindowInjectable,
); );
return { const getContentUrl = () => `http://localhost:${lensProxyPortNumberState.get()}`;
show: async () => {
if (!browserWindow) {
browserWindow = await createBrowserWindow("only-application-window");
}
browserWindow.show(); return createLensWindow({
id: "only-application-window",
title: applicationName,
defaultHeight: 900,
defaultWidth: 1440,
getContentUrl,
resizable: true,
windowFrameUtilitiesAreShown: isMac,
centered: false,
onFocus: () => {
appEventBus.emit({ name: "app", action: "focus" });
}, },
hide: hideWindow, onBlur: () => {
appEventBus.emit({ name: "app", action: "blur" });
close: () => {
hideWindow();
browserWindow = null;
}, },
send: async (args: SendToViewArgs) => { onDomReady: () => {
if (!browserWindow) { appEventBus.emit({ name: "app", action: "dom-ready" });
browserWindow = await createBrowserWindow("only-application-window");
}
return sendToChannelInLensWindow(browserWindow, args);
}, },
};
beforeOpen: async () => {
const viewHasLoaded = new Promise<void>((resolve) => {
ipcMain.once(bundledExtensionsLoaded, () => resolve());
});
await viewHasLoaded;
await delay(50); // wait just a bit longer to let the first round of rendering happen
},
});
}, },
injectionToken: lensWindowInjectionToken, injectionToken: lensWindowInjectionToken,

View File

@ -4,32 +4,51 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import loggerInjectable from "../../../../common/logger.injectable"; import loggerInjectable from "../../../../common/logger.injectable";
import appNameInjectable from "../../../app-paths/app-name/app-name.injectable";
import applicationWindowStateInjectable from "./application-window-state.injectable"; import applicationWindowStateInjectable from "./application-window-state.injectable";
import lensProxyPortNumberStateInjectable from "../../../lens-proxy-port-number-state.injectable";
import isMacInjectable from "../../../../common/vars/is-mac.injectable"; import isMacInjectable from "../../../../common/vars/is-mac.injectable";
import { BrowserWindow, ipcMain } from "electron"; import { BrowserWindow } from "electron";
import { delay, openBrowser } from "../../../../common/utils"; import { openBrowser } from "../../../../common/utils";
import { bundledExtensionsLoaded } from "../../../../common/ipc/extension-handling"; import type { SendToViewArgs } from "./lens-window-injection-token";
import electronAppInjectable from "../../../electron-app/electron-app.injectable"; import sendToChannelInElectronBrowserWindowInjectable from "./send-to-channel-in-electron-browser-window.injectable";
import appEventBusInjectable from "../../../../common/app-event-bus/app-event-bus.injectable"; import type { LensWindow } from "./create-lens-window.injectable";
const createBrowserWindowInjectable = getInjectable({ interface ElectronWindowConfiguration {
id: "create-browser-window", id: string;
title: string;
defaultHeight: number;
defaultWidth: number;
getContentUrl: () => string;
resizable: boolean;
windowFrameUtilitiesAreShown: boolean;
centered: boolean;
beforeOpen?: () => Promise<void>;
onClose: () => void;
onFocus?: () => void;
onBlur?: () => void;
onDomReady?: () => void;
}
const createElectronWindowFor = getInjectable({
id: "create-electron-window-for",
instantiate: (di) => { instantiate: (di) => {
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const applicationName = di.inject(appNameInjectable);
const isMac = di.inject(isMacInjectable); const isMac = di.inject(isMacInjectable);
const appEventBus = di.inject(appEventBusInjectable);
const lensProxyPortNumberState = di.inject( const sendToChannelInLensWindow = di.inject(
lensProxyPortNumberStateInjectable, sendToChannelInElectronBrowserWindowInjectable,
); );
return async (id: string) => { return (configuration: ElectronWindowConfiguration) => async (): Promise<LensWindow> => {
const applicationWindowState = di.inject(applicationWindowStateInjectable); const applicationWindowState = di.inject(
const app = di.inject(electronAppInjectable); applicationWindowStateInjectable,
{
id: configuration.id,
defaultHeight: configuration.defaultHeight,
defaultWidth: configuration.defaultWidth,
},
);
const { width, height, x, y } = applicationWindowState; const { width, height, x, y } = applicationWindowState;
@ -38,13 +57,16 @@ const createBrowserWindowInjectable = getInjectable({
y, y,
width, width,
height, height,
title: applicationName, title: configuration.title,
resizable: configuration.resizable,
center: configuration.centered,
frame: configuration.windowFrameUtilitiesAreShown,
show: false, show: false,
minWidth: 700, // accommodate 800 x 600 display minimum minWidth: 700, // accommodate 800 x 600 display minimum
minHeight: 500, // accommodate 800 x 600 display minimum minHeight: 500, // accommodate 800 x 600 display minimum
titleBarStyle: isMac ? "hiddenInset" : "hidden", titleBarStyle: isMac ? "hiddenInset" : "hidden",
frame: isMac,
backgroundColor: "#1e2124", backgroundColor: "#1e2124",
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
nodeIntegrationInSubFrames: true, nodeIntegrationInSubFrames: true,
@ -56,48 +78,51 @@ const createBrowserWindowInjectable = getInjectable({
applicationWindowState.manage(browserWindow); applicationWindowState.manage(browserWindow);
// open external links in default browser (target=_blank, window.open)
browserWindow browserWindow
.on("focus", () => { .on("focus", () => {
appEventBus.emit({ name: "app", action: "focus" }); configuration.onFocus?.();
}) })
.on("blur", () => { .on("blur", () => {
appEventBus.emit({ name: "app", action: "blur" }); configuration.onBlur?.();
}) })
.on("closed", () => { .on("closed", () => {
// clean up configuration.onClose();
applicationWindowState.unmanage(); applicationWindowState.unmanage();
// this.mainWindow = null;
// this.splashWindow = null;
app.dock?.hide(); // hide icon in dock (mac-os)
}) })
.webContents.on("dom-ready", () => { .webContents.on("dom-ready", () => {
appEventBus.emit({ name: "app", action: "dom-ready" }); configuration.onDomReady?.();
}) })
.on("did-fail-load", (_event, code, desc) => { .on("did-fail-load", (_event, code, desc) => {
logger.error(`[WINDOW-MANAGER]: Failed to load Main window`, { logger.error(
code, `[CREATE-ELECTRON-WINDOW]: Failed to load window "${configuration.id}"`,
desc, {
}); code,
desc,
},
);
}) })
.on("did-finish-load", () => { .on("did-finish-load", () => {
logger.info("[WINDOW-MANAGER]: Main window loaded"); logger.info(
`[CREATE-ELECTRON-WINDOW]: Window "${configuration.id}" loaded`,
);
}) })
.on("will-attach-webview", (event, webPreferences, params) => { .on("will-attach-webview", (event, webPreferences, params) => {
logger.debug("[WINDOW-MANAGER]: Attaching webview"); logger.debug(
`[CREATE-ELECTRON-WINDOW]: Attaching webview to window "${configuration.id}"`,
);
// Following is security recommendations because we allow webview tag (webviewTag: true) // Following is security recommendations because we allow webview tag (webviewTag: true)
// suggested by https://www.electronjs.org/docs/tutorial/security#11-verify-webview-options-before-creation // suggested by https://www.electronjs.org/docs/tutorial/security#11-verify-webview-options-before-creation
// and https://www.electronjs.org/docs/tutorial/security#10-do-not-use-allowpopups // and https://www.electronjs.org/docs/tutorial/security#10-do-not-use-allowpopups
if (webPreferences.preload) { if (webPreferences.preload) {
logger.warn( logger.warn(
"[WINDOW-MANAGER]: Strip away preload scripts of webview", "[CREATE-ELECTRON-WINDOW]: Strip away preload scripts of webview",
); );
delete webPreferences.preload; delete webPreferences.preload;
} }
@ -105,14 +130,14 @@ const createBrowserWindowInjectable = getInjectable({
// @ts-expect-error some electron version uses webPreferences.preloadURL/webPreferences.preload // @ts-expect-error some electron version uses webPreferences.preloadURL/webPreferences.preload
if (webPreferences.preloadURL) { if (webPreferences.preloadURL) {
logger.warn( logger.warn(
"[WINDOW-MANAGER]: Strip away preload scripts of webview", "[CREATE-ELECTRON-WINDOW]: Strip away preload scripts of webview",
); );
delete webPreferences.preload; delete webPreferences.preload;
} }
if (params.allowpopups) { if (params.allowpopups) {
logger.warn( logger.warn(
"[WINDOW-MANAGER]: We do not allow allowpopups props, stop webview from renderer", "[CREATE-ELECTRON-WINDOW]: We do not allow allowpopups props, stop webview from renderer",
); );
// event.preventDefault() will destroy the guest page. // event.preventDefault() will destroy the guest page.
@ -127,32 +152,35 @@ const createBrowserWindowInjectable = getInjectable({
.setWindowOpenHandler((details) => { .setWindowOpenHandler((details) => {
openBrowser(details.url).catch((error) => { openBrowser(details.url).catch((error) => {
logger.error("[WINDOW-MANAGER]: failed to open browser", { error }); logger.error("[CREATE-ELECTRON-WINDOW]: failed to open browser", {
error,
});
}); });
return { action: "deny" }; return { action: "deny" };
}); });
const contentUrl = `http://localhost:${lensProxyPortNumberState.get()}`; const contentUrl = configuration.getContentUrl();
logger.info( logger.info(
`[WINDOW-MANAGER]: Loading Main window from url: ${contentUrl} ...`, `[CREATE-ELECTRON-WINDOW]: Loading content for window "${configuration.id}" from url: ${contentUrl}...`,
); );
await browserWindow.loadURL(contentUrl); await browserWindow.loadURL(contentUrl);
const viewHasLoaded = new Promise<void>((resolve) => { await configuration.beforeOpen?.();
ipcMain.once(bundledExtensionsLoaded, () => resolve());
});
await viewHasLoaded; return {
await delay(50); // wait just a bit longer to let the first round of rendering happen show: () => browserWindow.show(),
close: () => browserWindow.close(),
return browserWindow; send: (args: SendToViewArgs) =>
sendToChannelInLensWindow(browserWindow, args),
};
}; };
}, },
causesSideEffects: true, causesSideEffects: true,
}); });
export default createBrowserWindowInjectable; export default createElectronWindowFor;

View File

@ -0,0 +1,89 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { SendToViewArgs } from "./lens-window-injection-token";
import createElectronWindowForInjectable from "./create-electron-window-for.injectable";
export interface LensWindow {
show: () => void;
close: () => void;
send: (args: SendToViewArgs) => void;
}
interface LensWindowConfiguration {
id: string;
title: string;
defaultHeight: number;
defaultWidth: number;
getContentUrl: () => string;
resizable: boolean;
windowFrameUtilitiesAreShown: boolean;
centered: boolean;
beforeOpen?: () => Promise<void>;
onFocus?: () => void;
onBlur?: () => void;
onDomReady?: () => void;
}
const createLensWindowInjectable = getInjectable({
id: "create-lens-window",
instantiate:
(di) =>
(configuration: LensWindowConfiguration) => {
let browserWindow: LensWindow = null;
const createElectronWindow = di.inject(createElectronWindowForInjectable)(
{
id: configuration.id,
title: configuration.title,
defaultHeight: configuration.defaultHeight,
defaultWidth: configuration.defaultWidth,
getContentUrl: configuration.getContentUrl,
resizable: configuration.resizable,
windowFrameUtilitiesAreShown: configuration.windowFrameUtilitiesAreShown,
centered: configuration.centered,
onFocus: configuration.onFocus,
onBlur: configuration.onBlur,
onDomReady: configuration.onDomReady,
beforeOpen: configuration.beforeOpen,
onClose: () => {
browserWindow = null;
},
},
);
return {
get visible() {
return !!this.browserWindow;
},
show: async () => {
if (!browserWindow) {
browserWindow = await createElectronWindow();
}
browserWindow.show();
},
close: () => {
browserWindow?.close();
browserWindow = null;
},
send: async (args: SendToViewArgs) => {
if (!browserWindow) {
browserWindow = await createElectronWindow();
}
return browserWindow.send(args);
},
};
},
});
export default createLensWindowInjectable;

View File

@ -13,9 +13,9 @@ export interface SendToViewArgs {
export interface LensWindow { export interface LensWindow {
show: () => Promise<void>; show: () => Promise<void>;
hide: () => void;
close: () => void; close: () => void;
send: (args: SendToViewArgs) => Promise<void>; send: (args: SendToViewArgs) => Promise<void>;
visible: boolean;
} }
export const lensWindowInjectionToken = getInjectionToken<LensWindow>({ export const lensWindowInjectionToken = getInjectionToken<LensWindow>({

View File

@ -11,7 +11,7 @@ const sendToChannelInElectronBrowserWindowInjectable = getInjectable({
instantiate: instantiate:
() => () =>
async ( (
browserWindow: BrowserWindow, browserWindow: BrowserWindow,
{ channel, frameInfo, data = [] }: SendToViewArgs, { channel, frameInfo, data = [] }: SendToViewArgs,
) => { ) => {

View File

@ -26,6 +26,8 @@ const setupListenerForCurrentClusterFrameInjectable = getInjectable({
}, },
}), }),
causesSideEffects: true,
injectionToken: whenApplicationIsLoadingInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });

View File

@ -5,16 +5,16 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { lensWindowInjectionToken } from "../application-window/lens-window-injection-token"; import { lensWindowInjectionToken } from "../application-window/lens-window-injection-token";
const hideAllWindowsInjectable = getInjectable({ const closeAllWindowsInjectable = getInjectable({
id: "hide-all-windows", id: "close-all-windows",
instantiate: (di) => () => { instantiate: (di) => () => {
const lensWindows = di.injectMany(lensWindowInjectionToken); const lensWindows = di.injectMany(lensWindowInjectionToken);
lensWindows.forEach((lensWindow) => { lensWindows.forEach((lensWindow) => {
lensWindow.hide(); lensWindow.close();
}); });
}, },
}); });
export default hideAllWindowsInjectable; export default closeAllWindowsInjectable;

View File

@ -4,8 +4,9 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { iter } from "../../../common/utils"; import { iter } from "../../../common/utils";
import applicationWindowInjectable from "./application-window/application-window.injectable";
import clusterFramesInjectable from "../../../common/cluster-frames.injectable"; import clusterFramesInjectable from "../../../common/cluster-frames.injectable";
import showApplicationWindowInjectable from "./show-application-window.injectable";
import applicationWindowInjectable from "./application-window/application-window.injectable";
const navigateForExtensionInjectable = getInjectable({ const navigateForExtensionInjectable = getInjectable({
id: "navigate-for-extension", id: "navigate-for-extension",
@ -13,6 +14,7 @@ const navigateForExtensionInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const applicationWindow = di.inject(applicationWindowInjectable); const applicationWindow = di.inject(applicationWindowInjectable);
const clusterFrames = di.inject(clusterFramesInjectable); const clusterFrames = di.inject(clusterFramesInjectable);
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
return async ( return async (
extId: string, extId: string,
@ -20,7 +22,7 @@ const navigateForExtensionInjectable = getInjectable({
params?: Record<string, any>, params?: Record<string, any>,
frameId?: number, frameId?: number,
) => { ) => {
await applicationWindow.show(); await showApplicationWindow();
const frameInfo = iter.find( const frameInfo = iter.find(
clusterFrames.values(), clusterFrames.values(),

View File

@ -7,16 +7,18 @@ import { iter } from "../../../common/utils";
import applicationWindowInjectable from "./application-window/application-window.injectable"; import applicationWindowInjectable from "./application-window/application-window.injectable";
import clusterFramesInjectable from "../../../common/cluster-frames.injectable"; import clusterFramesInjectable from "../../../common/cluster-frames.injectable";
import { IpcRendererNavigationEvents } from "../../../renderer/navigation/events"; import { IpcRendererNavigationEvents } from "../../../renderer/navigation/events";
import showApplicationWindowInjectable from "./show-application-window.injectable";
const navigateInjectable = getInjectable({ const navigateInjectable = getInjectable({
id: "navigate", id: "navigate",
instantiate: (di) => { instantiate: (di) => {
const applicationWindow = di.inject(applicationWindowInjectable); const applicationWindow = di.inject(applicationWindowInjectable);
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const clusterFrames = di.inject(clusterFramesInjectable); const clusterFrames = di.inject(clusterFramesInjectable);
return async (url: string, frameId?: number) => { return async (url: string, frameId?: number) => {
await applicationWindow.show(); await showApplicationWindow();
const frameInfo = iter.find( const frameInfo = iter.find(
clusterFrames.values(), clusterFrames.values(),

View File

@ -0,0 +1,34 @@
/**
* 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 applicationIsLoadingWindowInjectable
from "./application-is-loading-window/application-is-loading-window.injectable";
import applicationWindowInjectable from "./application-window/application-window.injectable";
const showApplicationWindowInjectable = getInjectable({
id: "show-application-window",
instantiate: (di) => {
const applicationWindow = di.inject(applicationWindowInjectable);
const applicationIsLoadingWindow = di.inject(
applicationIsLoadingWindowInjectable,
);
return async () => {
if (applicationWindow.visible) {
return;
}
await applicationIsLoadingWindow.show();
await applicationWindow.show();
applicationIsLoadingWindow.close();
};
},
});
export default showApplicationWindowInjectable;

View File

@ -4,9 +4,8 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import electronAppInjectable from "../electron-app/electron-app.injectable"; import { runManyFor } from "../../common/runnable/run-many-for";
import { runManyFor } from "./run-many-for"; import { runManySyncFor } from "../../common/runnable/run-many-sync-for";
import { runManySyncFor } from "./run-many-sync-for";
import { beforeElectronIsReadyInjectionToken } from "./before-electron-is-ready/before-electron-is-ready-injection-token"; import { beforeElectronIsReadyInjectionToken } from "./before-electron-is-ready/before-electron-is-ready-injection-token";
import { beforeApplicationIsLoadingInjectionToken } from "./before-application-is-loading/before-application-is-loading-injection-token"; import { beforeApplicationIsLoadingInjectionToken } from "./before-application-is-loading/before-application-is-loading-injection-token";
import { whenApplicationIsLoadingInjectionToken } from "./when-application-is-loading/when-application-is-loading-injection-token"; import { whenApplicationIsLoadingInjectionToken } from "./when-application-is-loading/when-application-is-loading-injection-token";
@ -19,6 +18,7 @@ import openDeepLinkInjectable from "../protocol-handler/lens-protocol-router-mai
import { pipeline } from "@ogre-tools/fp"; import { pipeline } from "@ogre-tools/fp";
import { find, map, startsWith, toLower } from "lodash/fp"; import { find, map, startsWith, toLower } from "lodash/fp";
import commandLineArgumentsInjectable from "../utils/command-line-arguments.injectable"; import commandLineArgumentsInjectable from "../utils/command-line-arguments.injectable";
import waitForElectronToBeReadyInjectable from "../electron-app/features/wait-for-electron-to-be-ready.injectable";
const startMainApplicationInjectable = getInjectable({ const startMainApplicationInjectable = getInjectable({
id: "start-main-application", id: "start-main-application",
@ -26,7 +26,7 @@ const startMainApplicationInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const runMany = runManyFor(di); const runMany = runManyFor(di);
const runManySync = runManySyncFor(di); const runManySync = runManySyncFor(di);
const electronApp = di.inject(electronAppInjectable); const waitForElectronToBeReady = di.inject(waitForElectronToBeReadyInjectable);
const applicationWindow = di.inject(applicationWindowInjectable); const applicationWindow = di.inject(applicationWindowInjectable);
const applicationIsLoadingWindow = di.inject(applicationIsLoadingWindowInjectable); const applicationIsLoadingWindow = di.inject(applicationIsLoadingWindowInjectable);
const shouldStartHidden = di.inject(shouldStartHiddenInjectable); const shouldStartHidden = di.inject(shouldStartHiddenInjectable);
@ -43,7 +43,7 @@ const startMainApplicationInjectable = getInjectable({
// https://github.com/electron/electron/issues/21370 // https://github.com/electron/electron/issues/21370
beforeElectronIsReady(); beforeElectronIsReady();
await electronApp.whenReady(); await waitForElectronToBeReady();
await beforeApplicationIsLoading(); await beforeApplicationIsLoading();
@ -54,10 +54,10 @@ const startMainApplicationInjectable = getInjectable({
await whenApplicationIsLoading(); await whenApplicationIsLoading();
if (!shouldStartHidden) { if (!shouldStartHidden) {
const url = getDeepLinkUrl(commandLineArguments); const deepLinkUrl = getDeepLinkUrl(commandLineArguments);
if (url) { if (deepLinkUrl) {
await openDeepLink(url); await openDeepLink(deepLinkUrl);
} else { } else {
await applicationWindow.show(); await applicationWindow.show();
} }

View File

@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import { ipcMainOn } from "../../../../common/ipc"; import { ipcMainOn } from "../../../../common/ipc";
import { IpcRendererNavigationEvents } from "../../../../renderer/navigation/events"; import { IpcRendererNavigationEvents } from "../../../../renderer/navigation/events";
import { afterRootFrameIsReadyInjectionToken } from "../../after-root-frame-is-ready/after-root-frame-is-ready-injection-token"; import { afterRootFrameIsReadyInjectionToken } from "../../after-root-frame-is-ready/after-root-frame-is-ready-injection-token";
import { runManyFor } from "../../run-many-for"; import { runManyFor } from "../../../../common/runnable/run-many-for";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token"; import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const setupRunnablesForAfterRootFrameIsReadyInjectable = getInjectable({ const setupRunnablesForAfterRootFrameIsReadyInjectable = getInjectable({

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../run-many-for"; import type { Runnable } from "../../../common/runnable/run-many-for";
export const whenApplicationIsLoadingInjectionToken = getInjectionToken<Runnable>({ export const whenApplicationIsLoadingInjectionToken = getInjectionToken<Runnable>({
id: "when-application-is-loading", id: "when-application-is-loading",

View File

@ -7,7 +7,7 @@ import exitAppInjectable from "./electron-app/features/exit-app.injectable";
import clusterManagerInjectable from "./cluster-manager.injectable"; import clusterManagerInjectable from "./cluster-manager.injectable";
import appEventBusInjectable from "../common/app-event-bus/app-event-bus.injectable"; import appEventBusInjectable from "../common/app-event-bus/app-event-bus.injectable";
import loggerInjectable from "../common/logger.injectable"; import loggerInjectable from "../common/logger.injectable";
import hideAllWindowsInjectable from "./start-main-application/lens-window/hide-all-windows/hide-all-windows.injectable"; import closeAllWindowsInjectable from "./start-main-application/lens-window/hide-all-windows/close-all-windows.injectable";
const stopServicesAndExitAppInjectable = getInjectable({ const stopServicesAndExitAppInjectable = getInjectable({
id: "stop-services-and-exit-app", id: "stop-services-and-exit-app",
@ -17,11 +17,11 @@ const stopServicesAndExitAppInjectable = getInjectable({
const clusterManager = di.inject(clusterManagerInjectable); const clusterManager = di.inject(clusterManagerInjectable);
const appEventBus = di.inject(appEventBusInjectable); const appEventBus = di.inject(appEventBusInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const hideAllWindows = di.inject(hideAllWindowsInjectable); const closeAllWindows = di.inject(closeAllWindowsInjectable);
return () => { return () => {
appEventBus.emit({ name: "service", action: "close" }); appEventBus.emit({ name: "service", action: "close" });
hideAllWindows(); closeAllWindows();
clusterManager.stop(); clusterManager.stop();
logger.info("SERVICE:QUIT"); logger.info("SERVICE:QUIT");
setTimeout(exitApp, 1000); setTimeout(exitApp, 1000);

View File

@ -10,8 +10,9 @@ import stopServicesAndExitAppInjectable from "../stop-services-and-exit-app.inje
import { getStartableStoppable } from "../../common/utils/get-startable-stoppable"; import { getStartableStoppable } from "../../common/utils/get-startable-stoppable";
import isAutoUpdateEnabledInjectable from "../is-auto-update-enabled.injectable"; import isAutoUpdateEnabledInjectable from "../is-auto-update-enabled.injectable";
import trayIconPathInjectable from "./tray-icon-path.injectable"; import trayIconPathInjectable from "./tray-icon-path.injectable";
import applicationWindowInjectable from "../start-main-application/lens-window/application-window/application-window.injectable";
import showAboutInjectable from "../menu/show-about.injectable"; import showAboutInjectable from "../menu/show-about.injectable";
import showApplicationWindowInjectable
from "../start-main-application/lens-window/show-application-window.injectable";
const trayInjectable = getInjectable({ const trayInjectable = getInjectable({
id: "tray", id: "tray",
@ -22,7 +23,7 @@ const trayInjectable = getInjectable({
const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable); const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable);
const isAutoUpdateEnabled = di.inject(isAutoUpdateEnabledInjectable); const isAutoUpdateEnabled = di.inject(isAutoUpdateEnabledInjectable);
const trayIconPath = di.inject(trayIconPathInjectable); const trayIconPath = di.inject(trayIconPathInjectable);
const applicationWindow = di.inject(applicationWindowInjectable); const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const showAboutPopup = di.inject(showAboutInjectable); const showAboutPopup = di.inject(showAboutInjectable);
return getStartableStoppable("build-of-tray", () => return getStartableStoppable("build-of-tray", () =>
@ -32,7 +33,7 @@ const trayInjectable = getInjectable({
stopServicesAndExitApp, stopServicesAndExitApp,
isAutoUpdateEnabled, isAutoUpdateEnabled,
trayIconPath, trayIconPath,
applicationWindow, showApplicationWindow,
showAboutPopup, showAboutPopup,
), ),
); );

View File

@ -14,7 +14,6 @@ import type { Disposer } from "../../common/utils";
import { disposer, toJS } from "../../common/utils"; import { disposer, toJS } from "../../common/utils";
import type { TrayMenuRegistration } from "./tray-menu-registration"; import type { TrayMenuRegistration } from "./tray-menu-registration";
import path from "path"; import path from "path";
import type { LensWindow } from "../start-main-application/lens-window/application-window/lens-window-injection-token";
const TRAY_LOG_PREFIX = "[TRAY]"; const TRAY_LOG_PREFIX = "[TRAY]";
@ -34,7 +33,7 @@ export function initTray(
navigateToPreferences: () => void, navigateToPreferences: () => void,
stopServicesAndExitApp: () => void, stopServicesAndExitApp: () => void,
isAutoUpdateEnabled: () => boolean, isAutoUpdateEnabled: () => boolean,
applicationWindow: LensWindow, showApplicationWindow: () => Promise<void>,
showAbout: () => void, showAbout: () => void,
): Disposer { ): Disposer {
const icon = getTrayIconPath(); const icon = getTrayIconPath();
@ -45,7 +44,7 @@ export function initTray(
if (isWindows) { if (isWindows) {
tray.on("click", () => { tray.on("click", () => {
applicationWindow.show() showApplicationWindow()
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error })); .catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error }));
}); });
} }
@ -53,7 +52,7 @@ export function initTray(
return disposer( return disposer(
autorun(() => { autorun(() => {
try { try {
const menu = createTrayMenu(toJS(trayMenuItems.get()), navigateToPreferences, stopServicesAndExitApp, isAutoUpdateEnabled, applicationWindow, showAbout); const menu = createTrayMenu(toJS(trayMenuItems.get()), navigateToPreferences, stopServicesAndExitApp, isAutoUpdateEnabled, showApplicationWindow, showAbout);
tray.setContextMenu(menu); tray.setContextMenu(menu);
} catch (error) { } catch (error) {
@ -82,14 +81,14 @@ function createTrayMenu(
navigateToPreferences: () => void, navigateToPreferences: () => void,
stopServicesAndExitApp: () => void, stopServicesAndExitApp: () => void,
isAutoUpdateEnabled: () => boolean, isAutoUpdateEnabled: () => boolean,
applicationWindow: LensWindow, showApplicationWindow: () => Promise<void>,
showAbout: () => void, showAbout: () => void,
): Menu { ): Menu {
let template: Electron.MenuItemConstructorOptions[] = [ let template: Electron.MenuItemConstructorOptions[] = [
{ {
label: `Open ${productName}`, label: `Open ${productName}`,
click() { click() {
applicationWindow.show().catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error })); showApplicationWindow().catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error }));
}, },
}, },
{ {
@ -105,7 +104,7 @@ function createTrayMenu(
label: "Check for updates", label: "Check for updates",
click() { click() {
checkForUpdates() checkForUpdates()
.then(() => applicationWindow.show()); .then(() => showApplicationWindow());
}, },
}); });
} }
@ -116,7 +115,7 @@ function createTrayMenu(
{ {
label: `About ${productName}`, label: `About ${productName}`,
click() { click() {
applicationWindow.show() showApplicationWindow()
.then(showAbout) .then(showAbout)
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to show Lens About view`, { error })); .catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to show Lens About view`, { error }));
}, },

View File

@ -3,10 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { AppPaths, appPathsIpcChannel } from "../../common/app-paths/app-path-injection-token"; import type { AppPaths } from "../../common/app-paths/app-path-injection-token";
import { appPathsIpcChannel } from "../../common/app-paths/app-path-injection-token";
import getValueFromRegisteredChannelInjectable from "./get-value-from-registered-channel/get-value-from-registered-channel.injectable"; import getValueFromRegisteredChannelInjectable from "./get-value-from-registered-channel/get-value-from-registered-channel.injectable";
import appPathsStateInjectable from "../../common/app-paths/app-paths-state.injectable"; import appPathsStateInjectable from "../../common/app-paths/app-paths-state.injectable";
import { afterApplicationIsReadyInjectionToken } from "../../main/start-main-application/after-application-is-ready/after-application-is-ready-injection-token"; import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token";
let syncAppPaths: AppPaths; let syncAppPaths: AppPaths;
@ -27,7 +28,7 @@ const setupAppPathsInjectable = getInjectable({
}, },
}), }),
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: beforeFrameStartsInjectionToken,
}); });
export default setupAppPathsInjectable; export default setupAppPathsInjectable;

View File

@ -3,8 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../run-many-for"; import type { Runnable } from "../../common/runnable/run-many-for";
export const afterApplicationIsReadyInjectionToken = getInjectionToken<Runnable>({ export const beforeFrameStartsInjectionToken = getInjectionToken<Runnable>({
id: "after-application-is-ready", id: "before-frame-starts",
}); });

View File

@ -43,7 +43,10 @@ import hotbarStoreInjectable from "../common/hotbar-store.injectable";
import { bindEvents } from "./navigation/events"; import { bindEvents } from "./navigation/events";
import deleteClusterDialogModelInjectable from "./components/delete-cluster-dialog/delete-cluster-dialog-model/delete-cluster-dialog-model.injectable"; import deleteClusterDialogModelInjectable from "./components/delete-cluster-dialog/delete-cluster-dialog-model/delete-cluster-dialog-model.injectable";
import { init } from "@sentry/electron/renderer"; import { init } from "@sentry/electron/renderer";
import { runSetups } from "../common/setupable-injection-token/run-setups"; import {
beforeFrameStartsInjectionToken,
} from "./before-frame-starts/before-frame-starts-injection-token";
import { runManyFor } from "../common/runnable/run-many-for";
configurePackages(); // global packages configurePackages(); // global packages
registerCustomThemes(); // monaco editor themes registerCustomThemes(); // monaco editor themes
@ -64,7 +67,9 @@ export async function bootstrap(di: DiContainer) {
initializeSentryReporting(init); initializeSentryReporting(init);
} }
await runSetups(di); const beforeFrameStarts = runManyFor(di)(beforeFrameStartsInjectionToken);
await beforeFrameStarts();
// TODO: Consolidate import time side-effect to setup time // TODO: Consolidate import time side-effect to setup time
bindEvents(); bindEvents();

View File

@ -4,6 +4,7 @@
*/ */
import "@testing-library/jest-dom/extend-expect"; import "@testing-library/jest-dom/extend-expect";
import { KubeConfig } from "@kubernetes/client-node"; import { KubeConfig } from "@kubernetes/client-node";
import type { RenderResult } from "@testing-library/react";
import { fireEvent } from "@testing-library/react"; import { fireEvent } from "@testing-library/react";
import mockFs from "mock-fs"; import mockFs from "mock-fs";
import React from "react"; import React from "react";
@ -13,14 +14,25 @@ import type { Cluster } from "../../../../common/cluster/cluster";
import { DeleteClusterDialog } from "../delete-cluster-dialog"; import { DeleteClusterDialog } from "../delete-cluster-dialog";
import type { ClusterModel } from "../../../../common/cluster-types"; import type { ClusterModel } from "../../../../common/cluster-types";
import { getDisForUnitTesting } from "../../../../test-utils/get-dis-for-unit-testing";
import { createClusterInjectionToken } from "../../../../common/cluster/create-cluster-injection-token"; import { createClusterInjectionToken } from "../../../../common/cluster/create-cluster-injection-token";
import createContextHandlerInjectable from "../../../../main/context-handler/create-context-handler.injectable"; import createContextHandlerInjectable from "../../../../main/context-handler/create-context-handler.injectable";
import deleteClusterDialogModelInjectable from "../delete-cluster-dialog-model/delete-cluster-dialog-model.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 type { DeleteClusterDialogModel } from "../delete-cluster-dialog-model/delete-cluster-dialog-model";
import type { DiRender } from "../../test-utils/renderFor";
import { renderFor } from "../../test-utils/renderFor";
import hotbarStoreInjectable from "../../../../common/hotbar-store.injectable"; 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";
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";
jest.mock("electron", () => ({ jest.mock("electron", () => ({
app: { app: {
@ -92,25 +104,50 @@ users:
let config: KubeConfig; let config: KubeConfig;
describe("<DeleteClusterDialog />", () => { describe("<DeleteClusterDialog />", () => {
let createCluster: (model: ClusterModel) => Cluster; let applicationBuilder: ApplicationBuilder;
let deleteClusterDialogModel: DeleteClusterDialogModel;
let render: DiRender;
beforeEach(async () => { beforeEach(async () => {
const { mainDi, rendererDi, runSetups } = getDisForUnitTesting({ doGeneralOverrides: true }); applicationBuilder = getApplicationBuilder();
render = renderFor(rendererDi);
mainDi.override(createContextHandlerInjectable, () => () => undefined);
mockFs(); mockFs();
rendererDi.override(hotbarStoreInjectable, () => ({})); applicationBuilder.beforeSetups(({ mainDi, rendererDi }) => {
mainDi.override(createContextHandlerInjectable, () => () => undefined);
await runSetups(); rendererDi.override(hotbarStoreInjectable, () => ({}));
deleteClusterDialogModel = rendererDi.inject(deleteClusterDialogModelInjectable); const testRouteInjectable = getInjectable({
createCluster = mainDi.inject(createClusterInjectionToken); 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: () => <DeleteClusterDialog />,
}),
injectionToken: routeSpecificComponentInjectionToken,
});
applicationBuilder.beforeRender(({ rendererDi }) => {
const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken);
const testRoute = rendererDi.inject(testRouteInjectable);
navigateToRoute(testRoute);
});
rendererDi.register(testRouteInjectable, testRouteComponent);
});
}); });
afterEach(() => { afterEach(() => {
@ -118,6 +155,10 @@ describe("<DeleteClusterDialog />", () => {
}); });
describe("Kubeconfig with different clusters", () => { describe("Kubeconfig with different clusters", () => {
let rendered: RenderResult;
let createCluster: (model: ClusterModel) => Cluster;
let deleteClusterDialogModel: DeleteClusterDialogModel;
beforeEach(async () => { beforeEach(async () => {
const mockOpts = { const mockOpts = {
"temp-kube-config": kubeconfig, "temp-kube-config": kubeconfig,
@ -127,6 +168,11 @@ describe("<DeleteClusterDialog />", () => {
config = new KubeConfig(); config = new KubeConfig();
config.loadFromString(kubeconfig); config.loadFromString(kubeconfig);
rendered = await applicationBuilder.render();
deleteClusterDialogModel = applicationBuilder.dis.rendererDi.inject(deleteClusterDialogModelInjectable);
createCluster = applicationBuilder.dis.mainDi.inject(createClusterInjectionToken);
}); });
afterEach(() => { afterEach(() => {
@ -134,9 +180,7 @@ describe("<DeleteClusterDialog />", () => {
}); });
it("renders w/o errors", () => { it("renders w/o errors", () => {
const { container } = render(<DeleteClusterDialog />); expect(rendered.container).toBeInstanceOf(HTMLElement);
expect(container).toBeInstanceOf(HTMLElement);
}); });
it("shows warning when deleting non-current-context cluster", () => { it("shows warning when deleting non-current-context cluster", () => {
@ -150,11 +194,10 @@ describe("<DeleteClusterDialog />", () => {
}); });
deleteClusterDialogModel.open({ cluster, config }); deleteClusterDialogModel.open({ cluster, config });
const { getByText } = render(<DeleteClusterDialog />);
const message = "The contents of kubeconfig file will be changed!"; const message = "The contents of kubeconfig file will be changed!";
expect(getByText(message)).toBeInstanceOf(HTMLElement); expect(rendered.getByText(message)).toBeInstanceOf(HTMLElement);
}); });
it("shows warning when deleting current-context cluster", () => { it("shows warning when deleting current-context cluster", () => {
@ -169,9 +212,7 @@ describe("<DeleteClusterDialog />", () => {
deleteClusterDialogModel.open({ cluster, config }); deleteClusterDialogModel.open({ cluster, config });
const { getByTestId } = render(<DeleteClusterDialog />); expect(rendered.getByTestId("current-context-warning")).toBeInstanceOf(HTMLElement);
expect(getByTestId("current-context-warning")).toBeInstanceOf(HTMLElement);
}); });
it("shows context switcher when deleting current cluster", async () => { it("shows context switcher when deleting current cluster", async () => {
@ -186,7 +227,7 @@ describe("<DeleteClusterDialog />", () => {
deleteClusterDialogModel.open({ cluster, config }); deleteClusterDialogModel.open({ cluster, config });
const { getByText } = render(<DeleteClusterDialog />); const { getByText } = rendered;
expect(getByText("Select...")).toBeInTheDocument(); expect(getByText("Select...")).toBeInTheDocument();
selectEvent.openMenu(getByText("Select...")); selectEvent.openMenu(getByText("Select..."));
@ -207,7 +248,7 @@ describe("<DeleteClusterDialog />", () => {
deleteClusterDialogModel.open({ cluster, config }); deleteClusterDialogModel.open({ cluster, config });
const { getByText, getByTestId } = render(<DeleteClusterDialog />); const { getByText, getByTestId } = rendered;
const link = getByTestId("context-switch"); const link = getByTestId("context-switch");
expect(link).toBeInstanceOf(HTMLElement); expect(link).toBeInstanceOf(HTMLElement);
@ -234,15 +275,17 @@ describe("<DeleteClusterDialog />", () => {
deleteClusterDialogModel.open({ cluster, config }); deleteClusterDialogModel.open({ cluster, config });
const { getByTestId } = render(<DeleteClusterDialog />); expect(rendered.getByTestId("internal-kubeconfig-warning")).toBeInstanceOf(HTMLElement);
expect(getByTestId("internal-kubeconfig-warning")).toBeInstanceOf(HTMLElement);
spy.mockRestore(); spy.mockRestore();
}); });
}); });
describe("Kubeconfig with single cluster", () => { describe("Kubeconfig with single cluster", () => {
let rendered: RenderResult;
let createCluster: (model: ClusterModel) => Cluster;
let deleteClusterDialogModel: DeleteClusterDialogModel;
beforeEach(async () => { beforeEach(async () => {
const mockOpts = { const mockOpts = {
"temp-kube-config": singleClusterConfig, "temp-kube-config": singleClusterConfig,
@ -252,6 +295,11 @@ describe("<DeleteClusterDialog />", () => {
config = new KubeConfig(); config = new KubeConfig();
config.loadFromString(singleClusterConfig); config.loadFromString(singleClusterConfig);
rendered = await applicationBuilder.render();
deleteClusterDialogModel = applicationBuilder.dis.rendererDi.inject(deleteClusterDialogModelInjectable);
createCluster = applicationBuilder.dis.mainDi.inject(createClusterInjectionToken);
}); });
afterEach(() => { afterEach(() => {
@ -270,9 +318,7 @@ describe("<DeleteClusterDialog />", () => {
deleteClusterDialogModel.open({ cluster, config }); deleteClusterDialogModel.open({ cluster, config });
const { getByTestId } = render(<DeleteClusterDialog />); expect(rendered.getByTestId("no-more-contexts-warning")).toBeInstanceOf(HTMLElement);
expect(getByTestId("no-more-contexts-warning")).toBeInstanceOf(HTMLElement);
}); });
}); });
}); });

View File

@ -40,6 +40,8 @@ import type { NamespaceStore } from "../+namespaces/namespace-store/namespace.st
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import namespaceStoreInjectable from "../+namespaces/namespace-store/namespace-store.injectable"; import namespaceStoreInjectable from "../+namespaces/namespace-store/namespace-store.injectable";
import clusterFrameContextInjectable from "../../cluster-frame-context/cluster-frame-context.injectable"; import clusterFrameContextInjectable from "../../cluster-frame-context/cluster-frame-context.injectable";
import startMainApplicationInjectable from "../../../main/start-main-application/start-main-application.injectable";
import startFrameInjectable from "../../start-frame/start-frame.injectable";
type Callback = (dis: DiContainers) => void | Promise<void>; type Callback = (dis: DiContainers) => void | Promise<void>;
@ -55,7 +57,7 @@ export interface ApplicationBuilder {
render: () => Promise<RenderResult>; render: () => Promise<RenderResult>;
applicationMenu: { applicationMenu: {
click: (path: string) => void; click: (path: string) => Promise<void>;
}; };
preferences: { preferences: {
@ -82,7 +84,7 @@ interface Environment {
} }
export const getApplicationBuilder = () => { export const getApplicationBuilder = () => {
const { rendererDi, mainDi, runSetups } = getDisForUnitTesting({ const { rendererDi, mainDi } = getDisForUnitTesting({
doGeneralOverrides: true, doGeneralOverrides: true,
}); });
@ -142,7 +144,7 @@ export const getApplicationBuilder = () => {
dis, dis,
applicationMenu: { applicationMenu: {
click: (path: string) => { click: async (path: string) => {
const applicationMenuItems = mainDi.inject( const applicationMenuItems = mainDi.inject(
applicationMenuItemsInjectable, applicationMenuItemsInjectable,
); );
@ -164,6 +166,10 @@ export const getApplicationBuilder = () => {
} }
menuItem.click(undefined, undefined, undefined); menuItem.click(undefined, undefined, undefined);
const flushPromises = () => new Promise(setImmediate);
await flushPromises();
}, },
}, },
@ -301,7 +307,13 @@ export const getApplicationBuilder = () => {
await callback(dis); await callback(dis);
} }
await runSetups(); const startMainApplication = mainDi.inject(startMainApplicationInjectable);
await startMainApplication();
const startFrame = rendererDi.inject(startFrameInjectable);
await startFrame();
const render = renderFor(rendererDi); const render = renderFor(rendererDi);

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { ipcChannelListenerInjectionToken } from "./ipc-channel-listener-injection-token"; import { ipcChannelListenerInjectionToken } from "./ipc-channel-listener-injection-token";
import registerIpcChannelListenerInjectable from "../app-paths/get-value-from-registered-channel/register-ipc-channel-listener.injectable"; import registerIpcChannelListenerInjectable from "../app-paths/get-value-from-registered-channel/register-ipc-channel-listener.injectable";
import { afterApplicationIsReadyInjectionToken } from "../../main/start-main-application/after-application-is-ready/after-application-is-ready-injection-token"; import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token";
const registerIpcChannelListenersInjectable = getInjectable({ const registerIpcChannelListenersInjectable = getInjectable({
id: "register-ipc-channel-listeners", id: "register-ipc-channel-listeners",
@ -22,7 +22,7 @@ const registerIpcChannelListenersInjectable = getInjectable({
}, },
}), }),
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: beforeFrameStartsInjectionToken,
}); });
export default registerIpcChannelListenersInjectable; export default registerIpcChannelListenersInjectable;

View File

@ -6,14 +6,15 @@ import { getInjectable } from "@ogre-tools/injectable";
import observableHistoryInjectable from "../navigation/observable-history.injectable"; import observableHistoryInjectable from "../navigation/observable-history.injectable";
import { runInAction } from "mobx"; import { runInAction } from "mobx";
import { navigateToUrlInjectionToken } from "../../common/front-end-routing/navigate-to-url-injection-token"; import { navigateToUrlInjectionToken } from "../../common/front-end-routing/navigate-to-url-injection-token";
import { broadcastMessage } from "../../common/ipc";
import { IpcRendererNavigationEvents } from "../navigation/events"; import { IpcRendererNavigationEvents } from "../navigation/events";
import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable";
const navigateToUrlInjectable = getInjectable({ const navigateToUrlInjectable = getInjectable({
id: "navigate-to-url", id: "navigate-to-url",
instantiate: (di) => { instantiate: (di) => {
const observableHistory = di.inject(observableHistoryInjectable); const observableHistory = di.inject(observableHistoryInjectable);
const broadcastMessage = di.inject(broadcastMessageInjectable);
return (url, options = {}) => { return (url, options = {}) => {
if (options.forceRootFrame) { if (options.forceRootFrame) {

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { runManyFor } from "../../common/runnable/run-many-for";
import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token";
const startFrameInjectable = getInjectable({
id: "start-frame",
// TODO: Consolidate contents of bootstrap.tsx here
instantiate: (di) => async () => {
const beforeFrameStarts = runManyFor(di)(beforeFrameStartsInjectionToken);
await beforeFrameStarts();
},
});
export default startFrameInjectable;

View File

@ -5,7 +5,6 @@
import { getDiForUnitTesting as getRendererDi } from "../renderer/getDiForUnitTesting"; import { getDiForUnitTesting as getRendererDi } from "../renderer/getDiForUnitTesting";
import { getDiForUnitTesting as getMainDi } from "../main/getDiForUnitTesting"; import { getDiForUnitTesting as getMainDi } from "../main/getDiForUnitTesting";
import { overrideIpcBridge } from "./override-ipc-bridge"; import { overrideIpcBridge } from "./override-ipc-bridge";
import { runSetups } from "../common/setupable-injection-token/run-setups";
export const getDisForUnitTesting = ( export const getDisForUnitTesting = (
{ doGeneralOverrides } = { doGeneralOverrides: false }, { doGeneralOverrides } = { doGeneralOverrides: false },
@ -18,8 +17,5 @@ export const getDisForUnitTesting = (
return { return {
rendererDi, rendererDi,
mainDi, mainDi,
runSetups: () =>
Promise.all([runSetups(rendererDi), runSetups(mainDi)]),
}; };
}; };

View File

@ -10,6 +10,7 @@ import asyncFn from "@async-fn/jest";
import registerIpcChannelListenerInjectable from "../renderer/app-paths/get-value-from-registered-channel/register-ipc-channel-listener.injectable"; import registerIpcChannelListenerInjectable from "../renderer/app-paths/get-value-from-registered-channel/register-ipc-channel-listener.injectable";
import type { SendToViewArgs } from "../main/start-main-application/lens-window/application-window/lens-window-injection-token"; import type { SendToViewArgs } from "../main/start-main-application/lens-window/application-window/lens-window-injection-token";
import sendToChannelInElectronBrowserWindowInjectable from "../main/start-main-application/lens-window/application-window/send-to-channel-in-electron-browser-window.injectable"; import sendToChannelInElectronBrowserWindowInjectable from "../main/start-main-application/lens-window/application-window/send-to-channel-in-electron-browser-window.injectable";
import { isEmpty } from "lodash/fp";
export const overrideIpcBridge = ({ export const overrideIpcBridge = ({
@ -89,9 +90,15 @@ export const overrideIpcBridge = ({
(browserWindow, { channel: channelName, data }: SendToViewArgs) => { (browserWindow, { channel: channelName, data }: SendToViewArgs) => {
const handles = rendererIpcFakeHandles.get(channelName); const handles = rendererIpcFakeHandles.get(channelName);
handles.forEach((handle) => handle(...data)); if (isEmpty(handles)) {
throw new Error(
`Tried to send message to channel "${channelName}" but there where no listeners. Current channels with listeners: "${[
...rendererIpcFakeHandles.keys(),
].join('", "')}"`,
);
}
return Promise.resolve(); handles.forEach((handle) => handle(...data));
}, },
); );
}; };