CFE-21 sidebar test

This commit is contained in:
Yasiu1376
2023-10-28 14:51:18 +03:30
parent 3add418f4e
commit c6586eb1f5
13 changed files with 340 additions and 58 deletions

View File

@@ -10,4 +10,6 @@ NEXT_PUBLIC_BASE_URL = "https://crm.witel.ir"
NEXT_PUBLIC_SERVER_SOCKET_URL = "wss://crmws.witel.ir"
NEXT_PUBLIC_POWERED_BY_URL = "https://witel.ir"
NEXT_HAS_NOTIFICATION = "false"
NODE_ENV = "development"

View File

@@ -6,8 +6,12 @@ export const handler = [
return res(ctx.json({
data:{
id: 10,
full_name: "Witel Company",
position: "Software Engineer",
permissions:[
"manage_users"
"manage_users",
"manage_roles",
"manage_boss"
],
}
}))

View File

@@ -4,7 +4,7 @@ import SidebarList from "./SidebarList";
import useUser from "@/lib/app/hooks/useUser";
const SidebarDrawer = ({handleDrawerToggle}) => {
const {user} = useUser()
const {user} = useUser();
const t = useTranslations();
return (
<>

View File

@@ -10,29 +10,15 @@ function reducer(state, action) {
case "COLLAPSE_MENU":
return state.map((itemsArr) =>
itemsArr.map((item) =>
action.key == item.key
? {...item, showSubItem: !item.showSubItem}
: item
action.key === item.key ? { ...item, showSubItem: !item.showSubItem } : item
)
);
case "SELECTED":
return state.map((itemsArr) =>
itemsArr.map((item) => {
if (item.type === "page") {
if (action.route === item.route)
return {...item, selected: true}
else
return {...item, selected: false}
}
return {...item}
// item.subItem.map((subitem) => {
// if (action.route === subitem.route)
// return {...item, showSubItem: true, subItem: {...subitem, selected: true}}
// else
// return {...item, showSubItem: false, subItem: {...subitem, selected: false}}
// })
}
)
itemsArr.map((item) => ({
...item,
selected: item.type === "page" && action.route === item.route,
}))
);
default:
throw new Error();
@@ -54,7 +40,7 @@ export default function SidebarList({handleDrawerToggle}) {
<Fragment key={index}>
{itemArr.map((item) =>
<Fragment key={item.key}>
{(user.permissions.includes(item.permission) || item.permission === "all") &&
{(user?.permissions?.includes(item.permission) || item.permission === "all") &&
<SidebarListItem
item={item}
dispatch={dispatch}

View File

@@ -9,23 +9,27 @@ import useNotification from "@/lib/app/hooks/useNotification";
const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
const t = useTranslations();
const {notification_count} = useNotification()
const { notification_count } = useNotification();
const hasSubItems = item.type === "menu" && item.subItem && item.subItem.length > 0;
const renderBadge = () => (
<IconButton>
<Badge
badgeContent={notification_count ? notification_count[item.name] : 0}
color="error"
variant="standard"
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
/>
</IconButton>
);
return (
<>
<ListItem disablePadding secondaryAction={
<IconButton>
<Badge
badgeContent={notification_count ? notification_count[item.name] : 0}
color="error"
variant="standard"
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
/>
</IconButton>
}>
<ListItem disablePadding secondaryAction={renderBadge()}>
<ListItemButton
selected={item.selected}
{...(item.type == "page" && {
@@ -35,10 +39,9 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
},
})}
onClick={() => {
if (item.type == "menu") {
dispatch({type: "COLLAPSE_MENU", key: item.key});
if (hasSubItems) {
dispatch({ type: "COLLAPSE_MENU", key: item.key });
}
handleDrawerToggle();
}}
sx={{
minHeight: 48,
@@ -66,9 +69,7 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
}
/>
{item.type == "menu" &&
(item.showSubItem ? <ExpandLess/> : <ExpandMore/>)}
{hasSubItems && (item.showSubItem ? <ExpandLess /> : <ExpandMore />)}
</ListItemButton>
</ListItem>
{item.subItem && (

View File

@@ -15,25 +15,27 @@ import useNotification from "@/lib/app/hooks/useNotification";
const SidebarListSubItem = ({item, handleDrawerToggle}) => {
const t = useTranslations();
const {notification_count} = useNotification()
const {notification_count} = useNotification();
const renderBadge = (subitem) => (
<IconButton>
<Badge
badgeContent={notification_count ? notification_count[subitem.name] : 0}
color="error"
variant="standard"
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
/>
</IconButton>
);
return (
<Collapse in={item.showSubItem} timeout="auto" mountOnEnter={true} unmountOnExit={true}>
<List component="div" disablePadding sx={{bgcolor: "#f6f6f6", pr: 1}}>
{item.subItem.map((subitem, index) => (
<ListItem key={subitem.key} disablePadding secondaryAction={
<IconButton>
<Badge
badgeContent={notification_count ? notification_count[subitem.name] : 0}
color="error"
variant="standard"
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
/>
</IconButton>
}>
{item.subItem.map((subitem) => (
<ListItem key={subitem.key} disablePadding secondaryAction={renderBadge(subitem)}>
<ListItemButton
selected={subitem.selected}
component={NextLinkComposed}

View File

@@ -0,0 +1,23 @@
import {render, screen, waitFor} from "@testing-library/react";
import MockAppWithProviders from "../../../../../../mocks/AppWithProvider";
import SidebarDrawer from "@/components/layouts/Dashboard/Sidebar/SidebarDrawer";
describe('SidebarDrawer', () => {
describe('Rendering', () => {
it('renders system name in sidebar', async () => {
render(<MockAppWithProviders><SidebarDrawer/></MockAppWithProviders>);
await waitFor(()=>{
const systemName = screen.getByRole('heading', { level: 6 });
expect(systemName).toHaveTextContent('سامانه CRM');
})
});
it('renders user full name and position in sidebar', async () => {
localStorage.setItem("_token", 'token');
render(<MockAppWithProviders><SidebarDrawer/></MockAppWithProviders>);
await waitFor(()=>{
const userName = screen.queryByText(/Witel Company | Software Engineer/i);
expect(userName).toBeInTheDocument();
})
});
})
})

View File

@@ -0,0 +1,16 @@
import {render, screen, waitFor} from "@testing-library/react";
import MockAppWithProviders from "../../../../../../mocks/AppWithProvider";
import SidebarList from "@/components/layouts/Dashboard/Sidebar/SidebarList";
describe('SidebarList', () => {
describe('Rendering', () => {
it('renders sidebar items based on user permissions', async () => {
localStorage.setItem("_token", 'token');
render(<MockAppWithProviders><SidebarList/></MockAppWithProviders>);
await waitFor(()=>{
const sidebarItem = screen.getByText('مدیریت نقش ها');
expect(sidebarItem).toBeInTheDocument();
})
});
})
})

View File

@@ -0,0 +1,125 @@
import AccessibilityIcon from "@mui/icons-material/Accessibility";
import {fireEvent, render, screen} from "@testing-library/react";
import SidebarListItem from "@/components/layouts/Dashboard/Sidebar/SidebarListItem";
import MockAppWithProviders from "../../../../../../mocks/AppWithProvider";
describe('SidebarListItemComponent', () => {
describe('Rendering', () => {
it('should show sidebarlistitem name in sidebar', () => {
const sampleItem={
key: "sidebar.role-management",
name : "role_management",
type: "page",
route: "/dashboard/role-management",
icon: <AccessibilityIcon />,
selected: false,
permission: "manage_roles",
}
render(<MockAppWithProviders><SidebarListItem item={sampleItem} /></MockAppWithProviders>);
expect(screen.getByText('مدیریت نقش ها')).toBeInTheDocument();
});
it('should show sidebarlistitem icon in sidebar', () => {
const sampleItem={
key: "sidebar.role-management",
name : "role_management",
type: "page",
route: "/dashboard/role-management",
icon: <AccessibilityIcon aria-label="Sidebar Icon"/>,
selected: false,
permission: "manage_roles",
}
render(<MockAppWithProviders><SidebarListItem item={sampleItem} /></MockAppWithProviders>);
const iconElement = screen.getByLabelText("Sidebar Icon");
expect(iconElement).toBeInTheDocument();
});
it('should have selected class for subitem', () => {
const sampleItem={
key: "sidebar.role-management",
name : "role_management",
type: "page",
route: "/dashboard/role-management",
icon: <AccessibilityIcon />,
selected: true,
permission: "manage_roles",
}
render(<MockAppWithProviders><SidebarListItem item={sampleItem} /></MockAppWithProviders>);
const element = screen.getByText('مدیریت نقش ها');
expect(element.parentElement.parentElement).toHaveClass('Mui-selected');
});
it('should not have selected class for subitem', () => {
const sampleItem={
key: "sidebar.role-management",
name : "role_management",
type: "menu",
route: "/dashboard/role-management",
icon: <AccessibilityIcon />,
selected: false,
permission: "manage_roles",
}
render(<MockAppWithProviders><SidebarListItem item={sampleItem} /></MockAppWithProviders>);
const element = screen.getByText('مدیریت نقش ها');
expect(element.parentElement.parentElement).not.toHaveClass('Mui-selected');
});
it('should show sidebarlistitem secondary in sidebar', () => {
const sampleItem={
key: "sidebar.role-management",
name : "role_management",
secondary: "between",
type: "menu",
route: "/dashboard/role-management",
icon: <AccessibilityIcon />,
selected: false,
permission: "manage_roles",
}
render(<MockAppWithProviders><SidebarListItem item={sampleItem} /></MockAppWithProviders>);
expect(screen.getByText('میان')).toBeInTheDocument();
});
it('should not show sidebarlistitem secondary in sidebar', () => {
const sampleItem={
key: "sidebar.role-management",
name : "role_management",
type: "menu",
route: "/dashboard/role-management",
icon: <AccessibilityIcon />,
selected: false,
permission: "manage_roles",
}
render(<MockAppWithProviders><SidebarListItem item={sampleItem} /></MockAppWithProviders>);
expect(screen.queryByText('secondry')).not.toBeInTheDocument();
});
})
describe('Behavior', () => {
it('should call dispatch when hasSubItems is true and item is clicked', () => {
const dispatchMock = jest.fn();
const sampleItem={
key: "sidebar.role-management",
name : "role_management",
type: "menu",
route: "/dashboard/role-management",
icon: <AccessibilityIcon />,
selected: false,
permission: "manage_roles",
subItem :[
{
key: "greaterThan",
route: "/dashboard/role-management",
secondary: "between",
name : "sidebar.admin",
selected: true,
icon: <AccessibilityIcon />,
}
]
}
render(<MockAppWithProviders><SidebarListItem item={sampleItem} dispatch={dispatchMock}/></MockAppWithProviders>);
const listItem = screen.getByText('مدیریت نقش ها');
fireEvent.click(listItem);
expect(dispatchMock).toHaveBeenCalledWith({
type: 'COLLAPSE_MENU',
key: "sidebar.role-management",
});
});
})
})

View File

@@ -0,0 +1,103 @@
import {render, screen} from "@testing-library/react";
import MockAppWithProviders from "../../../../../../mocks/AppWithProvider";
import SidebarListSubItem from "@/components/layouts/Dashboard/Sidebar/SidebarListSubItem";
import AccessibilityIcon from "@mui/icons-material/Accessibility";
describe('SidebarListSubItemComponent', () => {
describe('Rendering', () => {
const sampleItem={
showSubItem:true,
key: "sidebar.role-management",
name : "role_management",
type: "menu",
route: "/dashboard/role-management",
icon: <AccessibilityIcon />,
selected: false,
permission: "manage_roles",
subItem :[
{
key: "greaterThan",
route: "/dashboard/role-management",
secondary: "between",
name : "sidebar.admin",
selected: true,
icon: <AccessibilityIcon aria-label="Sidebar Icon"/>,
}
]
}
it('should show subitem name in sidebar', () => {
render(<MockAppWithProviders><SidebarListSubItem item={sampleItem} /></MockAppWithProviders>);
expect(screen.getByText('بزرگتر از')).toBeInTheDocument();
});
it('should not show subitem name in sidebar', () => {
const sampleItem={
showSubItem:false,
key: "sidebar.role-management",
name : "role_management",
type: "menu",
route: "/dashboard/role-management",
icon: <AccessibilityIcon />,
selected: false,
permission: "manage_roles",
subItem :[
{
key: "greaterThan",
route: "/dashboard/role-management",
secondary: "between",
name : "sidebar.admin",
selected: false,
icon: <AccessibilityIcon />,
}
]
}
render(<MockAppWithProviders><SidebarListSubItem item={sampleItem} /></MockAppWithProviders>);
expect(screen.queryByText('مدیریت نقش ها')).not.toBeInTheDocument();
});
it('should show subitem secondary in sidebar', () => {
render(<MockAppWithProviders><SidebarListSubItem item={sampleItem} /></MockAppWithProviders>);
expect(screen.getByText('میان')).toBeInTheDocument();
});
it('should not show subitem secondary in sidebar', () => {
render(<MockAppWithProviders><SidebarListSubItem item={sampleItem} /></MockAppWithProviders>);
expect(screen.queryByText('secondry')).not.toBeInTheDocument();
});
it('should have selected class for subitem', () => {
render(<MockAppWithProviders><SidebarListSubItem item={sampleItem} /></MockAppWithProviders>);
const parentElement = screen.queryByText('بزرگتر از').parentElement.parentElement;
expect(parentElement).toHaveClass('Mui-selected');
});
it('should not have selected class for subitem', () => {
const sampleItem={
showSubItem:true,
key: "sidebar.role-management",
name : "role_management",
type: "menu",
route: "/dashboard/role-management",
icon: <AccessibilityIcon />,
selected: false,
permission: "manage_roles",
subItem :[
{
key: "greaterThan",
route: "/dashboard/role-management",
secondary: "between",
name : "sidebar.admin",
selected: false,
icon: <AccessibilityIcon />,
}
]
}
render(<MockAppWithProviders><SidebarListSubItem item={sampleItem} /></MockAppWithProviders>);
const parentElement = screen.queryByText('بزرگتر از').parentElement.parentElement;
expect(parentElement).not.toHaveClass('Mui-selected');
});
it('should show subitem icon', () => {
render(<MockAppWithProviders><SidebarListSubItem item={sampleItem} /></MockAppWithProviders>);
const iconElement = screen.getByLabelText("Sidebar Icon");
expect(iconElement).toBeInTheDocument();
});
})
})

View File

@@ -0,0 +1,17 @@
import {render, screen, act} from "@testing-library/react";
import MockAppWithProviders from "../../../../../../mocks/AppWithProvider";
import Sidebar from "@/components/layouts/Dashboard/Sidebar";
describe('Sidebar', () => {
describe('Rendering', () => {
it('Mobile Drawer is displayed when mobileOpen is true', () => {
const props={
mobileOpen: true,
};
render(<MockAppWithProviders><Sidebar {...props}/></MockAppWithProviders>);
const mobileDrawer = screen.getByTestId('mobile-drawer');
expect(mobileDrawer).toBeVisible();
});
})
})

View File

@@ -9,6 +9,7 @@ const Sidebar = (props) => {
aria-label="mailbox folders"
>
<Drawer
data-testid="mobile-drawer"
container={props.container}
variant="temporary"
open={props.mobileOpen}

View File

@@ -1,8 +1,10 @@
import useSWR from 'swr'
import {GET_SIDEBAR_NOTIFICATION} from "@/core/data/apiRoutes";
import useRequest from "@/lib/app/hooks/useRequest";
const isNotificationEnabled = process.env.NEXT_HAS_NOTIFICATION === "true";
const useNotification = () => {
const requestServer = useRequest({auth: true, notification: false})
//swr config
@@ -13,7 +15,7 @@ const useNotification = () => {
})
};
const {data, mutate} = useSWR(GET_SIDEBAR_NOTIFICATION, fetcher)
const {data, mutate} = useSWR( isNotificationEnabled ? GET_SIDEBAR_NOTIFICATION : '', fetcher , {keepPreviousData:true})
const notification_count = data
//swr config