mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
extensions.test.tsx
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
955aa28bc8
commit
3c8b1d9a7b
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import "@testing-library/jest-dom/extend-expect";
|
import "@testing-library/jest-dom/extend-expect";
|
||||||
import { fireEvent, waitFor } from "@testing-library/react";
|
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
||||||
import fse from "fs-extra";
|
import fse from "fs-extra";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { UserStore } from "../../../../common/user-store";
|
import { UserStore } from "../../../../common/user-store";
|
||||||
@ -97,43 +97,43 @@ describe("Extensions", () => {
|
|||||||
it("disables uninstall and disable buttons while uninstalling", async () => {
|
it("disables uninstall and disable buttons while uninstalling", async () => {
|
||||||
extensionDiscovery.isLoaded = true;
|
extensionDiscovery.isLoaded = true;
|
||||||
|
|
||||||
const res = render((
|
render((
|
||||||
<>
|
<>
|
||||||
<Extensions />
|
<Extensions />
|
||||||
<ConfirmDialog />
|
<ConfirmDialog />
|
||||||
</>
|
</>
|
||||||
));
|
));
|
||||||
const table = res.getByTestId("extensions-table");
|
|
||||||
const menuTrigger = table.querySelector("div[role=row]:first-of-type .actions .Icon");
|
const table = await screen.findByTestId("extensions-table");
|
||||||
|
const menuTrigger = table.querySelector(".table div[role='rowgroup'] .actions .Icon");
|
||||||
|
|
||||||
assert(menuTrigger);
|
assert(menuTrigger);
|
||||||
|
|
||||||
fireEvent.click(menuTrigger);
|
fireEvent.click(menuTrigger);
|
||||||
|
|
||||||
expect(res.getByText("Disable")).toHaveAttribute("aria-disabled", "false");
|
expect(await screen.findByText("Disable")).toHaveAttribute("aria-disabled", "false");
|
||||||
expect(res.getByText("Uninstall")).toHaveAttribute("aria-disabled", "false");
|
expect(await screen.findByText("Uninstall")).toHaveAttribute("aria-disabled", "false");
|
||||||
|
|
||||||
fireEvent.click(res.getByText("Uninstall"));
|
fireEvent.click(await screen.findByText("Uninstall"));
|
||||||
|
|
||||||
// Approve confirm dialog
|
// Approve confirm dialog
|
||||||
fireEvent.click(res.getByText("Yes"));
|
fireEvent.click(await screen.findByText("Yes"));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(extensionDiscovery.uninstallExtension).toHaveBeenCalled();
|
expect(extensionDiscovery.uninstallExtension).toHaveBeenCalled();
|
||||||
fireEvent.click(menuTrigger);
|
fireEvent.click(menuTrigger);
|
||||||
expect(res.getByText("Disable")).toHaveAttribute("aria-disabled", "true");
|
expect(screen.getByText("Disable")).toHaveAttribute("aria-disabled", "true");
|
||||||
expect(res.getByText("Uninstall")).toHaveAttribute("aria-disabled", "true");
|
expect(screen.getByText("Uninstall")).toHaveAttribute("aria-disabled", "true");
|
||||||
}, {
|
}, {
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("disables install button while installing", async () => {
|
it("disables install button while installing", async () => {
|
||||||
const res = render(<Extensions />);
|
render(<Extensions />);
|
||||||
|
|
||||||
(fse.unlink as jest.MockedFunction<typeof fse.unlink>).mockReturnValue(Promise.resolve());
|
(fse.unlink as jest.MockedFunction<typeof fse.unlink>).mockReturnValue(Promise.resolve());
|
||||||
|
|
||||||
fireEvent.change(res.getByPlaceholderText("File path or URL", {
|
fireEvent.change(await screen.findByPlaceholderText("File path or URL", {
|
||||||
exact: false,
|
exact: false,
|
||||||
}), {
|
}), {
|
||||||
target: {
|
target: {
|
||||||
@ -141,8 +141,8 @@ describe("Extensions", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
fireEvent.click(res.getByText("Install"));
|
fireEvent.click(await screen.findByText("Install"));
|
||||||
expect(res.getByText("Install").closest("button")).toBeDisabled();
|
expect((await screen.findByText("Install")).closest("button")).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays spinner while extensions are loading", () => {
|
it("displays spinner while extensions are loading", () => {
|
||||||
|
|||||||
@ -81,63 +81,61 @@ const NonInjectedInstalledExtensions = observer(({ extensionDiscovery, extension
|
|||||||
);
|
);
|
||||||
|
|
||||||
const data = useMemo(
|
const data = useMemo(
|
||||||
() => {
|
() => extensions.map(extension => {
|
||||||
return extensions.map(extension => {
|
const { id, isEnabled, isCompatible, manifest } = extension;
|
||||||
const { id, isEnabled, isCompatible, manifest } = extension;
|
const { name, description, version } = manifest;
|
||||||
const { name, description, version } = manifest;
|
const isUninstalling = extensionInstallationStateStore.isExtensionUninstalling(id);
|
||||||
const isUninstalling = extensionInstallationStateStore.isExtensionUninstalling(id);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
extension: (
|
extension: (
|
||||||
<div className={"flex items-start"}>
|
<div className={"flex items-start"}>
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.extensionName}>{name}</div>
|
<div className={styles.extensionName}>{name}</div>
|
||||||
<div className={styles.extensionDescription}>{description}</div>
|
<div className={styles.extensionDescription}>{description}</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
),
|
</div>
|
||||||
version,
|
),
|
||||||
status: (
|
version,
|
||||||
<div className={cssNames({ [styles.enabled]: isEnabled, [styles.invalid]: !isCompatible })}>
|
status: (
|
||||||
{getStatus(extension)}
|
<div className={cssNames({ [styles.enabled]: isEnabled, [styles.invalid]: !isCompatible })}>
|
||||||
</div>
|
{getStatus(extension)}
|
||||||
),
|
</div>
|
||||||
actions: (
|
),
|
||||||
<MenuActions usePortal toolbar={false}>
|
actions: (
|
||||||
{ isCompatible && (
|
<MenuActions usePortal toolbar={false}>
|
||||||
<>
|
{isCompatible && (
|
||||||
{isEnabled ? (
|
<>
|
||||||
<MenuItem
|
{isEnabled ? (
|
||||||
disabled={isUninstalling}
|
<MenuItem
|
||||||
onClick={() => disable(id)}
|
disabled={isUninstalling}
|
||||||
>
|
onClick={() => disable(id)}
|
||||||
<Icon material="unpublished"/>
|
>
|
||||||
<span className="title" aria-disabled={isUninstalling}>Disable</span>
|
<Icon material="unpublished" />
|
||||||
</MenuItem>
|
<span className="title" aria-disabled={isUninstalling}>Disable</span>
|
||||||
) : (
|
</MenuItem>
|
||||||
<MenuItem
|
) : (
|
||||||
disabled={isUninstalling}
|
<MenuItem
|
||||||
onClick={() => enable(id)}
|
disabled={isUninstalling}
|
||||||
>
|
onClick={() => enable(id)}
|
||||||
<Icon material="check_circle"/>
|
>
|
||||||
<span className="title" aria-disabled={isUninstalling}>Enable</span>
|
<Icon material="check_circle" />
|
||||||
</MenuItem>
|
<span className="title" aria-disabled={isUninstalling}>Enable</span>
|
||||||
)}
|
</MenuItem>
|
||||||
</>
|
)}
|
||||||
)}
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
disabled={isUninstalling}
|
disabled={isUninstalling}
|
||||||
onClick={() => uninstall(extension)}
|
onClick={() => uninstall(extension)}
|
||||||
>
|
>
|
||||||
<Icon material="delete"/>
|
<Icon material="delete" />
|
||||||
<span className="title" aria-disabled={isUninstalling}>Uninstall</span>
|
<span className="title" aria-disabled={isUninstalling}>Uninstall</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuActions>
|
</MenuActions>
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
});
|
}), [extensions, extensionInstallationStateStore.anyUninstalling],
|
||||||
}, [extensions, extensionInstallationStateStore.anyUninstalling],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!extensionDiscovery.isLoaded) {
|
if (!extensionDiscovery.isLoaded) {
|
||||||
|
|||||||
@ -10,11 +10,11 @@ import { useFlexLayout, useSortBy, useTable } from "react-table";
|
|||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
|
|
||||||
export interface ReactTableProps extends UseTableOptions<any> {
|
export interface ReactTableProps<Data extends object> extends UseTableOptions<Data> {
|
||||||
headless?: boolean;
|
headless?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReactTable({ columns, data, headless }: ReactTableProps) {
|
export function ReactTable<Data extends object>({ columns, data, headless }: ReactTableProps<Data>) {
|
||||||
const defaultColumn = useMemo(
|
const defaultColumn = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
minWidth: 20,
|
minWidth: 20,
|
||||||
@ -40,11 +40,11 @@ export function ReactTable({ columns, data, headless }: ReactTableProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const renderRow = useCallback(
|
const renderRow = useCallback(
|
||||||
(row: Row<object>) => {
|
(row: Row<Data>) => {
|
||||||
prepareRow(row);
|
prepareRow(row);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.tr}>
|
<div className={styles.tr} key={row.id}>
|
||||||
{row.cells.map((cell, index) => (
|
{row.cells.map((cell, index) => (
|
||||||
<div
|
<div
|
||||||
{...cell.getCellProps()}
|
{...cell.getCellProps()}
|
||||||
|
|||||||
@ -5,22 +5,27 @@
|
|||||||
|
|
||||||
// Helper for combining css classes inside components
|
// Helper for combining css classes inside components
|
||||||
|
|
||||||
export type IClassName = string | string[] | IClassNameMap | undefined | false;
|
export type IgnoredClassNames = number | symbol | Function;
|
||||||
export type IClassNameMap = Record<string, any>;
|
|
||||||
|
|
||||||
export function cssNames(...args: IClassName[]): string {
|
export type IClassName = string | string[] | IClassNameMap | undefined | null | false | IgnoredClassNames;
|
||||||
const map: IClassNameMap = {};
|
export type IClassNameMap = Record<string, unknown>;
|
||||||
|
|
||||||
args.forEach(className => {
|
export function cssNames(...classNames: IClassName[]): string {
|
||||||
if (typeof className === "string" || Array.isArray(className)) {
|
const classNamesEnabled: IClassNameMap = {};
|
||||||
[className].flat().forEach(name => map[name] = true);
|
|
||||||
|
for (const className of classNames) {
|
||||||
|
if (typeof className === "string") {
|
||||||
|
classNamesEnabled[className] = true;
|
||||||
|
} else if (Array.isArray(className)) {
|
||||||
|
for (const name of className) {
|
||||||
|
classNamesEnabled[name] = true;
|
||||||
|
}
|
||||||
|
} else if (className && typeof className === "object") {
|
||||||
|
Object.assign(classNamesEnabled, className);
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
Object.assign(map, className);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Object.entries(map)
|
return Object.entries(classNamesEnabled)
|
||||||
.filter(([, isActive]) => !!isActive)
|
.filter(([, isActive]) => !!isActive)
|
||||||
.map(([className]) => className.trim())
|
.map(([className]) => className.trim())
|
||||||
.join(" ");
|
.join(" ");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user