diff --git a/src/components/AppBar/NavigationButtons.tsx b/src/components/AppBar/NavigationButtons.tsx index 5017200c9a..6f9a25d9ed 100644 --- a/src/components/AppBar/NavigationButtons.tsx +++ b/src/components/AppBar/NavigationButtons.tsx @@ -1,15 +1,19 @@ import { PlaylistAdd, Rule } from "@mui/icons-material"; import { Button, Hidden, Tooltip, Typography } from "@mui/material"; -import { ReactElement } from "react"; +import { ReactElement, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; +import { Permission } from "api/models"; +import { getCurrentPermissions } from "backend"; import { TabProps, appBarHeight, buttonMinHeight, tabColor, } from "components/AppBar/AppBarTypes"; +import { StoreState } from "types"; +import { useAppSelector } from "types/hooks"; import { Path } from "types/path"; import { useWindowSize } from "utilities/useWindowSize"; @@ -20,6 +24,20 @@ const navButtonMaxWidthProportion = 0.2; /** Buttons for navigating to Data Entry and Data Cleanup */ export default function NavigationButtons(props: TabProps): ReactElement { + const projectId = useAppSelector( + (state: StoreState) => state.currentProjectState.project.id + ); + const [hasGoalPermission, setHasGoalPermission] = useState(false); + + useEffect(() => { + getCurrentPermissions().then((perms) => { + setHasGoalPermission( + perms.includes(Permission.CharacterInventory) || + perms.includes(Permission.MergeAndReviewEntries) + ); + }); + }, [projectId]); + return ( <> - } - targetPath={Path.Goals} - textId="appBar.dataCleanup" - /> + {hasGoalPermission && ( + } + targetPath={Path.Goals} + textId="appBar.dataCleanup" + /> + )} ); } diff --git a/src/components/AppBar/tests/NavigationButtons.test.tsx b/src/components/AppBar/tests/NavigationButtons.test.tsx index 285205752e..2136106f80 100644 --- a/src/components/AppBar/tests/NavigationButtons.test.tsx +++ b/src/components/AppBar/tests/NavigationButtons.test.tsx @@ -1,7 +1,10 @@ +import { Provider } from "react-redux"; import renderer, { ReactTestInstance } from "react-test-renderer"; +import configureMockStore from "redux-mock-store"; import "tests/reactI18nextMock"; +import { Permission } from "api"; import NavigationButtons, { dataCleanupButtonId, dataEntryButtonId, @@ -13,38 +16,98 @@ jest.mock("react-router-dom", () => ({ useNavigate: jest.fn(), })); +jest.mock("backend", () => ({ + getCurrentPermissions: () => mockGetCurrentPermissions(), +})); + +const mockGetCurrentPermissions = jest.fn(); +const mockStore = configureMockStore()({ + currentProjectState: { project: { id: "" } }, +}); + let testRenderer: renderer.ReactTestRenderer; -let entryButton: ReactTestInstance | undefined; -let cleanButton: ReactTestInstance | undefined; +let entryButton: ReactTestInstance; +let cleanButton: ReactTestInstance; -const renderNavButtons = (path: Path): void => { - renderer.act(() => { - testRenderer = renderer.create(); +const renderNavButtons = async ( + path: Path, + permission = Permission.MergeAndReviewEntries +): Promise => { + mockGetCurrentPermissions.mockResolvedValue([permission]); + await renderer.act(async () => { + testRenderer = renderer.create( + + + + ); }); +}; + +const renderNavButtonsWithPath = async (path: Path): Promise => { + await renderNavButtons(path, Permission.MergeAndReviewEntries); entryButton = testRenderer.root.findByProps({ id: dataEntryButtonId }); cleanButton = testRenderer.root.findByProps({ id: dataCleanupButtonId }); }; +const renderNavButtonsWithPermission = async ( + perm: Permission +): Promise => { + await renderNavButtons(Path.DataEntry, perm); + entryButton = testRenderer.root.findByProps({ id: dataEntryButtonId }); + const cleanupButtons = testRenderer.root.findAllByProps({ + id: dataCleanupButtonId, + }); + if (cleanupButtons.length) { + cleanButton = cleanupButtons[0]; + } +}; + +beforeEach(() => { + jest.resetAllMocks(); +}); + describe("NavigationButtons", () => { - it("highlights the correct tab", () => { - renderNavButtons(Path.Statistics); - expect(entryButton?.props.style.background).toEqual(themeColors.lightShade); - expect(cleanButton?.props.style.background).toEqual(themeColors.lightShade); + it("only shows the data cleanup tab for the correct permissions", async () => { + for (const perm of Object.values(Permission)) { + await renderNavButtonsWithPermission(perm); + if ( + perm === Permission.CharacterInventory || + perm === Permission.MergeAndReviewEntries + ) { + expect(cleanButton).toBeTruthy; + } else { + expect(cleanButton).toBeUndefined; + } + } + }); - renderNavButtons(Path.DataEntry); + it("highlights the correct tab", async () => { + await renderNavButtonsWithPath(Path.DataEntry); expect(entryButton?.props.style.background).toEqual(themeColors.darkShade); expect(cleanButton?.props.style.background).toEqual(themeColors.lightShade); - renderNavButtons(Path.Goals); + await renderNavButtonsWithPath(Path.Goals); expect(entryButton?.props.style.background).toEqual(themeColors.lightShade); expect(cleanButton?.props.style.background).toEqual(themeColors.darkShade); - renderNavButtons(Path.GoalCurrent); + await renderNavButtonsWithPath(Path.GoalCurrent); expect(entryButton?.props.style.background).toEqual(themeColors.lightShade); expect(cleanButton?.props.style.background).toEqual(themeColors.darkShade); - renderNavButtons(Path.GoalNext); + await renderNavButtonsWithPath(Path.GoalNext); expect(entryButton?.props.style.background).toEqual(themeColors.lightShade); expect(cleanButton?.props.style.background).toEqual(themeColors.darkShade); + + await renderNavButtonsWithPath(Path.ProjSettings); + expect(entryButton?.props.style.background).toEqual(themeColors.lightShade); + expect(cleanButton?.props.style.background).toEqual(themeColors.lightShade); + + await renderNavButtonsWithPath(Path.Statistics); + expect(entryButton?.props.style.background).toEqual(themeColors.lightShade); + expect(cleanButton?.props.style.background).toEqual(themeColors.lightShade); + + await renderNavButtonsWithPath(Path.UserSettings); + expect(entryButton?.props.style.background).toEqual(themeColors.lightShade); + expect(cleanButton?.props.style.background).toEqual(themeColors.lightShade); }); }); diff --git a/src/components/ProjectScreen/ChooseProject.tsx b/src/components/ProjectScreen/ChooseProject.tsx index 8d7204017e..8abb790a58 100644 --- a/src/components/ProjectScreen/ChooseProject.tsx +++ b/src/components/ProjectScreen/ChooseProject.tsx @@ -36,7 +36,7 @@ export default function ChooseProject(): ReactElement { const selectProject = (project: Project): void => { dispatch(setNewCurrentProject(project)); - navigate(Path.Goals); + navigate(Path.DataEntry); }; return (