From 6c6d71941e1aaf18f53a93d42ca27fc4c63fe6cf Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 6 Apr 2021 17:19:44 -0400 Subject: [PATCH] some fixes to unit tests Signed-off-by: Sebastian Malton --- .../+extensions/__tests__/extensions.test.tsx | 76 ++++++++++--------- .../+extensions/extension-install.store.ts | 32 ++++---- src/renderer/components/button/button.tsx | 22 ++---- 3 files changed, 63 insertions(+), 67 deletions(-) diff --git a/src/renderer/components/+extensions/__tests__/extensions.test.tsx b/src/renderer/components/+extensions/__tests__/extensions.test.tsx index 88a1d99805..674a41595d 100644 --- a/src/renderer/components/+extensions/__tests__/extensions.test.tsx +++ b/src/renderer/components/+extensions/__tests__/extensions.test.tsx @@ -1,5 +1,5 @@ import "@testing-library/jest-dom/extend-expect"; -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { fireEvent, render, waitFor } from "@testing-library/react"; import fse from "fs-extra"; import React from "react"; import { extensionDiscovery } from "../../../../extensions/extension-discovery"; @@ -8,7 +8,9 @@ import { Notifications } from "../../notifications"; import { ExtensionInstallationStateStore } from "../extension-install.store"; import { Extensions } from "../extensions"; +jest.setTimeout(30000); jest.mock("fs-extra"); +jest.mock("../../notifications"); jest.mock("../../../../common/utils", () => ({ ...jest.requireActual("../../../../common/utils"), @@ -46,58 +48,56 @@ jest.mock("../../../../extensions/extension-loader", () => ({ } })); -jest.mock("../../notifications", () => ({ - ok: jest.fn(), - error: jest.fn(), - info: jest.fn() -})); - describe("Extensions", () => { beforeEach(() => { ExtensionInstallationStateStore.reset(); }); it("disables uninstall and disable buttons while uninstalling", async () => { - render(<>); + const res = render(<>); - expect(screen.getByText("Disable").closest("button")).not.toBeDisabled(); - expect(screen.getByText("Uninstall").closest("button")).not.toBeDisabled(); + expect(res.getByText("Disable").closest("button")).not.toBeDisabled(); + expect(res.getByText("Uninstall").closest("button")).not.toBeDisabled(); - fireEvent.click(screen.getByText("Uninstall")); + fireEvent.click(res.getByText("Uninstall")); // Approve confirm dialog - fireEvent.click(screen.getByText("Yes")); + fireEvent.click(res.getByText("Yes")); expect(extensionDiscovery.uninstallExtension).toHaveBeenCalled(); - expect(screen.getByText("Disable").closest("button")).toBeDisabled(); - expect(screen.getByText("Uninstall").closest("button")).toBeDisabled(); + expect(res.getByText("Disable").closest("button")).toBeDisabled(); + expect(res.getByText("Uninstall").closest("button")).toBeDisabled(); }); - it("displays error notification on uninstall error", () => { + it("displays error notification on uninstall error", async () => { (extensionDiscovery.uninstallExtension as any).mockImplementationOnce(() => Promise.reject() ); - render(<>); + const res = render(<>); - expect(screen.getByText("Disable").closest("button")).not.toBeDisabled(); - expect(screen.getByText("Uninstall").closest("button")).not.toBeDisabled(); + expect(res.getByText("Disable").closest("button")).not.toBeDisabled(); + expect(res.getByText("Uninstall").closest("button")).not.toBeDisabled(); - fireEvent.click(screen.getByText("Uninstall")); + fireEvent.click(res.getByText("Uninstall")); // Approve confirm dialog - fireEvent.click(screen.getByText("Yes")); + fireEvent.click(res.getByText("Yes")); - waitFor(() => { - expect(screen.getByText("Disable").closest("button")).not.toBeDisabled(); - expect(screen.getByText("Uninstall").closest("button")).not.toBeDisabled(); + await waitFor(() => { + expect(res.getByText("Disable").closest("button")).not.toBeDisabled(); + expect(res.getByText("Uninstall").closest("button")).not.toBeDisabled(); expect(Notifications.error).toHaveBeenCalledTimes(1); + }, { + timeout: 30000, }); }); - it("disables install button while installing", () => { - render(); + it("disables install button while installing", async () => { + const res = render(); - fireEvent.change(screen.getByPlaceholderText("Path or URL to an extension package", { + (fse.unlink as jest.MockedFunction).mockReturnValue(Promise.resolve() as any); + + fireEvent.change(res.getByPlaceholderText("Path or URL to an extension package", { exact: false }), { target: { @@ -105,25 +105,27 @@ describe("Extensions", () => { } }); - fireEvent.click(screen.getByText("Install")); + fireEvent.click(res.getByText("Install")); - waitFor(() => { - expect(screen.getByText("Install").closest("button")).toBeDisabled(); - expect(fse.move).toHaveBeenCalledWith(""); - expect(Notifications.error).not.toHaveBeenCalled(); + await waitFor(() => { + expect(res.getByText("Install").closest("button")).toBeDisabled(); + }, { + timeout: 10000, }); }); - it("displays spinner while extensions are loading", () => { + it("displays spinner while extensions are loading", async () => { extensionDiscovery.isLoaded = false; - const { container } = render(); + const res = render(); - expect(container.querySelector(".Spinner")).toBeInTheDocument(); + expect(res.container.querySelector(".Spinner")).toBeInTheDocument(); extensionDiscovery.isLoaded = true; - waitFor(() => - expect(container.querySelector(".Spinner")).not.toBeInTheDocument() - ); + await waitFor(() => { + expect(res.container.querySelector(".Spinner")).not.toBeInTheDocument(); + }, { + timeout: 30000, + }); }); }); diff --git a/src/renderer/components/+extensions/extension-install.store.ts b/src/renderer/components/+extensions/extension-install.store.ts index 4871529eeb..0d0bce1045 100644 --- a/src/renderer/components/+extensions/extension-install.store.ts +++ b/src/renderer/components/+extensions/extension-install.store.ts @@ -12,13 +12,13 @@ export enum ExtensionInstallationState { } const Prefix = "[ExtensionInstallationStore]"; -const installingExtensions = observable.set(); -const uninstallingExtensions = observable.set(); -const preInstallIds = observable.set(); export class ExtensionInstallationStateStore { private static InstallingFromMainChannel = "extension-installation-state-store:install"; private static ClearInstallingFromMainChannel = "extension-installation-state-store:clear-install"; + private static PreInstallIds = observable.set(); + private static UninstallingExtensions = observable.set(); + private static InstallingExtensions = observable.set(); static bindIpcListeners() { ipcRenderer @@ -32,9 +32,9 @@ export class ExtensionInstallationStateStore { @action static reset() { logger.warn(`${Prefix}: resetting, may throw errors`); - installingExtensions.clear(); - uninstallingExtensions.clear(); - preInstallIds.clear(); + ExtensionInstallationStateStore.InstallingExtensions.clear(); + ExtensionInstallationStateStore.UninstallingExtensions.clear(); + ExtensionInstallationStateStore.PreInstallIds.clear(); } /** @@ -51,7 +51,7 @@ export class ExtensionInstallationStateStore { throw new Error(`${Prefix}: cannot set ${extId} as installing. Is currently ${curState}.`); } - installingExtensions.add(extId); + ExtensionInstallationStateStore.InstallingExtensions.add(extId); } /** @@ -80,10 +80,10 @@ export class ExtensionInstallationStateStore { const preInstallStepId = uuid.v4(); logger.debug(`${Prefix}: starting a new preinstall phase: ${preInstallStepId}`); - preInstallIds.add(preInstallStepId); + ExtensionInstallationStateStore.PreInstallIds.add(preInstallStepId); return disposer(() => { - preInstallIds.delete(preInstallStepId); + ExtensionInstallationStateStore.PreInstallIds.delete(preInstallStepId); logger.debug(`${Prefix}: ending a preinstall phase: ${preInstallStepId}`); }); } @@ -102,7 +102,7 @@ export class ExtensionInstallationStateStore { throw new Error(`${Prefix}: cannot set ${extId} as uninstalling. Is currently ${curState}.`); } - uninstallingExtensions.add(extId); + ExtensionInstallationStateStore.UninstallingExtensions.add(extId); } /** @@ -117,7 +117,7 @@ export class ExtensionInstallationStateStore { switch (curState) { case ExtensionInstallationState.INSTALLING: - return void installingExtensions.delete(extId); + return void ExtensionInstallationStateStore.InstallingExtensions.delete(extId); default: throw new Error(`${Prefix}: cannot clear INSTALLING state for ${extId}, it is currently ${curState}`); } @@ -135,7 +135,7 @@ export class ExtensionInstallationStateStore { switch (curState) { case ExtensionInstallationState.UNINSTALLING: - return void uninstallingExtensions.delete(extId); + return void ExtensionInstallationStateStore.UninstallingExtensions.delete(extId); default: throw new Error(`${Prefix}: cannot clear UNINSTALLING state for ${extId}, it is currently ${curState}`); } @@ -146,11 +146,11 @@ export class ExtensionInstallationStateStore { * @param extId The ID of the extension */ static getInstallationState(extId: string): ExtensionInstallationState { - if (installingExtensions.has(extId)) { + if (ExtensionInstallationStateStore.InstallingExtensions.has(extId)) { return ExtensionInstallationState.INSTALLING; } - if (uninstallingExtensions.has(extId)) { + if (ExtensionInstallationStateStore.UninstallingExtensions.has(extId)) { return ExtensionInstallationState.UNINSTALLING; } @@ -185,7 +185,7 @@ export class ExtensionInstallationStateStore { * The current number of extensions installing */ @computed static get installing(): number { - return installingExtensions.size; + return ExtensionInstallationStateStore.InstallingExtensions.size; } /** @@ -199,7 +199,7 @@ export class ExtensionInstallationStateStore { * The current number of extensions preinstalling */ @computed static get preinstalling(): number { - return preInstallIds.size; + return ExtensionInstallationStateStore.PreInstallIds.size; } /** diff --git a/src/renderer/components/button/button.tsx b/src/renderer/components/button/button.tsx index 8bcb37bad4..da1d88d13f 100644 --- a/src/renderer/components/button/button.tsx +++ b/src/renderer/components/button/button.tsx @@ -1,5 +1,5 @@ import "./button.scss"; -import React, { ButtonHTMLAttributes, ReactNode } from "react"; +import React, { ButtonHTMLAttributes } from "react"; import { cssNames } from "../../utils"; import { TooltipDecoratorProps, withTooltip } from "../tooltip"; @@ -26,29 +26,22 @@ export class Button extends React.PureComponent { render() { const { - className, waiting, label, primary, accent, plain, hidden, active, big, - round, outlined, tooltip, light, children, ...props + waiting, label, primary, accent, plain, hidden, active, big, + round, outlined, tooltip, light, children, ...btnProps } = this.props; - const btnProps: Partial = props; if (hidden) return null; - btnProps.className = cssNames("Button", className, { + btnProps.className = cssNames("Button", btnProps.className, { waiting, primary, accent, plain, active, big, round, outlined, light, }); - const btnContent: ReactNode = ( - <> - {label} - {children} - - ); - // render as link if (this.props.href) { return ( this.link = e}> - {btnContent} + {label} + {children} ); } @@ -56,7 +49,8 @@ export class Button extends React.PureComponent { // render as button return ( ); }