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:
parent
90ec6809d7
commit
78a989a72a
@ -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",
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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>
|
||||
`;
|
||||
|
||||
@ -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(); });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
47
yarn.lock
47
yarn.lock
@ -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"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user