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 { fireEvent, waitFor } from "@testing-library/react";
|
||||
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import fse from "fs-extra";
|
||||
import React from "react";
|
||||
import { UserStore } from "../../../../common/user-store";
|
||||
@ -97,43 +97,43 @@ describe("Extensions", () => {
|
||||
it("disables uninstall and disable buttons while uninstalling", async () => {
|
||||
extensionDiscovery.isLoaded = true;
|
||||
|
||||
const res = render((
|
||||
render((
|
||||
<>
|
||||
<Extensions />
|
||||
<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);
|
||||
|
||||
fireEvent.click(menuTrigger);
|
||||
|
||||
expect(res.getByText("Disable")).toHaveAttribute("aria-disabled", "false");
|
||||
expect(res.getByText("Uninstall")).toHaveAttribute("aria-disabled", "false");
|
||||
expect(await screen.findByText("Disable")).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
|
||||
fireEvent.click(res.getByText("Yes"));
|
||||
fireEvent.click(await screen.findByText("Yes"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(extensionDiscovery.uninstallExtension).toHaveBeenCalled();
|
||||
fireEvent.click(menuTrigger);
|
||||
expect(res.getByText("Disable")).toHaveAttribute("aria-disabled", "true");
|
||||
expect(res.getByText("Uninstall")).toHaveAttribute("aria-disabled", "true");
|
||||
expect(screen.getByText("Disable")).toHaveAttribute("aria-disabled", "true");
|
||||
expect(screen.getByText("Uninstall")).toHaveAttribute("aria-disabled", "true");
|
||||
}, {
|
||||
timeout: 30000,
|
||||
});
|
||||
});
|
||||
|
||||
it("disables install button while installing", async () => {
|
||||
const res = render(<Extensions />);
|
||||
render(<Extensions />);
|
||||
|
||||
(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,
|
||||
}), {
|
||||
target: {
|
||||
@ -141,8 +141,8 @@ describe("Extensions", () => {
|
||||
},
|
||||
});
|
||||
|
||||
fireEvent.click(res.getByText("Install"));
|
||||
expect(res.getByText("Install").closest("button")).toBeDisabled();
|
||||
fireEvent.click(await screen.findByText("Install"));
|
||||
expect((await screen.findByText("Install")).closest("button")).toBeDisabled();
|
||||
});
|
||||
|
||||
it("displays spinner while extensions are loading", () => {
|
||||
|
||||
@ -81,63 +81,61 @@ const NonInjectedInstalledExtensions = observer(({ extensionDiscovery, extension
|
||||
);
|
||||
|
||||
const data = useMemo(
|
||||
() => {
|
||||
return extensions.map(extension => {
|
||||
const { id, isEnabled, isCompatible, manifest } = extension;
|
||||
const { name, description, version } = manifest;
|
||||
const isUninstalling = extensionInstallationStateStore.isExtensionUninstalling(id);
|
||||
() => extensions.map(extension => {
|
||||
const { id, isEnabled, isCompatible, manifest } = extension;
|
||||
const { name, description, version } = manifest;
|
||||
const isUninstalling = extensionInstallationStateStore.isExtensionUninstalling(id);
|
||||
|
||||
return {
|
||||
extension: (
|
||||
<div className={"flex items-start"}>
|
||||
<div>
|
||||
<div className={styles.extensionName}>{name}</div>
|
||||
<div className={styles.extensionDescription}>{description}</div>
|
||||
</div>
|
||||
return {
|
||||
extension: (
|
||||
<div className={"flex items-start"}>
|
||||
<div>
|
||||
<div className={styles.extensionName}>{name}</div>
|
||||
<div className={styles.extensionDescription}>{description}</div>
|
||||
</div>
|
||||
),
|
||||
version,
|
||||
status: (
|
||||
<div className={cssNames({ [styles.enabled]: isEnabled, [styles.invalid]: !isCompatible })}>
|
||||
{getStatus(extension)}
|
||||
</div>
|
||||
),
|
||||
actions: (
|
||||
<MenuActions usePortal toolbar={false}>
|
||||
{ isCompatible && (
|
||||
<>
|
||||
{isEnabled ? (
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => disable(id)}
|
||||
>
|
||||
<Icon material="unpublished"/>
|
||||
<span className="title" aria-disabled={isUninstalling}>Disable</span>
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => enable(id)}
|
||||
>
|
||||
<Icon material="check_circle"/>
|
||||
<span className="title" aria-disabled={isUninstalling}>Enable</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
version,
|
||||
status: (
|
||||
<div className={cssNames({ [styles.enabled]: isEnabled, [styles.invalid]: !isCompatible })}>
|
||||
{getStatus(extension)}
|
||||
</div>
|
||||
),
|
||||
actions: (
|
||||
<MenuActions usePortal toolbar={false}>
|
||||
{isCompatible && (
|
||||
<>
|
||||
{isEnabled ? (
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => disable(id)}
|
||||
>
|
||||
<Icon material="unpublished" />
|
||||
<span className="title" aria-disabled={isUninstalling}>Disable</span>
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => enable(id)}
|
||||
>
|
||||
<Icon material="check_circle" />
|
||||
<span className="title" aria-disabled={isUninstalling}>Enable</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => uninstall(extension)}
|
||||
>
|
||||
<Icon material="delete"/>
|
||||
<span className="title" aria-disabled={isUninstalling}>Uninstall</span>
|
||||
</MenuItem>
|
||||
</MenuActions>
|
||||
),
|
||||
};
|
||||
});
|
||||
}, [extensions, extensionInstallationStateStore.anyUninstalling],
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => uninstall(extension)}
|
||||
>
|
||||
<Icon material="delete" />
|
||||
<span className="title" aria-disabled={isUninstalling}>Uninstall</span>
|
||||
</MenuItem>
|
||||
</MenuActions>
|
||||
),
|
||||
};
|
||||
}), [extensions, extensionInstallationStateStore.anyUninstalling],
|
||||
);
|
||||
|
||||
if (!extensionDiscovery.isLoaded) {
|
||||
|
||||
@ -10,11 +10,11 @@ import { useFlexLayout, useSortBy, useTable } from "react-table";
|
||||
import { Icon } from "../icon";
|
||||
import { cssNames } from "../../utils";
|
||||
|
||||
export interface ReactTableProps extends UseTableOptions<any> {
|
||||
export interface ReactTableProps<Data extends object> extends UseTableOptions<Data> {
|
||||
headless?: boolean;
|
||||
}
|
||||
|
||||
export function ReactTable({ columns, data, headless }: ReactTableProps) {
|
||||
export function ReactTable<Data extends object>({ columns, data, headless }: ReactTableProps<Data>) {
|
||||
const defaultColumn = useMemo(
|
||||
() => ({
|
||||
minWidth: 20,
|
||||
@ -40,11 +40,11 @@ export function ReactTable({ columns, data, headless }: ReactTableProps) {
|
||||
);
|
||||
|
||||
const renderRow = useCallback(
|
||||
(row: Row<object>) => {
|
||||
(row: Row<Data>) => {
|
||||
prepareRow(row);
|
||||
|
||||
return (
|
||||
<div className={styles.tr}>
|
||||
<div className={styles.tr} key={row.id}>
|
||||
{row.cells.map((cell, index) => (
|
||||
<div
|
||||
{...cell.getCellProps()}
|
||||
|
||||
@ -5,22 +5,27 @@
|
||||
|
||||
// Helper for combining css classes inside components
|
||||
|
||||
export type IClassName = string | string[] | IClassNameMap | undefined | false;
|
||||
export type IClassNameMap = Record<string, any>;
|
||||
export type IgnoredClassNames = number | symbol | Function;
|
||||
|
||||
export function cssNames(...args: IClassName[]): string {
|
||||
const map: IClassNameMap = {};
|
||||
export type IClassName = string | string[] | IClassNameMap | undefined | null | false | IgnoredClassNames;
|
||||
export type IClassNameMap = Record<string, unknown>;
|
||||
|
||||
args.forEach(className => {
|
||||
if (typeof className === "string" || Array.isArray(className)) {
|
||||
[className].flat().forEach(name => map[name] = true);
|
||||
export function cssNames(...classNames: IClassName[]): string {
|
||||
const classNamesEnabled: IClassNameMap = {};
|
||||
|
||||
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)
|
||||
.map(([className]) => className.trim())
|
||||
.join(" ");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user