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 && (
-
+
)}
-