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 { 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 = () => (
|
||||
<div>
|
||||
<div className="flex gaps">
|
||||
<ActivationOfPublicHelmRepository />
|
||||
|
||||
<ActivationOfCustomHelmRepositoryOpenButton />
|
||||
</div>
|
||||
|
||||
<HelmRepositories />
|
||||
|
||||
<ActivationOfCustomHelmRepositoryDialog />
|
||||
</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
|
||||
scrollable?: boolean;
|
||||
children?: React.ReactNode | React.ReactNode[];
|
||||
testIdForNext?: string;
|
||||
testIdForPrev?: string;
|
||||
}
|
||||
|
||||
interface WizardStepState {
|
||||
@ -214,7 +216,7 @@ export class WizardStep<D> extends React.Component<WizardStepProps<D>, WizardSte
|
||||
step, isFirst, isLast, children,
|
||||
loading, customButtons, disabledNext, scrollable,
|
||||
hideNextBtn, hideBackBtn, beforeContent, afterContent, noValidate, skip, moreButtons,
|
||||
waiting, className, contentClass, prevLabel, nextLabel,
|
||||
waiting, className, contentClass, prevLabel, nextLabel, testIdForNext, testIdForPrev,
|
||||
} = this.props;
|
||||
|
||||
if (skip) {
|
||||
@ -242,6 +244,7 @@ export class WizardStep<D> extends React.Component<WizardStepProps<D>, WizardSte
|
||||
label={prevLabel || (isFirst?.() ? "Cancel" : "Back")}
|
||||
hidden={hideBackBtn}
|
||||
onClick={this.prev}
|
||||
data-testid={testIdForPrev}
|
||||
/>
|
||||
<Button
|
||||
primary
|
||||
@ -250,6 +253,7 @@ export class WizardStep<D> extends React.Component<WizardStepProps<D>, WizardSte
|
||||
hidden={hideNextBtn}
|
||||
waiting={waiting ?? this.state.waiting}
|
||||
disabled={disabledNext}
|
||||
data-testid={testIdForNext}
|
||||
/>
|
||||
</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 requestAnimationFrameInjectable from "./components/animate/request-animation-frame.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 } = {}) => {
|
||||
const {
|
||||
@ -95,6 +96,10 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {})
|
||||
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
|
||||
di.override(extensionsStoreInjectable, () => ({ isEnabled: ({ id, isBundled }) => false }) as ExtensionsStore);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user