diff --git a/src/app/core/submission/models/workspaceitem-section-duplicates.model.ts b/src/app/core/submission/models/workspaceitem-section-duplicates.model.ts index b31c2d1b34..f9441fa790 100644 --- a/src/app/core/submission/models/workspaceitem-section-duplicates.model.ts +++ b/src/app/core/submission/models/workspaceitem-section-duplicates.model.ts @@ -1,5 +1,5 @@ /* - * Object model for the data returned by the REST API to present minted identifiers in a submission section + * Object model for the data returned by the REST API to present potential duplicates in a submission section */ import { Duplicate } from '../../../shared/object-list/duplicate-data/duplicate.model'; diff --git a/src/app/core/submission/models/workspaceitem-sections.model.ts b/src/app/core/submission/models/workspaceitem-sections.model.ts index dd19c3fb8b..9f39c1ab04 100644 --- a/src/app/core/submission/models/workspaceitem-sections.model.ts +++ b/src/app/core/submission/models/workspaceitem-sections.model.ts @@ -5,6 +5,7 @@ import { WorkspaceitemSectionUploadObject } from './workspaceitem-section-upload import { WorkspaceitemSectionCcLicenseObject } from './workspaceitem-section-cc-license.model'; import {WorkspaceitemSectionIdentifiersObject} from './workspaceitem-section-identifiers.model'; import { WorkspaceitemSectionSherpaPoliciesObject } from './workspaceitem-section-sherpa-policies.model'; +import {WorkspaceitemSectionDuplicatesObject} from "./workspaceitem-section-duplicates.model"; /** * An interface to represent submission's section object. @@ -25,6 +26,7 @@ export type WorkspaceitemSectionDataType | WorkspaceitemSectionAccessesObject | WorkspaceitemSectionSherpaPoliciesObject | WorkspaceitemSectionIdentifiersObject + | WorkspaceitemSectionDuplicatesObject | string; diff --git a/src/app/shared/object-list/duplicate-data/duplicate.model.ts b/src/app/shared/object-list/duplicate-data/duplicate.model.ts index 7ca0364a01..858f284d5c 100644 --- a/src/app/shared/object-list/duplicate-data/duplicate.model.ts +++ b/src/app/shared/object-list/duplicate-data/duplicate.model.ts @@ -10,9 +10,9 @@ export class Duplicate { @autoserialize uuid: string; @autoserialize - workflowItemId: bigint; + workflowItemId: number; @autoserialize - workspaceItemId: bigint; + workspaceItemId: number; @autoserialize owningCollection: string; diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.spec.ts index d63ee4ba13..9aad4a8b7b 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.spec.ts @@ -35,6 +35,7 @@ const mockResultObject: PoolTaskSearchResult = new PoolTaskSearchResult(); mockResultObject.hitHighlights = {}; const item = Object.assign(new Item(), { + duplicates: observableOf([]), bundles: observableOf({}), metadata: { 'dc.title': [ diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts index c91948116c..ea87ce1b2e 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts @@ -114,7 +114,6 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen console.dir(remoteData); if (remoteData.hasSucceeded) { if (remoteData.payload.page) { - console.dir(remoteData.payload.page); return remoteData.payload.page; } } diff --git a/src/app/submission/sections/duplicates/section-duplicates.component.html b/src/app/submission/sections/duplicates/section-duplicates.component.html index 02805b12d5..285c6b2b68 100644 --- a/src/app/submission/sections/duplicates/section-duplicates.component.html +++ b/src/app/submission/sections/duplicates/section-duplicates.component.html @@ -2,14 +2,14 @@ Template for the detect duplicates submission section component @author Kim Shepherd --> -
+

{{ 'submission.sections.duplicates.none' }}

{{ 'submission.sections.duplicates.detected' | translate }}

- {{dupe.title}} + {{dupe.title}}
{{('item.preview.' + metadatum.key) | translate}} {{metadatum.value}}
diff --git a/src/app/submission/sections/duplicates/section-duplicates.component.spec.ts b/src/app/submission/sections/duplicates/section-duplicates.component.spec.ts new file mode 100644 index 0000000000..d15a255f14 --- /dev/null +++ b/src/app/submission/sections/duplicates/section-duplicates.component.spec.ts @@ -0,0 +1,252 @@ +import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { NgxPaginationModule } from 'ngx-pagination'; +import { cold } from 'jasmine-marbles'; +import { of as observableOf } from 'rxjs'; +import { TranslateModule } from '@ngx-translate/core'; + +import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { createTestComponent } from '../../../shared/testing/utils.test'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; +import { SubmissionService } from '../../submission.service'; +import { SubmissionServiceStub } from '../../../shared/testing/submission-service.stub'; +import { SectionsService } from '../sections.service'; +import { SectionsServiceStub } from '../../../shared/testing/sections-service.stub'; +import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; +import { getMockFormOperationsService } from '../../../shared/mocks/form-operations-service.mock'; +import { getMockFormService } from '../../../shared/mocks/form-service.mock'; +import { FormService } from '../../../shared/form/form.service'; +import { SubmissionFormsConfigDataService } from '../../../core/config/submission-forms-config-data.service'; +import { SectionsType } from '../sections-type'; +import { mockSubmissionCollectionId, mockSubmissionId } from '../../../shared/mocks/submission.mock'; +import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner'; +import { SubmissionSectionDuplicatesComponent } from './section-duplicates.component'; +import { CollectionDataService } from '../../../core/data/collection-data.service'; +import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; +import { SectionFormOperationsService } from '../form/section-form-operations.service'; +import { SubmissionScopeType } from '../../../core/submission/submission-scope-type'; +import { License } from '../../../core/shared/license.model'; +import { Collection } from '../../../core/shared/collection.model'; +import { ObjNgFor } from '../../../shared/utils/object-ngfor.pipe'; +import { VarDirective } from '../../../shared/utils/var.directive'; +import { PaginationService } from '../../../core/pagination/pagination.service'; +import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import {Duplicate} from "../../../shared/object-list/duplicate-data/duplicate.model"; +import {MetadataValue} from "../../../core/shared/metadata.models"; +import { + WorkspaceitemSectionDuplicatesObject +} from "../../../core/submission/models/workspaceitem-section-duplicates.model"; +import {SectionDataObject} from "../models/section-data.model"; +import {defaultUUID} from "../../../shared/mocks/uuid.service.mock"; + +function getMockSubmissionFormsConfigService(): SubmissionFormsConfigDataService { + return jasmine.createSpyObj('FormOperationsService', { + getConfigAll: jasmine.createSpy('getConfigAll'), + getConfigByHref: jasmine.createSpy('getConfigByHref'), + getConfigByName: jasmine.createSpy('getConfigByName'), + getConfigBySearch: jasmine.createSpy('getConfigBySearch') + }); +} + +function getMockCollectionDataService(): CollectionDataService { + return jasmine.createSpyObj('CollectionDataService', { + findById: jasmine.createSpy('findById'), + findByHref: jasmine.createSpy('findByHref') + }); +} + +const duplicates: Duplicate[]= [{ + title: 'Unique title', + uuid: defaultUUID, + workflowItemId: 1, + workspaceItemId: 2, + owningCollection: 'Test Collection', + metadata: { + 'dc.title': [ + Object.assign(new MetadataValue(), { + 'value': 'Unique title', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + })] + } + }]; + +const potentialDuplicates: WorkspaceitemSectionDuplicatesObject = { + potentialDuplicates: duplicates +}; + +const sectionObject: SectionDataObject = { + header: 'submission.sections.submit.progressbar.duplicates', + config: 'https://dspace.org/api/config/submissionforms/duplicates', + mandatory: true, + opened: true, + data: potentialDuplicates, + errorsToShow: [], + serverValidationErrors: [], + id: 'duplicates', + sectionType: SectionsType.Duplicates, + sectionVisibility: null +}; + +describe('SubmissionSectionDuplicatesComponent test suite', () => { + let comp: SubmissionSectionDuplicatesComponent; + let compAsAny: any; + let fixture: ComponentFixture; + let submissionServiceStub: any = new SubmissionServiceStub(); + const sectionsServiceStub: any = new SectionsServiceStub(); + let formService: any; + let formOperationsService: any; + let formBuilderService: any; + let collectionDataService: any; + + const submissionId = mockSubmissionId; + const collectionId = mockSubmissionCollectionId; + const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', { + add: jasmine.createSpy('add'), + replace: jasmine.createSpy('replace'), + remove: jasmine.createSpy('remove'), + }); + + const licenseText = 'License text'; + const mockCollection = Object.assign(new Collection(), { + name: 'Community 1-Collection 1', + id: collectionId, + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'Community 1-Collection 1' + }], + license: createSuccessfulRemoteDataObject$(Object.assign(new License(), { text: licenseText })) + }); + const paginationService = new PaginationServiceStub(); + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [ + BrowserModule, + CommonModule, + FormsModule, + ReactiveFormsModule, + NgxPaginationModule, + NoopAnimationsModule, + TranslateModule.forRoot(), + ], + declarations: [ + SubmissionSectionDuplicatesComponent, + TestComponent, + ObjNgFor, + VarDirective, + ], + providers: [ + { provide: CollectionDataService, useValue: getMockCollectionDataService() }, + { provide: SectionFormOperationsService, useValue: getMockFormOperationsService() }, + { provide: FormService, useValue: getMockFormService() }, + { provide: JsonPatchOperationsBuilder, useValue: jsonPatchOpBuilder }, + { provide: SubmissionFormsConfigDataService, useValue: getMockSubmissionFormsConfigService() }, + { provide: NotificationsService, useClass: NotificationsServiceStub }, + { provide: SectionsService, useClass: SectionsServiceStub }, + { provide: SubmissionService, useClass: SubmissionServiceStub }, + { provide: 'collectionIdProvider', useValue: collectionId }, + { provide: 'sectionDataProvider', useValue: sectionObject }, + { provide: 'submissionIdProvider', useValue: submissionId }, + { provide: PaginationService, useValue: paginationService }, + ChangeDetectorRef, + FormBuilderService, + SubmissionSectionDuplicatesComponent + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents().then(); + })); + + // First test to check the correct component creation + describe('', () => { + let testComp: TestComponent; + let testFixture: ComponentFixture; + + // synchronous beforeEach + beforeEach(() => { + sectionsServiceStub.isSectionReadOnly.and.returnValue(observableOf(false)); + sectionsServiceStub.getSectionErrors.and.returnValue(observableOf([])); + sectionsServiceStub.getSectionData.and.returnValue(observableOf(sectionObject)); + const html = ``; + testFixture = createTestComponent(html, TestComponent) as ComponentFixture; + testComp = testFixture.componentInstance; + }); + + afterEach(() => { + testFixture.destroy(); + }); + + it('should create SubmissionSectionDuplicatesComponent', () => { + expect(testComp).toBeDefined(); + }); + }); + + describe('', () => { + beforeEach(() => { + fixture = TestBed.createComponent(SubmissionSectionDuplicatesComponent); + comp = fixture.componentInstance; + compAsAny = comp; + submissionServiceStub = TestBed.inject(SubmissionService); + formService = TestBed.inject(FormService); + formBuilderService = TestBed.inject(FormBuilderService); + formOperationsService = TestBed.inject(SectionFormOperationsService); + collectionDataService = TestBed.inject(CollectionDataService); + compAsAny.pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionObject.id); + }); + + afterEach(() => { + fixture.destroy(); + comp = null; + compAsAny = null; + }); + + // Test initialisation of the submission section + it('Should init section properly', () => { + collectionDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(mockCollection)); + sectionsServiceStub.getSectionErrors.and.returnValue(observableOf([])); + sectionsServiceStub.isSectionReadOnly.and.returnValue(observableOf(false)); + compAsAny.submissionService.getSubmissionScope.and.returnValue(SubmissionScopeType.WorkspaceItem); + spyOn(comp, 'getSectionStatus').and.returnValue(observableOf(true)); + spyOn(comp, 'getDuplicateData').and.returnValue(observableOf(potentialDuplicates)); + expect(comp.isLoading).toBeTruthy(); + comp.onSectionInit(); + fixture.detectChanges(); + expect(comp.isLoading).toBeFalsy(); + }); + + // The following tests look for proper logic in the getSectionStatus() implementation + // These are very simple as we don't really have a 'false' state unless we're still loading + it('Should return TRUE if the isLoading is FALSE', () => { + compAsAny.isLoading = false; + expect(compAsAny.getSectionStatus()).toBeObservable(cold('(a|)', { + a: true + })); + }); + it('Should return FALSE', () => { + compAsAny.isLoadin = true; + expect(compAsAny.getSectionStatus()).toBeObservable(cold('(a|)', { + a: false + })); + }); + }); + +}); + +// declare a test component +@Component({ + selector: 'ds-test-cmp', + template: `` +}) +class TestComponent { + +} diff --git a/src/app/submission/sections/duplicates/section-duplicates.component.ts b/src/app/submission/sections/duplicates/section-duplicates.component.ts index 54d3080fb0..a8a64a0d17 100644 --- a/src/app/submission/sections/duplicates/section-duplicates.component.ts +++ b/src/app/submission/sections/duplicates/section-duplicates.component.ts @@ -9,12 +9,12 @@ import { SectionDataObject } from '../models/section-data.model'; import { SubmissionService } from '../../submission.service'; import { AlertType } from '../../../shared/alert/alert-type'; import { SectionsService } from '../sections.service'; -import {map} from "rxjs/operators"; -import {ItemDataService} from "../../../core/data/item-data.service"; import { WorkspaceitemSectionDuplicatesObject } from "../../../core/submission/models/workspaceitem-section-duplicates.model"; import {Metadata} from "../../../core/shared/metadata.utils"; +import {URLCombiner} from "../../../core/url-combiner/url-combiner"; +import {getItemModuleRoute} from "../../../item-page/item-page-routing-paths"; /** * Detect duplicates step @@ -47,19 +47,12 @@ export class SubmissionSectionDuplicatesComponent extends SectionModelComponent */ protected subs: Subscription[] = []; - /** - * Section data observable - */ - public data$: Observable; - /** * Initialize instance variables. * * @param {TranslateService} translate * @param {SectionsService} sectionService * @param {SubmissionService} submissionService - * @param itemDataService - * @param nameService * @param {string} injectedCollectionId * @param {SectionDataObject} injectedSectionData * @param {string} injectedSubmissionId @@ -67,8 +60,6 @@ export class SubmissionSectionDuplicatesComponent extends SectionModelComponent constructor(protected translate: TranslateService, protected sectionService: SectionsService, protected submissionService: SubmissionService, - private itemDataService: ItemDataService, - // private nameService: DSONameService, @Inject('collectionIdProvider') public injectedCollectionId: string, @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, @Inject('submissionIdProvider') public injectedSubmissionId: string) { @@ -84,13 +75,7 @@ export class SubmissionSectionDuplicatesComponent extends SectionModelComponent */ onSectionInit() { this.isLoading = false; - this.data$ = this.getDuplicateData().pipe( - map((data: WorkspaceitemSectionDuplicatesObject) => { - console.dir(data); - return data; - }) - ); -} + } /** * Check if identifier section has read-only visibility @@ -123,5 +108,9 @@ export class SubmissionSectionDuplicatesComponent extends SectionModelComponent Observable; } + public getItemLink(uuid: any) { + return new URLCombiner(getItemModuleRoute(), uuid).toString(); + } + protected readonly Metadata = Metadata; }