Skip to content

Commit

Permalink
Prevent Harvesters from accessing Data Cleanup (#2673)
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec authored Oct 10, 2023
1 parent d7e36c2 commit 4b4eca7
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 22 deletions.
36 changes: 28 additions & 8 deletions src/components/AppBar/NavigationButtons.tsx
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -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 (
<>
<NavButton
Expand All @@ -29,13 +47,15 @@ export default function NavigationButtons(props: TabProps): ReactElement {
targetPath={Path.DataEntry}
textId="appBar.dataEntry"
/>
<NavButton
buttonId={dataCleanupButtonId}
currentTab={props.currentTab}
icon={<Rule />}
targetPath={Path.Goals}
textId="appBar.dataCleanup"
/>
{hasGoalPermission && (
<NavButton
buttonId={dataCleanupButtonId}
currentTab={props.currentTab}
icon={<Rule />}
targetPath={Path.Goals}
textId="appBar.dataCleanup"
/>
)}
</>
);
}
Expand Down
89 changes: 76 additions & 13 deletions src/components/AppBar/tests/NavigationButtons.test.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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(<NavigationButtons currentTab={path} />);
const renderNavButtons = async (
path: Path,
permission = Permission.MergeAndReviewEntries
): Promise<void> => {
mockGetCurrentPermissions.mockResolvedValue([permission]);
await renderer.act(async () => {
testRenderer = renderer.create(
<Provider store={mockStore}>
<NavigationButtons currentTab={path} />
</Provider>
);
});
};

const renderNavButtonsWithPath = async (path: Path): Promise<void> => {
await renderNavButtons(path, Permission.MergeAndReviewEntries);
entryButton = testRenderer.root.findByProps({ id: dataEntryButtonId });
cleanButton = testRenderer.root.findByProps({ id: dataCleanupButtonId });
};

const renderNavButtonsWithPermission = async (
perm: Permission
): Promise<void> => {
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);
});
});
2 changes: 1 addition & 1 deletion src/components/ProjectScreen/ChooseProject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function ChooseProject(): ReactElement {

const selectProject = (project: Project): void => {
dispatch(setNewCurrentProject(project));
navigate(Path.Goals);
navigate(Path.DataEntry);
};

return (
Expand Down

0 comments on commit 4b4eca7

Please sign in to comment.