Skip to content

Commit

Permalink
Merge pull request #5885 from hotosm/tests/5722-notifications-page
Browse files Browse the repository at this point in the history
Update tests for the notifications page components
  • Loading branch information
HelNershingThapa committed Jun 26, 2023
2 parents 1cfaf0a + a64180f commit 3b090d1
Show file tree
Hide file tree
Showing 17 changed files with 363 additions and 136 deletions.
22 changes: 18 additions & 4 deletions frontend/src/components/header/tests/notificationBell.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { act, screen, waitFor, within } from '@testing-library/react';

import '../../../utils/mockMatchMedia';
import { store } from '../../../store';
import { ReduxIntlProviders, renderWithRouter } from '../../../utils/testWithIntl';
import {
ReduxIntlProviders,
createComponentWithMemoryRouter,
renderWithRouter,
} from '../../../utils/testWithIntl';
import { NotificationBell } from '../notificationBell';

describe('Notification Bell', () => {
Expand All @@ -21,8 +25,8 @@ describe('Notification Bell', () => {
);
const inboxLink = screen.getAllByRole('link')[0];
expect(within(inboxLink).getByLabelText(/notifications/i)).toBeInTheDocument();
expect(await screen.findByText(/You have been added to team/i)).toBeInTheDocument();
expect(screen.getAllByRole('article').length).toBe(4);
expect(await screen.findByText(/Sample subject 1/i)).toBeInTheDocument();
expect(screen.getAllByRole('article').length).toBe(5);
await waitFor(() => {
expect(container.getElementsByClassName('redicon')[0]).toBeInTheDocument();
});
Expand All @@ -36,7 +40,7 @@ describe('Notification Bell', () => {
</ReduxIntlProviders>,
);
expect(screen.getAllByRole('link')[0]).not.toHaveClass('bb b--blue-dark bw1 pv2');
expect(await screen.findByText(/You have been added to team/i)).toBeInTheDocument();
expect(await screen.findByText(/Sample subject 1/i)).toBeInTheDocument();
await waitFor(() => {
expect(container.getElementsByClassName('redicon')[0]).toBeInTheDocument();
});
Expand All @@ -45,4 +49,14 @@ describe('Notification Bell', () => {
expect(container.querySelector('redicon')).not.toBeInTheDocument();
});
});

it('should navigate to the notifications page', async () => {
const { router, user } = createComponentWithMemoryRouter(
<ReduxIntlProviders>
<NotificationBell />
</ReduxIntlProviders>,
);
await user.click(await screen.findByText(/208 unread/i));
await waitFor(() => expect(router.state.location.pathname).toBe('/inbox'));
});
});
11 changes: 9 additions & 2 deletions frontend/src/components/notifications/actionButtons.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import deletionMessages from '../deleteModal/messages';
import messages from './messages';
import { EyeIcon, WasteIcon } from '../svgIcons';
import { Button } from '../button';
import { pushToLocalJSONAPI } from '../../network/genericJSONRequest';
import { fetchLocalJSONAPI, pushToLocalJSONAPI } from '../../network/genericJSONRequest';

export const ActionButtons = ({
selected,
Expand All @@ -16,7 +16,6 @@ export const ActionButtons = ({
isAllSelected,
inboxQuery,
setInboxQuery,
updateUnreadCount,
pageOfCards,
totalPages,
}) => {
Expand All @@ -25,6 +24,14 @@ export const ActionButtons = ({
const param = inboxQuery.types ? `?messageType=${inboxQuery.types?.join(',')}` : '';
const payload = JSON.stringify({ messageIds: selected });

const updateUnreadCount = () => {
fetchLocalJSONAPI(`notifications/?status=unread`, token)
.then((notifications) =>
dispatch({ type: 'SET_UNREAD_COUNT', payload: notifications.pagination?.total }),
)
.catch((e) => console.log(e));
};

const deleteMessages = () => {
if (isAllSelected) {
pushToLocalJSONAPI(`/api/v2/notifications/delete-all/${param}`, null, token, 'DELETE')
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/notifications/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export default defineMessages({
id: 'notifications.mainSection.title',
defaultMessage: 'Notifications',
},
notification: {
id: 'notifications.singular.notification',
defaultMessage: 'notification',
},
all: {
id: 'notifications.filter.all',
defaultMessage: 'All',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const NotificationBodyModal = (props) => {
<FormattedMessage
{...messages.errorLoadingTheX}
values={{
xWord: <FormattedMessage {...messages.message} />,
xWord: <FormattedMessage {...messages.notification} />,
}}
/>
</div>
Expand Down
25 changes: 10 additions & 15 deletions frontend/src/components/notifications/notificationOrderBy.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,16 @@ export function NotificationOrderBySelector(props) {
},
];

const onSortSelect = (arr) => {
if (arr.length === 1) {
props.setQuery(
{
...props.allQueryParams,
page: undefined,
orderBy: arr[0].sort,
orderByType: arr[0].type,
},
'pushIn',
);
} else if (arr.length > 1) {
throw new Error('filter select array is bigger.');
}
};
const onSortSelect = (arr) =>
props.setQuery(
{
...props.allQueryParams,
page: undefined,
orderBy: arr[0].sort,
orderByType: arr[0].type,
},
'pushIn',
);

return (
<Dropdown
Expand Down
13 changes: 0 additions & 13 deletions frontend/src/components/notifications/notificationResults.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect, useState } from 'react';
import ReactPlaceholder from 'react-placeholder';
import { useDispatch, useSelector } from 'react-redux';
import { FormattedMessage, FormattedNumber } from 'react-intl';
import 'react-placeholder/lib/reactPlaceholder.css';

Expand All @@ -10,7 +9,6 @@ import NotificationPlaceholder from './notificationPlaceholder';
import { RefreshIcon } from '../svgIcons';
import { SelectAll } from '../formInputs';
import { SelectAllNotifications } from './selectAllNotifications';
import { fetchLocalJSONAPI } from '../../network/genericJSONRequest';
import { ActionButtons } from './actionButtons';

export const NotificationResultsMini = (props) => {
Expand Down Expand Up @@ -110,8 +108,6 @@ const NotificationCards = ({
inboxQuery,
setInboxQuery,
}) => {
const dispatch = useDispatch();
const token = useSelector((state) => state.auth.token);
const [selected, setSelected] = useState([]);
const [isAllSelected, setIsAllSelected] = useState(false);

Expand All @@ -138,14 +134,6 @@ const NotificationCards = ({
return !msg.read && selected.includes(msg.messageId) ? acc + 1 : acc;
}, 0);

const updateUnreadCount = () => {
fetchLocalJSONAPI(`notifications/?status=unread`, token)
.then((notifications) =>
dispatch({ type: 'SET_UNREAD_COUNT', payload: notifications.pagination?.total }),
)
.catch((e) => console.log(e));
};

return (
<>
{!useMiniCard && (
Expand All @@ -165,7 +153,6 @@ const NotificationCards = ({
isAllSelected={isAllSelected}
inboxQuery={inboxQuery}
setInboxQuery={setInboxQuery}
updateUnreadCount={updateUnreadCount}
pageOfCards={pageOfCards}
totalPages={totalPages}
/>
Expand Down
54 changes: 36 additions & 18 deletions frontend/src/components/notifications/tests/actionButtons.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { setupFaultyHandlers } from '../../../network/tests/server';
import { store } from '../../../store';
import { ActionButtons } from '../actionButtons';
import { ReduxIntlProviders } from '../../../utils/testWithIntl';
import { generateSampleNotifications } from '../../../network/tests/mockData/notifications';

describe('Action Buttons', () => {
const retryFnMock = jest.fn();
const setSelectedMock = jest.fn();
const updateUnreadCountMock = jest.fn();
it('should return nothing if no notification is selected', () => {
act(() => {
store.dispatch({ type: 'SET_TOKEN', token: 'validToken' });
Expand All @@ -22,7 +22,6 @@ describe('Action Buttons', () => {
selected={[]}
retryFn={retryFnMock}
setSelected={setSelectedMock}
updateUnreadCount={updateUnreadCountMock}
/>
</ReduxIntlProviders>,
);
Expand All @@ -40,7 +39,6 @@ describe('Action Buttons', () => {
retryFn={retryFnMock}
isAllSelected={false}
setSelected={setSelectedMock}
updateUnreadCount={updateUnreadCountMock}
unreadCountInSelected={1}
/>
</ReduxIntlProviders>,
Expand All @@ -52,7 +50,6 @@ describe('Action Buttons', () => {
);
await waitFor(() => expect(setSelectedMock).toHaveBeenCalledTimes(1));
await waitFor(() => expect(retryFnMock).toHaveBeenCalledTimes(1));
expect(updateUnreadCountMock).not.toHaveBeenCalled();
});

it('should fetch unread count if all notifications are selected upon marking notifications as read', async () => {
Expand All @@ -65,7 +62,6 @@ describe('Action Buttons', () => {
retryFn={retryFnMock}
isAllSelected={true}
setSelected={setSelectedMock}
updateUnreadCount={updateUnreadCountMock}
/>
</ReduxIntlProviders>,
);
Expand All @@ -76,7 +72,6 @@ describe('Action Buttons', () => {
);
await waitFor(() => expect(setSelectedMock).toHaveBeenCalledTimes(1));
await waitFor(() => expect(retryFnMock).toHaveBeenCalledTimes(1));
expect(updateUnreadCountMock).toHaveBeenCalled();
});

it('should decrement unread count in redux store if all notifications are not selected upon deleting notifications', async () => {
Expand All @@ -89,7 +84,6 @@ describe('Action Buttons', () => {
retryFn={retryFnMock}
isAllSelected={false}
setSelected={setSelectedMock}
updateUnreadCount={updateUnreadCountMock}
unreadCountInSelected={1}
pageOfCards={6}
/>
Expand All @@ -102,7 +96,6 @@ describe('Action Buttons', () => {
);
await waitFor(() => expect(setSelectedMock).toHaveBeenCalledTimes(1));
await waitFor(() => expect(retryFnMock).toHaveBeenCalledTimes(1));
expect(updateUnreadCountMock).not.toHaveBeenCalled();
});

it('should fetch unread count if all notifications are selected upon deleting notifications', async () => {
Expand All @@ -115,7 +108,6 @@ describe('Action Buttons', () => {
retryFn={retryFnMock}
isAllSelected={true}
setSelected={setSelectedMock}
updateUnreadCount={updateUnreadCountMock}
/>
</ReduxIntlProviders>,
);
Expand All @@ -126,13 +118,12 @@ describe('Action Buttons', () => {
);
await waitFor(() => expect(setSelectedMock).toHaveBeenCalledTimes(1));
await waitFor(() => expect(retryFnMock).toHaveBeenCalledTimes(1));
expect(updateUnreadCountMock).toHaveBeenCalled();
});

// Error are consoled in all cases of POST error
it('should catch error when marking multiple selected notifications as read', async () => {
setupFaultyHandlers();
const user = userEvent.setup();
setupFaultyHandlers();
render(
<ReduxIntlProviders>
<ActionButtons
Expand All @@ -141,7 +132,6 @@ describe('Action Buttons', () => {
retryFn={retryFnMock}
isAllSelected={false}
setSelected={setSelectedMock}
updateUnreadCount={updateUnreadCountMock}
unreadCountInSelected={1}
/>
</ReduxIntlProviders>,
Expand All @@ -155,8 +145,8 @@ describe('Action Buttons', () => {
});

it('should catch error when marking all notifications in a category as read', async () => {
setupFaultyHandlers();
const user = userEvent.setup();
setupFaultyHandlers();
render(
<ReduxIntlProviders>
<ActionButtons
Expand All @@ -165,7 +155,6 @@ describe('Action Buttons', () => {
retryFn={retryFnMock}
isAllSelected={true}
setSelected={setSelectedMock}
updateUnreadCount={updateUnreadCountMock}
/>
</ReduxIntlProviders>,
);
Expand All @@ -178,9 +167,9 @@ describe('Action Buttons', () => {
});

it('should catch error when deleting multiple selected notifications', async () => {
const user = userEvent.setup();
act(() => {});
setupFaultyHandlers();
const user = userEvent.setup();
render(
<ReduxIntlProviders>
<ActionButtons
Expand All @@ -189,7 +178,6 @@ describe('Action Buttons', () => {
retryFn={retryFnMock}
isAllSelected={false}
setSelected={setSelectedMock}
updateUnreadCount={updateUnreadCountMock}
unreadCountInSelected={1}
/>
</ReduxIntlProviders>,
Expand All @@ -203,8 +191,8 @@ describe('Action Buttons', () => {
});

it('should catch error when deleting all notifications in a category', async () => {
setupFaultyHandlers();
const user = userEvent.setup();
setupFaultyHandlers();
render(
<ReduxIntlProviders>
<ActionButtons
Expand All @@ -213,7 +201,6 @@ describe('Action Buttons', () => {
retryFn={retryFnMock}
isAllSelected={true}
setSelected={setSelectedMock}
updateUnreadCount={updateUnreadCountMock}
/>
</ReduxIntlProviders>,
);
Expand All @@ -224,4 +211,35 @@ describe('Action Buttons', () => {
);
// Error is then consoled
});

it('should decrement the page query by 1 if the user deletes all notifications on the last page', async () => {
// ACT: there are 3 notifications pages in total, and we're trying to delete
// all the six notifications in the last page
const setInboxQueryMock = jest.fn();
const user = userEvent.setup();
render(
<ReduxIntlProviders>
<ActionButtons
inboxQuery={{ types: undefined, page: 3 }}
selected={[1, 2, 3, 4, 5, 6]}
retryFn={retryFnMock}
isAllSelected={false}
setSelected={setSelectedMock}
pageOfCards={generateSampleNotifications(6)}
totalPages={3}
setInboxQuery={setInboxQueryMock}
/>
</ReduxIntlProviders>,
);
await user.click(
screen.getByRole('button', {
name: /delete/i,
}),
);
await waitFor(() =>
expect(setInboxQueryMock).toHaveBeenCalledWith({ page: 2, types: undefined }, 'pushIn'),
);
await waitFor(() => expect(setSelectedMock).toHaveBeenCalledWith([]));
expect(retryFnMock).not.toBeCalled();
});
});
Loading

0 comments on commit 3b090d1

Please sign in to comment.