From bbc8b6ac8a15bdd619d4f7dac90655ab52dbad2c Mon Sep 17 00:00:00 2001 From: Vlad Nouski Date: Mon, 23 Oct 2023 18:34:13 +0200 Subject: [PATCH] [CST-12043] unit tests --- .../mocks/section-upload.service.mock.ts | 3 + src/app/shared/mocks/submission.mock.ts | 6 ++ .../objects/submission-objects.actions.ts | 4 +- ...section-upload-file-edit.component.spec.ts | 30 ++++-- .../section-upload-file-edit.component.ts | 51 ++++++---- .../edit/section-upload-file-edit.model.ts | 4 +- .../file/section-upload-file.component.html | 10 +- .../section-upload-file.component.spec.ts | 22 ++++- .../file/section-upload-file.component.ts | 95 +++---------------- .../upload/section-upload.component.spec.ts | 24 +++-- .../upload/section-upload.service.spec.ts | 63 ++++++++++++ .../sections/upload/section-upload.service.ts | 23 ++++- 12 files changed, 207 insertions(+), 128 deletions(-) create mode 100644 src/app/submission/sections/upload/section-upload.service.spec.ts diff --git a/src/app/shared/mocks/section-upload.service.mock.ts b/src/app/shared/mocks/section-upload.service.mock.ts index ae3515105d..4e872522c3 100644 --- a/src/app/shared/mocks/section-upload.service.mock.ts +++ b/src/app/shared/mocks/section-upload.service.mock.ts @@ -5,6 +5,9 @@ import { SubmissionFormsConfigDataService } from '../../core/config/submission-f */ export function getMockSectionUploadService(): SubmissionFormsConfigDataService { return jasmine.createSpyObj('SectionUploadService', { + updatePrimaryBitstreamOperation: jasmine.createSpy('updatePrimaryBitstreamOperation'), + updateFilePrimaryBitstream: jasmine.createSpy('updateFilePrimaryBitstream'), + getUploadedFilesData: jasmine.createSpy('getUploadedFilesData'), getUploadedFileList: jasmine.createSpy('getUploadedFileList'), getFileData: jasmine.createSpy('getFileData'), getDefaultPolicies: jasmine.createSpy('getDefaultPolicies'), diff --git a/src/app/shared/mocks/submission.mock.ts b/src/app/shared/mocks/submission.mock.ts index 268ae33ab3..bec08013c3 100644 --- a/src/app/shared/mocks/submission.mock.ts +++ b/src/app/shared/mocks/submission.mock.ts @@ -1612,7 +1612,13 @@ export const mockUploadFiles = [ } ]; +export const mockUploadFilesData = { + primary: null, + files: JSON.parse(JSON.stringify(mockUploadFiles)) +}; + export const mockFileFormData = { + primary: [true], metadata: { 'dc.title': [ { diff --git a/src/app/submission/objects/submission-objects.actions.ts b/src/app/submission/objects/submission-objects.actions.ts index 2f1ecf7fa8..86d90f05f3 100644 --- a/src/app/submission/objects/submission-objects.actions.ts +++ b/src/app/submission/objects/submission-objects.actions.ts @@ -766,7 +766,7 @@ export class EditFilePrimaryBitstreamAction implements Action { payload: { submissionId: string; sectionId: string; - fileId: string; + fileId: string | null; }; /** @@ -779,7 +779,7 @@ export class EditFilePrimaryBitstreamAction implements Action { * @param fileId * the file's ID */ - constructor(submissionId: string, sectionId: string, fileId: string) { + constructor(submissionId: string, sectionId: string, fileId: string | null) { this.payload = { submissionId, sectionId, fileId: fileId }; } } diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts index d008bf61f1..865f59d80d 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts @@ -47,6 +47,7 @@ import { } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { dateToISOFormat } from '../../../../../shared/date.util'; import { of } from 'rxjs'; +import { DynamicCustomSwitchModel } from '../../../../../shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.model'; const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', { add: jasmine.createSpy('add'), @@ -78,7 +79,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { const fileIndex = '0'; const fileId = '123456-test-upload'; const fileData: any = mockUploadFiles[0]; - const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId, 'files', fileIndex); + const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId); beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -183,11 +184,15 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { comp.ngOnInit(); + const models = [DynamicCustomSwitchModel, DynamicFormGroupModel, DynamicFormArrayModel]; + expect(comp.formModel).toBeDefined(); - expect(comp.formModel.length).toBe(2); - expect(comp.formModel[0] instanceof DynamicFormGroupModel).toBeTruthy(); - expect(comp.formModel[1] instanceof DynamicFormArrayModel).toBeTruthy(); - expect((comp.formModel[1] as DynamicFormArrayModel).groups.length).toBe(2); + expect(comp.formModel.length).toBe(models.length); + models.forEach((model, i) => { + expect(comp.formModel[i] instanceof model).toBeTruthy(); + }); + + expect((comp.formModel[2] as DynamicFormArrayModel).groups.length).toBe(2); const startDateModel = formbuilderService.findById('startDate', comp.formModel); expect(startDateModel.max).toEqual(maxStartDate); const endDateModel = formbuilderService.findById('endDate', comp.formModel); @@ -251,6 +256,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { compAsAny.formRef = {formGroup: null}; compAsAny.fileData = fileData; compAsAny.pathCombiner = pathCombiner; + compAsAny.isPrimary = null; formService.validateAllFormFields.and.callFake(() => null); formService.isValid.and.returnValue(of(true)); formService.getFormData.and.returnValue(of(mockFileFormData)); @@ -259,6 +265,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { Object.assign(mockSubmissionObject, { sections: { upload: { + primary: true, files: mockUploadFiles } } @@ -274,23 +281,28 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { comp.saveBitstreamData(); tick(); - let path = 'metadata/dc.title'; + let path = 'primary'; + expect(uploadService.updatePrimaryBitstreamOperation).toHaveBeenCalledWith(pathCombiner.getPath(path), compAsAny.isPrimary, mockFileFormData.primary[0], compAsAny.fileId); + + const pathFragment = ['files', fileIndex]; + + path = 'metadata/dc.title'; expect(operationsBuilder.add).toHaveBeenCalledWith( - pathCombiner.getPath(path), + pathCombiner.getPath([...pathFragment, path]), mockFileFormData.metadata['dc.title'], true ); path = 'metadata/dc.description'; expect(operationsBuilder.add).toHaveBeenCalledWith( - pathCombiner.getPath(path), + pathCombiner.getPath([...pathFragment, path]), mockFileFormData.metadata['dc.description'], true ); path = 'accessConditions'; expect(operationsBuilder.add).toHaveBeenCalledWith( - pathCombiner.getPath(path), + pathCombiner.getPath([...pathFragment, path]), accessConditionsToSave, true ); diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts index 648271b7b7..d2b387dc32 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts @@ -29,7 +29,6 @@ import { BITSTREAM_FORM_ACCESS_CONDITION_TYPE_CONFIG, BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT, BITSTREAM_FORM_PRIMARY, - BITSTREAM_FORM_PRIMARY_LAYOUT, BITSTREAM_METADATA_FORM_GROUP_CONFIG, BITSTREAM_METADATA_FORM_GROUP_LAYOUT } from './section-upload-file-edit.model'; @@ -42,7 +41,7 @@ import { SubmissionService } from '../../../../submission.service'; import { FormService } from '../../../../../shared/form/form.service'; import { FormComponent } from '../../../../../shared/form/form.component'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { filter, mergeMap, take, tap } from 'rxjs/operators'; +import { filter, mergeMap, take } from 'rxjs/operators'; import { dateToISOFormat } from '../../../../../shared/date.util'; @@ -54,10 +53,12 @@ import { JsonPatchOperationPathCombiner } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { SectionUploadService } from '../../section-upload.service'; -import { Observable, Subject, Subscription } from 'rxjs'; +import { Subscription } from 'rxjs'; import { DynamicFormControlCondition } from '@ng-dynamic-forms/core/lib/model/misc/dynamic-form-control-relation.model'; import { DynamicDateControlValue } from '@ng-dynamic-forms/core/lib/model/dynamic-date-control.model'; import { DynamicCustomSwitchModel } from 'src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.model'; +import { SubmissionObject } from 'src/app/core/submission/models/submission-object.model'; +import { WorkspaceitemSectionUploadObject } from 'src/app/core/submission/models/workspaceitem-section-upload.model'; /** * This component represents the edit form for bitstream @@ -168,12 +169,6 @@ export class SubmissionSectionUploadFileEditComponent protected subscriptions: Subscription[] = []; - private onSaveBitstreamData$: Subject = new Subject(); - - get saveBitstreamDataEvent$(): Observable { - return this.onSaveBitstreamData$.asObservable(); - } - /** * Initialize instance variables * @@ -206,8 +201,8 @@ export class SubmissionSectionUploadFileEditComponent */ public initModelData(formModel: DynamicFormControlModel[]) { - const primaryBitstreammodel: any = this.formBuilderService.findById('primaryBitstream', formModel, this.fileIndex); - primaryBitstreammodel.value = this.isPrimary || false; + const primaryBitstreamModel: any = this.formBuilderService.findById('primary', formModel, this.fileIndex); + primaryBitstreamModel.value = this.isPrimary || false; this.fileData.accessConditions.forEach((accessCondition, index) => { Array.of('name', 'startDate', 'endDate') @@ -417,12 +412,14 @@ export class SubmissionSectionUploadFileEditComponent // validate form this.formService.validateAllFormFields(this.formRef.formGroup); - const subscription = this.formService.isValid(this.formId).pipe( + const saveBitstreamDataSubscription = this.formService.isValid(this.formId).pipe( take(1), filter((isValid) => isValid), mergeMap(() => this.formService.getFormData(this.formId)), take(1), - tap((formData: any) => { + mergeMap((formData: any) => { + this.uploadService.updatePrimaryBitstreamOperation(this.pathCombiner.getPath('primary'), this.isPrimary, formData.primary[0], this.fileId); + // collect bitstream metadata Object.keys((formData.metadata)) .filter((key) => isNotEmpty(formData.metadata[key])) @@ -494,9 +491,31 @@ export class SubmissionSectionUploadFileEditComponent if (isNotEmpty(accessConditionsToSave)) { this.operationsBuilder.add(this.pathCombiner.getPath([...pathFragment, 'accessConditions']), accessConditionsToSave, true); } - }) - ).subscribe((formData) => this.onSaveBitstreamData$.next(formData)); - this.subscriptions.push(subscription); + // dispatch a PATCH request to save metadata + return this.operationsService.jsonPatchByResourceID( + this.submissionService.getSubmissionObjectLinkName(), + this.submissionId, + this.pathCombiner.rootElement, + this.pathCombiner.subRootElement); + }) + ).subscribe((result: SubmissionObject[]) => { + const section = result[0].sections[this.sectionId]; + if (!section) { + return; + } + const uploadSection = (section as WorkspaceitemSectionUploadObject); + + this.uploadService.updateFilePrimaryBitstream(this.submissionId, this.sectionId, uploadSection.primary); + + Object.keys(uploadSection.files) + .filter((key) => uploadSection.files[key].uuid === this.fileId) + .forEach((key) => this.uploadService.updateFileData( + this.submissionId, this.sectionId, this.fileId, uploadSection.files[key]) + ); + this.isSaving = false; + this.activeModal.close(); + }); + this.subscriptions.push(saveBitstreamDataSubscription); } private unsubscribeAll() { diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.model.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.model.ts index 8d02173281..c658d4b87e 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.model.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.model.ts @@ -64,8 +64,8 @@ export const BITSTREAM_FORM_PRIMARY_LAYOUT: DynamicFormControlLayout = { }; export const BITSTREAM_FORM_PRIMARY: DynamicSwitchModelConfig = { - id: 'primaryBitstream', - name: 'primaryBitstream', + id: 'primary', + name: 'primary', label: 'Primary bitstream', }; diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.html b/src/app/submission/sections/upload/file/section-upload-file.component.html index d4acd10e77..10b27c11a1 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.html +++ b/src/app/submission/sections/upload/file/section-upload-file.component.html @@ -3,13 +3,19 @@
- +
-

{{fileName}} ({{fileData?.sizeBytes | dsFileSize}})

+

{{fileName}}({{fileData?.sizeBytes | dsFileSize}})

diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts index 4f62ceef6c..c12991c55f 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts @@ -66,7 +66,7 @@ describe('SubmissionSectionUploadFileComponent test suite', () => { const fileName = '123456-test-upload.jpg'; const fileId = '123456-test-upload'; const fileData: any = mockUploadFiles[0]; - const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId, 'files', fileIndex); + const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId); const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', { add: jasmine.createSpy('add'), @@ -201,6 +201,23 @@ describe('SubmissionSectionUploadFileComponent test suite', () => { }); }); + it('should delete primary if file we delete is primary', () => { + compAsAny.isPrimary = true; + compAsAny.pathCombiner = pathCombiner; + operationsService.jsonPatchByResourceID.and.returnValue(observableOf({})); + compAsAny.deleteFile(); + expect(operationsBuilder.remove).toHaveBeenCalledWith(pathCombiner.getPath('primary')); + expect(uploadService.updateFilePrimaryBitstream).toHaveBeenCalledWith(submissionId, sectionId, null); + }); + + it('should NOT delete primary if file we delete is NOT primary', () => { + compAsAny.isPrimary = false; + compAsAny.pathCombiner = pathCombiner; + operationsService.jsonPatchByResourceID.and.returnValue(observableOf({})); + compAsAny.deleteFile(); + expect(uploadService.updateFilePrimaryBitstream).not.toHaveBeenCalledTimes(1); + }); + it('should delete file properly', () => { compAsAny.pathCombiner = pathCombiner; operationsService.jsonPatchByResourceID.and.returnValue(observableOf({})); @@ -209,7 +226,8 @@ describe('SubmissionSectionUploadFileComponent test suite', () => { compAsAny.deleteFile(); expect(uploadService.removeUploadedFile).toHaveBeenCalledWith(submissionId, sectionId, fileId); - expect(operationsBuilder.remove).toHaveBeenCalledWith(pathCombiner.getPath()); + expect(operationsBuilder.remove).toHaveBeenCalledWith(pathCombiner.getPath(['files', fileIndex])); + expect(operationsService.jsonPatchByResourceID).toHaveBeenCalledWith( 'workspaceitems', submissionId, diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.ts b/src/app/submission/sections/upload/file/section-upload-file.component.ts index 2e268ed3a3..f95e8213ca 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.ts @@ -1,5 +1,4 @@ import { - ChangeDetectorRef, Component, Input, OnChanges, @@ -9,8 +8,8 @@ import { ViewChild } from '@angular/core'; -import { BehaviorSubject, Subscription } from 'rxjs'; -import { filter, switchMap, tap } from 'rxjs/operators'; +import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { filter } from 'rxjs/operators'; import { DynamicFormControlModel, } from '@ng-dynamic-forms/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -22,14 +21,10 @@ import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/buil import { WorkspaceitemSectionUploadFileObject } from '../../../../core/submission/models/workspaceitem-section-upload-file.model'; import { SubmissionFormsModel } from '../../../../core/config/models/config-submission-forms.model'; import { SubmissionService } from '../../../submission.service'; -import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; import { SubmissionJsonPatchOperationsService } from '../../../../core/submission/submission-json-patch-operations.service'; import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component'; import { Bitstream } from '../../../../core/shared/bitstream.model'; import { NgbModalOptions } from '@ng-bootstrap/ng-bootstrap/modal/modal-config'; -import { WorkspaceitemSectionUploadObject } from 'src/app/core/submission/models/workspaceitem-section-upload.model'; -import { SubmissionObject } from 'src/app/core/submission/models/submission-object.model'; -import { NotificationsService } from 'src/app/shared/notifications/notifications.service'; /** * This component represents a single bitstream contained in the submission @@ -45,14 +40,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, * it will be null if no primary bitstream is set for the ORIGINAL bundle * @type {boolean, null} */ - @Input() set isPrimaryBitstream(status: boolean | null) { - this.initialPrimaryStatus = status; - this.isPrimary = status; - } - - - private initialPrimaryStatus = false; - isPrimary = false; + @Input() isPrimary: boolean | null; /** * The list of available access condition @@ -116,6 +104,11 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, */ @ViewChild(SubmissionSectionUploadFileEditComponent) fileEditComp: SubmissionSectionUploadFileEditComponent; + /** + * A boolean representing if a submission save operation is pending + * @type {Observable} + */ + public processingSaveStatus$: Observable; /** * The bitstream's metadata data @@ -184,10 +177,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, * @param {SectionUploadService} uploadService */ constructor( - private cdr: ChangeDetectorRef, private formService: FormService, - private halService: HALEndpointService, - private notificationsService: NotificationsService, private modalService: NgbModal, private operationsBuilder: JsonPatchOperationsBuilder, private operationsService: SubmissionJsonPatchOperationsService, @@ -220,6 +210,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, */ ngOnInit() { this.formId = this.formService.getUniqueId(this.fileId); + this.processingSaveStatus$ = this.submissionService.getSubmissionSaveProcessingStatus(this.submissionId); this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionId); this.loadFormMetadata(); } @@ -249,23 +240,6 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, }); } - private updatePrimaryBitstream(primaryBitstream: boolean): void { - const path = this.pathCombiner.getPath('primary'); - - if (this.isPrimary === null && primaryBitstream) { - this.operationsBuilder.add(path, this.fileId, false, true); - return; - } - - if (this.isPrimary !== primaryBitstream) { - if (primaryBitstream) { - this.operationsBuilder.replace(path, this.fileId, true); - return; - } - this.operationsBuilder.remove(path); - } - } - editBitstreamData() { const options: NgbModalOptions = { @@ -288,50 +262,10 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, activeModal.componentInstance.pathCombiner = this.pathCombiner; activeModal.componentInstance.submissionId = this.submissionId; activeModal.componentInstance.isPrimary = this.isPrimary; - - activeModal.componentInstance.saveBitstreamDataEvent$.pipe( - // TODO: uncoment - tap((data: any) => { - this.updatePrimaryBitstream(data.primaryBitstream[0]); - }), - switchMap(() => this.patchFileOperations()) - ).subscribe((result: SubmissionObject[]) => { - this.uploadFileData(result); - activeModal.componentInstance.isSaving = false; - activeModal.close(); - }); } - private uploadFileData(result: SubmissionObject[]) { - const section = result[0].sections[this.sectionId]; - if (!section) { - return; - } - let uploadSection = (section as WorkspaceitemSectionUploadObject); - uploadSection = {...uploadSection, primary: this.fileId}; - - // TODO: update only if primary bitstream changed - // TODO: if the result contains primary id of this file of null then update - this.uploadService.updateFilePrimaryBitstream(this.submissionId, this.sectionId, this.fileId); - - Object.keys(uploadSection.files) - .filter((key) => uploadSection.files[key].uuid === this.fileId) - .forEach((key) => { - this.uploadService.updateFileData( - this.submissionId, this.sectionId, this.fileId, uploadSection.files[key]); - }); - } - - private patchFileOperations() { - return this.operationsService.jsonPatchByResourceID( - this.submissionService.getSubmissionObjectLinkName(), - this.submissionId, - this.pathCombiner.rootElement, - this.pathCombiner.subRootElement); - } - - togglePrimaryBitstream() { - this.updatePrimaryBitstream(!this.isPrimary); + togglePrimaryBitstream(event) { + this.uploadService.updatePrimaryBitstreamOperation(this.pathCombiner.getPath('primary'), this.isPrimary, event.target.checked, this.fileId); this.submissionService.dispatchSaveSection(this.submissionId, this.sectionId); } @@ -359,10 +293,8 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, */ protected deleteFile() { this.operationsBuilder.remove(this.pathCombiner.getPath(['files', this.fileIndex])); - if (this.isPrimary) { - // TODO: uncoment - // this.operationsBuilder.remove(this.pathCombiner.getPath('primary')); + this.operationsBuilder.remove(this.pathCombiner.getPath('primary')); } this.subscriptions.push(this.operationsService.jsonPatchByResourceID( @@ -371,6 +303,9 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, this.pathCombiner.rootElement, this.pathCombiner.subRootElement) .subscribe(() => { + if (this.isPrimary) { + this.uploadService.updateFilePrimaryBitstream(this.submissionId, this.sectionId, null); + } this.uploadService.removeUploadedFile(this.submissionId, this.sectionId, this.fileId); this.processingDelete$.next(false); })); diff --git a/src/app/submission/sections/upload/section-upload.component.spec.ts b/src/app/submission/sections/upload/section-upload.component.spec.ts index 068fc5c766..eaa510a696 100644 --- a/src/app/submission/sections/upload/section-upload.component.spec.ts +++ b/src/app/submission/sections/upload/section-upload.component.spec.ts @@ -25,6 +25,7 @@ import { mockUploadConfigResponse, mockUploadConfigResponseNotRequired, mockUploadFiles, + mockUploadFilesData, } from '../../../shared/mocks/submission.mock'; import { SubmissionUploadsConfigDataService } from '../../../core/config/submission-uploads-config-data.service'; import { SectionUploadService } from './section-upload.service'; @@ -160,6 +161,7 @@ describe('SubmissionSectionUploadComponent test suite', () => { ); bitstreamService.getUploadedFileList.and.returnValue(observableOf([])); + bitstreamService.getUploadedFilesData.and.returnValue(observableOf({ primary: null, files: [] })); }; TestBed.configureTestingModule({ @@ -230,7 +232,7 @@ describe('SubmissionSectionUploadComponent test suite', () => { }); it('should init component properly', () => { - + bitstreamService.getUploadedFilesData.and.returnValue(observableOf({ primary: null, files: [] })); submissionServiceStub.getSubmissionObject.and.returnValue(observableOf(submissionState)); collectionDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(Object.assign(new Collection(), mockCollection, { @@ -246,15 +248,8 @@ describe('SubmissionSectionUploadComponent test suite', () => { createSuccessfulRemoteDataObject$(Object.assign(new Group(), mockGroup)) ); - bitstreamService.getUploadedFileList.and.returnValue(observableOf([])); - comp.onSectionInit(); - const expectedGroupsMap = new Map([ - [mockUploadConfigResponse.accessConditionOptions[1].name, [mockGroup as any]], - [mockUploadConfigResponse.accessConditionOptions[2].name, [mockGroup as any]], - ]); - expect(comp.collectionId).toBe(collectionId); expect(comp.collectionName).toBe(mockCollection.name); expect(comp.availableAccessConditionOptions.length).toBe(4); @@ -262,12 +257,12 @@ describe('SubmissionSectionUploadComponent test suite', () => { expect(comp.required$.getValue()).toBe(true); expect(compAsAny.subs.length).toBe(2); expect(compAsAny.fileList).toEqual([]); - expect(compAsAny.fileIndexes).toEqual([]); expect(compAsAny.fileNames).toEqual([]); - + expect(compAsAny.primaryBitstreamUUID).toEqual(null); }); it('should init file list properly', () => { + bitstreamService.getUploadedFilesData.and.returnValue(observableOf({ primary: null, files: [] })); submissionServiceStub.getSubmissionObject.and.returnValue(observableOf(submissionState)); @@ -282,7 +277,7 @@ describe('SubmissionSectionUploadComponent test suite', () => { createSuccessfulRemoteDataObject$(Object.assign(new Group(), mockGroup)) ); - bitstreamService.getUploadedFileList.and.returnValue(observableOf(mockUploadFiles)); + bitstreamService.getUploadedFilesData.and.returnValue(observableOf(mockUploadFilesData)); comp.onSectionInit(); @@ -298,12 +293,14 @@ describe('SubmissionSectionUploadComponent test suite', () => { expect(comp.required$.getValue()).toBe(true); expect(compAsAny.subs.length).toBe(2); expect(compAsAny.fileList).toEqual(mockUploadFiles); - expect(compAsAny.fileIndexes).toEqual(['123456-test-upload']); + expect(compAsAny.primaryBitstreamUUID).toEqual(null); expect(compAsAny.fileNames).toEqual(['123456-test-upload.jpg']); }); it('should properly read the section status when required is true', () => { + bitstreamService.getUploadedFilesData.and.returnValue(observableOf({ primary: null, files: [] })); + submissionServiceStub.getSubmissionObject.and.returnValue(observableOf(submissionState)); collectionDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(mockCollection)); @@ -324,7 +321,7 @@ describe('SubmissionSectionUploadComponent test suite', () => { comp.onSectionInit(); - expect(comp.required$.getValue()).toBe(true); + expect(comp.required$.getValue()).toBe(true); expect(compAsAny.getSectionStatus()).toBeObservable(cold('-c-d', { c: false, @@ -335,6 +332,7 @@ describe('SubmissionSectionUploadComponent test suite', () => { it('should properly read the section status when required is false', () => { submissionServiceStub.getSubmissionObject.and.returnValue(observableOf(submissionState)); + bitstreamService.getUploadedFilesData.and.returnValue(observableOf({ primary: null, files: [] })); collectionDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(mockCollection)); resourcePolicyService.findByHref.and.returnValue(createSuccessfulRemoteDataObject$(mockDefaultAccessCondition)); diff --git a/src/app/submission/sections/upload/section-upload.service.spec.ts b/src/app/submission/sections/upload/section-upload.service.spec.ts new file mode 100644 index 0000000000..e1d2abe048 --- /dev/null +++ b/src/app/submission/sections/upload/section-upload.service.spec.ts @@ -0,0 +1,63 @@ +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { JsonPatchOperationPathCombiner } from 'src/app/core/json-patch/builder/json-patch-operation-path-combiner'; +import { JsonPatchOperationsBuilder } from 'src/app/core/json-patch/builder/json-patch-operations-builder'; +import { SectionUploadService } from './section-upload.service'; +import { Store, StoreModule } from '@ngrx/store'; + +const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', { + add: jasmine.createSpy('add'), + replace: jasmine.createSpy('replace'), + remove: jasmine.createSpy('remove'), +}); + +describe('SectionUploadService test suite', () => { + let sectionUploadService: SectionUploadService; + let operationsBuilder: any; + const pathCombiner = new JsonPatchOperationPathCombiner('sections', 'upload'); + const primaryPath = pathCombiner.getPath('primary'); + const fileId = 'test'; + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [StoreModule], + providers: [ + { provide: Store, useValue: {} }, + SectionUploadService, + { provide: JsonPatchOperationsBuilder, useValue: jsonPatchOpBuilder }, + ], + schemas: [NO_ERRORS_SCHEMA] + }); + })); + + beforeEach(() => { + sectionUploadService = TestBed.inject(SectionUploadService); + operationsBuilder = TestBed.inject(JsonPatchOperationsBuilder); + }); + + [ + { + initialPrimary: null, + primary: true, + operationName: 'add', + expected: [primaryPath, fileId, false, true] + }, + { + initialPrimary: true, + primary: false, + operationName: 'remove', + expected: [primaryPath] + }, + { + initialPrimary: false, + primary: true, + operationName: 'replace', + expected: [primaryPath, fileId, true] + } + ].forEach(({ initialPrimary, primary, operationName, expected }) => { + it(`updatePrimaryBitstreamOperation should add ${operationName} operation`, () => { + const path = pathCombiner.getPath('primary'); + sectionUploadService.updatePrimaryBitstreamOperation(path, initialPrimary, primary, fileId); + expect(operationsBuilder[operationName]).toHaveBeenCalledWith(...expected); + }); + }); +}); diff --git a/src/app/submission/sections/upload/section-upload.service.ts b/src/app/submission/sections/upload/section-upload.service.ts index 726c9fd767..599e8ccbe2 100644 --- a/src/app/submission/sections/upload/section-upload.service.ts +++ b/src/app/submission/sections/upload/section-upload.service.ts @@ -15,6 +15,8 @@ import { submissionSectionDataFromIdSelector, submissionUploadedFileFromUuidSele import { isUndefined } from '../../../shared/empty.util'; import { WorkspaceitemSectionUploadFileObject } from '../../../core/submission/models/workspaceitem-section-upload-file.model'; import { WorkspaceitemSectionUploadObject } from 'src/app/core/submission/models/workspaceitem-section-upload.model'; +import { JsonPatchOperationPathObject } from 'src/app/core/json-patch/builder/json-patch-operation-path-combiner'; +import { JsonPatchOperationsBuilder } from 'src/app/core/json-patch/builder/json-patch-operations-builder'; /** * A service that provides methods to handle submission's bitstream state. @@ -26,8 +28,25 @@ export class SectionUploadService { * Initialize service variables * * @param {Store} store + * @param {JsonPatchOperationsBuilder} operationsBuilder */ - constructor(private store: Store) {} + constructor(private store: Store, private operationsBuilder: JsonPatchOperationsBuilder) {} + + + public updatePrimaryBitstreamOperation(path: JsonPatchOperationPathObject, intitialPrimary: boolean | null, primary: boolean | null, fileId: string): void { + if (intitialPrimary === null && primary) { + this.operationsBuilder.add(path, fileId, false, true); + return; + } + + if (intitialPrimary !== primary) { + if (primary) { + this.operationsBuilder.replace(path, fileId, true); + return; + } + this.operationsBuilder.remove(path); + } + } /** * Return submission's bitstream data from state @@ -132,7 +151,7 @@ export class SectionUploadService { * @param fileUUID * The bitstream UUID */ - public updateFilePrimaryBitstream(submissionId: string, sectionId: string, fileUUID: string) { + public updateFilePrimaryBitstream(submissionId: string, sectionId: string, fileUUID: string | null) { this.store.dispatch( new EditFilePrimaryBitstreamAction(submissionId, sectionId, fileUUID) );