@@ -28,16 +28,21 @@ import {
2828import { provideMockStore } from '@ngrx/store/testing' ;
2929import { TranslateModule } from '@ngx-translate/core' ;
3030import { NgxMaskModule } from 'ngx-mask' ;
31- import { of } from 'rxjs' ;
31+ import {
32+ of ,
33+ throwError ,
34+ } from 'rxjs' ;
3235
3336import {
3437 APP_CONFIG ,
3538 APP_DATA_SERVICES_MAP ,
3639} from '../../../../../../config/app-config.interface' ;
3740import { environment } from '../../../../../../environments/environment.test' ;
41+ import { AuthorizationDataService } from '../../../../../core/data/feature-authorization/authorization-data.service' ;
3842import { JsonPatchOperationPathCombiner } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner' ;
3943import { JsonPatchOperationsBuilder } from '../../../../../core/json-patch/builder/json-patch-operations-builder' ;
4044import { SubmissionJsonPatchOperationsService } from '../../../../../core/submission/submission-json-patch-operations.service' ;
45+ import { SubmissionScopeType } from '../../../../../core/submission/submission-scope-type' ;
4146import { XSRFService } from '../../../../../core/xsrf/xsrf.service' ;
4247import { dateToISOFormat } from '../../../../../shared/date.util' ;
4348import { DsDynamicTypeBindRelationService } from '../../../../../shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service' ;
@@ -79,6 +84,10 @@ const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', {
7984 remove : jasmine . createSpy ( 'remove' ) ,
8085} ) ;
8186
87+ const authorizationServiceMock : any = jasmine . createSpyObj ( 'authorizationService' , {
88+ isAuthorized : of ( true ) ,
89+ } ) ;
90+
8291const formMetadataMock = [ 'dc.title' , 'dc.description' ] ;
8392
8493const initialState : any = {
@@ -108,6 +117,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
108117 let operationsService : any ;
109118 let formService : any ;
110119 let uploadService : any ;
120+ let authorizationService : any ;
111121
112122 const submissionJsonPatchOperationsServiceStub = new SubmissionJsonPatchOperationsServiceStub ( ) ;
113123 const submissionId = mockSubmissionId ;
@@ -145,6 +155,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
145155 { provide : SubmissionService , useClass : SubmissionServiceStub } ,
146156 { provide : SubmissionJsonPatchOperationsService , useValue : submissionJsonPatchOperationsServiceStub } ,
147157 { provide : JsonPatchOperationsBuilder , useValue : jsonPatchOpBuilder } ,
158+ { provide : AuthorizationDataService , useValue : authorizationServiceMock } ,
148159 { provide : SectionUploadService , useValue : getMockSectionUploadService ( ) } ,
149160 provideMockStore ( { initialState } ) ,
150161 FormBuilderService ,
@@ -204,6 +215,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
204215 operationsService = TestBed . inject ( SubmissionJsonPatchOperationsService ) ;
205216 formService = TestBed . inject ( FormService ) ;
206217 uploadService = TestBed . inject ( SectionUploadService ) ;
218+ authorizationService = TestBed . inject ( AuthorizationDataService ) ;
207219
208220 comp . submissionId = submissionId ;
209221 comp . collectionId = collectionId ;
@@ -215,6 +227,8 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
215227 comp . configMetadataForm = configMetadataForm ;
216228 comp . formMetadata = formMetadataMock ;
217229
230+ submissionServiceStub . getSubmissionScope . and . returnValue ( SubmissionScopeType . WorkspaceItem ) ;
231+ authorizationService . isAuthorized . and . returnValue ( of ( true ) ) ;
218232 formService . isValid . and . returnValue ( of ( true ) ) ;
219233 } ) ;
220234
@@ -227,6 +241,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
227241 it ( 'should init form model properly' , ( ) => {
228242 comp . fileData = fileData ;
229243 comp . formId = 'testFileForm' ;
244+ comp . canEditAccessConditions = true ;
230245 const maxStartDate = { year : 2022 , month : 1 , day : 12 } ;
231246 const maxEndDate = { year : 2019 , month : 7 , day : 12 } ;
232247
@@ -267,6 +282,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
267282
268283 comp . fileData = fileData ;
269284 comp . formId = 'testFileForm' ;
285+ comp . canEditAccessConditions = true ;
270286
271287 comp . formModel = compAsAny . buildFileEditForm ( ) ;
272288
@@ -300,11 +316,40 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
300316 expect ( compAsAny . retrieveValueFromField ( field ) ) . toBe ( 'test' ) ;
301317 } ) ;
302318
319+ it ( 'should show access-condition form controls for admin users' , ( ) => {
320+ comp . fileData = fileData ;
321+ comp . formId = 'testFileForm' ;
322+ authorizationService . isAuthorized . and . returnValue ( of ( true ) ) ;
323+ // only the form model is under test — don't render the template
324+ spyOn ( compAsAny . cdr , 'detectChanges' ) ;
325+
326+ comp . ngOnInit ( ) ;
327+
328+ expect ( comp . canEditAccessConditions ) . toBeTrue ( ) ;
329+ expect ( comp . formModel ) . toBeDefined ( ) ;
330+ expect ( formbuilderService . findById ( 'accessConditions' , comp . formModel ) ) . not . toBeNull ( ) ;
331+ } ) ;
332+
333+ it ( 'should hide access-condition form controls for non-admin users' , ( ) => {
334+ comp . fileData = fileData ;
335+ comp . formId = 'testFileForm' ;
336+ authorizationService . isAuthorized . and . returnValue ( of ( false ) ) ;
337+ // only the form model is under test — don't render the template
338+ spyOn ( compAsAny . cdr , 'detectChanges' ) ;
339+
340+ comp . ngOnInit ( ) ;
341+
342+ expect ( comp . canEditAccessConditions ) . toBeFalse ( ) ;
343+ expect ( comp . formModel ) . toBeDefined ( ) ;
344+ expect ( formbuilderService . findById ( 'accessConditions' , comp . formModel ) ) . toBeNull ( ) ;
345+ } ) ;
346+
303347 it ( 'should save Bitstream File data properly when form is valid' , fakeAsync ( ( ) => {
304348 compAsAny . formRef = { formGroup : null } ;
305349 compAsAny . fileData = fileData ;
306350 compAsAny . pathCombiner = pathCombiner ;
307351 compAsAny . isPrimary = null ;
352+ comp . canEditAccessConditions = true ;
308353 formService . validateAllFormFields . and . callFake ( ( ) => null ) ;
309354 formService . isValid . and . returnValue ( of ( true ) ) ;
310355 formService . getFormData . and . returnValue ( of ( mockFileFormData ) ) ;
@@ -381,6 +426,56 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
381426 expect ( uploadService . updateFileData ) . toHaveBeenCalled ( ) ;
382427 } ) ) ;
383428
429+ it ( 'should not patch access conditions for non-admin users' , fakeAsync ( ( ) => {
430+ compAsAny . formRef = { formGroup : null } ;
431+ compAsAny . fileData = fileData ;
432+ compAsAny . pathCombiner = pathCombiner ;
433+ compAsAny . canEditAccessConditions = false ;
434+
435+ operationsBuilder . add . calls . reset ( ) ;
436+ formService . validateAllFormFields . and . callFake ( ( ) => null ) ;
437+ formService . isValid . and . returnValue ( of ( true ) ) ;
438+ formService . getFormData . and . returnValue ( of ( mockFileFormData ) ) ;
439+
440+ const response = [
441+ Object . assign ( mockSubmissionObject , {
442+ sections : {
443+ upload : {
444+ files : mockUploadFiles ,
445+ } ,
446+ } ,
447+ } ) ,
448+ ] ;
449+ operationsService . jsonPatchByResourceID . and . returnValue ( of ( response ) ) ;
450+
451+ comp . saveBitstreamData ( ) ;
452+ tick ( ) ;
453+
454+ expect ( operationsBuilder . add ) . not . toHaveBeenCalledWith (
455+ pathCombiner . getPath ( [ 'files' , fileIndex , 'accessConditions' ] ) ,
456+ jasmine . anything ( ) ,
457+ true ,
458+ ) ;
459+ } ) ) ;
460+
461+ it ( 'should clear isSaving and keep the modal open when the patch request fails' , fakeAsync ( ( ) => {
462+ compAsAny . formRef = { formGroup : null } ;
463+ compAsAny . fileData = fileData ;
464+ compAsAny . pathCombiner = pathCombiner ;
465+ spyOn ( compAsAny . cdr , 'detectChanges' ) ;
466+ const modalCloseSpy = spyOn ( compAsAny . activeModal , 'close' ) ;
467+ formService . validateAllFormFields . and . callFake ( ( ) => null ) ;
468+ formService . isValid . and . returnValue ( of ( true ) ) ;
469+ formService . getFormData . and . returnValue ( of ( mockFileFormData ) ) ;
470+ operationsService . jsonPatchByResourceID . and . returnValue ( throwError ( ( ) => new Error ( 'patch failed' ) ) ) ;
471+
472+ comp . saveBitstreamData ( ) ;
473+ tick ( ) ;
474+
475+ expect ( compAsAny . isSaving ) . toBeFalse ( ) ;
476+ expect ( modalCloseSpy ) . not . toHaveBeenCalled ( ) ;
477+ } ) ) ;
478+
384479 it ( 'should not save Bitstream File data properly when form is not valid' , fakeAsync ( ( ) => {
385480 compAsAny . formRef = { formGroup : null } ;
386481 compAsAny . pathCombiner = pathCombiner ;
0 commit comments