From bd174edfb3a4a2975a8a8182367c54b6d4f96eb3 Mon Sep 17 00:00:00 2001 From: Huw Wilkins Date: Mon, 2 Dec 2024 15:22:55 +1100 Subject: [PATCH] chore(tests): add SidePanel tests --- ui/src/components/Loader/Loader.tsx | 5 +- ui/src/components/Loader/index.ts | 1 + ui/src/components/Loader/test-types.ts | 3 + .../components/SidePanel/SidePanel.test.tsx | 135 ++++++++++++++++++ ui/src/components/SidePanel/SidePanel.tsx | 31 ++-- ui/src/components/SidePanel/types.ts | 4 + ui/src/test/utils.tsx | 3 + 7 files changed, 165 insertions(+), 17 deletions(-) create mode 100644 ui/src/components/Loader/test-types.ts create mode 100644 ui/src/components/SidePanel/SidePanel.test.tsx create mode 100644 ui/src/components/SidePanel/types.ts diff --git a/ui/src/components/Loader/Loader.tsx b/ui/src/components/Loader/Loader.tsx index e1a43ab63..3420a1428 100644 --- a/ui/src/components/Loader/Loader.tsx +++ b/ui/src/components/Loader/Loader.tsx @@ -1,13 +1,16 @@ import { FC } from "react"; import { Spinner } from "@canonical/react-components"; +import { LoaderTestId } from "./index"; +import { testId } from "test/utils"; + interface Props { text?: string; } const Loader: FC = ({ text = "Loading..." }) => { return ( -
+
); diff --git a/ui/src/components/Loader/index.ts b/ui/src/components/Loader/index.ts index a9737ca27..24b88a5a6 100644 --- a/ui/src/components/Loader/index.ts +++ b/ui/src/components/Loader/index.ts @@ -1 +1,2 @@ export { default } from "./Loader"; +export { TestId as LoaderTestId } from "./test-types"; diff --git a/ui/src/components/Loader/test-types.ts b/ui/src/components/Loader/test-types.ts new file mode 100644 index 000000000..bffa73f39 --- /dev/null +++ b/ui/src/components/Loader/test-types.ts @@ -0,0 +1,3 @@ +export enum TestId { + COMPONENT = "Loader", +} diff --git a/ui/src/components/SidePanel/SidePanel.test.tsx b/ui/src/components/SidePanel/SidePanel.test.tsx new file mode 100644 index 000000000..a5ce94a1f --- /dev/null +++ b/ui/src/components/SidePanel/SidePanel.test.tsx @@ -0,0 +1,135 @@ +import { screen, within } from "@testing-library/dom"; +import { render } from "@testing-library/react"; + +import SidePanel from "./SidePanel"; +import { Label } from "./types"; +import { LoaderTestId } from "components/Loader"; + +describe("SidePanel", () => { + test("displays content", () => { + const content = "Content"; + render({content}); + expect( + screen.getByRole("complementary", { + name: Label.SIDE_PANEL, + }), + ).toHaveTextContent(content); + }); + + test("can display as normal width", () => { + render(Content); + const sidePanel = screen.getByRole("complementary", { + name: Label.SIDE_PANEL, + }); + expect(sidePanel).not.toHaveClass("is-wide"); + expect(sidePanel).not.toHaveClass("is-narrow"); + }); + + test("can display as wide", () => { + render(Content); + expect( + screen.getByRole("complementary", { + name: Label.SIDE_PANEL, + }), + ).toHaveClass("is-wide"); + }); + + test("can display as narrow", () => { + render(Content); + expect( + screen.getByRole("complementary", { + name: Label.SIDE_PANEL, + }), + ).toHaveClass("is-narrow"); + }); + + test("can display as split", () => { + render(Content); + expect( + screen.getByRole("complementary", { + name: Label.SIDE_PANEL, + }), + ).toHaveClass("is-split"); + }); + + test("can display as an overlay", () => { + render(Content); + expect( + screen.getByRole("complementary", { + name: Label.SIDE_PANEL, + }), + ).toHaveClass("is-overlay"); + }); + + test("can display as pinned", () => { + render(Content); + expect( + screen.getByRole("complementary", { + name: Label.SIDE_PANEL, + }), + ).toHaveClass("is-pinned"); + }); + + test("can display loading state", () => { + render(Content); + expect( + within( + screen.getByRole("complementary", { + name: Label.SIDE_PANEL, + }), + ).getByTestId(LoaderTestId.COMPONENT), + ).toBeInTheDocument(); + }); + + test("can display loading error", () => { + render(Content); + expect( + within( + screen.getByRole("complementary", { + name: Label.SIDE_PANEL, + }), + ).getByText(Label.ERROR_LOADING), + ).toBeInTheDocument(); + }); +}); + +test("HeaderControls", () => { + const content = "Content"; + render({content}); + expect(screen.getByText(content)).toHaveClass("p-panel__controls"); +}); + +test("HeaderTitle", () => { + const content = "Content"; + render({content}); + expect(screen.getByText(content)).toHaveClass("p-panel__title"); +}); +test("Sticky", () => { + const content = "Content"; + render({content}); + expect(screen.getByText(content)).toHaveClass("sticky-wrapper"); +}); + +test("Header", () => { + const content = "Content"; + render({content}); + expect(screen.getByText(content)).toHaveClass("p-panel__header"); +}); + +test("Container", () => { + const content = "Content"; + render({content}); + expect(screen.getByText(content).parentElement).toHaveClass("p-panel"); +}); + +test("Content", () => { + const content = "Content"; + render({content}); + expect(screen.getByText(content)).toHaveClass("p-panel__content"); +}); + +test("Footer", () => { + const content = "Content"; + render({content}); + expect(screen.getByText(content)).toHaveClass("panel-footer"); +}); diff --git a/ui/src/components/SidePanel/SidePanel.tsx b/ui/src/components/SidePanel/SidePanel.tsx index af295afe9..911ac0230 100644 --- a/ui/src/components/SidePanel/SidePanel.tsx +++ b/ui/src/components/SidePanel/SidePanel.tsx @@ -1,7 +1,8 @@ import { FC, PropsWithChildren, ReactNode } from "react"; import Loader from "components/Loader"; import classnames from "classnames"; -import { AppAside, Panel, Spinner } from "@canonical/react-components"; +import { AppAside, Panel } from "@canonical/react-components"; +import { Label } from "./types"; interface CommonProps { className?: string; @@ -78,8 +79,8 @@ interface SidePanelProps { isOverlay?: boolean; isSplit?: boolean; children: ReactNode; - loading: boolean; - hasError: boolean; + loading?: boolean; + hasError?: boolean; className?: string; width?: "narrow" | "wide"; pinned?: boolean; @@ -90,11 +91,19 @@ const SidePanelComponent: FC = ({ isOverlay, isSplit = false, loading = false, - hasError, + hasError = false, className, width, pinned, }) => { + let content: ReactNode = null; + if (loading) { + content = ; + } else if (hasError) { + content = Label.ERROR_LOADING; + } else { + content = children; + } return ( = ({ "is-split": isSplit, "is-overlay": isOverlay, })} - aria-label="Side panel" + aria-label={Label.SIDE_PANEL} pinned={pinned} > - {loading ? ( -
- -
- ) : ( - <> - {loading && } - {!loading && hasError && <>Loading failed} - {!hasError && children} - - )} + {content}
); }; diff --git a/ui/src/components/SidePanel/types.ts b/ui/src/components/SidePanel/types.ts new file mode 100644 index 000000000..0c6aa3b11 --- /dev/null +++ b/ui/src/components/SidePanel/types.ts @@ -0,0 +1,4 @@ +export enum Label { + ERROR_LOADING = "Loading failed", + SIDE_PANEL = "Side panel", +} diff --git a/ui/src/test/utils.tsx b/ui/src/test/utils.tsx index bc86fcb6c..1ba112376 100644 --- a/ui/src/test/utils.tsx +++ b/ui/src/test/utils.tsx @@ -64,3 +64,6 @@ export const renderWrappedHook = ( }); return { changeURL, result, queryClient }; }; + +export const testId = (testId: string) => + import.meta.env.PROD ? undefined : { "data-testid": testId };