From 32b84d16581dfd2fed34e6384ee41e1c79e302c8 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 4 Jul 2022 13:17:44 -0700 Subject: [PATCH] Fix crash in HelmChartDetails (#5667) * Fix crash in HelmChartDetails Signed-off-by: Sebastian Malton * Fix bug in helm chart icon fallback Signed-off-by: Sebastian Malton * Update snapshots Signed-off-by: Sebastian Malton * Factor out HelmChartIcon for better reusability Signed-off-by: Sebastian Malton * Fix compile error Signed-off-by: Sebastian Malton * Fix remaining type errors Signed-off-by: Sebastian Malton * Remove HelmChart list figure background Signed-off-by: Alex Andreev * Removing dangerouslySetInnerHTML usage Signed-off-by: Alex Andreev * Snapshots update Signed-off-by: Alex Andreev * Using currentColor from cascade instead of specific one Signed-off-by: Alex Andreev Co-authored-by: Alex Andreev --- .../k8s-api/endpoints/helm-charts.api.ts | 2 + .../helm-chart-details.test.tsx.snap | 423 ++++++++++++++++++ .../get-char-details.injectable.ts | 17 + .../+helm-charts/helm-chart-details.scss | 4 + .../+helm-charts/helm-chart-details.test.tsx | 92 ++++ .../+helm-charts/helm-chart-details.tsx | 47 +- .../components/+helm-charts/helm-charts.scss | 9 +- .../components/+helm-charts/helm-charts.tsx | 6 +- .../+helm-charts/helm-placeholder.svg | 1 - src/renderer/components/+helm-charts/icon.tsx | 49 ++ .../create-install-chart-tab.injectable.ts | 6 +- .../notifications/notifications.store.tsx | 6 + .../notifications/notifications.tsx | 76 +--- .../show-checked-error.injectable.ts | 32 ++ .../show-error-notification.injectable.ts | 6 +- .../show-info-notification.injectable.ts | 6 +- .../show-short-info.injectable.ts | 24 + .../show-success-notification.injectable.ts | 6 +- src/renderer/themes/lens-dark.ts | 1 - src/renderer/themes/lens-light.ts | 1 - src/renderer/themes/theme-vars.css | 1 - 21 files changed, 712 insertions(+), 103 deletions(-) create mode 100644 src/renderer/components/+helm-charts/__snapshots__/helm-chart-details.test.tsx.snap create mode 100644 src/renderer/components/+helm-charts/get-char-details.injectable.ts create mode 100644 src/renderer/components/+helm-charts/helm-chart-details.test.tsx delete mode 100644 src/renderer/components/+helm-charts/helm-placeholder.svg create mode 100644 src/renderer/components/+helm-charts/icon.tsx create mode 100644 src/renderer/components/notifications/show-checked-error.injectable.ts create mode 100644 src/renderer/components/notifications/show-short-info.injectable.ts diff --git a/src/common/k8s-api/endpoints/helm-charts.api.ts b/src/common/k8s-api/endpoints/helm-charts.api.ts index 2690e95afb..f4ba0cf45f 100644 --- a/src/common/k8s-api/endpoints/helm-charts.api.ts +++ b/src/common/k8s-api/endpoints/helm-charts.api.ts @@ -315,6 +315,8 @@ export class HelmChart implements HelmChartData { autoBind(this); } + static create(data: RawHelmChart): HelmChart; + static create(data: RawHelmChart, opts?: HelmChartCreateOpts): HelmChart | undefined; static create(data: RawHelmChart, { onError = "throw" }: HelmChartCreateOpts = {}): HelmChart | undefined { const result = helmChartValidator.validate(data, { abortEarly: false, diff --git a/src/renderer/components/+helm-charts/__snapshots__/helm-chart-details.test.tsx.snap b/src/renderer/components/+helm-charts/__snapshots__/helm-chart-details.test.tsx.snap new file mode 100644 index 0000000000..b558d1bef1 --- /dev/null +++ b/src/renderer/components/+helm-charts/__snapshots__/helm-chart-details.test.tsx.snap @@ -0,0 +1,423 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` before getChartDetails resolves renders 1`] = ` + +
+
+
+
+
+
+
+ Chart: a galaxy far far away/a name + + + content_copy + +
+ +
+ + + close + +
+ +
+
+
+
+
+
+
+ +`; + +exports[` before getChartDetails resolves when getChartDetails resolves with one version renders 1`] = ` + +
+
+
+
+
+
+
+ Chart: a galaxy far far away/a name + + + content_copy + +
+ +
+ + + close + +
+ +
+
+
+
+ +
+
+ + +
+
+ + Version + + +
+ + +
+
+
+ 1 +
+
+ +
+
+
+ + +
+
+
+
+
+
+ + Home + + + + +
+
+ + Maintainers + + +
+
+
+
+
+

+ I am a readme +

+ + +
+
+
+
+
+
+
+ +`; + +exports[` before getChartDetails resolves with getChartDetails rejects renders 1`] = ` + +
+
+
+
+ + + info_outline + + +
+
+ Error: some error +
+
+ + + close + + +
+
+
+
+
+
+
+
+ Chart: a galaxy far far away/a name + + + content_copy + +
+ +
+ + + close + +
+ +
+
+
+
+
+
+
+ +`; diff --git a/src/renderer/components/+helm-charts/get-char-details.injectable.ts b/src/renderer/components/+helm-charts/get-char-details.injectable.ts new file mode 100644 index 0000000000..e2c5ccca62 --- /dev/null +++ b/src/renderer/components/+helm-charts/get-char-details.injectable.ts @@ -0,0 +1,17 @@ +/** + * 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 { GetChartDetailsOptions, IHelmChartDetails } from "../../../common/k8s-api/endpoints/helm-charts.api"; +import { getChartDetails } from "../../../common/k8s-api/endpoints/helm-charts.api"; + +export type GetChartDetails = (repo: string, name: string, opts?: GetChartDetailsOptions) => Promise; + +const getChartDetailsInjectable = getInjectable({ + id: "get-chart-details", + instantiate: (): GetChartDetails => getChartDetails, + causesSideEffects: true, +}); + +export default getChartDetailsInjectable; diff --git a/src/renderer/components/+helm-charts/helm-chart-details.scss b/src/renderer/components/+helm-charts/helm-chart-details.scss index 9f6d64223d..b82512dff4 100644 --- a/src/renderer/components/+helm-charts/helm-chart-details.scss +++ b/src/renderer/components/+helm-charts/helm-chart-details.scss @@ -14,6 +14,10 @@ box-sizing: content-box; } + div.intro-logo { + width: 100px; + } + .Select__option { span.deprecated { text-decoration: line-through; diff --git a/src/renderer/components/+helm-charts/helm-chart-details.test.tsx b/src/renderer/components/+helm-charts/helm-chart-details.test.tsx new file mode 100644 index 0000000000..4af6543a9d --- /dev/null +++ b/src/renderer/components/+helm-charts/helm-chart-details.test.tsx @@ -0,0 +1,92 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { AsyncFnMock } from "@async-fn/jest"; +import asyncFn from "@async-fn/jest"; +import type { DiContainer } from "@ogre-tools/injectable"; +import type { RenderResult } from "@testing-library/react"; +import React from "react"; +import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable"; +import { HelmChart } from "../../../common/k8s-api/endpoints/helm-charts.api"; +import { getDiForUnitTesting } from "../../getDiForUnitTesting"; +import { noop } from "../../utils"; +import type { CreateInstallChartTab } from "../dock/install-chart/create-install-chart-tab.injectable"; +import createInstallChartTabInjectable from "../dock/install-chart/create-install-chart-tab.injectable"; +import { Notifications } from "../notifications"; +import type { DiRender } from "../test-utils/renderFor"; +import { renderFor } from "../test-utils/renderFor"; +import type { GetChartDetails } from "./get-char-details.injectable"; +import getChartDetailsInjectable from "./get-char-details.injectable"; +import { HelmChartDetails } from "./helm-chart-details"; + +describe("", () => { + let di: DiContainer; + let getChartDetails: AsyncFnMock; + let chart: HelmChart; + let render: DiRender; + let result: RenderResult; + let createInstallChartTab: jest.MockedFunction; + + beforeEach(() => { + di = getDiForUnitTesting({ doGeneralOverrides: true }); + getChartDetails = asyncFn(); + createInstallChartTab = jest.fn(); + chart = HelmChart.create({ + apiVersion: "some-api-version", + created: "a long time ago", + name: "a name", + repo: "a galaxy far far away", + version: "1", + }); + + di.override(directoryForLensLocalStorageInjectable, () => "some-directory-for-lens-local-storage"); + di.override(getChartDetailsInjectable, () => getChartDetails); + di.override(createInstallChartTabInjectable, () => createInstallChartTab); + render = renderFor(di); + result = render(( + <> + + + + )); + }); + + describe("before getChartDetails resolves", () => { + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + + describe("when getChartDetails resolves with one version", () => { + beforeEach(async () => { + await getChartDetails.resolve({ + readme: "I am a readme", + versions: [chart], + }); + }); + + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + + it("shows the readme", () => { + expect(result.queryByTestId("helmchart-readme")).not.toBeNull(); + }); + + it("shows the selected chart", () => { + expect(result.queryByTestId("selected-chart-description")).not.toBeNull(); + }); + }); + + describe("with getChartDetails rejects", () => { + beforeEach(async () => { + await getChartDetails.reject(new Error("some error")); + }); + + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/src/renderer/components/+helm-charts/helm-chart-details.tsx b/src/renderer/components/+helm-charts/helm-chart-details.tsx index 4e0076e068..e33f2836ae 100644 --- a/src/renderer/components/+helm-charts/helm-chart-details.tsx +++ b/src/renderer/components/+helm-charts/helm-chart-details.tsx @@ -7,7 +7,6 @@ import "./helm-chart-details.scss"; import React, { Component } from "react"; import type { HelmChart } from "../../../common/k8s-api/endpoints/helm-charts.api"; -import { getChartDetails } from "../../../common/k8s-api/endpoints/helm-charts.api"; import { computed, observable, reaction, runInAction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { Drawer, DrawerItem } from "../drawer"; @@ -20,10 +19,13 @@ import { Badge } from "../badge"; import { Tooltip, withStyles } from "@material-ui/core"; import { withInjectables } from "@ogre-tools/injectable-react"; import createInstallChartTabInjectable from "../dock/install-chart/create-install-chart-tab.injectable"; -import { Notifications } from "../notifications"; -import HelmLogoPlaceholder from "./helm-placeholder.svg"; +import type { ShowCheckedErrorNotification } from "../notifications/show-checked-error.injectable"; import type { SingleValue } from "react-select"; -import { AbortController } from "abort-controller"; +import AbortController from "abort-controller"; +import showCheckedErrorNotificationInjectable from "../notifications/show-checked-error.injectable"; +import type { GetChartDetails } from "./get-char-details.injectable"; +import getChartDetailsInjectable from "./get-char-details.injectable"; +import { HelmChartIcon } from "./icon"; export interface HelmChartDetailsProps { chart: HelmChart; @@ -38,6 +40,8 @@ const LargeTooltip = withStyles({ interface Dependencies { createInstallChartTab: (helmChart: HelmChart) => void; + showCheckedErrorNotification: ShowCheckedErrorNotification; + getChartDetails: GetChartDetails; } @observer @@ -73,7 +77,7 @@ class NonInjectedHelmChartDetails extends Component { this.readme.set(readme); @@ -81,7 +85,7 @@ class NonInjectedHelmChartDetails extends Component - event.currentTarget.src = HelmLogoPlaceholder} />
-
+
{selectedChart.getDescription()}