Skip to content

Commit 3f4bc3d

Browse files
committed
refactor: streamline permission handling in ScheduleAndDetails component and tests
1 parent 4102655 commit 3f4bc3d

3 files changed

Lines changed: 26 additions & 51 deletions

File tree

src/schedule-and-details/ScheduleAndDetails.test.jsx

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,21 @@ import ScheduleAndDetails from '.';
2727
jest.mock('@src/authz/hooks', () => ({
2828
useCourseUserPermissions: jest.fn().mockReturnValue({
2929
isLoading: false,
30-
isAuthzEnabled: true,
3130
canViewScheduleAndDetails: true,
3231
canEditSchedule: true,
3332
canEditDetails: true,
3433
}),
3534
}));
3635

36+
const mockPermissions = (overrides = {}) =>
37+
jest.mocked(useCourseUserPermissions).mockReturnValue({
38+
isLoading: false,
39+
canViewScheduleAndDetails: true,
40+
canEditSchedule: true,
41+
canEditDetails: true,
42+
...overrides,
43+
});
44+
3745
let axiosMock;
3846
let store;
3947
const courseId = '123';
@@ -191,13 +199,7 @@ describe('<ScheduleAndDetails /> permissions', () => {
191199
axiosMock.onGet(getCourseDetailsApiUrl(courseId)).reply(200, courseDetailsMock);
192200
axiosMock.onGet(getCourseSettingsApiUrl(courseId)).reply(200, courseSettingsMock);
193201
axiosMock.onPut(getCourseDetailsApiUrl(courseId)).reply(200);
194-
jest.mocked(useCourseUserPermissions).mockReturnValue({
195-
isLoading: false,
196-
isAuthzEnabled: true,
197-
canViewScheduleAndDetails: true,
198-
canEditSchedule: true,
199-
canEditDetails: true,
200-
});
202+
mockPermissions();
201203
});
202204

203205
it('renders normally when authz flag is disabled (no regression)', async () => {
@@ -218,13 +220,7 @@ describe('<ScheduleAndDetails /> permissions', () => {
218220

219221
it('shows PermissionDeniedAlert when user lacks view permission', async () => {
220222
mockWaffleFlags({ enableAuthzCourseAuthoring: true });
221-
jest.mocked(useCourseUserPermissions).mockReturnValue({
222-
isLoading: false,
223-
isAuthzEnabled: true,
224-
canViewScheduleAndDetails: false,
225-
canEditSchedule: false,
226-
canEditDetails: false,
227-
});
223+
mockPermissions({ canViewScheduleAndDetails: false, canEditSchedule: false, canEditDetails: false });
228224
const { getByTestId } = renderComponent();
229225
await waitFor(() => {
230226
expect(getByTestId('permissionDeniedAlert')).toBeInTheDocument();
@@ -233,13 +229,7 @@ describe('<ScheduleAndDetails /> permissions', () => {
233229

234230
it('disables schedule date inputs when user lacks edit_schedule permission', async () => {
235231
mockWaffleFlags({ enableAuthzCourseAuthoring: true });
236-
jest.mocked(useCourseUserPermissions).mockReturnValue({
237-
isLoading: false,
238-
isAuthzEnabled: true,
239-
canViewScheduleAndDetails: true,
240-
canEditSchedule: false,
241-
canEditDetails: true,
242-
});
232+
mockPermissions({ canEditSchedule: false });
243233
const { getAllByPlaceholderText } = renderComponent();
244234
await waitFor(() => {
245235
const dateInputs = getAllByPlaceholderText(DATE_FORMAT.toLocaleUpperCase());
@@ -249,13 +239,7 @@ describe('<ScheduleAndDetails /> permissions', () => {
249239

250240
it('disables pacing and details inputs when user lacks edit_details permission', async () => {
251241
mockWaffleFlags({ enableAuthzCourseAuthoring: true });
252-
jest.mocked(useCourseUserPermissions).mockReturnValue({
253-
isLoading: false,
254-
isAuthzEnabled: true,
255-
canViewScheduleAndDetails: true,
256-
canEditSchedule: true,
257-
canEditDetails: false,
258-
});
242+
mockPermissions({ canEditDetails: false });
259243
const { getAllByRole } = renderComponent();
260244
await waitFor(() => {
261245
const radios = getAllByRole('radio');
@@ -265,13 +249,7 @@ describe('<ScheduleAndDetails /> permissions', () => {
265249

266250
it('save button cannot be triggered when user has no edit permissions', async () => {
267251
mockWaffleFlags({ enableAuthzCourseAuthoring: true });
268-
jest.mocked(useCourseUserPermissions).mockReturnValue({
269-
isLoading: false,
270-
isAuthzEnabled: true,
271-
canViewScheduleAndDetails: true,
272-
canEditSchedule: false,
273-
canEditDetails: false,
274-
});
252+
mockPermissions({ canEditSchedule: false, canEditDetails: false });
275253
const { getAllByPlaceholderText, queryByText } = renderComponent();
276254
// Wait for page to load
277255
const dateInputs = await waitFor(() => getAllByPlaceholderText(DATE_FORMAT.toLocaleUpperCase()));

src/schedule-and-details/index.jsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,7 @@ const ScheduleAndDetails = () => {
176176
);
177177
}
178178

179-
const isScheduleEditable = canEditSchedule;
180-
const isDetailsEditable = canEditDetails;
179+
const canEdit = canEditSchedule || canEditDetails;
181180

182181
const showCreditSection = creditEligibilityEnabled && isCreditCourse;
183182
const showRequirementsSection = aboutPageEditable || isPrerequisiteCoursesEnabled || isEntranceExamsEnabled;
@@ -277,7 +276,7 @@ const ScheduleAndDetails = () => {
277276
<PacingSection
278277
selfPaced={selfPaced}
279278
startDate={startDate}
280-
isEditable={isDetailsEditable}
279+
isEditable={canEditDetails}
281280
onChange={handleValuesChange}
282281
/>
283282
<ScheduleSection
@@ -292,14 +291,14 @@ const ScheduleAndDetails = () => {
292291
certificateAvailableDate={certificateAvailableDate}
293292
certificatesDisplayBehavior={certificatesDisplayBehavior}
294293
canShowCertificateAvailableDateField={canShowCertificateAvailableDateField}
295-
isEditable={isScheduleEditable}
294+
isEditable={canEditSchedule}
296295
onChange={handleValuesChange}
297296
/>
298297
{aboutPageEditable && (
299298
<DetailsSection
300299
language={language}
301300
languageOptions={languageOptions}
302-
isEditable={isDetailsEditable}
301+
isEditable={canEditDetails}
303302
onChange={handleValuesChange}
304303
/>
305304
)}
@@ -320,19 +319,19 @@ const ScheduleAndDetails = () => {
320319
shortDescriptionEditable={shortDescriptionEditable}
321320
enableExtendedCourseDetails={enableExtendedCourseDetails}
322321
videoThumbnailImageAssetPath={videoThumbnailImageAssetPath}
323-
isEditable={isDetailsEditable}
322+
isEditable={canEditDetails}
324323
onChange={handleValuesChange}
325324
/>
326325
{enableExtendedCourseDetails && (
327326
<>
328327
<LearningOutcomesSection
329328
learningInfo={learningInfo}
330-
isEditable={isDetailsEditable}
329+
isEditable={canEditDetails}
331330
onChange={handleValuesChange}
332331
/>
333332
<InstructorsSection
334333
instructors={instructorInfo?.instructors}
335-
isEditable={isDetailsEditable}
334+
isEditable={canEditDetails}
336335
onChange={handleValuesChange}
337336
/>
338337
</>
@@ -348,14 +347,14 @@ const ScheduleAndDetails = () => {
348347
possiblePreRequisiteCourses={possiblePreRequisiteCourses}
349348
entranceExamMinimumScorePct={entranceExamMinimumScorePct}
350349
isPrerequisiteCoursesEnabled={isPrerequisiteCoursesEnabled}
351-
isEditable={isDetailsEditable}
350+
isEditable={canEditDetails}
352351
onChange={handleValuesChange}
353352
/>
354353
)}
355354
{licensingEnabled && (
356355
<LicenseSection
357356
license={license}
358-
isEditable={isDetailsEditable}
357+
isEditable={canEditDetails}
359358
onChange={handleValuesChange}
360359
/>
361360
)}
@@ -403,7 +402,7 @@ const ScheduleAndDetails = () => {
403402
<StatefulButton
404403
key="save-button"
405404
onClick={handleUpdateValues}
406-
disabled={hasErrors || (!isScheduleEditable && !isDetailsEditable)}
405+
disabled={hasErrors || !canEdit}
407406
state={isQueryPending
408407
? STATEFUL_BUTTON_STATES.pending
409408
: STATEFUL_BUTTON_STATES.default}

src/schedule-and-details/requirements-section/RequirementsSection.test.jsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,9 @@ describe('<RequirementsSection />', () => {
9797
});
9898

9999
it('disables effort input and prerequisite dropdown when isEditable is false', () => {
100-
const { getByDisplayValue, getByRole } = render(<RootWrapper {...props} isEditable={false} />);
100+
const { container, getByDisplayValue } = render(<RootWrapper {...props} isEditable={false} />);
101101
expect(getByDisplayValue(props.effort)).toBeDisabled();
102-
expect(getByRole('button', { name: messages.dropdownLabel.defaultMessage, hidden: true })).toBeDefined();
103-
const dropdown = getByRole('button', { name: new RegExp(props.preRequisiteCourses[0] || '', 'i'), hidden: true });
104-
expect(dropdown || getByRole('button', { name: messages.dropdownEmptyText?.defaultMessage || '' })).toBeDefined();
102+
expect(container.querySelector('#prerequisiteDropdown')).toBeDisabled();
105103
});
106104

107105
it('enables effort input when isEditable is true', () => {

0 commit comments

Comments
 (0)