diff --git a/src/data/redux.test.js b/src/data/redux.test.js index 3f2eb046..982a8ce2 100644 --- a/src/data/redux.test.js +++ b/src/data/redux.test.js @@ -8,6 +8,7 @@ import { initializeStore } from '../store'; import { executeThunk } from '../test-utils'; import { getBlocksAPIResponse } from './__factories__'; import { blocksAPIURL } from './api'; +import { RequestStatus } from './constants'; import { fetchCourseBlocks } from './thunks'; const courseId = 'course-v1:edX+DemoX+Demo_Course'; @@ -35,7 +36,7 @@ describe('Course blocks data layer tests', () => { axiosMock.reset(); }); - test('successfully processes block data', async () => { + it('successfully processes block data', async () => { axiosMock.onGet(blocksAPIURL) .reply(200, getBlocksAPIResponse()); @@ -77,4 +78,29 @@ describe('Course blocks data layer tests', () => { }, ); }); + + it('handles network error', async () => { + axiosMock.onGet(blocksAPIURL).networkError(); + + await executeThunk(fetchCourseBlocks(courseId, 'test-user'), store.dispatch, store.getState); + + expect(store.getState().blocks.status) + .toBe(RequestStatus.FAILED); + }); + it('handles network timeout', async () => { + axiosMock.onGet(blocksAPIURL).timeout(); + + await executeThunk(fetchCourseBlocks(courseId, 'test-user'), store.dispatch, store.getState); + + expect(store.getState().blocks.status) + .toBe(RequestStatus.FAILED); + }); + it('handles access denied', async () => { + axiosMock.onGet(blocksAPIURL).reply(403, {}); + + await executeThunk(fetchCourseBlocks(courseId, 'test-user'), store.dispatch, store.getState); + + expect(store.getState().blocks.status) + .toBe(RequestStatus.DENIED); + }); }); diff --git a/src/discussions/learners/LearnersContentView.test.jsx b/src/discussions/learners/LearnersContentView.test.jsx index 8326a902..432c7fb0 100644 --- a/src/discussions/learners/LearnersContentView.test.jsx +++ b/src/discussions/learners/LearnersContentView.test.jsx @@ -1,6 +1,8 @@ import React from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; +import { + fireEvent, render, screen, waitFor, +} from '@testing-library/react'; import MockAdapter from 'axios-mock-adapter'; import { act } from 'react-dom/test-utils'; import { IntlProvider } from 'react-intl'; @@ -141,7 +143,7 @@ describe('LearnersContentView', () => { await act(async () => { fireEvent.click(screen.getByRole('link', { name: /Posts \d+/i })); }); - expect(screen.queryAllByTestId('post')).toHaveLength(5); + await waitFor(() => expect(screen.queryAllByTestId('post')).toHaveLength(5)); }); describe('Posts Tab Button', () => { diff --git a/src/discussions/posts/PostsView.test.jsx b/src/discussions/posts/PostsView.test.jsx index b3b8d877..9288b592 100644 --- a/src/discussions/posts/PostsView.test.jsx +++ b/src/discussions/posts/PostsView.test.jsx @@ -219,16 +219,6 @@ describe('PostsView', () => { secondClick: 'Discussions', selected: ['Discussions', 'Any'], }, - { - firstClick: 'Questions', - secondClick: 'Unread', - selected: ['Discussions', 'Unread'], - }, - { - firstClick: 'Unread', - secondClick: 'Questions', - selected: ['Questions', 'Any'], - }, ])( 'incompatible combinations', ({ diff --git a/src/discussions/posts/post-filter-bar/PostFilterBar.jsx b/src/discussions/posts/post-filter-bar/PostFilterBar.jsx index a6c9e996..174f0c1f 100644 --- a/src/discussions/posts/post-filter-bar/PostFilterBar.jsx +++ b/src/discussions/posts/post-filter-bar/PostFilterBar.jsx @@ -60,10 +60,9 @@ function PostFilterBar({ if (name === 'type') { dispatch(setPostsTypeFilter(value)); if ( - (value === ThreadType.DISCUSSION && currentStatus === PostsStatusFilter.UNANSWERED) - || (value === ThreadType.QUESTION && currentStatus === PostsStatusFilter.UNREAD) + value === ThreadType.DISCUSSION && currentStatus === PostsStatusFilter.UNANSWERED ) { - // You can't filter discussions by unanswered or questions by unread + // You can't filter discussions by unanswered dispatch(setStatusFilter(PostsStatusFilter.ALL)); } } @@ -73,10 +72,6 @@ function PostFilterBar({ // You can't filter discussions by unanswered so switch type to questions dispatch(setPostsTypeFilter(ThreadType.QUESTION)); } - if (value === PostsStatusFilter.UNREAD && currentType !== ThreadType.DISCUSSION) { - // You can't filter questions by unread so switch type to discussion - dispatch(setPostsTypeFilter(ThreadType.DISCUSSION)); - } } if (name === 'sort') { dispatch(setSortedBy(value)); diff --git a/src/discussions/utils.js b/src/discussions/utils.js index 9d90ce26..3f403380 100644 --- a/src/discussions/utils.js +++ b/src/discussions/utils.js @@ -13,9 +13,9 @@ import messages from './messages'; /** * Get HTTP Error status from generic error. * @param error Generic caught error. - * @returns {number|undefined} + * @returns {number|null} */ -export const getHttpErrorStatus = error => error && error.customAttributes && error.customAttributes.httpErrorStatus; +export const getHttpErrorStatus = error => error?.customAttributes?.httpErrorStatus ?? error?.response?.status; /** * Return true if a field has been modified and is no longer valid