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

fix statefulsets/dialog/dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-04-11 15:00:45 -04:00
parent 474b509199
commit 1683f482f1
14 changed files with 151 additions and 63 deletions

View File

@ -11,6 +11,7 @@ import podApiInjectable from "./pod.api.injectable";
import replicaSetApiInjectable from "./replica-set.api.injectable";
import roleApiInjectable from "./role.api.injectable";
import serviceAccountApiInjectable from "./service-account.api.injectable";
import statefulSetApiInjectable from "./stateful-set.api.injectable";
/**
* @deprecated use `di.inject(clusterRoleBindingApiInjectable)` instead
@ -46,3 +47,8 @@ export const daemonSetApi = asLegacyGlobalForExtensionApi(daemonSetApiInjectable
* @deprecated use `di.inject(replicaSetApiInjectable)` instead
*/
export const replicaSetApi = asLegacyGlobalForExtensionApi(replicaSetApiInjectable);
/**
* @deprecated use `di.inject(statefulSetApiInjectable)` instead
*/
export const statefulSetApi = asLegacyGlobalForExtensionApi(statefulSetApiInjectable);

View File

@ -0,0 +1,19 @@
/**
* 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 assert from "assert";
import { createStoresAndApisInjectionToken } from "../create-stores-apis.token";
import { StatefulSetApi } from "./stateful-set.api";
const statefulSetApiInjectable = getInjectable({
id: "stateful-set-api",
instantiate: (di) => {
assert(di.inject(createStoresAndApisInjectionToken), "statefulSetApi is only available in certain environments");
return new StatefulSetApi();
},
});
export default statefulSetApiInjectable;

View File

@ -7,7 +7,6 @@ import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api";
import { KubeApi } from "../kube-api";
import { metricsApi } from "./metrics.api";
import type { PodMetricData } from "./pod.api";
import { isClusterPageContext } from "../../utils/cluster-id-url-parsing";
import type { KubeObjectScope, LabelSelector } from "../kube-object";
import { KubeObject } from "../kube-object";
import type { PodTemplateSpec } from "./types/pod-template-spec";
@ -121,7 +120,3 @@ export class StatefulSet extends KubeObject<StatefulSetStatus, StatefulSetSpec,
return containers.map(container => container.image);
}
}
export const statefulSetApi = isClusterPageContext()
? new StatefulSetApi()
: undefined as never;

View File

@ -10,6 +10,7 @@ import podApiInjectable from "../../common/k8s-api/endpoints/pod.api.injectable"
import replicaSetApiInjectable from "../../common/k8s-api/endpoints/replica-set.api.injectable";
import roleApiInjectable from "../../common/k8s-api/endpoints/role.api.injectable";
import serviceAccountApiInjectable from "../../common/k8s-api/endpoints/service-account.api.injectable";
import statefulSetApiInjectable from "../../common/k8s-api/endpoints/stateful-set.api.injectable";
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
/**
@ -27,6 +28,7 @@ export const roleApi = asLegacyGlobalForExtensionApi(roleApiInjectable);
export const podsApi = asLegacyGlobalForExtensionApi(podApiInjectable);
export const daemonSetApi = asLegacyGlobalForExtensionApi(daemonSetApiInjectable);
export const replicaSetApi = asLegacyGlobalForExtensionApi(replicaSetApiInjectable);
export const statefulSetApi = asLegacyGlobalForExtensionApi(statefulSetApiInjectable);
export { ResourceStack } from "../../common/k8s/resource-stack";
export { apiManager } from "../../common/k8s-api/api-manager";
@ -37,7 +39,7 @@ export { Pod, PodApi as PodsApi } from "../../common/k8s-api/endpoints/pod.api";
export { Node, nodeApi as nodesApi, NodeApi as NodesApi } from "../../common/k8s-api/endpoints/nodes.api";
export { Deployment, deploymentApi, DeploymentApi } from "../../common/k8s-api/endpoints/deployment.api";
export { DaemonSet } from "../../common/k8s-api/endpoints/daemon-set.api";
export { StatefulSet, statefulSetApi } from "../../common/k8s-api/endpoints/stateful-set.api";
export { StatefulSet } from "../../common/k8s-api/endpoints/stateful-set.api";
export { Job, jobApi } from "../../common/k8s-api/endpoints/job.api";
export { CronJob, cronJobApi } from "../../common/k8s-api/endpoints/cron-job.api";
export { ConfigMap, configMapApi } from "../../common/k8s-api/endpoints/configmap.api";

View File

@ -15,6 +15,7 @@ import roleApiInjectable from "../../common/k8s-api/endpoints/role.api.injectabl
import podApiInjectable from "../../common/k8s-api/endpoints/pod.api.injectable";
import daemonSetApiInjectable from "../../common/k8s-api/endpoints/daemon-set.api.injectable";
import replicaSetApiInjectable from "../../common/k8s-api/endpoints/replica-set.api.injectable";
import statefulSetApiInjectable from "../../common/k8s-api/endpoints/stateful-set.api.injectable";
export function isAllowedResource(resource: KubeResource | KubeResource[]) {
const resources = castArray(resource);
@ -36,6 +37,7 @@ export const roleApi = asLegacyGlobalForExtensionApi(roleApiInjectable);
export const podsApi = asLegacyGlobalForExtensionApi(podApiInjectable);
export const daemonSetApi = asLegacyGlobalForExtensionApi(daemonSetApiInjectable);
export const replicaSetApi = asLegacyGlobalForExtensionApi(replicaSetApiInjectable);
export const statefulSetApi = asLegacyGlobalForExtensionApi(statefulSetApiInjectable);
export {
BaseKubeJsonApiObjectMetadata,
@ -49,7 +51,7 @@ export { Pod, PodApi as PodsApi } from "../../common/k8s-api/endpoints/pod.api";
export { Node, nodeApi as nodesApi, NodeApi as NodesApi } from "../../common/k8s-api/endpoints/nodes.api";
export { Deployment, deploymentApi, DeploymentApi } from "../../common/k8s-api/endpoints/deployment.api";
export { DaemonSet } from "../../common/k8s-api/endpoints/daemon-set.api";
export { StatefulSet, statefulSetApi } from "../../common/k8s-api/endpoints/stateful-set.api";
export { StatefulSet } from "../../common/k8s-api/endpoints/stateful-set.api";
export { Job, jobApi } from "../../common/k8s-api/endpoints/job.api";
export { CronJob, cronJobApi } from "../../common/k8s-api/endpoints/cron-job.api";
export { ConfigMap, configMapApi } from "../../common/k8s-api/endpoints/configmap.api";

View File

@ -5,10 +5,17 @@
import "@testing-library/jest-dom/extend-expect";
import { StatefulSet, StatefulSetApi } from "../../../common/k8s-api/endpoints";
import { StatefulSetScaleDialog } from "./statefulset-scale-dialog";
import { render, waitFor, fireEvent } from "@testing-library/react";
import type { StatefulSetApi } from "../../../../common/k8s-api/endpoints";
import { StatefulSet } from "../../../../common/k8s-api/endpoints";
import { StatefulSetScaleDialog } from "./dialog";
import { waitFor, fireEvent } from "@testing-library/react";
import React from "react";
import type { OpenStatefulSetDialog } from "./open.injectable";
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
import statefulSetApiInjectable from "../../../../common/k8s-api/endpoints/stateful-set.api.injectable";
import createStoresAndApisInjectable from "../../../create-stores-apis.injectable";
import openStatefulSetDialogInjectable from "./open.injectable";
import { type DiRender, renderFor } from "../../test-utils/renderFor";
const dummyStatefulSet = new StatefulSet({
apiVersion: "v1",
@ -81,15 +88,21 @@ const dummyStatefulSet = new StatefulSet({
describe("<StatefulSetScaleDialog />", () => {
let statefulSetApi: StatefulSetApi;
let openStatefulSetDialog: OpenStatefulSetDialog;
let render: DiRender;
beforeEach(() => {
statefulSetApi = new StatefulSetApi({
objectConstructor: StatefulSet,
});
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(createStoresAndApisInjectable, () => true);
render = renderFor(di);
statefulSetApi = di.inject(statefulSetApiInjectable);
openStatefulSetDialog = di.inject(openStatefulSetDialogInjectable);
});
it("renders w/o errors", () => {
const { container } = render(<StatefulSetScaleDialog statefulSetApi={statefulSetApi} />);
const { container } = render(<StatefulSetScaleDialog />);
expect(container).toBeInstanceOf(HTMLElement);
});
@ -100,9 +113,9 @@ describe("<StatefulSetScaleDialog />", () => {
const initReplicas = 1;
statefulSetApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas);
const { getByTestId } = render(<StatefulSetScaleDialog statefulSetApi={statefulSetApi} />);
const { getByTestId } = render(<StatefulSetScaleDialog />);
StatefulSetScaleDialog.open(dummyStatefulSet);
openStatefulSetDialog(dummyStatefulSet);
// we need to wait for the StatefulSetScaleDialog to show up
// because there is an <Animate /> in <Dialog /> which renders null at start.
await waitFor(async () => {
@ -120,9 +133,9 @@ describe("<StatefulSetScaleDialog />", () => {
const initReplicas = 1;
statefulSetApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas);
const component = render(<StatefulSetScaleDialog statefulSetApi={statefulSetApi} />);
const component = render(<StatefulSetScaleDialog />);
StatefulSetScaleDialog.open(dummyStatefulSet);
openStatefulSetDialog(dummyStatefulSet);
await waitFor(async () => {
expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas}`);
expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`);

View File

@ -3,51 +3,45 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import "./statefulset-scale-dialog.scss";
import "./dialog.scss";
import type { StatefulSet, StatefulSetApi } from "../../../common/k8s-api/endpoints";
import { statefulSetApi } from "../../../common/k8s-api/endpoints";
import type { StatefulSet, StatefulSetApi } from "../../../../common/k8s-api/endpoints";
import React, { Component } from "react";
import type { IObservableValue } from "mobx";
import { computed, makeObservable, observable } from "mobx";
import { observer } from "mobx-react";
import type { DialogProps } from "../dialog";
import { Dialog } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
import { Icon } from "../icon";
import { Slider } from "../slider";
import { Notifications } from "../notifications";
import { cssNames } from "../../utils";
import type { DialogProps } from "../../dialog";
import { Dialog } from "../../dialog";
import { Wizard, WizardStep } from "../../wizard";
import { Icon } from "../../icon";
import { Slider } from "../../slider";
import { Notifications } from "../../notifications";
import { cssNames } from "../../../utils";
import { withInjectables } from "@ogre-tools/injectable-react";
import statefulSetApiInjectable from "../../../../common/k8s-api/endpoints/stateful-set.api.injectable";
import statefulSetDialogStateInjectable from "./state.injectable";
export interface StatefulSetScaleDialogProps extends Partial<DialogProps> {
statefulSetApi: StatefulSetApi;
}
const dialogState = observable.box<StatefulSet | undefined>();
interface Dependencies {
statefulSetApi: StatefulSetApi;
state: IObservableValue<StatefulSet | undefined>;
}
@observer
export class StatefulSetScaleDialog extends Component<StatefulSetScaleDialogProps> {
static defaultProps = {
statefulSetApi,
};
class NonInjectedStatefulSetScaleDialog extends Component<StatefulSetScaleDialogProps & Dependencies> {
@observable ready = false;
@observable currentReplicas = 0;
@observable desiredReplicas = 0;
constructor(props: StatefulSetScaleDialogProps) {
constructor(props: StatefulSetScaleDialogProps & Dependencies) {
super(props);
makeObservable(this);
}
static open(statefulSet: StatefulSet) {
dialogState.set(statefulSet);
}
static close() {
dialogState.set(undefined);
}
close = () => {
StatefulSetScaleDialog.close();
this.props.state.set(undefined);
};
onOpen = async (statefulSet: StatefulSet) => {
@ -161,8 +155,8 @@ export class StatefulSetScaleDialog extends Component<StatefulSetScaleDialogProp
}
render() {
const { className, ...dialogProps } = this.props;
const statefulSet = dialogState.get();
const { className, state, ...dialogProps } = this.props;
const statefulSet = state.get();
return (
<Dialog
@ -178,3 +172,11 @@ export class StatefulSetScaleDialog extends Component<StatefulSetScaleDialogProp
);
}
}
export const StatefulSetScaleDialog = withInjectables<Dependencies, StatefulSetScaleDialogProps>(NonInjectedStatefulSetScaleDialog, {
getProps: (di, props) => ({
...props,
statefulSetApi: di.inject(statefulSetApiInjectable),
state: di.inject(statefulSetDialogStateInjectable),
}),
});

View File

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

View File

@ -0,0 +1,15 @@
/**
* 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 { StatefulSet } from "../../../../common/k8s-api/endpoints";
const statefulSetDialogStateInjectable = getInjectable({
id: "stateful-set-dialog-state",
instantiate: () => observable.box<StatefulSet | undefined>(),
});
export default statefulSetDialogStateInjectable;

View File

@ -6,22 +6,33 @@ import React from "react";
import type { KubeObjectMenuProps } from "../kube-object-menu";
import type { StatefulSet } from "../../../common/k8s-api/endpoints";
import { MenuItem } from "../menu";
import { StatefulSetScaleDialog } from "./statefulset-scale-dialog";
import { Icon } from "../icon";
import type { OpenStatefulSetDialog } from "./dialog/open.injectable";
import { withInjectables } from "@ogre-tools/injectable-react";
import openStatefulSetDialogInjectable from "./dialog/open.injectable";
export function StatefulSetMenu(props: KubeObjectMenuProps<StatefulSet>) {
const { object, toolbar } = props;
export interface StatefulSetMenuProps extends KubeObjectMenuProps<StatefulSet> {}
return (
<>
<MenuItem onClick={() => StatefulSetScaleDialog.open(object)}>
<Icon
material="open_with"
tooltip="Scale"
interactive={toolbar}
/>
<span className="title">Scale</span>
</MenuItem>
</>
);
interface Dependencies {
openStatefulSetDialog: OpenStatefulSetDialog;
}
const NonInjectedStatefulSetMenu = ({ object, toolbar, openStatefulSetDialog }: Dependencies & StatefulSetMenuProps) => (
<>
<MenuItem onClick={() => openStatefulSetDialog(object)}>
<Icon
material="open_with"
tooltip="Scale"
interactive={toolbar}
/>
<span className="title">Scale</span>
</MenuItem>
</>
);
export const StatefulSetMenu = withInjectables<Dependencies, StatefulSetMenuProps>(NonInjectedStatefulSetMenu, {
getProps: (di, props) => ({
...props,
openStatefulSetDialog: di.inject(openStatefulSetDialogInjectable),
}),
});

View File

@ -19,6 +19,7 @@ export interface MainLayoutProps {
sidebar: React.ReactNode;
className?: string;
footer?: React.ReactNode;
children?: React.ReactNode | React.ReactNode[];
}
/**

View File

@ -18,7 +18,6 @@ export interface WizardCommonProps<D> {
reset?: () => void;
done?: () => void;
hideSteps?: boolean;
children?: React.ReactElement<WizardStepProps<D>>[] | React.ReactElement<WizardStepProps<D>>;
}
export interface WizardProps<D> extends WizardCommonProps<D> {
@ -27,6 +26,7 @@ export interface WizardProps<D> extends WizardCommonProps<D> {
title?: string;
header?: React.ReactNode;
onChange?: (step: number) => void;
children?: React.ReactElement<WizardStepProps<D>>[] | React.ReactElement<WizardStepProps<D>>;
}
interface State {
@ -128,6 +128,7 @@ export interface WizardStepProps<D> extends WizardCommonProps<D> {
noValidate?: boolean; // no validate form attribute
skip?: boolean; // don't render the step
scrollable?: boolean;
children?: React.ReactNode | React.ReactNode[];
}
interface WizardStepState {

View File

@ -9,7 +9,7 @@ import { Redirect } from "react-router";
import { ConfirmDialog } from "../../components/confirm-dialog";
import { DeploymentScaleDialog } from "../../components/+workloads-deployments/deployment-scale-dialog";
import { CronJobTriggerDialog } from "../../components/+workloads-cronjobs/cronjob-trigger-dialog";
import { StatefulSetScaleDialog } from "../../components/+workloads-statefulsets/statefulset-scale-dialog";
import { StatefulSetScaleDialog } from "../../components/+workloads-statefulsets/dialog/dialog";
import { ReplicaSetScaleDialog } from "../../components/+workloads-replicasets/replicaset-scale-dialog";
import { CommandContainer } from "../../components/command-palette/command-container";
import { ErrorBoundary } from "../../components/error-boundary";