Skip to content

Commit 5bdef7c

Browse files
authored
fix: disable special exams config if feature flag is disabled (#2325)
* fix: disable special exams config if feature flag is disabled * test: add testcases * fix: convert AdvancedTab to typescript
1 parent f0c5a51 commit 5bdef7c

File tree

11 files changed

+986
-67
lines changed

11 files changed

+986
-67
lines changed

src/course-outline/CourseOutline.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ import { ContentType } from '@src/library-authoring/routes';
3636
import { NOTIFICATION_MESSAGES } from '@src/constants';
3737
import { COMPONENT_TYPES } from '@src/generic/block-type-utils/constants';
3838
import { XBlock } from '@src/data/types';
39-
import { getCurrentItem, getProctoredExamsFlag } from './data/selectors';
39+
import {
40+
getCurrentItem,
41+
getProctoredExamsFlag,
42+
getTimedExamsFlag,
43+
} from './data/selectors';
4044
import { COURSE_BLOCK_NAMES } from './constants';
4145
import StatusBar from './status-bar/StatusBar';
4246
import EnableHighlightsModal from './enable-highlights-modal/EnableHighlightsModal';
@@ -167,6 +171,7 @@ const CourseOutline = ({ courseId }: CourseOutlineProps) => {
167171
const deleteCategory = COURSE_BLOCK_NAMES[currentItemData.category]?.name.toLowerCase();
168172

169173
const enableProctoredExams = useSelector(getProctoredExamsFlag);
174+
const enableTimedExams = useSelector(getTimedExamsFlag);
170175

171176
/**
172177
* Move section to new index
@@ -505,6 +510,7 @@ const CourseOutline = ({ courseId }: CourseOutlineProps) => {
505510
onConfigureSubmit={handleConfigureItemSubmit}
506511
currentItemData={currentItemData}
507512
enableProctoredExams={enableProctoredExams}
513+
enableTimedExams={enableTimedExams}
508514
isSelfPaced={statusBarData.isSelfPaced}
509515
/>
510516
<DeleteModal
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { getTimedExamsFlag, getProctoredExamsFlag } from './selectors';
2+
3+
const mockState = {
4+
courseOutline: {
5+
enableTimedExams: true,
6+
enableProctoredExams: false,
7+
},
8+
};
9+
10+
describe('course-outline selectors', () => {
11+
describe('getTimedExamsFlag', () => {
12+
it('returns enableTimedExams value from state', () => {
13+
expect(getTimedExamsFlag(mockState)).toBe(true);
14+
});
15+
16+
it('returns false when enableTimedExams is false', () => {
17+
const stateWithDisabledExams = {
18+
courseOutline: {
19+
...mockState.courseOutline,
20+
enableTimedExams: false,
21+
},
22+
};
23+
expect(getTimedExamsFlag(stateWithDisabledExams)).toBe(false);
24+
});
25+
26+
it('returns undefined when enableTimedExams is not set', () => {
27+
const stateWithoutProperty = {
28+
courseOutline: {
29+
enableProctoredExams: false,
30+
},
31+
};
32+
expect(getTimedExamsFlag(stateWithoutProperty)).toBeUndefined();
33+
});
34+
});
35+
36+
describe('getProctoredExamsFlag', () => {
37+
it('returns enableProctoredExams value from state', () => {
38+
expect(getProctoredExamsFlag(mockState)).toBe(false);
39+
});
40+
});
41+
});

src/course-outline/data/selectors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const getCurrentSubsection = (state) => state.courseOutline.currentSubsec
99
export const getCourseActions = (state) => state.courseOutline.actions;
1010
export const getCustomRelativeDatesActiveFlag = (state) => state.courseOutline.isCustomRelativeDatesActive;
1111
export const getProctoredExamsFlag = (state) => state.courseOutline.enableProctoredExams;
12+
export const getTimedExamsFlag = (state) => state.courseOutline.enableTimedExams;
1213
export const getPasteFileNotices = (state) => state.courseOutline.pasteFileNotices;
1314
export const getErrors = (state) => state.courseOutline.errors;
1415
export const getCreatedOn = (state) => state.courseOutline.createdOn;

src/course-outline/data/slice.test.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { configureStore } from '@reduxjs/toolkit';
2+
import { reducer, fetchOutlineIndexSuccess } from './slice';
3+
4+
describe('course-outline slice', () => {
5+
let store;
6+
7+
beforeEach(() => {
8+
store = configureStore({
9+
reducer: {
10+
courseOutline: reducer,
11+
},
12+
});
13+
});
14+
15+
describe('fetchOutlineIndexSuccess action', () => {
16+
it('sets enableTimedExams from payload', () => {
17+
const mockPayload = {
18+
courseStructure: {
19+
enableProctoredExams: true,
20+
enableTimedExams: false,
21+
childInfo: {
22+
children: [],
23+
},
24+
},
25+
isCustomRelativeDatesActive: false,
26+
createdOn: null,
27+
};
28+
29+
store.dispatch(fetchOutlineIndexSuccess(mockPayload));
30+
31+
const state = store.getState();
32+
expect(state.courseOutline.enableTimedExams).toBe(false);
33+
expect(state.courseOutline.enableProctoredExams).toBe(true);
34+
});
35+
36+
it('sets enableTimedExams to true when provided', () => {
37+
const mockPayload = {
38+
courseStructure: {
39+
enableProctoredExams: false,
40+
enableTimedExams: true,
41+
childInfo: {
42+
children: [],
43+
},
44+
},
45+
isCustomRelativeDatesActive: false,
46+
createdOn: null,
47+
};
48+
49+
store.dispatch(fetchOutlineIndexSuccess(mockPayload));
50+
51+
const state = store.getState();
52+
expect(state.courseOutline.enableTimedExams).toBe(true);
53+
});
54+
55+
it('handles missing enableTimedExams field gracefully', () => {
56+
const mockPayload = {
57+
courseStructure: {
58+
enableProctoredExams: true,
59+
childInfo: {
60+
children: [],
61+
},
62+
},
63+
isCustomRelativeDatesActive: false,
64+
createdOn: null,
65+
};
66+
67+
store.dispatch(fetchOutlineIndexSuccess(mockPayload));
68+
69+
const state = store.getState();
70+
expect(state.courseOutline.enableTimedExams).toBeUndefined();
71+
expect(state.courseOutline.enableProctoredExams).toBe(true);
72+
});
73+
74+
it('initializes with enableTimedExams false by default', () => {
75+
const state = store.getState();
76+
expect(state.courseOutline.enableTimedExams).toBe(false);
77+
});
78+
});
79+
});

src/course-outline/data/slice.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const initialState = {
4747
allowMoveDown: false,
4848
},
4949
enableProctoredExams: false,
50+
enableTimedExams: false,
5051
pasteFileNotices: {},
5152
createdOn: null,
5253
} satisfies CourseOutlineState as unknown as CourseOutlineState;
@@ -60,6 +61,7 @@ const slice = createSlice({
6061
state.sectionsList = payload.courseStructure?.childInfo?.children || [];
6162
state.isCustomRelativeDatesActive = payload.isCustomRelativeDatesActive;
6263
state.enableProctoredExams = payload.courseStructure?.enableProctoredExams;
64+
state.enableTimedExams = payload.courseStructure?.enableTimedExams;
6365
state.createdOn = payload.createdOn;
6466
},
6567
updateOutlineIndexLoadingStatus: (state: CourseOutlineState, { payload }) => {

src/course-outline/data/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export interface CourseOutlineState {
5959
currentItem: XBlock | {};
6060
actions: XBlockActions;
6161
enableProctoredExams: boolean;
62+
enableTimedExams: boolean;
6263
pasteFileNotices: object;
6364
createdOn: null | Date;
6465
}

0 commit comments

Comments
 (0)