diff --git a/src/course-home/outline-tab/OutlineTab.test.jsx b/src/course-home/outline-tab/OutlineTab.test.jsx index 41335e33..c9e8aa68 100644 --- a/src/course-home/outline-tab/OutlineTab.test.jsx +++ b/src/course-home/outline-tab/OutlineTab.test.jsx @@ -1017,6 +1017,22 @@ describe('Outline Tab', () => { }); it('displays expiration warning', async () => { + const expirationDate = new Date(); + // This message will render if the expiration date is within 28 days; set the date 10 days in future + expirationDate.setTime(expirationDate.getTime() + 864800000); + axiosMock.onGet(proctoringInfoUrl).reply(200, { + onboarding_status: 'verified', + onboarding_link: 'test', + expiration_date: expirationDate.toString(), + onboarding_release_date: onboardingReleaseDate.toISOString(), + }); + await fetchAndRender(); + await screen.findByText('This course contains proctored exams'); + expect(screen.queryByText('Your onboarding profile has been approved. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.')).toBeInTheDocument(); + expect(screen.queryByText('Onboarding profile review can take 2+ business days.')).toBeInTheDocument(); + }); + + it('displays expiration warning for other course', async () => { const expirationDate = new Date(); // This message will render if the expiration date is within 28 days; set the date 10 days in future expirationDate.setTime(expirationDate.getTime() + 864800000); @@ -1028,7 +1044,23 @@ describe('Outline Tab', () => { }); await fetchAndRender(); await screen.findByText('This course contains proctored exams'); - expect(screen.queryByText('Your onboarding profile has been approved in another course. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.')).toBeInTheDocument(); + expect(screen.queryByText('Your onboarding profile has been approved. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.')).toBeInTheDocument(); + expect(screen.queryByText('Onboarding profile review can take 2+ business days.')).toBeInTheDocument(); + }); + + it('displays expired', async () => { + const expirationDate = new Date(); + // This message appears after expiration, set the date 10 days in the past + expirationDate.setTime(expirationDate.getTime() - 864800000); + axiosMock.onGet(proctoringInfoUrl).reply(200, { + onboarding_status: 'verified', + onboarding_link: 'test', + expiration_date: expirationDate.toString(), + onboarding_release_date: onboardingReleaseDate.toISOString(), + }); + await fetchAndRender(); + await screen.findByText('This course contains proctored exams'); + expect(screen.queryByText('Your onboarding status has expired. Please complete onboarding again to continue taking proctored exams.')).toBeInTheDocument(); expect(screen.queryByText('Onboarding profile review can take 2+ business days.')).toBeInTheDocument(); }); diff --git a/src/course-home/outline-tab/messages.js b/src/course-home/outline-tab/messages.js index 48c56a13..8d320459 100644 --- a/src/course-home/outline-tab/messages.js +++ b/src/course-home/outline-tab/messages.js @@ -231,6 +231,11 @@ const messages = defineMessages({ defaultMessage: 'Expiring Soon', description: 'A label to indicate that proctortrack onboarding exam will expire soon', }, + expiredProctoringStatus: { + id: 'learning.proctoringPanel.status.expired', + defaultMessage: 'Expired', + description: 'A label to indicate that proctortrack onboarding exam has expired', + }, proctoringCurrentStatus: { id: 'learning.proctoringPanel.status', defaultMessage: 'Current Onboarding Status:', @@ -278,9 +283,14 @@ const messages = defineMessages({ }, expiringSoonProctoringMessage: { id: 'learning.proctoringPanel.message.expiringSoon', - defaultMessage: 'Your onboarding profile has been approved in another course. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.', + defaultMessage: 'Your onboarding profile has been approved. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.', description: 'The text that recommend an action when the status of the proctortrack onboarding exam is (expiring soon)', }, + expiredProctoringMessage: { + id: 'learning.proctoringPanel.message.expired', + defaultMessage: 'Your onboarding status has expired. Please complete onboarding again to continue taking proctored exams.', + description: 'The text that recommend an action when the status of the proctortrack onboarding exam is (expired)', + }, proctoringPanelGeneralInfo: { id: 'learning.proctoringPanel.generalInfo', defaultMessage: 'You must complete the onboarding process prior to taking any proctored exam. ', diff --git a/src/course-home/outline-tab/widgets/ProctoringInfoPanel.jsx b/src/course-home/outline-tab/widgets/ProctoringInfoPanel.jsx index 0ee5d70e..37d90bfe 100644 --- a/src/course-home/outline-tab/widgets/ProctoringInfoPanel.jsx +++ b/src/course-home/outline-tab/widgets/ProctoringInfoPanel.jsx @@ -35,6 +35,7 @@ function ProctoringInfoPanel({ intl }) { error: 'error', otherCourseApproved: 'otherCourseApproved', expiringSoon: 'expiringSoon', + expired: 'expired', }; function getReadableStatusClass(examStatus) { @@ -54,9 +55,14 @@ function ProctoringInfoPanel({ intl }) { return readableClass; } - function isNotYetSubmitted(examStatus) { - const NO_SHOW_STATES = ['submitted', 'second_review_required', 'verified']; - return !NO_SHOW_STATES.includes(examStatus); + function isCurrentlySubmitted(examStatus) { + const SUBMITTED_STATES = ['submitted', 'second_review_required']; + return SUBMITTED_STATES.includes(examStatus); + } + + function isSubmissionRequired(examStatus) { + const OK_STATES = [readableStatuses.submitted, readableStatuses.verified]; + return !OK_STATES.includes(examStatus); } function isNotYetReleased(examReleaseDate) { @@ -77,11 +83,19 @@ function ProctoringInfoPanel({ intl }) { return borderClass; } - function isExpiringSoon(dateString) { - // Returns true if the expiration date is within 28 days + function isExpired(dateString) { + // Returns true if the expiration date has passed const today = new Date(); const expirationDateObject = new Date(dateString); - return today > expirationDateObject.getTime() - 2419200000; + return today >= expirationDateObject.getTime(); + } + + function isExpiringSoon(dateString) { + // Returns true if the expiration date is within 28 days + const twentyeightDays = 28 * 24 * 60 * 60 * 1000; + const today = new Date(); + const expirationDateObject = new Date(dateString); + return today > expirationDateObject.getTime() - twentyeightDays; } useEffect(() => { @@ -96,7 +110,9 @@ function ProctoringInfoPanel({ intl }) { setStatus(response.onboarding_status); setLink(response.onboarding_link); const expirationDate = response.expiration_date; - if (expirationDate && isExpiringSoon(expirationDate)) { + if (expirationDate && isExpired(expirationDate)) { + setReadableStatus(getReadableStatusClass('expired')); + } else if (expirationDate && isExpiringSoon(expirationDate)) { setReadableStatus(getReadableStatusClass('expiringSoon')); } else { setReadableStatus(getReadableStatusClass(response.onboarding_status)); @@ -175,17 +191,17 @@ function ProctoringInfoPanel({ intl }) { {![readableStatuses.verified, readableStatuses.otherCourseApproved].includes(readableStatus) && ( <>

- {isNotYetSubmitted(status) && ( + {!isCurrentlySubmitted(status) && ( intl.formatMessage(messages.proctoringPanelGeneralInfo) )} - {!isNotYetSubmitted(status) && ( + {isCurrentlySubmitted(status) && ( intl.formatMessage(messages.proctoringPanelGeneralInfoSubmitted) )}

{intl.formatMessage(messages.proctoringPanelGeneralTime)}

)} - {isNotYetSubmitted(status) && ( + {isSubmissionRequired(readableStatus) && ( onboardingExamButton )}