mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Introduce competition for activating custom helm repository in preferences
Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
parent
467698bb83
commit
2e51e33853
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,330 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import { fireEvent } from "@testing-library/react";
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import execFileInjectable from "../../common/fs/exec-file.injectable";
|
||||||
|
import helmBinaryPathInjectable from "../../main/helm/helm-binary-path.injectable";
|
||||||
|
import getActiveHelmRepositoriesInjectable from "../../main/helm/repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
|
||||||
|
import type { HelmRepo } from "../../common/helm-repo";
|
||||||
|
import callForPublicHelmRepositoriesInjectable from "../../renderer/components/+preferences/kubernetes/helm-charts/activation-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
|
||||||
|
import isPathInjectable from "../../renderer/components/input/validators/is-path.injectable";
|
||||||
|
|
||||||
|
// TODO: Make tooltips free of side effects by making it deterministic
|
||||||
|
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
|
||||||
|
withTooltip: (target: any) => target,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("activate custom helm repository in preferences", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let rendered: RenderResult;
|
||||||
|
let execFileMock: AsyncFnMock<
|
||||||
|
ReturnType<typeof execFileInjectable["instantiate"]>
|
||||||
|
>;
|
||||||
|
let getActiveHelmRepositoriesMock: AsyncFnMock<() => Promise<HelmRepo[]>>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.useFakeTimers("modern");
|
||||||
|
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
execFileMock = asyncFn();
|
||||||
|
getActiveHelmRepositoriesMock = asyncFn();
|
||||||
|
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
|
||||||
|
rendererDi.override(callForPublicHelmRepositoriesInjectable, () => async () => []);
|
||||||
|
|
||||||
|
rendererDi.override(isPathInjectable, () => ({ debounce: 0, validate: async () => {} }));
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
getActiveHelmRepositoriesInjectable,
|
||||||
|
() => getActiveHelmRepositoriesMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(execFileInjectable, () => execFileMock);
|
||||||
|
mainDi.override(helmBinaryPathInjectable, () => "some-helm-binary-path");
|
||||||
|
});
|
||||||
|
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when navigating to preferences containing helm repositories", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.preferences.navigate();
|
||||||
|
applicationBuilder.preferences.navigation.click("kubernetes");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when active repositories resolve", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await Promise.all([
|
||||||
|
getActiveHelmRepositoriesMock.resolve([
|
||||||
|
{ name: "Some active repository", url: "some-url" },
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when selecting to add custom repository", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("add-custom-helm-repo-button");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows dialog", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("activate-custom-helm-repository-dialog"),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Figure out how to close dialog by clicking outside of it
|
||||||
|
xdescribe("when closing the dialog by clicking outside", () => {
|
||||||
|
beforeEach(() => {});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show dialog anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("activate-custom-helm-repository-dialog"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when closing the dialog by clicking cancel", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("custom-helm-repository-cancel-button");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show dialog anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("activate-custom-helm-repository-dialog"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when inputted minimal options for the repository", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
getActiveHelmRepositoriesMock.mockClear();
|
||||||
|
|
||||||
|
const nameInput = rendered.getByTestId("custom-helm-repository-name-input");
|
||||||
|
|
||||||
|
fireEvent.change(nameInput, { target: { value: "some-custom-repository" }});
|
||||||
|
|
||||||
|
const urlInput = rendered.getByTestId("custom-helm-repository-url-input");
|
||||||
|
|
||||||
|
fireEvent.change(urlInput, { target: { value: "http://some.url" }});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when submitted and some time passes", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const submitButton = rendered.getByTestId("custom-helm-repository-submit-button");
|
||||||
|
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
// TODO: Remove when debounce is removed from WizardStep.submit
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("activates the repository", () => {
|
||||||
|
expect(execFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "add", "some-custom-repository", "http://some.url"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not reload active repositories yet", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when activating resolves", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await execFileMock.resolveSpecific(
|
||||||
|
[
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "add", "some-custom-repository", "http://some.url"],
|
||||||
|
],
|
||||||
|
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show dialog anymore", () => {
|
||||||
|
expect(rendered.queryByTestId("activate-custom-helm-repository-dialog")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reloads active repositories", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when adding custom repository again", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("add-custom-helm-repo-button");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("repository name is empty", () => {
|
||||||
|
const input = rendered.getByTestId("custom-helm-repository-name-input") as HTMLInputElement;
|
||||||
|
|
||||||
|
expect(input.value).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("repository url is empty", () => {
|
||||||
|
const input = rendered.getByTestId("custom-helm-repository-url-input") as HTMLInputElement;
|
||||||
|
|
||||||
|
expect(input.value).toBe("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when showing the maximal options", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("toggle-maximal-options-for-custom-helm-repository-button");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows maximal options", () => {
|
||||||
|
const maximalOptions = rendered.getByTestId("maximal-options-for-custom-helm-repository-dialog");
|
||||||
|
|
||||||
|
expect(maximalOptions).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given closing the dialog, when reopening the dialog, still shows maximal options", () => {
|
||||||
|
const cancelButton = rendered.getByTestId("custom-helm-repository-cancel-button");
|
||||||
|
|
||||||
|
fireEvent.click(cancelButton);
|
||||||
|
|
||||||
|
const openButton = rendered.getByTestId("add-custom-helm-repo-button");
|
||||||
|
|
||||||
|
fireEvent.click(openButton);
|
||||||
|
|
||||||
|
const maximalOptions = rendered.getByTestId("maximal-options-for-custom-helm-repository-dialog");
|
||||||
|
|
||||||
|
expect(maximalOptions).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when hiding maximal options", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("toggle-maximal-options-for-custom-helm-repository-button");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show maximal options anymore", () => {
|
||||||
|
const maximalOptions = rendered.queryByTestId("maximal-options-for-custom-helm-repository-dialog");
|
||||||
|
|
||||||
|
expect(maximalOptions).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when inputted maximal options", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
[
|
||||||
|
{ selector: "username-input", value: "some-username" },
|
||||||
|
{ selector: "password-input", value: "some-password" },
|
||||||
|
{ selector: "ca-cert-file-input", value: "some-ca-cert-file" },
|
||||||
|
{ selector: "cert-file-input", value: "some-cert-file" },
|
||||||
|
{ selector: "key-file-input", value: "some-key-file" },
|
||||||
|
].forEach(({ selector, value }) => {
|
||||||
|
const input = rendered.getByTestId(`custom-helm-repository-${selector}`);
|
||||||
|
|
||||||
|
fireEvent.change(input, { target: { value }});
|
||||||
|
});
|
||||||
|
|
||||||
|
const checkbox = rendered.getByTestId(`custom-helm-repository-verify-tls-input`);
|
||||||
|
|
||||||
|
fireEvent.click(checkbox);
|
||||||
|
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when submitted and some time passes, activates the repository with maximal options", () => {
|
||||||
|
const submitButton = rendered.getByTestId("custom-helm-repository-submit-button");
|
||||||
|
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
// TODO: Remove when debounce is removed from WizardStep.submit
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
expect(execFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
[
|
||||||
|
"repo",
|
||||||
|
"add",
|
||||||
|
"some-custom-repository",
|
||||||
|
"http://some.url",
|
||||||
|
"--insecure-skip-tls-verify",
|
||||||
|
"--username",
|
||||||
|
"some-username",
|
||||||
|
"--password",
|
||||||
|
"some-password",
|
||||||
|
"--ca-file",
|
||||||
|
"some-ca-cert-file",
|
||||||
|
"--key-file",
|
||||||
|
"some-key-file",
|
||||||
|
"--cert-file",
|
||||||
|
"some-cert-file",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "../../../add-helm-repo-dialog.scss";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Wizard, WizardStep } from "../../../../wizard";
|
||||||
|
import { Input } from "../../../../input";
|
||||||
|
import { systemName, isUrl } from "../../../../input/input_validators";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import customHelmRepoInjectable from "./custom-helm-repo.injectable";
|
||||||
|
import type { HelmRepo } from "../../../../../../common/helm-repo";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import type { IObservableValue } from "mobx";
|
||||||
|
import { action } from "mobx";
|
||||||
|
import submitCustomHelmRepositoryInjectable from "./submit-custom-helm-repository.injectable";
|
||||||
|
import hideDialogForActivatingCustomHelmRepositoryInjectable from "./dialog-visibility/hide-dialog-for-activating-custom-helm-repository.injectable";
|
||||||
|
import { Button } from "../../../../button";
|
||||||
|
import { Icon } from "../../../../icon";
|
||||||
|
import maximalCustomHelmRepoOptionsAreShownInjectable from "./maximal-custom-helm-repo-options-are-shown.injectable";
|
||||||
|
import { SubTitle } from "../../../../layout/sub-title";
|
||||||
|
import { Checkbox } from "../../../../checkbox";
|
||||||
|
import { HelmFileInput } from "./helm-file-input/helm-file-input";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
helmRepo: HelmRepo;
|
||||||
|
hideDialog: () => void;
|
||||||
|
submitCustomRepository: (repository: HelmRepo) => Promise<void>;
|
||||||
|
maximalOptionsAreShown: IObservableValue<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedActivationOfCustomHelmRepositoryDialogContent = observer(({ helmRepo, submitCustomRepository, maximalOptionsAreShown, hideDialog } : Dependencies) => (
|
||||||
|
<Wizard header={<h5>Add custom Helm Repo</h5>} done={hideDialog}>
|
||||||
|
<WizardStep
|
||||||
|
contentClass="flow column"
|
||||||
|
nextLabel="Add"
|
||||||
|
next={() => submitCustomRepository(helmRepo)}
|
||||||
|
testIdForNext="custom-helm-repository-submit-button"
|
||||||
|
testIdForPrev="custom-helm-repository-cancel-button"
|
||||||
|
>
|
||||||
|
<div className="flex column gaps" data-testid="activate-custom-helm-repository-dialog">
|
||||||
|
<Input
|
||||||
|
autoFocus
|
||||||
|
required
|
||||||
|
placeholder="Helm repo name"
|
||||||
|
trim
|
||||||
|
validators={systemName}
|
||||||
|
value={helmRepo.name}
|
||||||
|
onChange={action(v => helmRepo.name = v)}
|
||||||
|
data-testid="custom-helm-repository-name-input"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
required
|
||||||
|
placeholder="URL"
|
||||||
|
validators={isUrl}
|
||||||
|
value={helmRepo.url}
|
||||||
|
onChange={action(v => helmRepo.url = v)}
|
||||||
|
data-testid="custom-helm-repository-url-input"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
plain
|
||||||
|
className="accordion"
|
||||||
|
data-testid="toggle-maximal-options-for-custom-helm-repository-button"
|
||||||
|
onClick={action(() => maximalOptionsAreShown.set(!maximalOptionsAreShown.get()))}
|
||||||
|
>
|
||||||
|
More
|
||||||
|
<Icon
|
||||||
|
small
|
||||||
|
tooltip="More"
|
||||||
|
material={maximalOptionsAreShown.get() ? "remove" : "add"}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{maximalOptionsAreShown.get() && (
|
||||||
|
<div data-testid="maximal-options-for-custom-helm-repository-dialog">
|
||||||
|
<SubTitle title="Security settings" />
|
||||||
|
|
||||||
|
<Checkbox
|
||||||
|
label="Skip TLS certificate checks for the repository"
|
||||||
|
value={helmRepo.insecureSkipTlsVerify}
|
||||||
|
onChange={action(v => {
|
||||||
|
helmRepo.insecureSkipTlsVerify = v;
|
||||||
|
})}
|
||||||
|
data-testid="custom-helm-repository-verify-tls-input"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HelmFileInput
|
||||||
|
placeholder="Key file"
|
||||||
|
value={helmRepo.keyFile || ""}
|
||||||
|
setValue={action((value) => helmRepo.keyFile = value)}
|
||||||
|
fileExtensions={keyExtensions}
|
||||||
|
data-testid="custom-helm-repository-key-file-input"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HelmFileInput
|
||||||
|
placeholder="Ca file"
|
||||||
|
value={helmRepo.caFile || ""}
|
||||||
|
setValue={action((value) => helmRepo.caFile = value)}
|
||||||
|
fileExtensions={certExtensions}
|
||||||
|
data-testid="custom-helm-repository-ca-cert-file-input"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HelmFileInput
|
||||||
|
placeholder="Certificate file"
|
||||||
|
value={helmRepo.certFile || ""}
|
||||||
|
setValue={action((value) => helmRepo.certFile = value)}
|
||||||
|
fileExtensions={certExtensions}
|
||||||
|
data-testid="custom-helm-repository-cert-file-input"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SubTitle title="Chart Repository Credentials" />
|
||||||
|
|
||||||
|
<Input
|
||||||
|
placeholder="Username"
|
||||||
|
value={helmRepo.username}
|
||||||
|
onChange= {action(v => helmRepo.username = v)}
|
||||||
|
data-testid="custom-helm-repository-username-input"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
placeholder="Password"
|
||||||
|
value={helmRepo.password}
|
||||||
|
onChange={action(v => helmRepo.password = v)}
|
||||||
|
data-testid="custom-helm-repository-password-input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</WizardStep>
|
||||||
|
</Wizard>
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const ActivationOfCustomHelmRepositoryDialogContent = withInjectables<Dependencies>(
|
||||||
|
NonInjectedActivationOfCustomHelmRepositoryDialogContent,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
helmRepo: di.inject(customHelmRepoInjectable),
|
||||||
|
hideDialog: di.inject(hideDialogForActivatingCustomHelmRepositoryInjectable),
|
||||||
|
submitCustomRepository: di.inject(submitCustomHelmRepositoryInjectable),
|
||||||
|
maximalOptionsAreShown: di.inject(maximalCustomHelmRepoOptionsAreShownInjectable),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const keyExtensions = ["key", "keystore", "jks", "p12", "pfx", "pem"];
|
||||||
|
const certExtensions = ["crt", "cer", "ca-bundle", "p7b", "p7c", "p7s", "p12", "pfx", "pem"];
|
||||||
|
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "../../../add-helm-repo-dialog.scss";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Dialog } from "../../../../dialog";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import { ActivationOfCustomHelmRepositoryDialogContent } from "./activation-of-custom-helm-repository-dialog-content";
|
||||||
|
import activationOfCustomHelmRepositoryDialogIsVisibleInjectable from "./dialog-visibility/activation-of-custom-helm-repository-dialog-is-visible.injectable";
|
||||||
|
import type { IObservableValue } from "mobx";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import hideDialogForActivatingCustomHelmRepositoryInjectable from "./dialog-visibility/hide-dialog-for-activating-custom-helm-repository.injectable";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
contentIsVisible: IObservableValue<boolean>;
|
||||||
|
hideDialog: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedActivationOfCustomHelmRepositoryDialog = observer(({
|
||||||
|
contentIsVisible,
|
||||||
|
hideDialog,
|
||||||
|
}: Dependencies) => (
|
||||||
|
<div>
|
||||||
|
<Dialog
|
||||||
|
className="AddHelmRepoDialog"
|
||||||
|
isOpen={contentIsVisible.get()}
|
||||||
|
close={hideDialog}
|
||||||
|
>
|
||||||
|
{contentIsVisible.get() && <ActivationOfCustomHelmRepositoryDialogContent />}
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
export const ActivationOfCustomHelmRepositoryDialog = withInjectables<Dependencies>(
|
||||||
|
NonInjectedActivationOfCustomHelmRepositoryDialog,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
contentIsVisible: di.inject(activationOfCustomHelmRepositoryDialogIsVisibleInjectable),
|
||||||
|
hideDialog: di.inject(hideDialogForActivatingCustomHelmRepositoryInjectable),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import React from "react";
|
||||||
|
import { Button } from "../../../../button";
|
||||||
|
import showDialogForActivatingCustomHelmRepositoryInjectable from "./dialog-visibility/show-dialog-for-activating-custom-helm-repository.injectable";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
showDialog: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedActivationOfCustomHelmRepositoryOpenButton = ({ showDialog }: Dependencies) => (
|
||||||
|
<Button
|
||||||
|
primary
|
||||||
|
label="Add Custom Helm Repo"
|
||||||
|
onClick={showDialog}
|
||||||
|
data-testid="add-custom-helm-repo-button"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ActivationOfCustomHelmRepositoryOpenButton = withInjectables<Dependencies>(
|
||||||
|
NonInjectedActivationOfCustomHelmRepositoryOpenButton,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
showDialog: di.inject(showDialogForActivatingCustomHelmRepositoryInjectable),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
@ -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, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
|
import { observable } from "mobx";
|
||||||
|
|
||||||
|
const customHelmRepoInjectable = getInjectable({
|
||||||
|
id: "custom-helm-repo",
|
||||||
|
|
||||||
|
instantiate: () => observable({
|
||||||
|
name: "",
|
||||||
|
url: "",
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
insecureSkipTlsVerify: false,
|
||||||
|
caFile: "",
|
||||||
|
keyFile: "",
|
||||||
|
certFile: "",
|
||||||
|
}),
|
||||||
|
|
||||||
|
lifecycle: lifecycleEnum.transient,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default customHelmRepoInjectable;
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* 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 { observable } from "mobx";
|
||||||
|
|
||||||
|
const activationOfCustomHelmRepositoryDialogIsVisibleInjectable = getInjectable({
|
||||||
|
id: "add-custom-helm-repository-dialog-is-visible",
|
||||||
|
instantiate: () => observable.box(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default activationOfCustomHelmRepositoryDialogIsVisibleInjectable;
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { action } from "mobx";
|
||||||
|
import activationOfCustomHelmRepositoryDialogIsVisibleInjectable from "./activation-of-custom-helm-repository-dialog-is-visible.injectable";
|
||||||
|
|
||||||
|
const hideDialogForActivatingCustomHelmRepositoryInjectable = getInjectable({
|
||||||
|
id: "hide-dialog-for-activating-custom-helm-repository",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const state = di.inject(activationOfCustomHelmRepositoryDialogIsVisibleInjectable);
|
||||||
|
|
||||||
|
return action(() => {
|
||||||
|
state.set(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default hideDialogForActivatingCustomHelmRepositoryInjectable;
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { action } from "mobx";
|
||||||
|
import activationOfCustomHelmRepositoryDialogIsVisibleInjectable from "./activation-of-custom-helm-repository-dialog-is-visible.injectable";
|
||||||
|
|
||||||
|
const showDialogForActivatingCustomHelmRepositoryInjectable = getInjectable({
|
||||||
|
id: "show-dialog-for-activating-custom-helm-repository",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const state = di.inject(activationOfCustomHelmRepositoryDialogIsVisibleInjectable);
|
||||||
|
|
||||||
|
return action(() => {
|
||||||
|
state.set(true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default showDialogForActivatingCustomHelmRepositoryInjectable;
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 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 { FileFilter } from "electron";
|
||||||
|
import { requestOpenFilePickingDialog } from "../../../../../../ipc";
|
||||||
|
|
||||||
|
const getFilePathsInjectable = getInjectable({
|
||||||
|
id: "get-file-paths",
|
||||||
|
|
||||||
|
instantiate: () => async (fileFilter: FileFilter) =>
|
||||||
|
await requestOpenFilePickingDialog({
|
||||||
|
// defaultPath: this.getFilePath(type),
|
||||||
|
properties: ["openFile", "showHiddenFiles"],
|
||||||
|
message: `Select file`,
|
||||||
|
buttonLabel: `Use file`,
|
||||||
|
filters: [fileFilter, { name: "Any", extensions: ["*"] }],
|
||||||
|
}),
|
||||||
|
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default getFilePathsInjectable;
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { InputValidator } from "../../../../../input";
|
||||||
|
import { Input } from "../../../../../input";
|
||||||
|
import { Icon } from "../../../../../icon";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import React from "react";
|
||||||
|
import getFilePathsInjectable from "./get-file-paths.injectable";
|
||||||
|
import type { FileFilter } from "electron";
|
||||||
|
import isPathInjectable from "../../../../../input/validators/is-path.injectable";
|
||||||
|
|
||||||
|
interface HelmFileInputProps {
|
||||||
|
placeholder: string;
|
||||||
|
fileExtensions: string[];
|
||||||
|
value: string;
|
||||||
|
setValue: (value: string) => void;
|
||||||
|
"data-testid"?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
getFilePaths: (fileFilter: FileFilter) => Promise<{ canceled: boolean; filePaths: string[] }>;
|
||||||
|
isPath: InputValidator<true>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedHelmFileInput = ({
|
||||||
|
placeholder,
|
||||||
|
value,
|
||||||
|
setValue,
|
||||||
|
fileExtensions,
|
||||||
|
getFilePaths,
|
||||||
|
isPath,
|
||||||
|
"data-testid": testId,
|
||||||
|
}: Dependencies & HelmFileInputProps) => (
|
||||||
|
<div className="flex gaps align-center">
|
||||||
|
<Input
|
||||||
|
placeholder={placeholder}
|
||||||
|
validators={isPath}
|
||||||
|
className="box grow"
|
||||||
|
value={value}
|
||||||
|
onChange={(v) => setValue(v)}
|
||||||
|
data-testid={testId}
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
material="folder"
|
||||||
|
|
||||||
|
onClick={async () => {
|
||||||
|
const { canceled, filePaths } = await getFilePaths({
|
||||||
|
name: placeholder,
|
||||||
|
extensions: fileExtensions,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!canceled && filePaths.length) {
|
||||||
|
setValue(filePaths[0]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
tooltip="Browse"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const HelmFileInput = withInjectables<Dependencies, HelmFileInputProps>(
|
||||||
|
NonInjectedHelmFileInput,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
getFilePaths: di.inject(getFilePathsInjectable),
|
||||||
|
isPath: di.inject(isPathInjectable),
|
||||||
|
...props,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* 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 { observable } from "mobx";
|
||||||
|
|
||||||
|
const maximalCustomHelmRepoOptionsAreShownInjectable = getInjectable({
|
||||||
|
id: "maximal-custom-helm-repo-options-are-shown",
|
||||||
|
instantiate: () => observable.box(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default maximalCustomHelmRepoOptionsAreShownInjectable;
|
||||||
@ -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 type { HelmRepo } from "../../../../../../common/helm-repo";
|
||||||
|
import activateHelmRepositoryInjectable from "../activation-of-public-helm-repository/select-helm-repository/activate-helm-repository.injectable";
|
||||||
|
import hideDialogForActivatingCustomHelmRepositoryInjectable from "./dialog-visibility/hide-dialog-for-activating-custom-helm-repository.injectable";
|
||||||
|
|
||||||
|
const submitCustomHelmRepositoryInjectable = getInjectable({
|
||||||
|
id: "submit-custom-helm-repository",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const activateHelmRepository = di.inject(activateHelmRepositoryInjectable);
|
||||||
|
const hideDialog = di.inject(hideDialogForActivatingCustomHelmRepositoryInjectable);
|
||||||
|
|
||||||
|
return async (repository: HelmRepo) => {
|
||||||
|
await activateHelmRepository(repository);
|
||||||
|
|
||||||
|
hideDialog();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default submitCustomHelmRepositoryInjectable;
|
||||||
@ -8,14 +8,20 @@ import React from "react";
|
|||||||
|
|
||||||
import { HelmRepositories } from "./helm-repositories";
|
import { HelmRepositories } from "./helm-repositories";
|
||||||
import { ActivationOfPublicHelmRepository } from "./activation-of-public-helm-repository/activation-of-public-helm-repository";
|
import { ActivationOfPublicHelmRepository } from "./activation-of-public-helm-repository/activation-of-public-helm-repository";
|
||||||
|
import { ActivationOfCustomHelmRepositoryOpenButton } from "./activation-of-custom-helm-repository/activation-of-custom-helm-repository-open-button";
|
||||||
|
import { ActivationOfCustomHelmRepositoryDialog } from "./activation-of-custom-helm-repository/activation-of-custom-helm-repository-dialog";
|
||||||
|
|
||||||
export const HelmCharts = () => (
|
export const HelmCharts = () => (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex gaps">
|
<div className="flex gaps">
|
||||||
<ActivationOfPublicHelmRepository />
|
<ActivationOfPublicHelmRepository />
|
||||||
|
|
||||||
|
<ActivationOfCustomHelmRepositoryOpenButton />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<HelmRepositories />
|
<HelmRepositories />
|
||||||
|
|
||||||
|
<ActivationOfCustomHelmRepositoryDialog />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* 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 { AsyncInputValidationError, inputValidator } from "../input_validators";
|
||||||
|
import pathExistsInjectable from "../../../../common/fs/path-exists.injectable";
|
||||||
|
|
||||||
|
const isPathInjectable = getInjectable({
|
||||||
|
id: "is-path",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const pathExists = di.inject(pathExistsInjectable);
|
||||||
|
|
||||||
|
return inputValidator<true>({
|
||||||
|
debounce: 100,
|
||||||
|
condition: ({ type }) => type === "text",
|
||||||
|
|
||||||
|
validate: async (value) => {
|
||||||
|
try {
|
||||||
|
await pathExists(value);
|
||||||
|
} catch {
|
||||||
|
throw new AsyncInputValidationError(
|
||||||
|
`${value} is not a valid file path`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default isPathInjectable;
|
||||||
@ -129,6 +129,8 @@ export interface WizardStepProps<D> extends WizardCommonProps<D> {
|
|||||||
skip?: boolean; // don't render the step
|
skip?: boolean; // don't render the step
|
||||||
scrollable?: boolean;
|
scrollable?: boolean;
|
||||||
children?: React.ReactNode | React.ReactNode[];
|
children?: React.ReactNode | React.ReactNode[];
|
||||||
|
testIdForNext?: string;
|
||||||
|
testIdForPrev?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WizardStepState {
|
interface WizardStepState {
|
||||||
@ -214,7 +216,7 @@ export class WizardStep<D> extends React.Component<WizardStepProps<D>, WizardSte
|
|||||||
step, isFirst, isLast, children,
|
step, isFirst, isLast, children,
|
||||||
loading, customButtons, disabledNext, scrollable,
|
loading, customButtons, disabledNext, scrollable,
|
||||||
hideNextBtn, hideBackBtn, beforeContent, afterContent, noValidate, skip, moreButtons,
|
hideNextBtn, hideBackBtn, beforeContent, afterContent, noValidate, skip, moreButtons,
|
||||||
waiting, className, contentClass, prevLabel, nextLabel,
|
waiting, className, contentClass, prevLabel, nextLabel, testIdForNext, testIdForPrev,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (skip) {
|
if (skip) {
|
||||||
@ -242,6 +244,7 @@ export class WizardStep<D> extends React.Component<WizardStepProps<D>, WizardSte
|
|||||||
label={prevLabel || (isFirst?.() ? "Cancel" : "Back")}
|
label={prevLabel || (isFirst?.() ? "Cancel" : "Back")}
|
||||||
hidden={hideBackBtn}
|
hidden={hideBackBtn}
|
||||||
onClick={this.prev}
|
onClick={this.prev}
|
||||||
|
data-testid={testIdForPrev}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
primary
|
primary
|
||||||
@ -250,6 +253,7 @@ export class WizardStep<D> extends React.Component<WizardStepProps<D>, WizardSte
|
|||||||
hidden={hideNextBtn}
|
hidden={hideNextBtn}
|
||||||
waiting={waiting ?? this.state.waiting}
|
waiting={waiting ?? this.state.waiting}
|
||||||
disabled={disabledNext}
|
disabled={disabledNext}
|
||||||
|
data-testid={testIdForNext}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -45,6 +45,7 @@ import appVersionInjectable from "../common/get-configuration-file-model/app-ver
|
|||||||
import provideInitialValuesForSyncBoxesInjectable from "./utils/sync-box/provide-initial-values-for-sync-boxes.injectable";
|
import provideInitialValuesForSyncBoxesInjectable from "./utils/sync-box/provide-initial-values-for-sync-boxes.injectable";
|
||||||
import requestAnimationFrameInjectable from "./components/animate/request-animation-frame.injectable";
|
import requestAnimationFrameInjectable from "./components/animate/request-animation-frame.injectable";
|
||||||
import getRandomIdInjectable from "../common/utils/get-random-id.injectable";
|
import getRandomIdInjectable from "../common/utils/get-random-id.injectable";
|
||||||
|
import getFilePathsInjectable from "./components/+preferences/kubernetes/helm-charts/activation-of-custom-helm-repository/helm-file-input/get-file-paths.injectable";
|
||||||
|
|
||||||
export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => {
|
export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => {
|
||||||
const {
|
const {
|
||||||
@ -95,6 +96,10 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {})
|
|||||||
throw new Error("Tried to broadcast message over IPC without explicit override.");
|
throw new Error("Tried to broadcast message over IPC without explicit override.");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
di.override(getFilePathsInjectable, () => () => {
|
||||||
|
throw new Error("Tried to get file paths without explicit override.");
|
||||||
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line unused-imports/no-unused-vars-ts
|
// eslint-disable-next-line unused-imports/no-unused-vars-ts
|
||||||
di.override(extensionsStoreInjectable, () => ({ isEnabled: ({ id, isBundled }) => false }) as ExtensionsStore);
|
di.override(extensionsStoreInjectable, () => ({ isEnabled: ({ id, isBundled }) => false }) as ExtensionsStore);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user