diff --git a/src/common/__tests__/cluster-store.test.ts b/src/common/__tests__/cluster-store.test.ts index f0e03b1b12..8c319d3f97 100644 --- a/src/common/__tests__/cluster-store.test.ts +++ b/src/common/__tests__/cluster-store.test.ts @@ -7,8 +7,6 @@ import { workspaceStore } from "../workspace-store"; const testDataIcon = fs.readFileSync("test-data/cluster-store-migration-icon.png"); -console.log(""); // fix bug - let clusterStore: ClusterStore; describe("empty config", () => { diff --git a/src/common/utils/defineGlobal.ts b/src/common/utils/defineGlobal.ts index 1a4c5993d9..d8883bb38d 100755 --- a/src/common/utils/defineGlobal.ts +++ b/src/common/utils/defineGlobal.ts @@ -4,9 +4,10 @@ export function defineGlobal(propName: string, descriptor: PropertyDescriptor) { const scope = typeof global !== "undefined" ? global : window; + if (scope.hasOwnProperty(propName)) { - console.info(`Global variable "${propName}" already exists. Skipping.`); return; } + Object.defineProperty(scope, propName, descriptor); } diff --git a/src/renderer/components/+extensions/__tests__/extensions.test.tsx b/src/renderer/components/+extensions/__tests__/extensions.test.tsx index ce85139a76..b5a036ad88 100644 --- a/src/renderer/components/+extensions/__tests__/extensions.test.tsx +++ b/src/renderer/components/+extensions/__tests__/extensions.test.tsx @@ -2,13 +2,15 @@ import '@testing-library/jest-dom/extend-expect'; import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import { extensionDiscovery } from "../../../../extensions/extension-discovery"; +import { ConfirmDialog } from "../../confirm-dialog"; +import { Notifications } from "../../notifications"; import { Extensions } from "../extensions"; jest.mock("../../../../extensions/extension-discovery", () => ({ ...jest.requireActual("../../../../extensions/extension-discovery"), extensionDiscovery: { localFolderPath: "/fake/path", - uninstallExtension: jest.fn() + uninstallExtension: jest.fn(() => Promise.resolve()) } })); @@ -31,14 +33,47 @@ jest.mock("../../../../extensions/extension-loader", () => ({ } })); +jest.mock("../../notifications", () => ({ + ok: jest.fn(), + error: jest.fn(), + info: jest.fn() +})); + describe("Extensions", () => { - it("disables uninstall and disable buttons while uninstalling", () => { - render(); + it("disables uninstall and disable buttons while uninstalling", async () => { + render(<>); + + expect(screen.getByText("Disable").closest("button")).not.toBeDisabled(); + expect(screen.getByText("Uninstall").closest("button")).not.toBeDisabled(); fireEvent.click(screen.getByText("Uninstall")); + + // Approve confirm dialog + fireEvent.click(screen.getByText("Yes")); expect(extensionDiscovery.uninstallExtension).toHaveBeenCalledWith("/absolute/path"); expect(screen.getByText("Disable").closest("button")).toBeDisabled(); expect(screen.getByText("Uninstall").closest("button")).toBeDisabled(); }); + + it("displays error notification on uninstall error", () => { + (extensionDiscovery.uninstallExtension as any).mockImplementationOnce(() => + Promise.reject() + ); + render(<>); + + expect(screen.getByText("Disable").closest("button")).not.toBeDisabled(); + expect(screen.getByText("Uninstall").closest("button")).not.toBeDisabled(); + + fireEvent.click(screen.getByText("Uninstall")); + + // Approve confirm dialog + fireEvent.click(screen.getByText("Yes")); + + setTimeout(() => { + expect(screen.getByText("Disable").closest("button")).not.toBeDisabled(); + expect(screen.getByText("Uninstall").closest("button")).not.toBeDisabled(); + expect(Notifications.error).toHaveBeenCalledTimes(1); + }, 100); + }); }); diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx index 4b2a1d6dd7..327dccee99 100644 --- a/src/renderer/components/+extensions/extensions.tsx +++ b/src/renderer/components/+extensions/extensions.tsx @@ -1,8 +1,7 @@ import { t, Trans } from "@lingui/macro"; import { remote, shell } from "electron"; import fse from "fs-extra"; -import { map, omit } from "lodash"; -import { computed, observable, ObservableMap, reaction } from "mobx"; +import { computed, observable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import os from "os"; import path from "path"; @@ -16,6 +15,7 @@ import logger from "../../../main/logger"; import { _i18n } from "../../i18n"; import { prevDefault } from "../../utils"; import { Button } from "../button"; +import { ConfirmDialog } from "../confirm-dialog"; import { Icon } from "../icon"; import { DropFileInput, Input, InputValidator, InputValidators, SearchInput } from "../input"; import { PageLayout } from "../layout/page-layout"; @@ -314,6 +314,17 @@ export class Extensions extends React.Component { } } + confirmUninstallExtension = (extension: InstalledExtension) => { + const displayName = extensionDisplayName(extension.manifest.name, extension.manifest.version); + + ConfirmDialog.open({ + message:

Are you sure you want to uninstall extension {displayName}?

, + labelOk: Yes, + labelCancel: No, + ok: () => this.uninstallExtension(extension) + }); + }; + async uninstallExtension(extension: InstalledExtension) { const displayName = extensionDisplayName(extension.manifest.name, extension.manifest.version); @@ -348,8 +359,8 @@ export class Extensions extends React.Component { ); } - return extensions.map(ext => { - const { id, isEnabled, manifest } = ext; + return extensions.map(extension => { + const { id, isEnabled, manifest } = extension; const { name, description } = manifest; const isUninstalling = this.extensionState.get(id)?.state === "uninstalling"; @@ -365,13 +376,17 @@ export class Extensions extends React.Component {
{!isEnabled && ( - + )} {isEnabled && ( - + )} -