fix: corrects navigation if the student does not pass the entrance exam (#1429)

This commit is contained in:
Ishan Masdekar
2024-09-24 00:11:52 +05:30
committed by GitHub
parent e4a0105042
commit fbe16483ac
4 changed files with 77 additions and 1 deletions

View File

@@ -19,6 +19,10 @@ const CertificateStatus = ({ intl }) => {
courseId,
} = useSelector(state => state.courseHome);
const {
entranceExamData,
} = useModel('coursewareMeta', courseId);
const {
isEnrolled,
org,
@@ -42,6 +46,8 @@ const CertificateStatus = ({ intl }) => {
certificateAvailableDate,
} = certificateData || {};
const entranceExamPassed = entranceExamData?.entranceExamPassed ?? null;
const mode = getCourseExitMode(
certificateData,
hasScheduledContent,
@@ -49,6 +55,7 @@ const CertificateStatus = ({ intl }) => {
userHasPassingGrade,
null, // CourseExitPageIsActive
canViewCertificate,
entranceExamPassed,
);
const eventProperties = {

View File

@@ -9,6 +9,7 @@ const COURSE_EXIT_MODES = {
celebration: 1,
nonPassing: 2,
inProgress: 3,
entranceExamFail: 4,
};
// These are taken from the edx-platform `get_cert_data` function found in lms/courseware/views/views.py
@@ -32,9 +33,14 @@ function getCourseExitMode(
userHasPassingGrade,
courseExitPageIsActive = null,
canImmediatelyViewCertificate = false,
entranceExamPassed = null,
) {
const authenticatedUser = getAuthenticatedUser();
if (entranceExamPassed === false) {
return COURSE_EXIT_MODES.entranceExamFail;
}
if (courseExitPageIsActive === false || !authenticatedUser || !isEnrolled) {
return COURSE_EXIT_MODES.disabled;
}
@@ -73,6 +79,7 @@ function GetCourseExitNavigation(courseId, intl) {
isEnrolled,
userHasPassingGrade,
courseExitPageIsActive,
entranceExamData: { entranceExamPassed },
} = useModel('coursewareMeta', courseId);
const { canViewCertificate } = useModel('courseHomeMeta', courseId);
const exitMode = getCourseExitMode(
@@ -82,8 +89,15 @@ function GetCourseExitNavigation(courseId, intl) {
userHasPassingGrade,
courseExitPageIsActive,
canViewCertificate,
entranceExamPassed,
);
const exitActive = exitMode !== COURSE_EXIT_MODES.disabled;
/** exitActive is used to enable/disable the exit i.e. next buttons.
COURSE_EXIT_MODES denote the current status of the course.
Available COURSE_EXIT_MODES: disabled, celebration, nonPassing, inProgress, entranceExamFail
If the user fails the entrance exam,
access to further course sections should not be allowed i.e. disable the next buttons. */
const exitActive = ((exitMode !== COURSE_EXIT_MODES.disabled) && (exitMode !== COURSE_EXIT_MODES.entranceExamFail));
let exitText;
switch (exitMode) {

View File

@@ -86,6 +86,50 @@ describe('Unit Navigation', () => {
expect(screen.getByRole('button', { name: /next/i })).toBeDisabled();
});
it('has the "Next" button disabled for entrance exam failed', async () => {
const testCourseMetadata = {
...courseMetadata,
certificate_data: { cert_status: 'bogus_status' },
enrollment: { is_active: true },
entrance_exam_data: {
entrance_exam_current_score: 0, entrance_exam_enabled: true, entrance_exam_id: '1', entrance_exam_minimum_score_pct: 0.65, entrance_exam_passed: false,
},
};
const testStore = await initializeTestStore({ courseMetadata: testCourseMetadata, unitBlocks }, false);
// Have to refetch the sequenceId since the new store generates new sequences
const { courseware } = testStore.getState();
const testData = { ...mockData, sequenceId: courseware.sequenceId };
render(
<UnitNavigation {...testData} unitId={unitBlocks[0].id} />,
{ store: testStore, wrapWithRouter: true },
);
expect(screen.getByRole('button', { name: /next/i })).toBeDisabled();
});
it('has the "Next" button enabled for entrance exam pass', async () => {
const testCourseMetadata = {
...courseMetadata,
certificate_data: { cert_status: 'bogus_status' },
enrollment: { is_active: true },
entrance_exam_data: {
entrance_exam_current_score: 1.0, entrance_exam_enabled: true, entrance_exam_id: '1', entrance_exam_minimum_score_pct: 0.65, entrance_exam_passed: true,
},
};
const testStore = await initializeTestStore({ courseMetadata: testCourseMetadata, unitBlocks }, false);
// Have to refetch the sequenceId since the new store generates new sequences
const { courseware } = testStore.getState();
const testData = { ...mockData, sequenceId: courseware.sequenceId };
render(
<UnitNavigation {...testData} unitId={unitBlocks[0].id} />,
{ store: testStore, wrapWithRouter: true },
);
expect(screen.getByRole('link', { name: /next/i })).toBeEnabled();
});
it('displays end of course message instead of the "Next" button as needed', async () => {
const testCourseMetadata = { ...courseMetadata, certificate_data: { cert_status: 'notpassing' }, enrollment: { is_active: true } };
const testStore = await initializeTestStore({ courseMetadata: testCourseMetadata, unitBlocks }, false);

View File

@@ -13,6 +13,7 @@ export function useSequenceNavigationMetadata(currentSequenceId, currentUnitId)
const sequence = useModel('sequences', currentSequenceId);
const courseId = useSelector(state => state.courseware.courseId);
const courseStatus = useSelector(state => state.courseware.courseStatus);
const { entranceExamData: { entranceExamPassed } } = useModel('coursewareMeta', courseId);
const sequenceStatus = useSelector(state => state.courseware.sequenceStatus);
// If we don't know the sequence and unit yet, then assume no.
@@ -25,6 +26,16 @@ export function useSequenceNavigationMetadata(currentSequenceId, currentUnitId)
};
}
// if entrance exam is not passed then we should treat this as 1st and last unit
if (entranceExamPassed === false) {
return {
isFirstUnit: true,
isLastUnit: true,
navigationDisabledNextSequence: false,
navigationDisabledPrevSequence: false,
};
}
const sequenceIndex = sequenceIds.indexOf(currentSequenceId);
const unitIndex = sequence.unitIds.indexOf(currentUnitId);