This commit is contained in:
Zeeshaun
2026-03-23 11:21:52 -05:00
parent de9ddd5001
commit a17bc4b4a9
426 changed files with 60574 additions and 1 deletions
@@ -0,0 +1,48 @@
import { mount } from "enzyme";
import React from "react";
import AsyncState from "../../shared/enumerations/AsyncState";
import AsyncGuard from "./AsyncGuard";
describe("A component for displaying async containers", () => {
it("Displays an async status: Loading", () => {
const container = {
data: [],
state: AsyncState.Loading
};
let wrapper = mount(
<AsyncGuard container={container}>
<>!!ERROR!!</>
</AsyncGuard>
);
expect(wrapper.html()).toBe("Loading...");
});
it("Displays an async custom status: Error", () => {
const container = {
data: [],
state: AsyncState.Failure
};
let wrapper = mount(
<AsyncGuard container={container} error={<h1>OH NO</h1>}>
<>!!ERROR!!</>
</AsyncGuard>
);
expect(wrapper.html()).toBe("<h1>OH NO</h1>");
});
it("Displays an async child on Success", () => {
const container = {
data: [],
state: AsyncState.Success
};
let wrapper = mount(
<AsyncGuard container={container}>
<>YES</>
</AsyncGuard>
);
expect(wrapper.html()).toBe("YES");
});
});
@@ -0,0 +1,35 @@
import React from "react";
import AsyncState from "../../shared/enumerations/AsyncState";
import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer";
export interface IAsyncProps {
container: IAsyncStateContainer<any>;
children: JSX.Element;
error?: JSX.Element;
loading?: JSX.Element;
notFound?: JSX.Element;
inactive?: JSX.Element;
}
const AsyncGuard: React.FC<IAsyncProps> = (props) => {
switch (props.container.state) {
case AsyncState.Inactive:
default:
return props.inactive ? props.inactive : <></>;
case AsyncState.Loading:
return props.loading ? props.loading : <>Loading...</>;
case AsyncState.Invalid:
case AsyncState.Failure:
return props.error ? props.error : <>Data failed to load</>;
case AsyncState.NotFound:
return props.notFound ? props.notFound : <>Data was not found</>;
case AsyncState.Success:
return props.container.data ? (
props.children
) : (
<>No Data Returned</>
);
}
};
export default AsyncGuard;
@@ -0,0 +1,25 @@
import { mount } from "enzyme";
import React from "react";
import BusinessLineDisplay from "./BusinessLineDisplay";
import { getMockAppSettings } from "../../shared/testing/mocks/MockAppSettings";
describe("A component for displaying business line", () => {
const useSelectorMock = jest.spyOn(React, "useContext");
beforeEach(() => {
useSelectorMock.mockClear();
});
it("Displays a valid business line name", () => {
useSelectorMock.mockReturnValue(getMockAppSettings());
let wrapper = mount(<BusinessLineDisplay businessLineCode="OPS" />);
expect(wrapper.html()).toBe("Operations");
});
it("Displays an invalid business line name for a code", () => {
useSelectorMock.mockReturnValue(getMockAppSettings());
let wrapper = mount(<BusinessLineDisplay businessLineCode="XYZ" />);
expect(wrapper.html()).toBe("Missing");
});
});
@@ -0,0 +1,21 @@
import React from "react";
import { SettingsContext } from "../../shared/providers/SettingsProvider";
interface IBusinessLineDisplayProps {
businessLineCode: string;
}
const BusinessLineDisplay: React.FC<IBusinessLineDisplayProps> = (props) => {
const settings = React.useContext(SettingsContext);
const businessName = settings?.businessLines.find(
(x) => x.code === props.businessLineCode
);
return props.businessLineCode && businessName ? (
<>{businessName.name}</>
) : (
<>Missing</>
);
};
export default BusinessLineDisplay;
@@ -0,0 +1,58 @@
import { mount } from "enzyme";
import React from "react";
import DepartmentDisplay from "./DepartmentDisplay";
import AsyncState from "../../shared/enumerations/AsyncState";
import { IDepartmentState } from "../../reducers/DepartmentReducer";
import { getMockAppSettings } from "../../shared/testing/mocks/MockAppSettings";
import { getMockDepartments } from "../../shared/testing/mocks/MockDepartments";
describe("A component for displaying departments", () => {
const useContextMock = jest.spyOn(React, "useContext");
beforeEach(() => {
useContextMock.mockClear();
});
it("Displays a valid department", () => {
const departments: IDepartmentState = {
departments: {
data: [
{
id: 4,
departmentId: 5,
departmentName: "bad test",
state: "Active",
businessLineCode: "XY"
},
{
id: 5,
departmentId: 6,
departmentName: "good test",
state: "Active",
businessLineCode: "XY"
}
],
state: AsyncState.Success
},
submitState: {
state: AsyncState.Success
}
};
const settings = getMockAppSettings();
settings.departments = getMockDepartments();
useContextMock.mockReturnValue(settings);
const wrapper = mount(<DepartmentDisplay departmentId={55} />);
const found = wrapper.find('[data-test="department-display-wrapper"]');
expect(found.length).toBe(1);
expect(found.children().first().props().title).toBe("55");
expect(found.children().first().text()).toBe("WAUWATOSA");
});
it("Displays a invalid department", () => {
const wrapper = mount(<DepartmentDisplay departmentId={55} />);
const found = wrapper.find('[data-test="department-display-wrapper"]');
expect(found.length).toBe(1);
expect(found.children().first().text()).toBe("Missing");
});
});
@@ -0,0 +1,47 @@
import React from "react";
import { SettingsContext } from "../../shared/providers/SettingsProvider";
import Configuration from "../../shared/configuration/Configuration";
export enum DepartmentDisplayTargets {
Detail = "detail",
Dashboard = "dashboard"
}
interface IDepartmentDisplayProps {
departmentId: number;
target?: DepartmentDisplayTargets;
nameOverride?: string;
}
const DepartmentDisplay: React.FC<IDepartmentDisplayProps> = (props) => {
const settings = React.useContext(SettingsContext);
const department = settings?.departments.find(
(x) => x.id === props.departmentId
);
let href = Configuration.PrefixAppUrlWithBasename(
Configuration.Get.appUrls.department
).replace(":id", props.departmentId.toString());
if (props.target && props.target === DepartmentDisplayTargets.Dashboard) {
href =
Configuration.PrefixAppUrlWithBasename(
Configuration.Get.appUrls.dashboard
) +
"?dashboard=department&departmentId=" +
props.departmentId;
}
return (
<span data-test="department-display-wrapper">
{props.departmentId && department ? (
<a href={href} title={department.id + ""}>
{props.nameOverride ?? department.departmentName}
</a>
) : (
<>Missing</>
)}
</span>
);
};
export default DepartmentDisplay;
@@ -0,0 +1,17 @@
import React from "react";
import { IEmployee } from "../../shared/models/Employee";
export interface IEmployeeStatusGuardProps {
employee: IEmployee;
children: JSX.Element;
}
const EmployeeStatusGuard: React.FC<IEmployeeStatusGuardProps> = (props) => {
const shouldRenderChildren = () => {
return props.employee && !props.employee.terminated;
};
return shouldRenderChildren() ? props.children : <></>;
};
export default EmployeeStatusGuard;
@@ -0,0 +1,211 @@
import { mount } from "enzyme";
import React from "react";
import AsyncState from "../../shared/enumerations/AsyncState";
import * as reactRedux from "react-redux";
import { IProfileState } from "../../reducers/ProfileReducer";
import PermissionGuard from "./PermissionGuard";
import moment from "moment";
import { UserType } from "../../shared/models/Auditable";
import PermissionCode from "../../shared/enumerations/PermissionCode";
describe("A component for requiring permissions", () => {
const useSelectorMock = jest.spyOn(reactRedux, "useSelector");
const useDispatchMock = jest.spyOn(reactRedux, "useDispatch");
beforeEach(() => {
useSelectorMock.mockClear();
useDispatchMock.mockClear();
});
it("Displays children when role permission present", () => {
jest.mock("react-router-dom", () => ({
...jest.requireActual("react-router-dom"),
useParams: () => ({
employeeId: "11771"
})
}));
const profile: IProfileState = {
profile: {
data: {
adEmail: "[email protected]",
personId: 11,
employeeId: "11771",
homeDepartment: "HOME",
baseRole: "QA",
fullName: "John Tester",
role: {
name: "Admin",
description: "admin",
permissions: [
{
code: "VIEW_DASHBOARD",
description: "View Dashboard"
}
],
hasCommissions: false
},
additionalPermissions: [],
departments: [
{
id: 1,
role: {
name: "Admin",
description: "admin",
permissions: [
{
code: "VIEW_DASHBOARD",
description: "View Dashboard"
}
],
hasCommissions: false
},
departmentId: 114,
employeeId: "11771",
isHome: true,
note: "asdf",
slidingPayout: 100,
startDate: moment(),
lastUpdated: moment(),
createdByType: UserType.Associate,
createdBy: "asdf",
endDate: undefined,
lastUpdatedByType: UserType.Associate,
created: moment(),
lastUpdatedBy: "asdf"
}
],
assignments: []
},
state: AsyncState.Success
},
searchResults: {
state: AsyncState.Success,
data: []
}
};
const dummyDispatch = jest.fn();
useDispatchMock.mockReturnValue(dummyDispatch);
useSelectorMock.mockReturnValue(profile);
let wrapper = mount(
<PermissionGuard requiredPermissions={["VIEW_DASHBOARD"]}>
<h1>Authorized</h1>
</PermissionGuard>
);
expect(wrapper.html()).toBe("<h1>Authorized</h1>");
});
it("Displays children when additional permission present", () => {
jest.mock("react-router-dom", () => ({
...jest.requireActual("react-router-dom"),
useParams: () => ({
employeeId: "11771"
})
}));
const profile: IProfileState = {
profile: {
data: {
adEmail: "[email protected]",
personId: 11,
employeeId: "11771",
homeDepartment: "HOME",
baseRole: "QA",
fullName: "John Tester",
role: {
name: "Admin",
description: "admin",
permissions: [],
hasCommissions: false
},
additionalPermissions: [
{
id: 1,
created: moment().utc(),
createdByType: UserType.System,
createdBy: "Tester1",
permission: {
code: "VIEW_DASHBOARD",
description: "View Dashboard"
}
}
],
departments: [],
assignments: []
},
state: AsyncState.Success
},
searchResults: {
state: AsyncState.Success,
data: []
}
};
const dummyDispatch = jest.fn();
useDispatchMock.mockReturnValue(dummyDispatch);
useSelectorMock.mockReturnValue(profile);
const wrapper = mount(
<PermissionGuard
requiredPermissions={[PermissionCode.VIEW_DASHBOARD]}
>
<h1>Authorized</h1>
</PermissionGuard>
);
expect(wrapper.html()).toBe("<h1>Authorized</h1>");
});
it("Displays nothing when permission not present", () => {
jest.mock("react-router-dom", () => ({
...jest.requireActual("react-router-dom"),
useParams: () => ({
employeeId: "11771"
})
}));
const profile: IProfileState = {
profile: {
data: {
adEmail: "[email protected]",
personId: 11,
employeeId: "11771",
homeDepartment: "HOME",
baseRole: "QA",
fullName: "John Tester",
role: {
name: "Admin",
description: "admin",
permissions: [
{
code: "VIEW_DASHBOARD",
description: "View Dashboard"
}
],
hasCommissions: false
},
additionalPermissions: [],
departments: [],
assignments: []
},
state: AsyncState.Success
},
searchResults: {
state: AsyncState.Success,
data: []
}
};
const dummyDispatch = jest.fn();
useDispatchMock.mockReturnValue(dummyDispatch);
useSelectorMock.mockReturnValue(profile);
let wrapper = mount(
<PermissionGuard requiredPermissions={["ALL_THE_THINGS"]}>
<h1>Authorized</h1>
</PermissionGuard>
);
expect(wrapper.html()).toBe(null);
});
});
@@ -0,0 +1,47 @@
import React from "react";
import { useSelector } from "react-redux";
import { IAppState } from "../../store/Store";
import { IProfileState } from "../../reducers/ProfileReducer";
import AsyncState from "../../shared/enumerations/AsyncState";
import { Alert } from "reactstrap";
import ProfileService from "../../shared/services/ProfileService";
export interface IPermissionProps {
requiredPermissions: string[];
showWarning?: boolean;
children: JSX.Element;
}
const PermissionGuard: React.FC<IPermissionProps> = (props) => {
const profileState = useSelector<IAppState, IProfileState>(
(state) => state.profileState
);
const profile = profileState.profile;
const shouldRenderChildren = () => {
const requiredPermissions = props.requiredPermissions;
if (!requiredPermissions || requiredPermissions.length < 1) {
return true;
}
if (profile.state !== AsyncState.Success || !profile.data) {
return false;
}
const profileData = profile.data;
return ProfileService.hasAnyPermission(
profileData,
requiredPermissions
);
};
if (shouldRenderChildren()) {
return props.children;
}
return props.showWarning ? (
<Alert color="danger">You do not have access.</Alert>
) : (
<></>
);
};
export default PermissionGuard;
@@ -0,0 +1,25 @@
import { mount } from "enzyme";
import React from "react";
import ProductDisplay from "./ProductDisplay";
import { getMockAppSettings } from "../../shared/testing/mocks/MockAppSettings";
describe("A component for displaying products", () => {
const useSelectorMock = jest.spyOn(React, "useContext");
beforeEach(() => {
useSelectorMock.mockClear();
});
it("Displays a valid product", () => {
useSelectorMock.mockReturnValue(getMockAppSettings());
let wrapper = mount(<ProductDisplay productCode="CheckCard" />);
expect(wrapper.html()).toBe("Check Card");
});
it("Displays an invalid product", () => {
useSelectorMock.mockReturnValue(getMockAppSettings());
let wrapper = mount(<ProductDisplay productCode="XYZ" />);
expect(wrapper.html()).toBe("Missing");
});
});
@@ -0,0 +1,17 @@
import React from "react";
import { SettingsContext } from "../../shared/providers/SettingsProvider";
interface IDepartmentDisplayProps {
productCode: string;
}
const ProductDisplay: React.FC<IDepartmentDisplayProps> = (props) => {
const settings = React.useContext(SettingsContext);
const product = settings?.productDetails.find(
(x) => x.product === props.productCode
);
return props.productCode && product ? <>{product.title}</> : <>Missing</>;
};
export default ProductDisplay;
@@ -0,0 +1,7 @@
.term-user {
text-decoration: line-through;
color: darkred;
}
.active-user {
}
@@ -0,0 +1,70 @@
import React from "react";
import { SettingsContext } from "../../shared/providers/SettingsProvider";
import Configuration from "../../shared/configuration/Configuration";
import { IUserSummary } from "../../shared/models/Settings";
import "./UserDisplay.css";
import moment from "moment";
export enum UserDisplayTargets {
Profile = "profile",
Dashboard = "dashboard"
}
interface IUserDisplayProps {
employeeId: string;
target?: UserDisplayTargets;
}
const UserDisplay: React.FC<IUserDisplayProps> = (props) => {
const settings = React.useContext(SettingsContext);
const user = settings?.employees.find(
(x) => x.employeeId === props.employeeId
);
function determineDecoration(user: IUserSummary) {
return user.terminated ? "term-appUser" : "active-appUser";
}
let terminated = "";
if (user && user.terminated) {
terminated = moment(user.terminated).format(" (MM/DD/YYYY)");
}
const toTitleCase = (str: string) => {
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
};
let href = Configuration.PrefixAppUrlWithBasename(
Configuration.Get.appUrls.profile
).replace(":employeeId?", props.employeeId);
if (props.target && props.target === UserDisplayTargets.Dashboard) {
href =
Configuration.PrefixAppUrlWithBasename(
Configuration.Get.appUrls.dashboard
) +
"?dashboard=employee&employeeId=" +
props.employeeId;
}
return (
<span data-test="department-display-wrapper">
{props.employeeId && user ? (
<a
href={href}
title={user.employeeId + ", manager is " + user.managerKey}
className={determineDecoration(user)}
>
{toTitleCase(user.fullName)}
{terminated}
</a>
) : (
<>Missing</>
)}
</span>
);
};
export default UserDisplay;