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, + ); + }); + }); });