From 5eadf0245bb4d3a4234194156c4a74f2f89c9808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Thu, 21 Nov 2024 18:04:42 +0000 Subject: [PATCH] test: Coverage for `EditorNavMenu` --- .../src/components/EditorNavMenu.test.tsx | 205 ++++++++++++++++++ .../src/components/EditorNavMenu.tsx | 18 +- 2 files changed, 215 insertions(+), 8 deletions(-) create mode 100644 editor.planx.uk/src/components/EditorNavMenu.test.tsx diff --git a/editor.planx.uk/src/components/EditorNavMenu.test.tsx b/editor.planx.uk/src/components/EditorNavMenu.test.tsx new file mode 100644 index 0000000000..6b83c7355f --- /dev/null +++ b/editor.planx.uk/src/components/EditorNavMenu.test.tsx @@ -0,0 +1,205 @@ +import { within } from "@testing-library/react"; +import { useStore } from "pages/FlowEditor/lib/store"; +import React from "react"; +import * as ReactNavi from "react-navi"; +import { setup } from "testUtils"; +import { Mocked, vi } from "vitest"; + +import EditorNavMenu from "./EditorNavMenu"; + +vi.mock("react-navi", () => ({ + useCurrentRoute: vi.fn(), + useNavigation: () => ({ navigate: vi.fn() }), + // Mock completed loading process + useLoadingRoute: () => undefined, +})); + +const mockNavi = ReactNavi as Mocked; + +let mockTeamName: string | undefined = undefined; +let mockFlowName: string | undefined = undefined; +let mockAnalyticsLink: string | undefined = undefined; +const mockGetUserRoleForCurrentTeam = vi.fn(); + +vi.mock("pages/FlowEditor/lib/store", async () => ({ + useStore: vi.fn(() => [ + mockTeamName, + mockFlowName, + mockAnalyticsLink, + mockGetUserRoleForCurrentTeam(), + ]), +})); + +describe("globalLayoutRoutes", () => { + beforeEach(() => { + mockNavi.useCurrentRoute.mockReturnValue({ + url: { href: "/" }, + } as ReturnType); + }); + + it("does not display for teamEditors", () => { + mockGetUserRoleForCurrentTeam.mockReturnValue("teamEditor"); + + const { queryAllByRole } = setup(); + const menuItems = queryAllByRole("listitem"); + expect(menuItems).toHaveLength(0); + }); + + it("displays for platformAdmins", () => { + mockGetUserRoleForCurrentTeam.mockReturnValue("platformAdmin"); + + const { getAllByRole } = setup(); + const menuItems = getAllByRole("listitem"); + expect(menuItems).toHaveLength(3); + expect(within(menuItems[0]).getByText("Select a team")).toBeInTheDocument(); + }); +}); + +describe("teamLayoutRoutes", () => { + beforeEach(() => { + mockNavi.useCurrentRoute.mockReturnValue({ + url: { href: "/test-team" }, + } as ReturnType); + mockTeamName = "test-team"; + }); + + it("does not display for teamViewers", () => { + mockGetUserRoleForCurrentTeam.mockReturnValue("teamViewer"); + + const { queryAllByRole } = setup(); + const menuItems = queryAllByRole("listitem"); + expect(menuItems).toHaveLength(0); + }); + + it("displays for teamEditors", () => { + mockGetUserRoleForCurrentTeam.mockReturnValue("teamEditor"); + + const { getAllByRole } = setup(); + const menuItems = getAllByRole("listitem"); + expect(menuItems).toHaveLength(4); + expect(within(menuItems[0]).getByText("Services")).toBeInTheDocument(); + }); + + it("displays for platformAdmins", () => { + mockGetUserRoleForCurrentTeam.mockReturnValue("platformAdmin"); + + const { getAllByRole } = setup(); + const menuItems = getAllByRole("listitem"); + expect(menuItems).toHaveLength(4); + expect(within(menuItems[0]).getByText("Services")).toBeInTheDocument(); + }); +}); + +describe("flowLayoutRoutes", () => { + beforeEach(() => { + mockNavi.useCurrentRoute.mockReturnValue({ + url: { href: "/test-team/test-flow" }, + } as ReturnType); + mockTeamName = "test-team"; + mockFlowName = "test-flow"; + }); + + it("does not display for teamViewers", () => { + mockGetUserRoleForCurrentTeam.mockReturnValue("teamViewer"); + + const { queryAllByRole } = setup(); + const menuItems = queryAllByRole("listitem"); + expect(menuItems).toHaveLength(0); + }); + + it("displays for teamEditors", () => { + mockGetUserRoleForCurrentTeam.mockReturnValue("teamEditor"); + + const { getAllByRole, getByLabelText } = setup(); + const menuItems = getAllByRole("listitem"); + expect(menuItems).toHaveLength(5); + expect(getByLabelText("Submissions log")).toBeInTheDocument(); + }); + + it("displays for platformAdmins", () => { + mockGetUserRoleForCurrentTeam.mockReturnValue("platformAdmin"); + + const { getAllByRole, getByLabelText } = setup(); + const menuItems = getAllByRole("listitem"); + expect(menuItems).toHaveLength(5); + expect(getByLabelText("Submissions log")).toBeInTheDocument(); + }); +}); + +describe("flowAnalyticsRoute", () => { + beforeEach(() => { + mockNavi.useCurrentRoute.mockReturnValue({ + url: { href: "/test-team/test-flow" }, + } as ReturnType); + mockTeamName = "test-team"; + mockFlowName = "test-flow"; + mockGetUserRoleForCurrentTeam.mockReturnValue("teamEditor"); + }); + + it("is disabled without an analytics link", () => { + const { getByRole } = setup(); + expect(getByRole("button", { name: /Analytics/ })).toBeDisabled(); + }); + + it("is enabled with an analytics link", () => { + mockAnalyticsLink = "https://link-to-metabase"; + + const { getByRole } = setup(); + expect(getByRole("button", { name: /Analytics/ })).not.toBeDisabled(); + }); +}); + +describe("layout", () => { + it("displays in a full mode on global routes", () => { + mockNavi.useCurrentRoute.mockReturnValue({ + url: { href: "/" }, + } as ReturnType); + mockGetUserRoleForCurrentTeam.mockReturnValue("platformAdmin"); + + const { queryAllByRole, queryByLabelText } = setup(); + const menuItems = queryAllByRole("listitem"); + + // Tooltip not present + expect(queryByLabelText("Select a team")).not.toBeInTheDocument(); + + // Full text present + expect(within(menuItems[0]).getByText("Select a team")).toBeInTheDocument(); + }); + + it("displays in a full mode on team routes", () => { + mockNavi.useCurrentRoute.mockReturnValue({ + url: { href: "/test-team" }, + } as ReturnType); + mockGetUserRoleForCurrentTeam.mockReturnValue("platformAdmin"); + mockTeamName = "test-team"; + + const { queryAllByRole, queryByLabelText } = setup(); + const menuItems = queryAllByRole("listitem"); + + // Tooltip not present + expect(queryByLabelText("Services")).not.toBeInTheDocument(); + + // Full text present + expect(within(menuItems[0]).getByText("Services")).toBeInTheDocument(); + }); + + it("displays in a compact mode on flow routes", () => { + mockNavi.useCurrentRoute.mockReturnValue({ + url: { href: "/test-team/test-flow" }, + } as ReturnType); + mockGetUserRoleForCurrentTeam.mockReturnValue("platformAdmin"); + mockTeamName = "test-team"; + mockFlowName = "test-flow"; + + const { queryAllByRole, getByLabelText } = setup(); + const menuItems = queryAllByRole("listitem"); + + // Tooltip present + expect(getByLabelText("Submissions log")).toBeInTheDocument(); + + // Full text present + expect( + within(menuItems[0]).queryByText("Submissions log"), + ).not.toBeInTheDocument(); + }); +}); diff --git a/editor.planx.uk/src/components/EditorNavMenu.tsx b/editor.planx.uk/src/components/EditorNavMenu.tsx index 05ad74e1a5..ff7d56fb3b 100644 --- a/editor.planx.uk/src/components/EditorNavMenu.tsx +++ b/editor.planx.uk/src/components/EditorNavMenu.tsx @@ -95,13 +95,12 @@ function EditorNavMenu() { const { navigate } = useNavigation(); const { url } = useCurrentRoute(); const isRouteLoading = useLoadingRoute(); - const [teamSlug, flowSlug, flowAnalyticsLink, role] = - useStore((state) => [ - state.teamSlug, - state.flowSlug, - state.flowAnalyticsLink, - state.getUserRoleForCurrentTeam() - ]); + const [teamSlug, flowSlug, flowAnalyticsLink, role] = useStore((state) => [ + state.teamSlug, + state.flowSlug, + state.flowAnalyticsLink, + state.getUserRoleForCurrentTeam(), + ]); const isActive = (route: string) => url.href.endsWith(route); @@ -242,7 +241,9 @@ function EditorNavMenu() { const { routes, compact } = getRoutesForUrl(url.href); - const visibleRoutes = routes.filter(({ accessibleBy }) => role && accessibleBy.includes(role)); + const visibleRoutes = routes.filter( + ({ accessibleBy }) => role && accessibleBy.includes(role), + ); // Hide menu if the user does not have a selection of items if (visibleRoutes.length < 2) return null; @@ -256,6 +257,7 @@ function EditorNavMenu() {