Skip to content

Commit

Permalink
feat: add unit test before migrating date-fns
Browse files Browse the repository at this point in the history
  • Loading branch information
EmmanuelDemey committed Nov 6, 2024
1 parent e2b4378 commit c6d58a5
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 115 deletions.
108 changes: 108 additions & 0 deletions src/ui/Header/Notification.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import BuildIcon from '@mui/icons-material/Build';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import SupportAgentIcon from '@mui/icons-material/SupportAgent';
import Accordion from '@mui/material/Accordion';
import AccordionDetails from '@mui/material/AccordionDetails';
import AccordionSummary from '@mui/material/AccordionSummary';
import Badge from '@mui/material/Badge';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
import { useTheme } from '@mui/material/styles';
import { formatDistance } from 'date-fns';
import { useContext } from 'react';
import { dateFnsLocal } from '../../utils';
import { NOTIFICATION_TYPE_SYNC } from '../../utils/constants';
import { deleteNotification, markNotificationAsRead } from '../../utils/hooks/useNotifications';
import syncReportIdbService from '../../utils/indexeddb/services/syncReport-idb-service';
import { Row } from '../Row';
import { SyncContext } from '../Sync/SyncContextProvider';
import { Typography } from '../Typography';

export function Notification({ notification, onExit }) {
const theme = useTheme();
const date = `${formatDistance(notification.date || 0, new Date(), {
addSuffix: true,
locale: dateFnsLocal,
})}`;
const { setSyncResult } = useContext(SyncContext);
const handleOpen = async e => {
e.preventDefault();
e.stopPropagation();
if (notification.type === NOTIFICATION_TYPE_SYNC) {
const report = await syncReportIdbService.getById(notification.id);
setSyncResult({
date: date,
state: notification.state,
messages: notification.message,
details: report,
});
onExit();
}
};

const handleExpand = (_, expanded) => {
if (!notification.read && expanded) {
markNotificationAsRead(notification);
}
};

const handleDelete = () => deleteNotification(notification);

const isRead = notification.read;
const badge = notification.state === 'error' ? '!' : undefined;
const background = isRead ? undefined : theme.palette.surfaceTertiary.main;
const isSyncNotification = notification.type === NOTIFICATION_TYPE_SYNC;

return (
<Accordion
elevation={0}
disableGutters
variant="dense"
bgcolor="accent"
onChange={handleExpand}
>
<AccordionSummary
sx={{ padding: 0, background: background, borderRadius: 2 }}
expandIcon={<ExpandMoreIcon fontSize="large" color="textPrimary" />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Row justifyContent="space-between">
<Row gap={1}>
<Box p={2}>
<Badge badgeContent={badge} color="accent">
{isSyncNotification ? <BuildIcon /> : <SupportAgentIcon />}
</Badge>
</Box>
<Stack gap={0.5} paddingBlock={1}>
<Link href="#" underline={isSyncNotification ? 'hover' : 'none'} onClick={handleOpen}>
<Typography color="black" variant="s" fontWeight={700}>
{notification.title}
</Typography>
</Link>
<Typography color="textTertiary" variant="s">
{date}
</Typography>
</Stack>
</Row>
</Row>
</AccordionSummary>
<AccordionDetails>
<Stack gap={1}>
{notification.messages.map(message => (
<Typography as="p" variant="s" key={message}>
{message}
</Typography>
))}
<Button sx={{ alignSelf: 'flex-end' }} color="textPrimary" onClick={handleDelete}>
<DeleteOutlineIcon />
Supprimer
</Button>
</Stack>
</AccordionDetails>
</Accordion>
);
}
31 changes: 31 additions & 0 deletions src/ui/Header/Notification.spec.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { render, screen } from '@testing-library/react';
import { SyncContext } from 'ui/Sync/SyncContextProvider';
import { Notification } from './Notification';

vi.mock('@mui/material/Button', () => ({
default: props => <button {...props}>{props.children}</button>,
}));

const renderWithContext = notification => {
return render(
<SyncContext.Provider value={{}}>
<Notification notification={notification} />
</SyncContext.Provider>
);
};

describe('Notification Component', () => {
it('should render the notification with the correct date format', () => {
vi.setSystemTime(new Date(2023, 0, 2));

const mockDate = new Date(2023, 0, 1);
const notification = { date: mockDate, messages: ['New message'], read: true };

renderWithContext(notification);

screen.getByText('New message');
screen.getByText('1 day ago');

vi.useRealTimers();
});
});
127 changes: 12 additions & 115 deletions src/ui/Header/Notifications.jsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,23 @@
import DeleteIcon from '@mui/icons-material/Delete';
import MarkAsReadIcon from '@mui/icons-material/MarkAsUnread';
import IconButton from '@mui/material/IconButton';
import Popover from '@mui/material/Popover';
import Tabs from '@mui/material/Tabs';
import Tab from '@mui/material/Tab';
import Box from '@mui/material/Box';
import Accordion from '@mui/material/Accordion';
import AccordionSummary from '@mui/material/AccordionSummary';
import AccordionDetails from '@mui/material/AccordionDetails';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import Button from '@mui/material/Button';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
import { useContext, useState } from 'react';
import { Typography } from '../Typography';
import { HeaderBackdrop } from './HeaderBackdrop';
import { useTheme } from '@mui/material/styles';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import { useState } from 'react';
import D from '../../i18n/build-dictionary';
import { NOTIFICATION_TYPE_MANAGEMENT, NOTIFICATION_TYPE_SYNC } from '../../utils/constants';
import {
deleteNotification,
deleteNotifications,
markNotificationAsRead,
markNotificationsAsRead,
useNotifications,
} from '../../utils/hooks/useNotifications';
import { Row } from '../Row';
import { formatDistance } from 'date-fns';
import { dateFnsLocal } from '../../utils';
import BuildIcon from '@mui/icons-material/Build';
import { useTheme } from '@mui/material/styles';
import { NOTIFICATION_TYPE_MANAGEMENT, NOTIFICATION_TYPE_SYNC } from '../../utils/constants';
import syncReportIdbService from '../../utils/indexeddb/services/syncReport-idb-service';
import SupportAgentIcon from '@mui/icons-material/SupportAgent';
import Badge from '@mui/material/Badge';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import { SyncContext } from '../Sync/SyncContextProvider';
import IconButton from '@mui/material/IconButton';
import D from '../../i18n/build-dictionary';
import DeleteIcon from '@mui/icons-material/Delete';
import MarkAsReadIcon from '@mui/icons-material/MarkAsUnread';
import { Typography } from '../Typography';
import { HeaderBackdrop } from './HeaderBackdrop';
import { Notification } from './Notification';

export function Notifications({ target, onClose }) {
const open = !!target;
Expand Down Expand Up @@ -105,90 +89,3 @@ export function Notifications({ target, onClose }) {
</>
);
}

function Notification({ notification, onExit }) {
const theme = useTheme();
const date = `${formatDistance(notification.date || 0, new Date(), {
addSuffix: true,
locale: dateFnsLocal,
})}`;
const { setSyncResult } = useContext(SyncContext);

const handleOpen = async e => {
e.preventDefault();
e.stopPropagation();
if (notification.type === NOTIFICATION_TYPE_SYNC) {
const report = await syncReportIdbService.getById(notification.id);
setSyncResult({
date: date,
state: notification.state,
messages: notification.message,
details: report,
});
onExit();
}
};

const handleExpand = (_, expanded) => {
if (!notification.read && expanded) {
markNotificationAsRead(notification);
}
};

const handleDelete = () => deleteNotification(notification);

const isRead = notification.read;
const badge = notification.state === 'error' ? '!' : undefined;
const background = isRead ? undefined : theme.palette.surfaceTertiary.main;
const isSyncNotification = notification.type === NOTIFICATION_TYPE_SYNC;

return (
<Accordion
elevation={0}
disableGutters
variant="dense"
bgcolor="accent"
onChange={handleExpand}
>
<AccordionSummary
sx={{ padding: 0, background: background, borderRadius: 2 }}
expandIcon={<ExpandMoreIcon fontSize="large" color="textPrimary" />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Row justifyContent="space-between">
<Row gap={1}>
<Box p={2}>
<Badge badgeContent={badge} color="accent">
{isSyncNotification ? <BuildIcon /> : <SupportAgentIcon />}
</Badge>
</Box>
<Stack gap={0.5} paddingBlock={1}>
<Link href="#" underline={isSyncNotification ? 'hover' : 'none'} onClick={handleOpen}>
<Typography color="black" variant="s" fontWeight={700}>
{notification.title}
</Typography>
</Link>
<Typography color="textTertiary" variant="s">
{date}
</Typography>
</Stack>
</Row>
</Row>
</AccordionSummary>
<AccordionDetails>
<Stack gap={1}>
{notification.messages.map(message => (
<Typography as="p" variant="s" key={message}>
{message}
</Typography>
))}
<Button sx={{ alignSelf: 'flex-end' }} color="textPrimary" onClick={handleDelete}>
<DeleteOutlineIcon />
Supprimer
</Button>
</Stack>
</AccordionDetails>
</Accordion>
);
}
26 changes: 26 additions & 0 deletions src/utils/functions/surveyUnitFunctions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,37 @@ import {
updateStateWithDates,
isQuestionnaireAvailable,
getCommentByType,
getAge,
isValidForTransmission,
lastContactAttemptIsSuccessfull,
areCaEqual,
} from 'utils/functions/index';

describe('getAge', () => {
it('devrait retourner undefined pour une date de naissance vide ou invalide', () => {
expect(getAge('')).toBeUndefined();
expect(getAge(null)).toBeUndefined();
expect(getAge(undefined)).toBeUndefined();
});

it('devrait retourner l\'âge correct pour une date de naissance valide', () => {
const birthdate = new Date();
birthdate.setFullYear(birthdate.getFullYear() - 30);
expect(getAge(birthdate.toISOString())).toBe(30);
});

it('devrait gérer correctement les dates de naissance au format ISO', () => {
const birthdate = new Date();
birthdate.setFullYear(birthdate.getFullYear() - 25);
expect(getAge(birthdate.toISOString())).toBe(25);
});

it('devrait retourner 0 si la date de naissance est aujourd\'hui', () => {
const today = new Date().toISOString().split('T')[0];
expect(getAge(today)).toBe(0);
});
});

describe('getCommentByType', () => {
const noCommentsSu = {};
const emptyCommentsSu = { comments: [] };
Expand Down

0 comments on commit c6d58a5

Please sign in to comment.