From 9ba92cb072d9c5363d721a586543ffbe4c277107 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 2 Dec 2022 07:31:27 -0800 Subject: [PATCH] Replace CatalogEntityDetailRegistry with an injectable solution (#6605) * Replace EntityDetailRegistry with an injectable solution - Add some behavioural tests Signed-off-by: Sebastian Malton * Update snapshots Signed-off-by: Sebastian Malton * Fix import error Signed-off-by: Sebastian Malton * Simplify loading extensions Signed-off-by: Sebastian Malton * Fix lint Signed-off-by: Sebastian Malton * Update snapshot Signed-off-by: Sebastian Malton * Remove the last reminents of BaseRegistry Signed-off-by: Sebastian Malton * Fix import errors Signed-off-by: Sebastian Malton * Fix TypeError when loading extensions Signed-off-by: Sebastian Malton * Update snapshots Signed-off-by: Sebastian Malton * Cleanup LensExtensions Signed-off-by: Sebastian Malton * Remove bad comment Signed-off-by: Sebastian Malton * Fix type errors Signed-off-by: Sebastian Malton Signed-off-by: Sebastian Malton --- __mocks__/react-beautiful-dnd.tsx | 4 +- src/common/catalog-entities/web-link.ts | 9 +- .../protocol-handler/registration.ts} | 0 src/common/protocol-handler/router.ts | 2 +- src/extensions/common-api/registrations.ts | 6 +- .../extension-loader/extension-loader.ts | 48 +- .../lens-extension-set-dependencies.ts | 2 + src/extensions/lens-extension.ts | 21 +- src/extensions/lens-renderer-extension.ts | 11 +- src/extensions/registries/base-registry.ts | 46 - .../catalog-entity-detail-registry.ts | 38 - src/extensions/registries/index.ts | 11 - ...acters-in-page-registrations.test.tsx.snap | 102 +- .../navigate-to-extension-page.test.tsx.snap | 255 +- .../navigating-between-routes.test.tsx.snap | 102 +- ...ation-using-application-menu.test.tsx.snap | 102 +- .../installing-update.test.ts.snap | 306 +- ...g-update-using-topbar-button.test.tsx.snap | 102 +- .../installing-update-using-tray.test.ts.snap | 306 +- ...e-since-update-was-downloaded.test.ts.snap | 153 +- ...eriodical-checking-of-updates.test.ts.snap | 51 +- ...selection-of-update-stability.test.ts.snap | 51 +- .../opening-entity-details.test.tsx.snap | 6511 +++++++++++++++++ .../catalog/opening-entity-details.test.tsx | 223 + .../delete-cluster-dialog.test.tsx.snap | 1199 ++- .../keyboard-shortcuts.test.tsx.snap | 408 +- ...-settings-for-correct-entity.test.tsx.snap | 204 +- ...gation-using-application-menu.test.ts.snap | 102 +- ...elm-repository-in-preferences.test.ts.snap | 612 +- ...tory-from-list-in-preferences.test.ts.snap | 510 +- ...m-repositories-in-preferences.test.ts.snap | 510 +- ...ive-repository-in-preferences.test.ts.snap | 204 +- .../closing-preferences.test.tsx.snap | 408 +- ...nsion-adding-preference-tabs.test.tsx.snap | 51 +- .../hiding-of-empty-branches.test.tsx.snap | 153 +- ...n-to-application-preferences.test.tsx.snap | 255 +- ...igation-to-editor-preferences.test.ts.snap | 102 +- ...tension-specific-preferences.test.tsx.snap | 459 +- ...ion-to-kubernetes-preferences.test.ts.snap | 102 +- ...vigation-to-proxy-preferences.test.ts.snap | 102 +- ...ion-to-telemetry-preferences.test.tsx.snap | 255 +- ...ation-to-terminal-preferences.test.ts.snap | 102 +- ...gation-using-application-menu.test.ts.snap | 102 +- .../navigation-using-tray.test.ts.snap | 102 +- .../urls-of-legacy-extensions.test.tsx.snap | 255 +- ...-originating-from-extensions.test.tsx.snap | 51 +- ...dability-using-extension-api.test.tsx.snap | 204 +- ...gation-using-application-menu.test.ts.snap | 153 +- .../create-extension-instance.injectable.ts | 5 +- src/renderer/bootstrap.tsx | 6 - .../+catalog/catalog-entity-details.tsx | 124 - .../components/+catalog/catalog.test.tsx | 7 - src/renderer/components/+catalog/catalog.tsx | 49 +- .../entity-details/detail-items.injectable.ts | 33 + .../kubernetes-cluster-details.injectable.tsx | 39 + .../internal/weblink-details.injectable.tsx | 34 + .../entity-details/registrator.injectable.ts | 39 + .../+catalog/entity-details/token.ts | 35 + .../view.module.scss} | 0 .../+catalog/entity-details/view.tsx | 127 + src/renderer/components/icon/icon.tsx | 1 + .../item-object-list/list-layout.tsx | 4 +- .../components/layout/cluster-page-menu.ts} | 5 +- src/renderer/components/menu/menu-actions.tsx | 19 +- src/renderer/components/menu/menu.tsx | 20 +- .../test-utils/get-extension-fake.ts | 13 +- .../create-extension-instance.injectable.ts | 6 +- .../init-cluster-frame.injectable.ts | 4 +- .../init-cluster-frame/init-cluster-frame.ts | 6 +- .../frames/load-extensions.injectable.ts | 17 + .../init-root-frame.injectable.ts | 4 +- src/renderer/getDiForUnitTesting.tsx | 14 +- .../catalog-entity-detail-registry.tsx | 52 - src/renderer/initializers/index.ts | 2 - src/renderer/initializers/registries.ts | 10 - .../extension-page-parameters.injectable.ts | 2 +- ...extension-route-registrator.injectable.tsx | 2 +- .../routes/page-registration.ts} | 2 +- 78 files changed, 15163 insertions(+), 585 deletions(-) rename src/{extensions/registries/protocol-handler.ts => common/protocol-handler/registration.ts} (100%) delete mode 100644 src/extensions/registries/base-registry.ts delete mode 100644 src/extensions/registries/catalog-entity-detail-registry.ts delete mode 100644 src/extensions/registries/index.ts create mode 100644 src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap create mode 100644 src/features/catalog/opening-entity-details.test.tsx delete mode 100644 src/renderer/components/+catalog/catalog-entity-details.tsx create mode 100644 src/renderer/components/+catalog/entity-details/detail-items.injectable.ts create mode 100644 src/renderer/components/+catalog/entity-details/internal/kubernetes-cluster-details.injectable.tsx create mode 100644 src/renderer/components/+catalog/entity-details/internal/weblink-details.injectable.tsx create mode 100644 src/renderer/components/+catalog/entity-details/registrator.injectable.ts create mode 100644 src/renderer/components/+catalog/entity-details/token.ts rename src/renderer/components/+catalog/{catalog-entity-details.module.scss => entity-details/view.module.scss} (100%) create mode 100644 src/renderer/components/+catalog/entity-details/view.tsx rename src/{extensions/registries/page-menu-registry.ts => renderer/components/layout/cluster-page-menu.ts} (76%) create mode 100644 src/renderer/frames/load-extensions.injectable.ts delete mode 100644 src/renderer/initializers/catalog-entity-detail-registry.tsx delete mode 100644 src/renderer/initializers/registries.ts rename src/{extensions/registries/page-registry.ts => renderer/routes/page-registration.ts} (94%) diff --git a/__mocks__/react-beautiful-dnd.tsx b/__mocks__/react-beautiful-dnd.tsx index 6404bfd450fb..79eec81f6ede 100644 --- a/__mocks__/react-beautiful-dnd.tsx +++ b/__mocks__/react-beautiful-dnd.tsx @@ -11,5 +11,5 @@ import type { } from "react-beautiful-dnd"; export const DragDropContext = ({ children }: DragDropContextProps) => <>{ children }; -export const Draggable = ({ children }: DraggableProps) => <>{ children }; -export const Droppable = ({ children }: DroppableProps) => <>{ children }; +export const Draggable = ({ children }: DraggableProps) => <>{ children({} as any, {} as any, {} as any) }; +export const Droppable = ({ children }: DroppableProps) => <>{ children({} as any, {} as any) }; diff --git a/src/common/catalog-entities/web-link.ts b/src/common/catalog-entities/web-link.ts index dade63af161d..b79f798566d2 100644 --- a/src/common/catalog-entities/web-link.ts +++ b/src/common/catalog-entities/web-link.ts @@ -3,11 +3,11 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; +import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog"; import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity"; import productNameInjectable from "../vars/product-name.injectable"; -import { WeblinkStore } from "../weblink-store"; +import weblinkStoreInjectable from "../weblink-store.injectable"; export type WebLinkStatusPhase = "available" | "unavailable"; @@ -31,14 +31,15 @@ export class WebLink extends CatalogEntity WeblinkStore.getInstance().removeById(this.getId()), + onClick: async () => di.inject(weblinkStoreInjectable).removeById(this.getId()), confirm: { message: `Remove Web Link "${this.getName()}" from ${productName}?`, }, diff --git a/src/extensions/registries/protocol-handler.ts b/src/common/protocol-handler/registration.ts similarity index 100% rename from src/extensions/registries/protocol-handler.ts rename to src/common/protocol-handler/registration.ts diff --git a/src/common/protocol-handler/router.ts b/src/common/protocol-handler/router.ts index 3204a5356ce0..8c9915b28707 100644 --- a/src/common/protocol-handler/router.ts +++ b/src/common/protocol-handler/router.ts @@ -13,7 +13,7 @@ import { RoutingError, RoutingErrorType } from "./error"; import type { ExtensionsStore } from "../../extensions/extensions-store/extensions-store"; import type { ExtensionLoader } from "../../extensions/extension-loader"; import type { LensExtension } from "../../extensions/lens-extension"; -import type { RouteHandler, RouteParams } from "../../extensions/registries/protocol-handler"; +import type { RouteHandler, RouteParams } from "./registration"; import { when } from "mobx"; import { ipcRenderer } from "electron"; import type { Logger } from "../logger"; diff --git a/src/extensions/common-api/registrations.ts b/src/extensions/common-api/registrations.ts index 4bfda25170f3..50a92624190f 100644 --- a/src/extensions/common-api/registrations.ts +++ b/src/extensions/common-api/registrations.ts @@ -7,9 +7,9 @@ export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../.. export type { AppPreferenceRegistration, AppPreferenceComponents } from "../../features/preferences/renderer/compliance-for-legacy-extension-api/app-preference-registration"; export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../../renderer/components/kube-object-details/kube-object-detail-registration"; export type { KubeObjectStatusRegistration } from "../../renderer/components/kube-object-status-icon/kube-object-status-registration"; -export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../registries/page-registry"; -export type { ClusterPageMenuRegistration, ClusterPageMenuComponents } from "../registries/page-menu-registry"; -export type { ProtocolHandlerRegistration, RouteParams as ProtocolRouteParams, RouteHandler as ProtocolRouteHandler } from "../registries/protocol-handler"; +export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../../renderer/routes/page-registration"; +export type { ClusterPageMenuRegistration, ClusterPageMenuComponents } from "../../renderer/components/layout/cluster-page-menu"; +export type { ProtocolHandlerRegistration, RouteParams as ProtocolRouteParams, RouteHandler as ProtocolRouteHandler } from "../../common/protocol-handler/registration"; export type { CustomCategoryViewProps, CustomCategoryViewComponents, CustomCategoryViewRegistration } from "../../renderer/components/+catalog/custom-views"; export type { ShellEnvModifier, ShellEnvContext } from "../../main/shell-session/shell-env-modifier/shell-env-modifier-registration"; export type { KubeObjectContextMenuItem, KubeObjectOnContextMenuOpenContext, KubeObjectOnContextMenuOpen, KubeObjectHandlers, KubeObjectHandlerRegistration } from "../../renderer/kube-object/handler"; diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts index 23840b67c4fa..c00414e88d08 100644 --- a/src/extensions/extension-loader/extension-loader.ts +++ b/src/extensions/extension-loader/extension-loader.ts @@ -8,12 +8,9 @@ import { isEqual } from "lodash"; import type { ObservableMap } from "mobx"; import { action, computed, makeObservable, observable, observe, reaction, when } from "mobx"; import { broadcastMessage, ipcMainOn, ipcRendererOn, ipcMainHandle } from "../../common/ipc"; -import type { Disposer } from "../../common/utils"; import { isDefined, toJS } from "../../common/utils"; import type { InstalledExtension } from "../extension-discovery/extension-discovery"; import type { LensExtension, LensExtensionConstructor, LensExtensionId } from "../lens-extension"; -import type { LensRendererExtension } from "../lens-renderer-extension"; -import * as registries from "../registries"; import type { LensExtensionState } from "../extensions-store/extensions-store"; import { extensionLoaderFromMainChannel, extensionLoaderFromRendererChannel } from "../../common/ipc/extension-handling"; import { requestExtensionLoaderInitialState } from "../../renderer/ipc"; @@ -203,7 +200,7 @@ export class ExtensionLoader { protected async initMain() { this.isLoaded = true; - this.loadOnMain(); + await this.autoInitExtensions(); ipcMainHandle(extensionLoaderFromMainChannel, () => { return Array.from(this.toJSON()); @@ -251,38 +248,7 @@ export class ExtensionLoader { }); } - loadOnMain() { - this.autoInitExtensions(() => Promise.resolve([])); - } - - loadOnClusterManagerRenderer = () => { - this.dependencies.logger.debug(`${logModule}: load on main renderer (cluster manager)`); - - return this.autoInitExtensions(async (ext) => { - const extension = ext as LensRendererExtension; - const removeItems = [ - registries.CatalogEntityDetailRegistry.getInstance().add(extension.catalogEntityDetailItems), - ]; - - this.onRemoveExtensionId.addListener((removedExtensionId) => { - if (removedExtensionId === extension.id) { - removeItems.forEach(remove => { - remove(); - }); - } - }); - - return removeItems; - }); - }; - - loadOnClusterRenderer = () => { - this.dependencies.logger.debug(`${logModule}: load on cluster renderer (dashboard)`); - - this.autoInitExtensions(async () => []); - }; - - protected async loadExtensions(installedExtensions: Map, register: (ext: LensExtension) => Promise) { + protected async loadExtensions(installedExtensions: Map) { // Steps of the function: // 1. require and call .activate for each Extension // 2. Wait until every extension's onActivate has been resolved @@ -346,7 +312,7 @@ export class ExtensionLoader { // Return ExtensionLoading[] return extensions.map(extension => { - const loaded = extension.instance.enable(register).catch((err) => { + const loaded = extension.instance.enable().catch((err) => { this.dependencies.logger.error(`${logModule}: failed to enable`, { ext: extension, err }); }); @@ -357,12 +323,14 @@ export class ExtensionLoader { }); } - protected autoInitExtensions(register: (ext: LensExtension) => Promise) { + autoInitExtensions() { + this.dependencies.logger.info(`${logModule}: auto initializing extensions`); + // Setup reaction to load extensions on JSON changes - reaction(() => this.toJSON(), installedExtensions => this.loadExtensions(installedExtensions, register)); + reaction(() => this.toJSON(), installedExtensions => this.loadExtensions(installedExtensions)); // Load initial extensions - return this.loadExtensions(this.toJSON(), register); + return this.loadExtensions(this.toJSON()); } protected requireExtension(extension: InstalledExtension): LensExtensionConstructor | null { diff --git a/src/extensions/lens-extension-set-dependencies.ts b/src/extensions/lens-extension-set-dependencies.ts index 0f06103af226..7b7c62597aa7 100644 --- a/src/extensions/lens-extension-set-dependencies.ts +++ b/src/extensions/lens-extension-set-dependencies.ts @@ -12,9 +12,11 @@ import type { CatalogEntityRegistry as RendererCatalogEntityRegistry } from "../ import type { GetExtensionPageParameters } from "../renderer/routes/get-extension-page-parameters.injectable"; import type { FileSystemProvisionerStore } from "./extension-loader/file-system-provisioner-store/file-system-provisioner-store"; import type { NavigateForExtension } from "../main/start-main-application/lens-window/navigate-for-extension.injectable"; +import type { Logger } from "../common/logger"; export interface LensExtensionDependencies { readonly fileSystemProvisionerStore: FileSystemProvisionerStore; + readonly logger: Logger; } export interface LensMainExtensionDependencies extends LensExtensionDependencies { diff --git a/src/extensions/lens-extension.ts b/src/extensions/lens-extension.ts index ab1f1b0addf7..831822fd2b81 100644 --- a/src/extensions/lens-extension.ts +++ b/src/extensions/lens-extension.ts @@ -5,12 +5,10 @@ import type { InstalledExtension } from "./extension-discovery/extension-discovery"; import { action, computed, makeObservable, observable } from "mobx"; -import logger from "../main/logger"; -import type { ProtocolHandlerRegistration } from "./registries"; import type { PackageJson } from "type-fest"; -import type { Disposer } from "../common/utils"; import { disposer } from "../common/utils"; import type { LensExtensionDependencies } from "./lens-extension-set-dependencies"; +import type { ProtocolHandlerRegistration } from "./common-api/registrations"; export type LensExtensionId = string; // path to manifest (package.json) export type LensExtensionConstructor = new (...args: ConstructorParameters) => LensExtension; @@ -88,20 +86,13 @@ export class LensExtension Promise) { + async enable() { if (this._isEnabled) { return; } - try { - this._isEnabled = true; - - this[Disposers].push(...await register(this)); - logger.info(`[EXTENSION]: enabled ${this.name}@${this.version}`); - - } catch (error) { - logger.error(`[EXTENSION]: failed to activate ${this.name}@${this.version}: ${error}`); - } + this._isEnabled = true; + this[lensExtensionDependencies].logger.info(`[EXTENSION]: enabled ${this.name}@${this.version}`); } @action @@ -115,9 +106,9 @@ export class LensExtension { - globalPages: registries.PageRegistration[] = []; - clusterPages: registries.PageRegistration[] = []; - clusterPageMenus: registries.ClusterPageMenuRegistration[] = []; + globalPages: PageRegistration[] = []; + clusterPages: PageRegistration[] = []; + clusterPageMenus: ClusterPageMenuRegistration[] = []; clusterFrameComponents: ClusterFrameChildComponent[] = []; kubeObjectStatusTexts: KubeObjectStatusRegistration[] = []; appPreferences: AppPreferenceRegistration[] = []; @@ -47,7 +48,7 @@ export class LensRendererExtension extends LensExtension[] = []; + catalogEntityDetailItems: CatalogEntityDetailRegistration[] = []; topBarItems: TopBarRegistration[] = []; additionalCategoryColumns: AdditionalCategoryColumnRegistration[] = []; customCategoryViews: CustomCategoryViewRegistration[] = []; diff --git a/src/extensions/registries/base-registry.ts b/src/extensions/registries/base-registry.ts deleted file mode 100644 index b0c833f29514..000000000000 --- a/src/extensions/registries/base-registry.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Base class for extensions-api registries -import { action, observable, makeObservable } from "mobx"; -import type { Disposer } from "../../common/utils"; -import { Singleton } from "../../common/utils"; -import type { LensExtension } from "../lens-extension"; - -export class BaseRegistry extends Singleton { - private items = observable.map([], { deep: false }); - - constructor() { - super(); - makeObservable(this); - } - - getItems(): I[] { - return Array.from(this.items.values()); - } - - @action - add(items: T | T[], extension?: LensExtension): Disposer { - const itemArray = [items].flat() as T[]; - - itemArray.forEach(item => { - this.items.set(item, this.getRegisteredItem(item, extension)); - }); - - return () => this.remove(...itemArray); - } - - // eslint-disable-next-line unused-imports/no-unused-vars-ts - protected getRegisteredItem(item: T, extension?: LensExtension): I { - return item as never; - } - - @action - remove(...items: T[]) { - items.forEach(item => { - this.items.delete(item); - }); - } -} diff --git a/src/extensions/registries/catalog-entity-detail-registry.ts b/src/extensions/registries/catalog-entity-detail-registry.ts deleted file mode 100644 index 9c2aeb348c2b..000000000000 --- a/src/extensions/registries/catalog-entity-detail-registry.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type React from "react"; -import type { Disposer } from "../../common/utils"; -import type { CatalogEntity } from "../common-api/catalog"; -import { BaseRegistry } from "./base-registry"; - -export interface CatalogEntityDetailsProps { - entity: T; -} - -export interface CatalogEntityDetailComponents { - Details: React.ComponentType>; -} - -export interface CatalogEntityDetailRegistration { - kind: string; - apiVersions: string[]; - components: CatalogEntityDetailComponents; - priority?: number; -} - -export class CatalogEntityDetailRegistry extends BaseRegistry> { - add(items: CatalogEntityDetailRegistration | CatalogEntityDetailRegistration[]): Disposer { - return super.add(items as never); - } - - getItemsForKind(kind: string, apiVersion: string) { - const items = this.getItems().filter((item) => { - return item.kind === kind && item.apiVersions.includes(apiVersion); - }); - - return items.sort((a, b) => (b.priority ?? 50) - (a.priority ?? 50)); - } -} diff --git a/src/extensions/registries/index.ts b/src/extensions/registries/index.ts deleted file mode 100644 index 2841b4c0319a..000000000000 --- a/src/extensions/registries/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// All registries managed by extensions api - -export * from "./page-registry"; -export * from "./page-menu-registry"; -export * from "./catalog-entity-detail-registry"; -export * from "./protocol-handler"; diff --git a/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap b/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap index d755fa45fa73..9046b189a9aa 100644 --- a/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap +++ b/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap @@ -160,7 +160,56 @@ exports[`extension special characters in page registrations renders 1`] = ` >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -299,7 +348,56 @@ exports[`extension special characters in page registrations when navigating to r >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap b/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap index f65a4515587e..cb830f1a10c2 100644 --- a/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap +++ b/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap @@ -160,7 +160,56 @@ exports[`navigate to extension page renders 1`] = ` >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -299,7 +348,56 @@ exports[`navigate to extension page when extension navigates to child route rend >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -454,7 +552,56 @@ exports[`navigate to extension page when extension navigates to route with param >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -609,7 +756,56 @@ exports[`navigate to extension page when extension navigates to route without pa >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -764,7 +960,56 @@ exports[`navigate to extension page when extension navigates to route without pa >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/__snapshots__/navigating-between-routes.test.tsx.snap b/src/features/__snapshots__/navigating-between-routes.test.tsx.snap index f4e6b4155946..cc6b1fa395a1 100644 --- a/src/features/__snapshots__/navigating-between-routes.test.tsx.snap +++ b/src/features/__snapshots__/navigating-between-routes.test.tsx.snap @@ -84,7 +84,56 @@ exports[`navigating between routes given route with optional path parameters whe >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -223,7 +272,56 @@ exports[`navigating between routes given route without path parameters when navi >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap b/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap index 904428268050..110c9d7f07c6 100644 --- a/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap +++ b/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap @@ -160,7 +160,56 @@ exports[`add-cluster - navigation using application menu renders 1`] = ` >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -383,7 +432,56 @@ exports[`add-cluster - navigation using application menu when navigating to add >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/application-update/__snapshots__/installing-update.test.ts.snap b/src/features/application-update/__snapshots__/installing-update.test.ts.snap index 395670cdda2a..deae3b999d0a 100644 --- a/src/features/application-update/__snapshots__/installing-update.test.ts.snap +++ b/src/features/application-update/__snapshots__/installing-update.test.ts.snap @@ -161,7 +161,56 @@ exports[`installing update when started renders 1`] = ` >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -381,7 +430,56 @@ exports[`installing update when started when user checks for updates renders 1`] >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -601,7 +699,56 @@ exports[`installing update when started when user checks for updates when new up >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -846,7 +993,56 @@ exports[`installing update when started when user checks for updates when new up >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1091,7 +1287,56 @@ exports[`installing update when started when user checks for updates when new up >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1311,7 +1556,56 @@ exports[`installing update when started when user checks for updates when no new >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap b/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap index dab5bce6ef7b..70b6c0864821 100644 --- a/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap +++ b/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap @@ -186,7 +186,56 @@ exports[`encourage user to update when sufficient time passed since update was d >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -406,7 +455,56 @@ exports[`encourage user to update when sufficient time passed since update was d >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap b/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap index da6aa0ed8067..adfb7eaf5c57 100644 --- a/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap +++ b/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap @@ -161,7 +161,56 @@ exports[`installing update using tray when started renders 1`] = ` >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -381,7 +430,56 @@ exports[`installing update using tray when started when user checks for updates >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -601,7 +699,56 @@ exports[`installing update using tray when started when user checks for updates >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -846,7 +993,56 @@ exports[`installing update using tray when started when user checks for updates >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1091,7 +1287,56 @@ exports[`installing update using tray when started when user checks for updates >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1311,7 +1556,56 @@ exports[`installing update using tray when started when user checks for updates >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/__snapshots__/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts.snap b/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/__snapshots__/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts.snap index 6ba121489ba7..601d4d74b679 100644 --- a/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/__snapshots__/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts.snap +++ b/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/__snapshots__/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts.snap @@ -186,7 +186,56 @@ exports[`force user to update when too long since update was downloaded when app >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -431,7 +480,56 @@ exports[`force user to update when too long since update was downloaded when app >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
@@ -723,7 +821,56 @@ exports[`force user to update when too long since update was downloaded when app >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap b/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap index 36e52e2d5f0b..02f530b63739 100644 --- a/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap +++ b/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap @@ -161,7 +161,56 @@ exports[`periodical checking of updates given updater is enabled and configurati >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap b/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap index 253dedc1dff4..bf6bf4674e4c 100644 --- a/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap +++ b/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap @@ -161,7 +161,56 @@ exports[`selection of update stability when started renders 1`] = ` >
+ > +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap b/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap new file mode 100644 index 000000000000..56c4a912e4a9 --- /dev/null +++ b/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap @@ -0,0 +1,6511 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`opening catalog entity details panel renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+ +
+
+

+ Welcome to some-product-name! +

+

+ To get you started we have auto-detected your clusters in your + + kubeconfig file and added them to the catalog, your centralized + + view for managing all your cloud-native resources. +
+
+ If you have any questions or feedback, please join our + + Lens Community slack channel + + . +

+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 0 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ +`; + +exports[`opening catalog entity details panel when navigated to the catalog renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 0 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ +`; + +exports[`opening catalog entity details panel when navigated to the catalog when opening the menu 'some-kubernetes-cluster' renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 0 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ + +`; + +exports[`opening catalog entity details panel when navigated to the catalog when opening the menu 'some-kubernetes-cluster' when clicking the 'View Details' menu item renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 0 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ + +`; + +exports[`opening catalog entity details panel when navigated to the catalog when opening the menu 'some-kubernetes-cluster' when clicking the 'View Details' menu item when the panel opens renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 0 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ KubernetesCluster: some-kubernetes-cluster + + + content_copy + + +
+ Copy +
+
+ + + + close + + +
+ Close +
+
+
+
+
+
+ skc +
+
+ Click to open +
+
+ +
+
+
+ Kubernetes Information +
+ +
+
+
+
+
+ +`; + +exports[`opening catalog entity details panel when navigated to the catalog when opening the menu 'some-weblink' renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 0 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ + +`; + +exports[`opening catalog entity details panel when navigated to the catalog when opening the menu 'some-weblink' when clicking the 'View Details' menu item renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 0 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ + +`; + +exports[`opening catalog entity details panel when navigated to the catalog when opening the menu 'some-weblink' when clicking the 'View Details' menu item when the panel opens renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 0 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ WebLink: some-weblink + + + content_copy + + +
+ Copy +
+
+ + + close + + +
+ Close +
+
+
+
+
+
+ sw +
+
+ Click to open +
+
+ +
+
+
+ More Information +
+
+ + URL + + + https://my-websome.com + +
+
+
+
+
+
+ +`; diff --git a/src/features/catalog/opening-entity-details.test.tsx b/src/features/catalog/opening-entity-details.test.tsx new file mode 100644 index 000000000000..2d548697ed6d --- /dev/null +++ b/src/features/catalog/opening-entity-details.test.tsx @@ -0,0 +1,223 @@ +/** + * 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 type { RenderResult } from "@testing-library/react"; +import { KubernetesCluster, WebLink } from "../../common/catalog-entities"; +import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injectable"; +import type { Cluster } from "../../common/cluster/cluster"; +import navigateToCatalogInjectable from "../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; +import catalogEntityRegistryInjectable from "../../renderer/api/catalog/entity/registry.injectable"; +import { type ApplicationBuilder, getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import createClusterInjectable from "../../renderer/create-cluster/create-cluster.injectable"; + +describe("opening catalog entity details panel", () => { + let builder: ApplicationBuilder; + let rendered: RenderResult; + let windowDi: DiContainer; + let clusterEntity: KubernetesCluster; + let localClusterEntity: KubernetesCluster; + let otherEntity: WebLink; + let cluster: Cluster; + + beforeEach(async () => { + builder = getApplicationBuilder(); + + builder.afterWindowStart((windowDi) => { + const createCluster = windowDi.inject(createClusterInjectable); + + clusterEntity = new KubernetesCluster({ + metadata: { + labels: {}, + name: "some-kubernetes-cluster", + uid: "some-entity-id", + }, + spec: { + kubeconfigContext: "some-context", + kubeconfigPath: "/some/path/to/kubeconfig", + }, + status: { + phase: "connecting", + }, + }); + localClusterEntity = new KubernetesCluster({ + metadata: { + labels: {}, + name: "some-local-kubernetes-cluster", + uid: "some-entity-id-2", + source: "local", + }, + spec: { + kubeconfigContext: "some-context", + kubeconfigPath: "/some/path/to/local/kubeconfig", + }, + status: { + phase: "connecting", + }, + }); + otherEntity = new WebLink({ + metadata: { + labels: {}, + name: "some-weblink", + uid: "some-weblink-id", + }, + spec: { + url: "https://my-websome.com", + }, + status: { + phase: "available", + }, + }); + cluster = createCluster({ + contextName: clusterEntity.spec.kubeconfigContext, + id: clusterEntity.getId(), + kubeConfigPath: clusterEntity.spec.kubeconfigPath, + }, { + clusterServerUrl: "https://localhost:9999", + }); + + // TODO: remove once ClusterStore can be used without overriding it + windowDi.override(getClusterByIdInjectable, () => (clusterId) => { + if (clusterId === cluster.id) { + return cluster; + } + + return undefined; + }); + + // TODO: replace with proper entity source once syncing entities between main and windows is injectable + const catalogEntityRegistry = windowDi.inject(catalogEntityRegistryInjectable); + + catalogEntityRegistry.updateItems([clusterEntity, otherEntity, localClusterEntity]); + }); + + rendered = await builder.render(); + windowDi = builder.applicationWindow.only.di; + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("shouldn't show the details yet", () => { + expect(rendered.queryByTestId("catalog-entity-details-drawer")).not.toBeInTheDocument(); + }); + + describe("when navigated to the catalog", () => { + beforeEach(async () => { + const navigateToCatalog = windowDi.inject(navigateToCatalogInjectable); + + navigateToCatalog(); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("should show the 'Browse All' view", () => { + expect(rendered.queryByTestId("catalog-list-for-browse-all")).toBeInTheDocument(); + }); + + it("shouldn't show the details yet", () => { + expect(rendered.queryByTestId("catalog-entity-details-drawer")).not.toBeInTheDocument(); + }); + + describe("when opening the menu 'some-kubernetes-cluster'", () => { + beforeEach(() => { + rendered.getByTestId("icon-for-menu-actions-for-catalog-for-some-entity-id").click(); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("opens the menu", () => { + expect(rendered.queryByTestId("menu-actions-for-catalog-for-some-entity-id")).toBeInTheDocument(); + }); + + it("shouldn't show the details yet", () => { + expect(rendered.queryByTestId("catalog-entity-details-drawer")).not.toBeInTheDocument(); + }); + + describe("when clicking the 'View Details' menu item", () => { + beforeEach(() => { + rendered.getByTestId("open-details-menu-item-for-some-entity-id").click(); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + describe("when the panel opens", () => { + beforeEach(async () => { + await rendered.findAllByTestId("catalog-entity-details-drawer"); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("opens the detail panel for the correct item", () => { + expect(rendered.queryByTestId("catalog-entity-details-content-for-some-entity-id")).toBeInTheDocument(); + }); + + it("shows the registered items", () => { + expect(rendered.queryByTestId("kubernetes-distro-for-some-entity-id")).toBeInTheDocument(); + }); + }); + }); + }); + + describe("when opening the menu 'some-weblink'", () => { + beforeEach(() => { + rendered.getByTestId("icon-for-menu-actions-for-catalog-for-some-weblink-id").click(); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("opens the menu", () => { + expect(rendered.queryByTestId("menu-actions-for-catalog-for-some-weblink-id")).toBeInTheDocument(); + }); + + it("shouldn't show the details yet", () => { + expect(rendered.queryByTestId("catalog-entity-details-drawer")).not.toBeInTheDocument(); + }); + + describe("when clicking the 'View Details' menu item", () => { + beforeEach(() => { + rendered.getByTestId("open-details-menu-item-for-some-weblink-id").click(); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + describe("when the panel opens", () => { + beforeEach(async () => { + await rendered.findAllByTestId("catalog-entity-details-drawer"); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("opens the detail panel for the correct item", () => { + expect(rendered.queryByTestId("catalog-entity-details-content-for-some-weblink-id")).toBeInTheDocument(); + }); + + it("shows the registered items", () => { + expect(rendered.queryByTestId("weblink-url-for-some-weblink-id")).toBeInTheDocument(); + }); + + it("should not show registered items for different kinds", () => { + expect(rendered.queryByTestId("kubernetes-distro-for-some-weblink-id")).not.toBeInTheDocument(); + }); + }); + }); + }); + }); +}); diff --git a/src/features/cluster/delete-dialog/__snapshots__/delete-cluster-dialog.test.tsx.snap b/src/features/cluster/delete-dialog/__snapshots__/delete-cluster-dialog.test.tsx.snap index f20067c863d3..09713333ad09 100644 --- a/src/features/cluster/delete-dialog/__snapshots__/delete-cluster-dialog.test.tsx.snap +++ b/src/features/cluster/delete-dialog/__snapshots__/delete-cluster-dialog.test.tsx.snap @@ -278,7 +278,189 @@ exports[`Deleting a cluster when an internal kubeconfig cluster is used when the >
+ > +
+
+
+ Browse All +
+
+ 0 items +
+
+ +
+
+
+
+
+
+
+
+ Name +
+ + + arrow_drop_down + + +
+
+
+ Kind +
+ + + arrow_drop_down + + +
+
+
+ Source +
+ + + arrow_drop_down + + +
+
+
+ Labels +
+
+
+
+ Status +
+ + + arrow_drop_down + + +
+ +
+
+
+ Item list is empty +
+
+
+
+
+
+