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

fix deployments/scale/dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-04-12 10:23:26 -04:00
parent 24db61fbef
commit 5c7314da0a
7 changed files with 154 additions and 85 deletions

View File

@ -4,57 +4,75 @@
*/ */
import React from "react"; import React from "react";
import type { KubeObjectMenuProps } from "../kube-object-menu"; import type { KubeObjectMenuProps } from "../kube-object-menu";
import type { Deployment } from "../../../common/k8s-api/endpoints"; import type { Deployment, DeploymentApi } from "../../../common/k8s-api/endpoints";
import { deploymentApi } from "../../../common/k8s-api/endpoints";
import { MenuItem } from "../menu"; import { MenuItem } from "../menu";
import { DeploymentScaleDialog } from "./deployment-scale-dialog";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { ConfirmDialog } from "../confirm-dialog"; import { ConfirmDialog } from "../confirm-dialog";
import { Notifications } from "../notifications"; import { Notifications } from "../notifications";
import type { OpenDeploymentScaleDialog } from "./scale/open.injectable";
import { withInjectables } from "@ogre-tools/injectable-react";
import deploymentApiInjectable from "../../../common/k8s-api/endpoints/deployment.api.injectable";
import openDeploymentScaleDialogInjectable from "./scale/open.injectable";
export function DeploymentMenu(props: KubeObjectMenuProps<Deployment>) { export interface DeploymentMenuProps extends KubeObjectMenuProps<Deployment> {}
const { object, toolbar } = props;
return ( interface Dependencies {
<> openDeploymentScaleDialog: OpenDeploymentScaleDialog;
<MenuItem onClick={() => DeploymentScaleDialog.open(object)}> deploymentApi: DeploymentApi;
<Icon
material="open_with"
tooltip="Scale"
interactive={toolbar}
/>
<span className="title">Scale</span>
</MenuItem>
<MenuItem
onClick={() => ConfirmDialog.open({
ok: async () =>
{
try {
await deploymentApi.restart({
namespace: object.getNs(),
name: object.getName(),
});
} catch (err) {
Notifications.checkedError(err, "Unknown error occured while restarting deployment");
}
},
labelOk: `Restart`,
message: (
<p>
{"Are you sure you want to restart deployment "}
<b>{object.getName()}</b>
?
</p>
),
})}
>
<Icon
material="autorenew"
tooltip="Restart"
interactive={toolbar}
/>
<span className="title">Restart</span>
</MenuItem>
</>
);
} }
const NonInjectedDeploymentMenu = ({
deploymentApi,
object,
openDeploymentScaleDialog,
toolbar,
}: Dependencies & DeploymentMenuProps) => (
<>
<MenuItem onClick={() => openDeploymentScaleDialog(object)}>
<Icon
material="open_with"
tooltip="Scale"
interactive={toolbar}
/>
<span className="title">Scale</span>
</MenuItem>
<MenuItem
onClick={() => ConfirmDialog.open({
ok: async () =>
{
try {
await deploymentApi.restart({
namespace: object.getNs(),
name: object.getName(),
});
} catch (err) {
Notifications.checkedError(err, "Unknown error occured while restarting deployment");
}
},
labelOk: "Restart",
message: (
<p>
{"Are you sure you want to restart deployment "}
<b>{object.getName()}</b>
?
</p>
),
})}
>
<Icon
material="autorenew"
tooltip="Restart"
interactive={toolbar}
/>
<span className="title">Restart</span>
</MenuItem>
</>
);
export const DeploymentMenu = withInjectables<Dependencies, DeploymentMenuProps>(NonInjectedDeploymentMenu, {
getProps: (di, props) => ({
...props,
deploymentApi: di.inject(deploymentApiInjectable),
openDeploymentScaleDialog: di.inject(openDeploymentScaleDialogInjectable),
}),
});

View File

@ -0,0 +1,14 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { observable } from "mobx";
import type { Deployment } from "../../../../common/k8s-api/endpoints";
const deploymentScaleDialogStateInjectable = getInjectable({
id: "deployment-scale-dialog-state",
instantiate: () => observable.box<Deployment | undefined>(),
});
export default deploymentScaleDialogStateInjectable;

View File

@ -4,11 +4,19 @@
*/ */
import React from "react"; import React from "react";
import { render, waitFor, fireEvent } from "@testing-library/react"; import { waitFor, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect"; import "@testing-library/jest-dom/extend-expect";
import { DeploymentScaleDialog } from "./deployment-scale-dialog"; import { DeploymentScaleDialog } from "./dialog";
import { Deployment, DeploymentApi } from "../../../common/k8s-api/endpoints/deployment.api"; import type { DeploymentApi } from "../../../../common/k8s-api/endpoints/deployment.api";
import { Deployment } from "../../../../common/k8s-api/endpoints/deployment.api";
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
import deploymentApiInjectable from "../../../../common/k8s-api/endpoints/deployment.api.injectable";
import type { OpenDeploymentScaleDialog } from "./open.injectable";
import openDeploymentScaleDialogInjectable from "./open.injectable";
import createStoresAndApisInjectable from "../../../create-stores-apis.injectable";
import type { DiRender } from "../../test-utils/renderFor";
import { renderFor } from "../../test-utils/renderFor";
const dummyDeployment = new Deployment({ const dummyDeployment = new Deployment({
apiVersion: "v1", apiVersion: "v1",
@ -76,9 +84,17 @@ const dummyDeployment = new Deployment({
describe("<DeploymentScaleDialog />", () => { describe("<DeploymentScaleDialog />", () => {
let deploymentApi: DeploymentApi; let deploymentApi: DeploymentApi;
let openDeploymentScaleDialog: OpenDeploymentScaleDialog;
let render: DiRender;
beforeEach(() => { beforeEach(() => {
deploymentApi = new DeploymentApi(); const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(createStoresAndApisInjectable, () => true);
deploymentApi = di.inject(deploymentApiInjectable);
openDeploymentScaleDialog = di.inject(openDeploymentScaleDialogInjectable);
render = renderFor(di);
}); });
it("renders w/o errors", () => { it("renders w/o errors", () => {
@ -93,9 +109,9 @@ describe("<DeploymentScaleDialog />", () => {
const initReplicas = 3; const initReplicas = 3;
deploymentApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas); deploymentApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas);
const { findByTestId } = render(<DeploymentScaleDialog deploymentApi={deploymentApi} />); const { findByTestId } = render(<DeploymentScaleDialog />);
DeploymentScaleDialog.open(dummyDeployment); openDeploymentScaleDialog(dummyDeployment);
// we need to wait for the DeploymentScaleDialog to show up // we need to wait for the DeploymentScaleDialog to show up
// because there is an <Animate /> in <Dialog /> which renders null at start. // because there is an <Animate /> in <Dialog /> which renders null at start.
await waitFor(async () => { await waitFor(async () => {
@ -114,9 +130,9 @@ describe("<DeploymentScaleDialog />", () => {
const initReplicas = 1; const initReplicas = 1;
deploymentApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas); deploymentApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas);
const component = render(<DeploymentScaleDialog deploymentApi={deploymentApi} />); const component = render(<DeploymentScaleDialog />);
DeploymentScaleDialog.open(dummyDeployment); openDeploymentScaleDialog(dummyDeployment);
await waitFor(async () => { await waitFor(async () => {
expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas}`); expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas}`);
expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`);

View File

@ -3,52 +3,45 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import "./deployment-scale-dialog.scss"; import "./dialog.scss";
import React, { Component } from "react"; import React, { Component } from "react";
import type { IObservableValue } from "mobx";
import { computed, observable, makeObservable } from "mobx"; import { computed, observable, makeObservable } from "mobx";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { DialogProps } from "../dialog"; import type { DialogProps } from "../../dialog";
import { Dialog } from "../dialog"; import { Dialog } from "../../dialog";
import { Wizard, WizardStep } from "../wizard"; import { Wizard, WizardStep } from "../../wizard";
import type { Deployment, DeploymentApi } from "../../../common/k8s-api/endpoints"; import type { Deployment, DeploymentApi } from "../../../../common/k8s-api/endpoints";
import { deploymentApi } from "../../../common/k8s-api/endpoints"; import { Icon } from "../../icon";
import { Icon } from "../icon"; import { Slider } from "../../slider";
import { Slider } from "../slider"; import { Notifications } from "../../notifications";
import { Notifications } from "../notifications"; import { cssNames } from "../../../utils";
import { cssNames } from "../../utils"; import { withInjectables } from "@ogre-tools/injectable-react";
import deploymentApiInjectable from "../../../../common/k8s-api/endpoints/deployment.api.injectable";
import deploymentScaleDialogStateInjectable from "./dialog-state.injectable";
export interface DeploymentScaleDialogProps extends Partial<DialogProps> { export interface DeploymentScaleDialogProps extends Partial<DialogProps> {
deploymentApi: DeploymentApi;
} }
const dialogState = observable.box<Deployment | undefined>(); interface Dependencies {
deploymentApi: DeploymentApi;
state: IObservableValue<Deployment | undefined>;
}
@observer @observer
export class DeploymentScaleDialog extends Component<DeploymentScaleDialogProps> { class NonInjectedDeploymentScaleDialog extends Component<DeploymentScaleDialogProps & Dependencies> {
static defaultProps = {
deploymentApi,
};
@observable ready = false; @observable ready = false;
@observable currentReplicas = 0; @observable currentReplicas = 0;
@observable desiredReplicas = 0; @observable desiredReplicas = 0;
constructor(props: DeploymentScaleDialogProps) { constructor(props: DeploymentScaleDialogProps & Dependencies) {
super(props); super(props);
makeObservable(this); makeObservable(this);
} }
static open(deployment: Deployment) {
dialogState.set(deployment);
}
static close() {
dialogState.set(undefined);
}
close = () => { close = () => {
DeploymentScaleDialog.close(); this.props.state.set(undefined);
}; };
@computed get scaleMax() { @computed get scaleMax() {
@ -166,8 +159,8 @@ export class DeploymentScaleDialog extends Component<DeploymentScaleDialogProps>
} }
render() { render() {
const { className, ...dialogProps } = this.props; const { className, state, ...dialogProps } = this.props;
const deployment = dialogState.get(); const deployment = state.get();
return ( return (
<Dialog <Dialog
@ -183,3 +176,11 @@ export class DeploymentScaleDialog extends Component<DeploymentScaleDialogProps>
); );
} }
} }
export const DeploymentScaleDialog = withInjectables<Dependencies, DeploymentScaleDialogProps>(NonInjectedDeploymentScaleDialog, {
getProps: (di, props) => ({
...props,
deploymentApi: di.inject(deploymentApiInjectable),
state: di.inject(deploymentScaleDialogStateInjectable),
}),
});

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { Deployment } from "../../../../common/k8s-api/endpoints";
import deploymentScaleDialogStateInjectable from "./dialog-state.injectable";
export type OpenDeploymentScaleDialog = (obj: Deployment) => void;
const openDeploymentScaleDialogInjectable = getInjectable({
id: "open-deployment-scale-dialog",
instantiate: (di): OpenDeploymentScaleDialog => {
const state = di.inject(deploymentScaleDialogStateInjectable);
return obj => state.set(obj);
},
});
export default openDeploymentScaleDialogInjectable;

View File

@ -7,7 +7,7 @@ import type { IComputedValue } from "mobx";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Redirect } from "react-router"; import { Redirect } from "react-router";
import { ConfirmDialog } from "../../components/confirm-dialog"; import { ConfirmDialog } from "../../components/confirm-dialog";
import { DeploymentScaleDialog } from "../../components/+workloads-deployments/deployment-scale-dialog"; import { DeploymentScaleDialog } from "../../components/+workloads-deployments/scale/dialog";
import { CronJobTriggerDialog } from "../../components/+workloads-cronjobs/cronjob-trigger-dialog"; import { CronJobTriggerDialog } from "../../components/+workloads-cronjobs/cronjob-trigger-dialog";
import { StatefulSetScaleDialog } from "../../components/+workloads-statefulsets/dialog/dialog"; import { StatefulSetScaleDialog } from "../../components/+workloads-statefulsets/dialog/dialog";
import { ReplicaSetScaleDialog } from "../../components/+workloads-replicasets/scale-dialog/dialog"; import { ReplicaSetScaleDialog } from "../../components/+workloads-replicasets/scale-dialog/dialog";