diff --git a/src/notification-preferences/NotificationPreferenceApp.jsx b/src/notification-preferences/NotificationPreferenceApp.jsx index 5ebad140f..ed57e66d9 100644 --- a/src/notification-preferences/NotificationPreferenceApp.jsx +++ b/src/notification-preferences/NotificationPreferenceApp.jsx @@ -41,7 +41,7 @@ const NotificationPreferenceApp = ({ appId }) => { return null; } return ( - +
diff --git a/src/notification-preferences/NotificationPreferenceRow.jsx b/src/notification-preferences/NotificationPreferenceRow.jsx index 113af1c57..8541a9e0c 100644 --- a/src/notification-preferences/NotificationPreferenceRow.jsx +++ b/src/notification-preferences/NotificationPreferenceRow.jsx @@ -78,6 +78,7 @@ const NotificationPreferenceRow = ({ appId, preferenceName }) => { value={preference[channel]} onChange={onToggle} disabled={nonEditable.includes(channel) || updatePreferencesStatus === LOADING_STATUS} + id={`${preferenceName}-${channel}`} />
))} diff --git a/src/notification-preferences/NotificationPreferences.test.jsx b/src/notification-preferences/NotificationPreferences.test.jsx index f57e50beb..6dba6af03 100644 --- a/src/notification-preferences/NotificationPreferences.test.jsx +++ b/src/notification-preferences/NotificationPreferences.test.jsx @@ -2,7 +2,9 @@ import { Provider } from 'react-redux'; import { BrowserRouter as Router } from 'react-router-dom'; import configureStore from 'redux-mock-store'; -import { fireEvent, render, screen } from '@testing-library/react'; +import { + fireEvent, render, screen, waitFor, act, within, +} from '@testing-library/react'; import * as auth from '@edx/frontend-platform/auth'; import { IntlProvider } from '@edx/frontend-platform/i18n'; import NotificationPreferences from './NotificationPreferences'; @@ -29,37 +31,51 @@ const defaultPreferences = { ], preferences: [ { - id: 'newPost', + id: 'core', appId: 'discussion', - web: false, - push: false, - mobile: false, + web: true, + push: true, + email: true, }, { id: 'newComment', appId: 'discussion', web: false, push: false, - mobile: false, + email: false, }, { id: 'newAssignment', appId: 'coursework', web: false, push: false, - mobile: false, + email: false, }, { id: 'newGrade', appId: 'coursework', web: false, push: false, - mobile: false, + email: false, }, ], - nonEditable: {}, + nonEditable: { + discussion: { + core: [ + 'web', + ], + }, + }, }; +const updateChannelPreferences = (toggleVal = false) => ({ + preferences: [ + { id: 'core', appId: 'discussion', web: true }, + { id: 'newComment', appId: 'discussion', web: toggleVal }, + { id: 'newAssignment', appId: 'coursework', web: toggleVal }, + ], +}); + const setupStore = (override = {}) => { const storeState = defaultState; storeState.courses = { @@ -78,17 +94,19 @@ const setupStore = (override = {}) => { return store; }; -const renderComponent = (store = {}) => render( +const notificationPreferences = (store = {}) => ( - , + ); + describe('Notification Preferences', () => { let store; + beforeEach(() => { store = setupStore({ ...defaultPreferences, @@ -108,30 +126,32 @@ describe('Notification Preferences', () => { afterEach(() => jest.clearAllMocks()); it('tests if all notification apps are listed', async () => { - await renderComponent(store); - expect(screen.queryAllByTestId('notification-app')).toHaveLength(2); + await render(notificationPreferences(store)); + expect(screen.queryByTestId('discussion-app')).toBeInTheDocument(); + expect(screen.queryByTestId('coursework-app')).toBeInTheDocument(); }); + it('show spinner if api call is in progress', async () => { store = setupStore({ status: LOADING_STATUS }); - await renderComponent(store); + await render(notificationPreferences(store)); expect(screen.queryByTestId('loading-spinner')).toBeInTheDocument(); }); it('tests if all notification preferences are listed', async () => { - await renderComponent(store); + await render(notificationPreferences(store)); expect(screen.queryAllByTestId('notification-preference')).toHaveLength(4); }); it('update group on click', async () => { - const wrapper = await renderComponent(store); + const wrapper = await render(notificationPreferences(store)); const element = wrapper.container.querySelector('#discussion-app-toggle'); await fireEvent.click(element); expect(mockDispatch).toHaveBeenCalled(); }); it('update preference on click', async () => { - const wrapper = await renderComponent(store); - const element = wrapper.container.querySelector('#newPost-web'); + const wrapper = await render(notificationPreferences(store)); + const element = wrapper.container.querySelector('#core-web'); expect(element).not.toBeChecked(); await fireEvent.click(element); expect(mockDispatch).toHaveBeenCalled(); @@ -139,7 +159,43 @@ describe('Notification Preferences', () => { it('show not found page if invalid course id is entered in url', async () => { store = setupStore({ status: FAILURE_STATUS, selectedCourse: 'invalid-course-id' }); - await renderComponent(store); + await render(notificationPreferences(store)); expect(screen.queryByTestId('not-found-page')).toBeInTheDocument(); }); + + it('updates all preferences in the column on web channel click', async () => { + store = setupStore(updateChannelPreferences(true)); + const wrapper = render(notificationPreferences(store)); + + const getChannelSwitch = (id) => screen.queryByTestId(`${id}-web`); + const notificationTypes = ['newComment', 'newAssignment']; + + const verifyState = (toggleState) => { + notificationTypes.forEach((notificationType) => { + if (toggleState) { + expect(getChannelSwitch(notificationType)).toBeChecked(); + } else { + expect(getChannelSwitch(notificationType)).not.toBeChecked(); + } + }); + }; + + verifyState(true); + expect(getChannelSwitch('core')).toBeChecked(); + + const discussionApp = screen.queryByTestId('discussion-app'); + const webChannel = within(discussionApp).queryByText('Web'); + + await act(async () => { + await fireEvent.click(webChannel); + }); + + store = setupStore(updateChannelPreferences(false)); + wrapper.rerender(notificationPreferences(store)); + + await waitFor(() => { + verifyState(false); + expect(getChannelSwitch('core')).toBeChecked(); + }); + }); }); diff --git a/src/notification-preferences/ToggleSwitch.jsx b/src/notification-preferences/ToggleSwitch.jsx index 83e1183ea..91c2c995d 100644 --- a/src/notification-preferences/ToggleSwitch.jsx +++ b/src/notification-preferences/ToggleSwitch.jsx @@ -7,12 +7,14 @@ const ToggleSwitch = ({ value, disabled, onChange, + id, }) => ( ); @@ -21,11 +23,13 @@ ToggleSwitch.propTypes = { value: PropTypes.bool.isRequired, disabled: PropTypes.bool, onChange: PropTypes.func, + id: PropTypes.string, }; ToggleSwitch.defaultProps = { onChange: () => null, disabled: false, + id: '', }; export default React.memo(ToggleSwitch);