import { TestBed } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; import { v4 as uuidv4 } from 'uuid'; import { EntityTypeDataService } from '../../../core/data/entity-type-data.service'; import { ItemDataService } from '../../../core/data/item-data.service'; import { FieldChangeType } from '../../../core/data/object-updates/field-change-type.model'; import { FieldUpdate } from '../../../core/data/object-updates/field-update.model'; import { FieldUpdates } from '../../../core/data/object-updates/field-updates.model'; import { DeleteRelationship, RelationshipIdentifiable, } from '../../../core/data/object-updates/object-updates.reducer'; import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service'; import { RelationshipDataService } from '../../../core/data/relationship-data.service'; import { Item } from '../../../core/shared/item.model'; import { ItemType } from '../../../core/shared/item-relationships/item-type.model'; import { Relationship } from '../../../core/shared/item-relationships/relationship.model'; import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { createFailedRemoteDataObject, createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$, } from '../../../shared/remote-data.utils'; import { EntityTypeDataServiceStub } from '../../../shared/testing/entity-type-data.service.stub'; import { ItemDataServiceStub } from '../../../shared/testing/item-data.service.stub'; import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; import { ObjectUpdatesServiceStub } from '../../../shared/testing/object-updates.service.stub'; import { RelationshipDataServiceStub } from '../../../shared/testing/relationship-data.service.stub'; import { EditItemRelationshipsService } from './edit-item-relationships.service'; describe('EditItemRelationshipsService', () => { let service: EditItemRelationshipsService; let itemService: ItemDataServiceStub; let objectUpdatesService: ObjectUpdatesServiceStub; let notificationsService: NotificationsServiceStub; let relationshipService: RelationshipDataServiceStub; let entityTypeDataService: EntityTypeDataServiceStub; let currentItem: Item; let relationshipItem1: Item; let relationshipIdentifiable1: RelationshipIdentifiable; let relationship1: Relationship; let relationshipItem2: Item; let relationshipIdentifiable2: RelationshipIdentifiable; let relationship2: Relationship; let orgUnitType: ItemType; let orgUnitToOrgUnitType: RelationshipType; beforeEach(() => { itemService = new ItemDataServiceStub(); objectUpdatesService = new ObjectUpdatesServiceStub(); notificationsService = new NotificationsServiceStub(); relationshipService = new RelationshipDataServiceStub(); entityTypeDataService = new EntityTypeDataServiceStub(); TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot(), ], providers: [ { provide: ItemDataService, useValue: itemService }, { provide: ObjectUpdatesService, useValue: objectUpdatesService }, { provide: NotificationsService, useValue: notificationsService }, { provide: RelationshipDataService, useValue: relationshipService }, { provide: EntityTypeDataService, useValue: entityTypeDataService }, ], }); service = TestBed.inject(EditItemRelationshipsService); }); beforeEach(() => { currentItem = Object.assign(new Item(), { uuid: uuidv4(), metadata: { 'dspace.entity.type': 'OrgUnit', }, _links: { self: { href: 'selfLink1', }, }, }); relationshipItem1 = Object.assign(new Item(), { uuid: uuidv4(), metadata: { 'dspace.entity.type': 'OrgUnit', }, _links: { self: { href: 'selfLink2', }, }, }); relationshipIdentifiable1 = { originalItem: currentItem, relatedItem: relationshipItem1, type: orgUnitToOrgUnitType, uuid: `1-${relationshipItem1.uuid}`, } as RelationshipIdentifiable; relationship1 = Object.assign(new Relationship(), { _links: { leftItem: currentItem._links.self, rightItem: relationshipItem1._links.self, }, }); relationshipItem2 = Object.assign(new Item(), { uuid: uuidv4(), metadata: { 'dspace.entity.type': 'OrgUnit', }, _links: { self: { href: 'selfLink3', }, }, }); relationshipIdentifiable2 = { originalItem: currentItem, relatedItem: relationshipItem2, type: orgUnitToOrgUnitType, uuid: `1-${relationshipItem2.uuid}`, } as RelationshipIdentifiable; relationship2 = Object.assign(new Relationship(), { _links: { leftItem: currentItem._links.self, rightItem: relationshipItem2._links.self, }, }); orgUnitType = Object.assign(new ItemType(), { id: '2', label: 'OrgUnit', }); orgUnitToOrgUnitType = Object.assign(new RelationshipType(), { id: '1', leftMaxCardinality: null, leftMinCardinality: 0, leftType: createSuccessfulRemoteDataObject$(orgUnitType), leftwardType: 'isOrgUnitOfOrgUnit', rightMaxCardinality: null, rightMinCardinality: 0, rightType: createSuccessfulRemoteDataObject$(orgUnitType), rightwardType: 'isOrgUnitOfOrgUnit', uuid: 'relationshiptype-1', }); }); describe('submit', () => { let fieldUpdateAddRelationship1: FieldUpdate; let fieldUpdateRemoveRelationship2: FieldUpdate; beforeEach(() => { fieldUpdateAddRelationship1 = { changeType: FieldChangeType.ADD, field: relationshipIdentifiable1, }; fieldUpdateRemoveRelationship2 = { changeType: FieldChangeType.REMOVE, field: relationshipIdentifiable2, }; spyOn(service, 'addRelationship').withArgs(relationshipIdentifiable1).and.returnValue(createSuccessfulRemoteDataObject$(relationship1)); spyOn(service, 'deleteRelationship').withArgs(relationshipIdentifiable2 as DeleteRelationship).and.returnValue(createSuccessfulRemoteDataObject$({})); spyOn(itemService, 'invalidateByHref').and.callThrough(); }); it('should support performing multiple relationships manipulations in one submit() call', () => { spyOn(objectUpdatesService, 'getFieldUpdates').and.returnValue(observableOf({ [`1-${relationshipItem1.uuid}`]: fieldUpdateAddRelationship1, [`1-${relationshipItem2.uuid}`]: fieldUpdateRemoveRelationship2, } as FieldUpdates)); service.submit(currentItem, `/entities/orgunit/${currentItem.uuid}/edit/relationships`); expect(service.addRelationship).toHaveBeenCalledWith(relationshipIdentifiable1); expect(service.deleteRelationship).toHaveBeenCalledWith(relationshipIdentifiable2 as DeleteRelationship); expect(itemService.invalidateByHref).toHaveBeenCalledWith(currentItem.self); expect(itemService.invalidateByHref).toHaveBeenCalledWith(relationshipItem1.self); expect(notificationsService.success).toHaveBeenCalledTimes(1); }); }); describe('deleteRelationship', () => { beforeEach(() => { spyOn(relationshipService, 'deleteRelationship').and.callThrough(); }); it('should pass "all" as copyVirtualMetadata when the user want to keep the data on both sides', () => { service.deleteRelationship({ uuid: relationshipItem1.uuid, keepLeftVirtualMetadata: true, keepRightVirtualMetadata: true, } as DeleteRelationship); expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationshipItem1.uuid, 'all', false); }); it('should pass "left" as copyVirtualMetadata when the user only want to keep the data on the left side', () => { service.deleteRelationship({ uuid: relationshipItem1.uuid, keepLeftVirtualMetadata: true, keepRightVirtualMetadata: false, } as DeleteRelationship); expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationshipItem1.uuid, 'left', false); }); it('should pass "right" as copyVirtualMetadata when the user only want to keep the data on the right side', () => { service.deleteRelationship({ uuid: relationshipItem1.uuid, keepLeftVirtualMetadata: false, keepRightVirtualMetadata: true, } as DeleteRelationship); expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationshipItem1.uuid, 'right', false); }); it('should pass "none" as copyVirtualMetadata when the user doesn\'t want to keep the virtual metadata', () => { service.deleteRelationship({ uuid: relationshipItem1.uuid, keepLeftVirtualMetadata: false, keepRightVirtualMetadata: false, } as DeleteRelationship); expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationshipItem1.uuid, 'none', false); }); }); describe('addRelationship', () => { beforeEach(() => { spyOn(relationshipService, 'addRelationship').and.callThrough(); }); it('should call the addRelationship from relationshipService correctly when original item is on the right', () => { service.addRelationship({ originalItem: currentItem, originalIsLeft: false, relatedItem: relationshipItem1, type: orgUnitToOrgUnitType, uuid: `1-${relationshipItem1.uuid}`, } as RelationshipIdentifiable); expect(relationshipService.addRelationship).toHaveBeenCalledWith(orgUnitToOrgUnitType.id, relationshipItem1, currentItem, undefined, null, false); }); it('should call the addRelationship from relationshipService correctly when original item is on the left', () => { service.addRelationship({ originalItem: currentItem, originalIsLeft: true, relatedItem: relationshipItem1, type: orgUnitToOrgUnitType, uuid: `1-${relationshipItem1.uuid}`, } as RelationshipIdentifiable); expect(relationshipService.addRelationship).toHaveBeenCalledWith(orgUnitToOrgUnitType.id, currentItem, relationshipItem1, null, undefined, false); }); }); describe('displayNotifications', () => { it('should show one success notification when multiple requests succeeded', () => { service.displayNotifications([ createSuccessfulRemoteDataObject({}), createSuccessfulRemoteDataObject({}), ]); expect(notificationsService.success).toHaveBeenCalledTimes(1); }); it('should show one success notification even when some requests failed', () => { service.displayNotifications([ createSuccessfulRemoteDataObject({}), createFailedRemoteDataObject('Request Failed'), createSuccessfulRemoteDataObject({}), ]); expect(notificationsService.success).toHaveBeenCalledTimes(1); expect(notificationsService.error).toHaveBeenCalledTimes(1); }); it('should show a separate error notification for each failed request', () => { service.displayNotifications([ createSuccessfulRemoteDataObject({}), createFailedRemoteDataObject('Request Failed 1'), createSuccessfulRemoteDataObject({}), createFailedRemoteDataObject('Request Failed 2'), ]); expect(notificationsService.success).toHaveBeenCalledTimes(1); expect(notificationsService.error).toHaveBeenCalledTimes(2); }); }); });