From 0e6a272f2b1a395859974bfa75925d8a65abcf9f Mon Sep 17 00:00:00 2001 From: SundasNoreen Date: Mon, 26 Jun 2023 13:11:28 +0500 Subject: [PATCH] test: added test cases for UI components --- src/Notifications/NotificationRowItem.jsx | 18 +++- src/Notifications/NotificationSections.jsx | 5 +- src/Notifications/NotificationTabs.jsx | 1 + src/Notifications/index.jsx | 6 +- src/Notifications/index.test.jsx | 84 +++++++++++++++ .../notificationRowItem.test.jsx | 87 +++++++++++++++ .../notificationSections.test.jsx | 101 ++++++++++++++++++ src/Notifications/notificationTabs.test.jsx | 94 ++++++++++++++++ src/Notifications/test-utils.js | 31 ++++++ 9 files changed, 418 insertions(+), 9 deletions(-) create mode 100644 src/Notifications/index.test.jsx create mode 100644 src/Notifications/notificationRowItem.test.jsx create mode 100644 src/Notifications/notificationSections.test.jsx create mode 100644 src/Notifications/notificationTabs.test.jsx create mode 100644 src/Notifications/test-utils.js diff --git a/src/Notifications/NotificationRowItem.jsx b/src/Notifications/NotificationRowItem.jsx index 8f7aaeb..90d96e4 100644 --- a/src/Notifications/NotificationRowItem.jsx +++ b/src/Notifications/NotificationRowItem.jsx @@ -29,27 +29,35 @@ const NotificationRowItem = ({ className="d-flex mb-2 align-items-center text-decoration-none" to={contentUrl} onClick={handleMarkAsRead} + data-testid={`notification-${id}`} > - -
+ +
- {courseName} + {courseName} + {intl.formatMessage(messages.fullStop)} - {timeago.format(createdAt, 'time-locale')} + {timeago.format(createdAt, 'time-locale')} +
{!lastRead && (
- +
)}
diff --git a/src/Notifications/NotificationSections.jsx b/src/Notifications/NotificationSections.jsx index 2048ce5..945ecec 100644 --- a/src/Notifications/NotificationSections.jsx +++ b/src/Notifications/NotificationSections.jsx @@ -44,6 +44,7 @@ const NotificationSections = () => { variant="link" className="text-info-500 font-size-14 line-height-10 text-decoration-none p-0 border-0" onClick={handleMarkAllAsRead} + data-testid="mark-all-read" > {intl.formatMessage(messages.notificationMarkAsRead)} @@ -66,11 +67,11 @@ const NotificationSections = () => { }; return ( -
+
{renderNotificationSection('today', today)} {renderNotificationSection('earlier', earlier)} {currentPage < numPages && ( - )} diff --git a/src/Notifications/NotificationTabs.jsx b/src/Notifications/NotificationTabs.jsx index 490ec06..79d3611 100644 --- a/src/Notifications/NotificationTabs.jsx +++ b/src/Notifications/NotificationTabs.jsx @@ -32,6 +32,7 @@ const NotificationTabs = () => { title={appName} notification={notificationUnseenCounts[appName]} tabClassName="pt-0 pb-10px px-2.5 d-flex border-top-0 mb-0 align-items-center line-height-24 text-capitalize" + data-testid={`notification-tab-${appName}`} > {appName === selectedAppName && ()} diff --git a/src/Notifications/index.jsx b/src/Notifications/index.jsx index 30d28b9..31b3aa2 100644 --- a/src/Notifications/index.jsx +++ b/src/Notifications/index.jsx @@ -54,7 +54,7 @@ const Notifications = () => { overlay={( {
{intl.formatMessage(messages.notificationTitle)} - + @@ -83,12 +83,14 @@ const Notifications = () => { variant="light" iconClassNames="text-primary-500" className="ml-4 mr-1 my-3 notification-button" + data-testid="notification-bell-icon" /> {notificationCounts?.count > 0 && ( {notificationCounts.count} diff --git a/src/Notifications/index.test.jsx b/src/Notifications/index.test.jsx new file mode 100644 index 0000000..e4fdec4 --- /dev/null +++ b/src/Notifications/index.test.jsx @@ -0,0 +1,84 @@ +import React from 'react'; + +import { + act, fireEvent, render, screen, waitFor, +} from '@testing-library/react'; +import MockAdapter from 'axios-mock-adapter'; +import { Context as ResponsiveContext } from 'react-responsive'; +import { Factory } from 'rosie'; + +import { initializeMockApp } from '@edx/frontend-platform'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { AppContext, AppProvider } from '@edx/frontend-platform/react'; + +import AuthenticatedUserDropdown from '../learning-header/AuthenticatedUserDropdown'; +import { initializeStore } from '../store'; +import executeThunk from '../test-utils'; +import { getNotificationsCountApiUrl } from './data/api'; +import { fetchAppsNotificationCount } from './data/thunks'; + +import './data/__factories__'; + +const notificationCountsApiUrl = getNotificationsCountApiUrl(); + +let axiosMock; +let store; + +async function renderComponent() { + await render( + + + + + + + + + , + ); +} + +describe('Notification test cases.', () => { + beforeEach(async () => { + initializeMockApp({ + authenticatedUser: { + userId: '123abc', + username: 'testuser', + administrator: false, + roles: [], + }, + }); + + axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + Factory.resetAll(); + store = initializeStore(); + + axiosMock.onGet(notificationCountsApiUrl).reply(200, (Factory.build('notificationsCount'))); + await executeThunk(fetchAppsNotificationCount(), store.dispatch, store.getState); + }); + + it('successfully showed bell icon and unseen count on it if unseen count is greater then 0.', async () => { + await renderComponent(); + + const bellIcon = screen.queryByTestId('notification-bell-icon'); + const notificationCount = screen.queryByTestId('notification-count'); + + expect(bellIcon).toBeInTheDocument(); + expect(notificationCount).toBeInTheDocument(); + expect(screen.queryByText(45)).toBeInTheDocument(); + }); + + it('successfully show/hide notification tray by clicking on the bell icon and setting icon.', async () => { + await renderComponent(); + + const bellIcon = screen.queryByTestId('notification-bell-icon'); + + await act(async () => { fireEvent.click(bellIcon); }); + expect(screen.queryByTestId('notification-tray')).toBeInTheDocument(); + expect(screen.queryByTestId('setting-icon')).toBeInTheDocument(); + + await act(async () => { fireEvent.click(bellIcon); }); + await waitFor(() => expect(screen.queryByTestId('notification-tray')).not.toBeInTheDocument()); + }); +}); diff --git a/src/Notifications/notificationRowItem.test.jsx b/src/Notifications/notificationRowItem.test.jsx new file mode 100644 index 0000000..7587d53 --- /dev/null +++ b/src/Notifications/notificationRowItem.test.jsx @@ -0,0 +1,87 @@ +import React from 'react'; + +import { + act, fireEvent, render, screen, +} from '@testing-library/react'; +import MockAdapter from 'axios-mock-adapter'; +import { Context as ResponsiveContext } from 'react-responsive'; +import { Factory } from 'rosie'; + +import { initializeMockApp } from '@edx/frontend-platform'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { AppContext, AppProvider } from '@edx/frontend-platform/react'; + +import AuthenticatedUserDropdown from '../learning-header/AuthenticatedUserDropdown'; +import { initializeStore } from '../store'; +import { markNotificationAsReadApiUrl } from './data/api'; +import setupLearnerMockResponse from './test-utils'; + +import './data/__factories__'; + +const markedNotificationAsReadApiUrl = markNotificationAsReadApiUrl(); + +let axiosMock; +let store; + +async function renderComponent() { + await render( + + + + + + + + + , + ); +} + +describe('Notification row item test cases.', () => { + beforeEach(async () => { + initializeMockApp({ + authenticatedUser: { + userId: 3, + username: 'abc123', + administrator: true, + roles: [], + }, + }); + + axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + Factory.resetAll(); + store = initializeStore(); + + ({ store, axiosMock } = await setupLearnerMockResponse()); + }); + + it( + 'Successfully viewed notification icon, notification context, unread , course name and notification time.', + async () => { + await renderComponent(); + + const bellIcon = screen.queryByTestId('notification-bell-icon'); + await act(async () => { fireEvent.click(bellIcon); }); + + expect(screen.queryByTestId('notification-icon-1')).toBeInTheDocument(); + expect(screen.queryByTestId('notification-content-1')).toBeInTheDocument(); + expect(screen.queryByTestId('notification-course-1')).toBeInTheDocument(); + expect(screen.queryByTestId('notification-created-date-1')).toBeInTheDocument(); + expect(screen.queryByTestId('unread-1')).toBeInTheDocument(); + }, + ); + + it('Successfully marked notification as read.', async () => { + axiosMock.onPut(markedNotificationAsReadApiUrl).reply(200, { message: 'Notification marked read.' }); + await renderComponent(); + + const bellIcon = screen.queryByTestId('notification-bell-icon'); + await act(async () => { fireEvent.click(bellIcon); }); + + const notification = screen.queryByTestId('notification-1'); + await act(async () => { fireEvent.click(notification); }); + + expect(screen.queryByTestId('unread-1')).not.toBeInTheDocument(); + }); +}); diff --git a/src/Notifications/notificationSections.test.jsx b/src/Notifications/notificationSections.test.jsx new file mode 100644 index 0000000..affd85e --- /dev/null +++ b/src/Notifications/notificationSections.test.jsx @@ -0,0 +1,101 @@ +import React from 'react'; + +import { + act, fireEvent, render, screen, within, +} from '@testing-library/react'; +import MockAdapter from 'axios-mock-adapter'; +import { Context as ResponsiveContext } from 'react-responsive'; +import { Factory } from 'rosie'; + +import { initializeMockApp } from '@edx/frontend-platform'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { AppContext, AppProvider } from '@edx/frontend-platform/react'; + +import AuthenticatedUserDropdown from '../learning-header/AuthenticatedUserDropdown'; +import { initializeStore } from '../store'; +import { markNotificationAsReadApiUrl } from './data/api'; +import setupLearnerMockResponse from './test-utils'; + +import './data/__factories__'; + +const markedAllNotificationsAsReadApiUrl = markNotificationAsReadApiUrl(); + +let axiosMock; +let store; + +async function renderComponent() { + await render( + + + + + + + + + , + ); +} + +describe('Notification sections test cases.', () => { + beforeEach(async () => { + initializeMockApp({ + authenticatedUser: { + userId: 3, + username: 'abc123', + administrator: true, + roles: [], + }, + }); + + axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + Factory.resetAll(); + store = initializeStore(); + + ({ store, axiosMock } = await setupLearnerMockResponse()); + }); + + it('Successfully viewed last 24 hours and earlier section along with nark all as read label.', async () => { + await renderComponent(); + + const bellIcon = screen.queryByTestId('notification-bell-icon'); + await act(async () => { fireEvent.click(bellIcon); }); + const notificationTraySection = screen.queryByTestId('notification-tray-section'); + + expect(within(notificationTraySection).queryByText('Last 24 hours')).toBeInTheDocument(); + expect(within(notificationTraySection).queryByText('Earlier')).toBeInTheDocument(); + expect(within(notificationTraySection).queryByText('Mark all as read')).toBeInTheDocument(); + }); + + it( + 'Successfully clicks on the mark all as read, the unread status disappeared across all the notifications', + async () => { + axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(200, { message: 'Notifications marked read.' }); + await renderComponent(); + + const bellIcon = screen.queryByTestId('notification-bell-icon'); + await act(async () => { fireEvent.click(bellIcon); }); + const markAllReadButton = screen.queryByTestId('mark-all-read'); + + expect(screen.queryByTestId('unread-1')).toBeInTheDocument(); + await act(async () => { fireEvent.click(markAllReadButton); }); + + expect(screen.queryByTestId('unread-1')).not.toBeInTheDocument(); + }, + ); + + it('Successfully load more notifications by clicking on load more notification button.', async () => { + await renderComponent(); + + const bellIcon = screen.queryByTestId('notification-bell-icon'); + await act(async () => { fireEvent.click(bellIcon); }); + + const loadMoreButton = screen.queryByTestId('load-more'); + + expect(screen.queryAllByTestId('notification-contents')).toHaveLength(10); + await act(async () => { fireEvent.click(loadMoreButton); }); + + expect(screen.queryAllByTestId('notification-contents')).toHaveLength(16); + }); +}); diff --git a/src/Notifications/notificationTabs.test.jsx b/src/Notifications/notificationTabs.test.jsx new file mode 100644 index 0000000..7d6e420 --- /dev/null +++ b/src/Notifications/notificationTabs.test.jsx @@ -0,0 +1,94 @@ +import React from 'react'; + +import { + act, fireEvent, render, screen, within, +} from '@testing-library/react'; +import { Context as ResponsiveContext } from 'react-responsive'; +import { Factory } from 'rosie'; + +import { initializeMockApp } from '@edx/frontend-platform'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { AppContext, AppProvider } from '@edx/frontend-platform/react'; + +import AuthenticatedUserDropdown from '../learning-header/AuthenticatedUserDropdown'; +import { initializeStore } from '../store'; +import setupLearnerMockResponse from './test-utils'; + +import './data/__factories__'; + +let store; + +async function renderComponent() { + await render( + + + + + + + + + , + ); +} + +describe('Notification Tabs test cases.', () => { + beforeEach(async () => { + initializeMockApp({ + authenticatedUser: { + userId: '123abc', + username: 'testuser', + administrator: false, + roles: [], + }, + }); + + Factory.resetAll(); + store = initializeStore(); + + ({ store } = await setupLearnerMockResponse()); + }); + + it( + 'Successfully showed list of notification tabs, discussion tab selected by default and no unseen counts for it.', + async () => { + await renderComponent(); + + const bellIcon = screen.queryByTestId('notification-bell-icon'); + await act(async () => { fireEvent.click(bellIcon); }); + + const tabs = screen.queryAllByRole('tab'); + const selectedTab = tabs.find(tab => tab.getAttribute('aria-selected') === 'true'); + + expect(tabs.length).toEqual(5); + expect(within(selectedTab).queryByText('discussions')).toBeInTheDocument(); + expect(within(selectedTab).queryByRole('status')).not.toBeInTheDocument(); + }, + ); + + it('Successfully showed unseen counts for unselected tabs.', async () => { + await renderComponent(); + const bellIcon = screen.queryByTestId('notification-bell-icon'); + await act(async () => { fireEvent.click(bellIcon); }); + + const tabs = screen.getAllByRole('tab'); + + expect(within(tabs[0]).queryByRole('status')).toBeInTheDocument(); + }); + + it('Successfully selected reminder tab.', async () => { + await renderComponent(); + + const bellIcon = screen.queryByTestId('notification-bell-icon'); + await act(async () => { fireEvent.click(bellIcon); }); + const notificationTab = screen.getAllByRole('tab'); + + await act(async () => { fireEvent.click(notificationTab[0], { dataset: { rbEventKey: 'reminders' } }); }); + + const tabs = screen.queryAllByRole('tab'); + const selectedTab = tabs.find(tab => tab.getAttribute('aria-selected') === 'true'); + + expect(within(selectedTab).queryByText('reminders')).toBeInTheDocument(); + expect(within(selectedTab).queryByRole('status')).not.toBeInTheDocument(); + }); +}); diff --git a/src/Notifications/test-utils.js b/src/Notifications/test-utils.js new file mode 100644 index 0000000..f6c077c --- /dev/null +++ b/src/Notifications/test-utils.js @@ -0,0 +1,31 @@ +import MockAdapter from 'axios-mock-adapter'; +import { Factory } from 'rosie'; + +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; + +import { initializeStore } from '../store'; +import executeThunk from '../test-utils'; +import { getNotificationsApiUrl, getNotificationsCountApiUrl } from './data/api'; +import { fetchAppsNotificationCount, fetchNotificationList } from './data/thunks'; + +import './data/__factories__'; + +const notificationCountsApiUrl = getNotificationsCountApiUrl(); +const notificationsApiUrl = getNotificationsApiUrl(); + +export default async function setupLearnerMockResponse() { + const store = initializeStore(); + const axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + + axiosMock.onGet(notificationCountsApiUrl).reply(200, (Factory.build('notificationsCount'))); + axiosMock.onGet(notificationsApiUrl).reply( + 200, + (Factory.buildList('notification', 8, null, { createdDate: new Date().toISOString() }).concat( + Factory.buildList('notification', 8, null, { createdDate: '2023-06-01T00:46:11.979531Z' }), + )), + ); + + await executeThunk(fetchAppsNotificationCount(), store.dispatch, store.getState); + await executeThunk(fetchNotificationList({ page: 1, pageSize: 10 }), store.dispatch, store.getState); + return { store, axiosMock }; +}