diff --git a/src/course-checklist/CourseChecklist.test.jsx b/src/course-checklist/CourseChecklist.test.jsx
index 53d52af77e..3b26b56ef8 100644
--- a/src/course-checklist/CourseChecklist.test.jsx
+++ b/src/course-checklist/CourseChecklist.test.jsx
@@ -149,5 +149,21 @@ describe('CourseChecklistPage', () => {
});
});
});
+
+ it('displays an alert and sets status to DENIED when API responds with 403', async () => {
+ const courseLaunchApiUrl = getCourseLaunchApiUrl({
+ courseId, gradedOnly: true, validateOras: true, all: true,
+ });
+ axiosMock.onGet(courseLaunchApiUrl).reply(403);
+
+ renderComponent();
+
+ await waitFor(() => {
+ const { launchChecklistStatus } = store.getState().courseChecklist.loadingStatus;
+
+ expect(launchChecklistStatus).toEqual(RequestStatus.DENIED);
+ expect(screen.getByRole('alert')).toBeInTheDocument();
+ });
+ });
});
});
diff --git a/src/course-outline/CourseOutline.test.jsx b/src/course-outline/CourseOutline.test.jsx
index b7f8332eeb..bfe6c07705 100644
--- a/src/course-outline/CourseOutline.test.jsx
+++ b/src/course-outline/CourseOutline.test.jsx
@@ -2291,4 +2291,18 @@ describe('', () => {
expect(await screen.findByText('Please wait. Creating export file for course tags...')).toBeInTheDocument();
expect(await screen.findByText('An error has occurred creating the file')).toBeInTheDocument();
});
+
+ it('displays an alert and sets status to DENIED when API responds with 403', async () => {
+ axiosMock
+ .onGet(getCourseOutlineIndexApiUrl(courseId))
+ .reply(403);
+
+ const { getByRole } = render();
+
+ await waitFor(() => {
+ expect(getByRole('alert')).toBeInTheDocument();
+ const { outlineIndexLoadingStatus } = store.getState().courseOutline.loadingStatus;
+ expect(outlineIndexLoadingStatus).toEqual(RequestStatus.DENIED);
+ });
+ });
});
diff --git a/src/course-team/CourseTeam.test.jsx b/src/course-team/CourseTeam.test.jsx
index 4f33788744..31f9c78fdc 100644
--- a/src/course-team/CourseTeam.test.jsx
+++ b/src/course-team/CourseTeam.test.jsx
@@ -1,4 +1,3 @@
-import React from 'react';
import {
render,
fireEvent,
@@ -18,6 +17,7 @@ import CourseTeam from './CourseTeam';
import messages from './messages';
import { USER_ROLES } from '../constants';
import { executeThunk } from '../utils';
+import { RequestStatus } from '../data/constants';
import { changeRoleTeamUserQuery, deleteCourseTeamQuery } from './data/thunk';
let axiosMock;
@@ -219,4 +219,18 @@ describe('', () => {
await executeThunk(changeRoleTeamUserQuery(courseId, 'staff@example.com', { role: USER_ROLES.admin }), store.dispatch);
expect(getAllByText('Admin')).toHaveLength(1);
});
+
+ it('displays an alert and sets status to DENIED when API responds with 403', async () => {
+ axiosMock
+ .onGet(getCourseTeamApiUrl(courseId))
+ .reply(403);
+
+ const { getByRole } = render();
+
+ await waitFor(() => {
+ expect(getByRole('alert')).toBeInTheDocument();
+ const { loadingCourseTeamStatus } = store.getState().courseTeam;
+ expect(loadingCourseTeamStatus).toEqual(RequestStatus.DENIED);
+ });
+ });
});
diff --git a/src/course-updates/CourseUpdates.test.jsx b/src/course-updates/CourseUpdates.test.jsx
index 387d3b3c26..763611a4a0 100644
--- a/src/course-updates/CourseUpdates.test.jsx
+++ b/src/course-updates/CourseUpdates.test.jsx
@@ -19,6 +19,7 @@ import {
} from './data/thunk';
import initializeStore from '../store';
import { executeThunk } from '../utils';
+import { RequestStatus } from '../data/constants';
import { courseUpdatesMock, courseHandoutsMock } from './__mocks__';
import CourseUpdates from './CourseUpdates';
import messages from './messages';
@@ -278,6 +279,24 @@ describe('', () => {
expect(getByTestId('course-handouts-edit-button')).toBeDisabled();
});
});
+
+ it('displays an alert and sets status to DENIED when API responds with 403', async () => {
+ axiosMock
+ .onGet(getCourseUpdatesApiUrl(courseId))
+ .reply(403, courseUpdatesMock);
+ axiosMock
+ .onGet(getCourseHandoutApiUrl(courseId))
+ .reply(403);
+
+ const { getByRole } = render();
+
+ await waitFor(() => {
+ expect(getByRole('alert')).toBeInTheDocument();
+ const { loadingStatuses } = store.getState().courseUpdates;
+ Object.values(loadingStatuses)
+ .some(status => expect(status).toEqual(RequestStatus.DENIED));
+ });
+ });
});
describe('saving failure API responses', () => {
diff --git a/src/grading-settings/GradingSettings.jsx b/src/grading-settings/GradingSettings.jsx
index 8ddd7a9bb5..bcb3a8f089 100644
--- a/src/grading-settings/GradingSettings.jsx
+++ b/src/grading-settings/GradingSettings.jsx
@@ -8,7 +8,6 @@ import {
useGradingSettings,
useGradingSettingUpdater,
} from 'CourseAuthoring/grading-settings/data/apiHooks';
-import { RequestStatus } from 'CourseAuthoring/data/constants';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import { Helmet } from 'react-helmet';
@@ -34,10 +33,12 @@ const GradingSettings = ({ courseId }) => {
const {
data: gradingSettings,
isLoading: isGradingSettingsLoading,
+ isError: isGradingSettingsError,
} = useGradingSettings(courseId);
const {
data: courseSettingsData,
isLoading: isCourseSettingsLoading,
+ isError: isCourseSettingsError,
} = useCourseSettings(courseId);
const {
mutate: updateGradingSettings,
@@ -48,7 +49,7 @@ const GradingSettings = ({ courseId }) => {
const courseAssignmentLists = gradingSettings?.courseAssignmentLists;
const courseGradingDetails = gradingSettings?.courseDetails;
- const isLoadingDenied = isCourseSettingsLoading === RequestStatus.DENIED;
+ const isLoadingDenied = isGradingSettingsError || isCourseSettingsError;
const [showSuccessAlert, setShowSuccessAlert] = useState(false);
const isLoading = isCourseSettingsLoading || isGradingSettingsLoading;
const [isQueryPending, setIsQueryPending] = useState(false);
diff --git a/src/group-configurations/GroupConfigurations.test.jsx b/src/group-configurations/GroupConfigurations.test.jsx
index 34486c368b..dd0dba55c0 100644
--- a/src/group-configurations/GroupConfigurations.test.jsx
+++ b/src/group-configurations/GroupConfigurations.test.jsx
@@ -103,4 +103,21 @@ describe('', () => {
RequestStatus.FAILED,
);
});
+
+ it('displays an alert and sets status to DENIED when API responds with 403', async () => {
+ axiosMock
+ .onGet(getContentStoreApiUrl(courseId))
+ .reply(403);
+
+ await executeThunk(fetchGroupConfigurationsQuery(courseId), store.dispatch);
+
+ const { getByRole } = renderComponent();
+
+ await waitFor(() => {
+ expect(getByRole('alert')).toBeInTheDocument();
+ expect(store.getState().groupConfigurations.loadingStatus).toBe(
+ RequestStatus.DENIED,
+ );
+ });
+ });
});
diff --git a/src/textbooks/Textbook.test.jsx b/src/textbooks/Textbook.test.jsx
index b8437f114f..65c0f5cb05 100644
--- a/src/textbooks/Textbook.test.jsx
+++ b/src/textbooks/Textbook.test.jsx
@@ -6,6 +6,7 @@ import { initializeMockApp } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import userEvent from '@testing-library/user-event';
+import { RequestStatus } from '../data/constants';
import initializeStore from '../store';
import { executeThunk } from '../utils';
import { getTextbooksApiUrl } from './data/api';
@@ -84,4 +85,19 @@ describe('', () => {
expect(queryAllByTestId('textbook-card')).toHaveLength(0);
});
});
+
+ it('displays an alert and sets status to FAILED when API responds with 403', async () => {
+ axiosMock
+ .onGet(getTextbooksApiUrl(courseId))
+ .reply(403);
+ await executeThunk(fetchTextbooksQuery(courseId), store.dispatch);
+ const { getByRole } = renderComponent();
+
+ await waitFor(() => {
+ expect(getByRole('alert')).toBeInTheDocument();
+ expect(store.getState().textbooks.loadingStatus).toBe(
+ RequestStatus.FAILED,
+ );
+ });
+ });
});