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

Implement first meaningful unit tests for a complete behavior

Note: in this case, the behavior is even asynchronous, and thus a tool "asyncFn" is used to keep the test as simple as possible.

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>
Signed-off-by: Mikko Aspiala <mikko.aspiala@gmail.com>
This commit is contained in:
Mikko Aspiala 2021-11-05 10:56:05 +02:00 committed by Janne Savolainen
parent 90ec6809d7
commit 78a989a72a
8 changed files with 235 additions and 72 deletions

View File

@ -260,6 +260,7 @@
"ws": "^7.5.5"
},
"devDependencies": {
"@async-fn/jest": "^1.4.2",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",

View File

@ -140,6 +140,7 @@ export class ConfirmDialog extends React.Component<ConfirmDialogProps> {
isOpen={dialogState.isOpen}
onClose={this.onClose}
close={this.close}
{...(dialogState.isOpen ? { "data-testid":"confirmation-dialog" } : {})}
>
<div className="confirm-content">
{icon} {message}
@ -158,6 +159,7 @@ export class ConfirmDialog extends React.Component<ConfirmDialogProps> {
label={labelOk}
onClick={prevDefault(this.ok)}
waiting={this.isSaving}
data-testid="confirm"
{...okButtonProps}
/>
</div>

View File

@ -41,6 +41,7 @@ export interface DialogProps {
modal?: boolean;
pinned?: boolean;
animated?: boolean;
"data-testid"?: string;
}
interface DialogState {
@ -149,7 +150,7 @@ export class Dialog extends React.PureComponent<DialogProps, DialogState> {
};
render() {
const { modal, animated, pinned } = this.props;
const { modal, animated, pinned, "data-testid": testId } = this.props;
let { className } = this.props;
className = cssNames("Dialog flex center", className, { modal, pinned });
@ -158,6 +159,7 @@ export class Dialog extends React.PureComponent<DialogProps, DialogState> {
className={className}
onClick={stopPropagation}
ref={this.ref}
data-testid={testId}
>
<div className="box" ref={e => this.contentElem = e}>
{this.props.children}

View File

@ -1,15 +1,146 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`kube-object-menu given kube object, renders 1`] = `
exports[`kube-object-menu given kube object renders 1`] = `
<body>
<div>
<ul
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
<div>
<ul
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
>
<div>
Some menu item
</div>
<li
class="MenuItem"
data-menu-action-remove-test="true"
data-testid="menu-action-remove"
tabindex="0"
>
<i
class="Icon material interactive focusable"
id="tooltip_target_3"
tabindex="0"
>
<span
class="icon"
data-icon-name="delete"
>
delete
</span>
<div />
</i>
<span
class="title"
>
Remove
</span>
</li>
</ul>
</div>
</div>
<div
class="Animate opacity-scale Dialog flex center ConfirmDialog modal"
/>
<div
class="Tooltip narrow invisible formatter"
>
Delete
</div>
</body>
`;
exports[`kube-object-menu given kube object when removing kube object renders 1`] = `
<body>
<div>
<div>
<ul
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
>
<div>
Some menu item
</div>
<li
class="MenuItem"
data-menu-action-remove-test="true"
data-testid="menu-action-remove"
tabindex="0"
>
<i
class="Icon material interactive focusable"
id="tooltip_target_7"
tabindex="0"
>
<span
class="icon"
data-icon-name="delete"
>
delete
</span>
<div />
</i>
<span
class="title"
>
Remove
</span>
</li>
</ul>
</div>
</div>
<div
class="Animate opacity-scale Dialog flex center ConfirmDialog modal"
data-testid="confirmation-dialog"
>
<div
class="box"
>
<div>
Some menu item
<div
class="confirm-content"
>
<i
class="Icon material focusable big"
>
<span
class="icon"
data-icon-name="warning"
>
warning
</span>
</i>
<div>
<p>
Remove
<b>
Some cluster name/some-kind/some-name
</b>
?
</p>
</div>
</div>
</ul>
<div
class="confirm-buttons"
>
<button
class="Button cancel plain"
type="button"
>
Cancel
</button>
<button
class="Button ok primary"
data-testid="confirm"
type="button"
>
Remove
</button>
</div>
</div>
</div>
<div
class="Tooltip narrow invisible formatter"
>
Delete
</div>
</body>
`;

View File

@ -24,6 +24,7 @@ import { render } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import { KubeObjectMenu } from "./kube-object-menu";
import { KubeObject } from "../../../common/k8s-api/kube-object";
import userEvent from "@testing-library/user-event";
import type { KubeApi } from "../../../common/k8s-api/kube-api";
import type {
IHasGettableItemsForKind,
@ -32,6 +33,8 @@ import type {
import type { IGettableStore } from "../../../common/k8s-api/api-manager";
import type { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import type { IHasName } from "../../../main/cluster";
import { ConfirmDialog } from "../confirm-dialog";
import asyncFn from "@async-fn/jest";
describe("kube-object-menu", () => {
let hideDetailsStub: () => void;
@ -64,7 +67,7 @@ describe("kube-object-menu", () => {
const MenuItemComponentStub: React.FC = () => <div>Some menu item</div>;
const menuItemStub: KubeObjectMenuRegistration = {
const dynamicMenuItemStub: KubeObjectMenuRegistration = {
apiVersions: ["irrelevant"],
components: { MenuItem: MenuItemComponentStub },
kind: "irrelevant",
@ -73,7 +76,7 @@ describe("kube-object-menu", () => {
kubeObjectMenuRegistryStub = {
// eslint-disable-next-line unused-imports/no-unused-vars-ts
getItemsForKind: (kind: string, apiVersion: string): any => [
menuItemStub,
dynamicMenuItemStub,
],
};
@ -100,29 +103,86 @@ describe("kube-object-menu", () => {
expect(baseElement).toMatchSnapshot();
});
it("given kube object, renders", () => {
objectStub = KubeObject.create({
apiVersion: "some-api-version",
kind: "some-kind",
metadata: {
uid: "some-uid",
name: "some-name",
resourceVersion: "some-resource-version",
},
describe("given kube object", () => {
let baseElement: Element;
let removeActionMock: any;
let getByTestId: (arg0: string) => any;
let queryByTestId: (arg0: string) => any;
beforeEach(async () => {
objectStub = KubeObject.create({
apiVersion: "some-api-version",
kind: "some-kind",
metadata: {
uid: "some-uid",
name: "some-name",
resourceVersion: "some-resource-version",
},
});
removeActionMock = asyncFn();
({ baseElement, getByTestId, queryByTestId } = render(
<div>
<ConfirmDialog/>
<KubeObjectMenu
object={objectStub}
apiManager={apiManagerStub}
cluster={clusterStub}
kubeObjectMenuRegistry={kubeObjectMenuRegistryStub}
hideDetails={hideDetailsStub}
editResourceTab={editResourceTabStub}
toolbar={true}
removeAction={removeActionMock}
/>
</div>,
));
});
const { baseElement } = render(
<KubeObjectMenu
object={objectStub}
apiManager={apiManagerStub}
cluster={clusterStub}
kubeObjectMenuRegistry={kubeObjectMenuRegistryStub}
hideDetails={hideDetailsStub}
editResourceTab={editResourceTabStub}
toolbar={true}
/>,
);
it("renders", () => {
expect(baseElement).toMatchSnapshot();
});
expect(baseElement).toMatchSnapshot();
it("does not open a confirmation dialog yet", () => {
expect(queryByTestId("confirmation-dialog")).toBeNull();
});
describe("when removing kube object", () => {
beforeEach(() => {
const menuItem = getByTestId("menu-action-remove");
userEvent.click(menuItem);
});
it("renders", () => {
expect(baseElement).toMatchSnapshot();
});
it("opens a confirmation dialog", () => {
getByTestId("confirmation-dialog");
});
describe("when remove is confirmed", () => {
beforeEach(() => {
const confirmRemovalButton = getByTestId("confirm");
userEvent.click(confirmRemovalButton);
});
it("calls for removal of the kube object", () => {
expect(removeActionMock).toHaveBeenCalledWith();
});
it("does not close the confirmation dialog yet", () => {
getByTestId("confirmation-dialog");
});
it("when removal resolves, closes the confirmation dialog", async () => {
await removeActionMock.resolve();
expect(queryByTestId("confirmation-dialog")).toBeNull(); });
});
});
});
});

View File

@ -137,7 +137,7 @@ export class KubeObjectMenu<
}: { components: { MenuItem: React.ReactType<any> }},
index: number,
) => (
<MenuItem
<MenuItem
object={object}
toolbar={toolbar}
// TODO: Fix misuse of index in key

View File

@ -144,7 +144,7 @@ export class MenuActions extends React.Component<MenuActionsProps> {
</MenuItem>
)}
{removeAction && (
<MenuItem onClick={this.remove}>
<MenuItem onClick={this.remove} data-menu-action-remove-test data-testid="menu-action-remove">
<Icon material="delete" interactive={toolbar} tooltip="Delete"/>
<span className="title">Delete</span>
</MenuItem>

View File

@ -7,6 +7,11 @@
resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.1.1.tgz#9274ec7460652f9c632c59addf24efb1684ef876"
integrity sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==
"@async-fn/jest@^1.4.2":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@async-fn/jest/-/jest-1.4.2.tgz#61b3e35aa1e7398675915b0c6f4967f0c26e0488"
integrity sha512-1o34++KnmxnW510+WNlEFjEIZHBhDWnxVmIs97z4z/3nyTLxSw7mz7tF2bhTdiPH0ahJlGpdaWzwqlBJopM06Q==
"@babel/code-frame@7.12.11":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
@ -14,7 +19,7 @@
dependencies:
"@babel/highlight" "^7.10.4"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.8.3":
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==
@ -1532,7 +1537,7 @@
"@types/parse5" "*"
"@types/tough-cookie" "*"
"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6":
"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6":
version "7.0.7"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
@ -6102,23 +6107,6 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
fork-ts-checker-webpack-plugin@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz#79326d869797906fa8b24e2abcf9421fc805450d"
integrity sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==
dependencies:
"@babel/code-frame" "^7.8.3"
"@types/json-schema" "^7.0.5"
chalk "^4.1.0"
cosmiconfig "^6.0.0"
deepmerge "^4.2.2"
fs-extra "^9.0.0"
memfs "^3.1.2"
minimatch "^3.0.4"
schema-utils "2.7.0"
semver "^7.3.2"
tapable "^1.0.0"
form-data@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
@ -6235,11 +6223,6 @@ fs-minipass@^2.0.0:
dependencies:
minipass "^3.0.0"
fs-monkey@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3"
integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==
fs-vacuum@^1.2.10, fs-vacuum@~1.2.10:
version "1.2.10"
resolved "https://registry.yarnpkg.com/fs-vacuum/-/fs-vacuum-1.2.10.tgz#b7629bec07a4031a2548fdf99f5ecf1cc8b31e36"
@ -9243,13 +9226,6 @@ mem@^1.1.0:
dependencies:
mimic-fn "^1.0.0"
memfs@^3.1.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.2.2.tgz#5de461389d596e3f23d48bb7c2afb6161f4df40e"
integrity sha512-RE0CwmIM3CEvpcdK3rZ19BC4E6hv9kADkMN5rPduRak58cNArWLi/9jFLsa4rhsjfVxMP3v0jO7FHXq7SvFY5Q==
dependencies:
fs-monkey "1.0.3"
"memoize-one@>=3.1.1 <6", memoize-one@^5.0.0, memoize-one@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
@ -12203,15 +12179,6 @@ scheduler@^0.20.2:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==
dependencies:
"@types/json-schema" "^7.0.4"
ajv "^6.12.2"
ajv-keywords "^3.4.1"
schema-utils@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"