Skip to content

Commit

Permalink
Merge pull request #169 from InseeFr/feat/bump-date-fns
Browse files Browse the repository at this point in the history
feat: add unit test before migrating date-fns
  • Loading branch information
EmmanuelDemey authored Nov 15, 2024
2 parents e2b4378 + 3feca11 commit 33a717c
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 118 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pearl",
"version": "2.2.2",
"version": "2.3.2",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.1",
Expand Down
109 changes: 109 additions & 0 deletions src/ui/Header/Notification.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
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';
import D from 'i18n';

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 />
{D.delete}
</Button>
</Stack>
</AccordionDetails>
</Accordion>
);
}
42 changes: 42 additions & 0 deletions src/ui/Header/Notification.spec.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { SyncContext } from 'ui/Sync/SyncContextProvider';
import { Notification } from './Notification';
import D from 'i18n';

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 = {
title: 'Notification',
date: mockDate,
messages: ['New message'],
read: true,
};

renderWithContext(notification);

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

const button = screen.getByRole('button', { name: 'Notification 1 day ago' });
fireEvent.click(button);

screen.getByRole('button', { name: D.delete });

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>
);
}
30 changes: 28 additions & 2 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('should return undefined for an empty or invalid birthdate', () => {
expect(getAge('')).toBeUndefined();
expect(getAge(null)).toBeUndefined();
expect(getAge(undefined)).toBeUndefined();
});

it('should return the correct age for a valid birthdate', () => {
const birthdate = new Date();
birthdate.setFullYear(birthdate.getFullYear() - 30);
expect(getAge(birthdate.toISOString())).toBe(30);
});

it('should correctly handle birthdates in ISO format', () => {
const birthdate = new Date();
birthdate.setFullYear(birthdate.getFullYear() - 25);
expect(getAge(birthdate.toISOString())).toBe(25);
});

it('should return 0 if the birthdate is today', () => {
const today = new Date().toISOString().split('T')[0];
expect(getAge(today)).toBe(0);
});
});

describe('getCommentByType', () => {
const noCommentsSu = {};
const emptyCommentsSu = { comments: [] };
Expand Down Expand Up @@ -67,7 +93,7 @@ describe('isValidForTransmission', () => {
expect(isValidForTransmission({ contactAttempts: cas })).toEqual(false);
});

it('should retur false if contactOuctome totalNumberOfContactAttempts = 0', () => {
it('should return false if contactOutcome totalNumberOfContactAttempts = 0', () => {
expect(
isValidForTransmission({
contactAttempts: cas,
Expand Down Expand Up @@ -166,7 +192,7 @@ describe('updateStateWithDates', () => {
expect(updateStateWithDates(surveyUnit)).toEqual([]);
expect(updateStateWithDates({ states: [] })).toEqual([]);
});
it('should return return initial states if SU lastState is VNC and currentDate < identificationPhaseStart', () => {
it('should return initial states if SU lastState is VNC and currentDate < identificationPhaseStart', () => {
const surveyUnit = {
states: [surveyUnitStateEnum.VISIBLE_NOT_CLICKABLE],
identificationPhaseStartDate: afterCurrent,
Expand Down

0 comments on commit 33a717c

Please sign in to comment.