@@ -10,6 +10,8 @@ import { executeThunk } from '@src/utils';
1010import genericMessages from '@src/generic/help-sidebar/messages' ;
1111import { DATE_FORMAT } from '@src/constants' ;
1212import { getCourseSettingsApiUrl } from '@src/data/api' ;
13+ import { mockWaffleFlags } from '@src/data/apiHooks.mock' ;
14+ import { useCourseUserPermissions } from '@src/authz/hooks' ;
1315
1416import { CourseAuthoringProvider } from '@src/CourseAuthoringContext' ;
1517import { courseDetailsMock , courseSettingsMock } from './__mocks__' ;
@@ -22,6 +24,26 @@ import scheduleMessages from './schedule-section/messages';
2224import messages from './messages' ;
2325import ScheduleAndDetails from '.' ;
2426
27+ jest . mock ( '@src/authz/hooks' , ( ) => ( {
28+ useCourseUserPermissions : jest . fn ( ) . mockReturnValue ( {
29+ isLoading : false ,
30+ isAuthzEnabled : true ,
31+ canViewScheduleAndDetails : true ,
32+ canEditSchedule : true ,
33+ canEditDetails : true ,
34+ } ) ,
35+ } ) ) ;
36+
37+ const mockPermissions = ( overrides = { } ) =>
38+ jest . mocked ( useCourseUserPermissions ) . mockReturnValue ( {
39+ isLoading : false ,
40+ isAuthzEnabled : true ,
41+ canViewScheduleAndDetails : true ,
42+ canEditSchedule : true ,
43+ canEditDetails : true ,
44+ ...overrides ,
45+ } ) ;
46+
2547let axiosMock ;
2648let store ;
2749const courseId = '123' ;
@@ -169,3 +191,73 @@ describe('<ScheduleAndDetails />', () => {
169191 expect ( getByText ( messages . alertFail . defaultMessage ) ) . toBeInTheDocument ( ) ;
170192 } ) ;
171193} ) ;
194+
195+ describe ( '<ScheduleAndDetails /> permissions' , ( ) => {
196+ beforeEach ( ( ) => {
197+ jest . restoreAllMocks ( ) ;
198+ const mocks = initializeMocks ( ) ;
199+ axiosMock = mocks . axiosMock ;
200+ store = mocks . reduxStore ;
201+ axiosMock . onGet ( getCourseDetailsApiUrl ( courseId ) ) . reply ( 200 , courseDetailsMock ) ;
202+ axiosMock . onGet ( getCourseSettingsApiUrl ( courseId ) ) . reply ( 200 , courseSettingsMock ) ;
203+ axiosMock . onPut ( getCourseDetailsApiUrl ( courseId ) ) . reply ( 200 ) ;
204+ mockPermissions ( ) ;
205+ } ) ;
206+
207+ it ( 'renders normally when authz flag is disabled (no regression)' , async ( ) => {
208+ mockWaffleFlags ( { enableAuthzCourseAuthoring : false } ) ;
209+ const { getAllByText } = renderComponent ( ) ;
210+ await waitFor ( ( ) => {
211+ expect ( getAllByText ( messages . headingTitle . defaultMessage ) . length ) . toBeGreaterThan ( 0 ) ;
212+ } ) ;
213+ } ) ;
214+
215+ it ( 'renders normally when user has all permissions' , async ( ) => {
216+ mockWaffleFlags ( { enableAuthzCourseAuthoring : true } ) ;
217+ const { getAllByText } = renderComponent ( ) ;
218+ await waitFor ( ( ) => {
219+ expect ( getAllByText ( messages . headingTitle . defaultMessage ) . length ) . toBeGreaterThan ( 0 ) ;
220+ } ) ;
221+ } ) ;
222+
223+ it ( 'shows PermissionDeniedAlert when user lacks view permission' , async ( ) => {
224+ mockWaffleFlags ( { enableAuthzCourseAuthoring : true } ) ;
225+ mockPermissions ( { canViewScheduleAndDetails : false , canEditSchedule : false , canEditDetails : false } ) ;
226+ const { getByTestId } = renderComponent ( ) ;
227+ await waitFor ( ( ) => {
228+ expect ( getByTestId ( 'permissionDeniedAlert' ) ) . toBeInTheDocument ( ) ;
229+ } ) ;
230+ } ) ;
231+
232+ it ( 'disables schedule date inputs when user lacks edit_schedule permission' , async ( ) => {
233+ mockWaffleFlags ( { enableAuthzCourseAuthoring : true } ) ;
234+ mockPermissions ( { canEditSchedule : false } ) ;
235+ const { getAllByPlaceholderText } = renderComponent ( ) ;
236+ await waitFor ( ( ) => {
237+ const dateInputs = getAllByPlaceholderText ( DATE_FORMAT . toLocaleUpperCase ( ) ) ;
238+ dateInputs . forEach ( ( input ) => expect ( input ) . toBeDisabled ( ) ) ;
239+ } ) ;
240+ } ) ;
241+
242+ it ( 'disables pacing and details inputs when user lacks edit_details permission' , async ( ) => {
243+ mockWaffleFlags ( { enableAuthzCourseAuthoring : true } ) ;
244+ mockPermissions ( { canEditDetails : false } ) ;
245+ const { getAllByRole } = renderComponent ( ) ;
246+ await waitFor ( ( ) => {
247+ const radios = getAllByRole ( 'radio' ) ;
248+ radios . forEach ( ( radio ) => expect ( radio ) . toBeDisabled ( ) ) ;
249+ } ) ;
250+ } ) ;
251+
252+ it ( 'save button cannot be triggered when user has no edit permissions' , async ( ) => {
253+ mockWaffleFlags ( { enableAuthzCourseAuthoring : true } ) ;
254+ mockPermissions ( { canEditSchedule : false , canEditDetails : false } ) ;
255+ const { getAllByPlaceholderText, queryByText } = renderComponent ( ) ;
256+ // Wait for page to load
257+ const dateInputs = await waitFor ( ( ) => getAllByPlaceholderText ( DATE_FORMAT . toLocaleUpperCase ( ) ) ) ;
258+ // All date inputs must be disabled (no edit_schedule permission)
259+ dateInputs . forEach ( ( input ) => expect ( input ) . toBeDisabled ( ) ) ;
260+ // No changes can be made so the save button never appears
261+ expect ( queryByText ( messages . buttonSaveText . defaultMessage ) ) . not . toBeInTheDocument ( ) ;
262+ } ) ;
263+ } ) ;
0 commit comments