Skip to content

Commit 479adf6

Browse files
115046: Fixed failing tests & added new test to cover added code
1 parent a658bf4 commit 479adf6

12 files changed

Lines changed: 633 additions & 151 deletions

src/app/core/data/relationship-data.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
196196
* Method to remove an item that's part of a relationship from the cache
197197
* @param item The item to remove from the cache
198198
*/
199-
public refreshRelationshipItemsInCache(item) {
199+
public refreshRelationshipItemsInCache(item: Item): void {
200200
this.objectCache.remove(item._links.self.href);
201201
this.requestService.removeByHrefSubstring(item.uuid);
202202
observableCombineLatest([
Lines changed: 290 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,303 @@
11
import { TestBed } from '@angular/core/testing';
2+
import { TranslateModule } from '@ngx-translate/core';
3+
import { of as observableOf } from 'rxjs';
4+
import { v4 as uuidv4 } from 'uuid';
25

6+
import { EntityTypeDataService } from '../../../core/data/entity-type-data.service';
7+
import { ItemDataService } from '../../../core/data/item-data.service';
8+
import { FieldChangeType } from '../../../core/data/object-updates/field-change-type.model';
9+
import { FieldUpdate } from '../../../core/data/object-updates/field-update.model';
10+
import { FieldUpdates } from '../../../core/data/object-updates/field-updates.model';
11+
import {
12+
DeleteRelationship,
13+
RelationshipIdentifiable,
14+
} from '../../../core/data/object-updates/object-updates.reducer';
15+
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
16+
import { RelationshipDataService } from '../../../core/data/relationship-data.service';
17+
import { Item } from '../../../core/shared/item.model';
18+
import { ItemType } from '../../../core/shared/item-relationships/item-type.model';
19+
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
20+
import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
21+
import { NotificationsService } from '../../../shared/notifications/notifications.service';
22+
import {
23+
createFailedRemoteDataObject,
24+
createSuccessfulRemoteDataObject,
25+
createSuccessfulRemoteDataObject$,
26+
} from '../../../shared/remote-data.utils';
27+
import { EntityTypeDataServiceStub } from '../../../shared/testing/entity-type-data.service.stub';
28+
import { ItemDataServiceStub } from '../../../shared/testing/item-data.service.stub';
29+
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
30+
import { ObjectUpdatesServiceStub } from '../../../shared/testing/object-updates.service.stub';
31+
import { RelationshipDataServiceStub } from '../../../shared/testing/relationship-data.service.stub';
332
import { EditItemRelationshipsService } from './edit-item-relationships.service';
433

534
describe('EditItemRelationshipsService', () => {
635
let service: EditItemRelationshipsService;
736

37+
let itemService: ItemDataServiceStub;
38+
let objectUpdatesService: ObjectUpdatesServiceStub;
39+
let notificationsService: NotificationsServiceStub;
40+
let relationshipService: RelationshipDataServiceStub;
41+
let entityTypeDataService: EntityTypeDataServiceStub;
42+
43+
let currentItem: Item;
44+
45+
let relationshipItem1: Item;
46+
let relationshipIdentifiable1: RelationshipIdentifiable;
47+
let relationship1: Relationship;
48+
49+
let relationshipItem2: Item;
50+
let relationshipIdentifiable2: RelationshipIdentifiable;
51+
let relationship2: Relationship;
52+
53+
let orgUnitType: ItemType;
54+
let orgUnitToOrgUnitType: RelationshipType;
55+
856
beforeEach(() => {
9-
TestBed.configureTestingModule({});
57+
itemService = new ItemDataServiceStub();
58+
objectUpdatesService = new ObjectUpdatesServiceStub();
59+
notificationsService = new NotificationsServiceStub();
60+
relationshipService = new RelationshipDataServiceStub();
61+
entityTypeDataService = new EntityTypeDataServiceStub();
62+
63+
TestBed.configureTestingModule({
64+
imports: [
65+
TranslateModule.forRoot(),
66+
],
67+
providers: [
68+
{ provide: ItemDataService, useValue: itemService },
69+
{ provide: ObjectUpdatesService, useValue: objectUpdatesService },
70+
{ provide: NotificationsService, useValue: notificationsService },
71+
{ provide: RelationshipDataService, useValue: relationshipService },
72+
{ provide: EntityTypeDataService, useValue: entityTypeDataService },
73+
],
74+
});
1075
service = TestBed.inject(EditItemRelationshipsService);
1176
});
1277

13-
it('should be created', () => {
14-
expect(service).toBeTruthy();
78+
beforeEach(() => {
79+
currentItem = Object.assign(new Item(), {
80+
uuid: uuidv4(),
81+
metadata: {
82+
'dspace.entity.type': 'OrgUnit',
83+
},
84+
_links: {
85+
self: {
86+
href: 'selfLink1',
87+
},
88+
},
89+
});
90+
91+
relationshipItem1 = Object.assign(new Item(), {
92+
uuid: uuidv4(),
93+
metadata: {
94+
'dspace.entity.type': 'OrgUnit',
95+
},
96+
_links: {
97+
self: {
98+
href: 'selfLink2',
99+
},
100+
},
101+
});
102+
relationshipIdentifiable1 = {
103+
originalItem: currentItem,
104+
relatedItem: relationshipItem1,
105+
type: orgUnitToOrgUnitType,
106+
uuid: `1-${relationshipItem1.uuid}`,
107+
} as RelationshipIdentifiable;
108+
relationship1 = Object.assign(new Relationship(), {
109+
_links: {
110+
leftItem: currentItem._links.self,
111+
rightItem: relationshipItem1._links.self,
112+
},
113+
});
114+
115+
relationshipItem2 = Object.assign(new Item(), {
116+
uuid: uuidv4(),
117+
metadata: {
118+
'dspace.entity.type': 'OrgUnit',
119+
},
120+
_links: {
121+
self: {
122+
href: 'selfLink3',
123+
},
124+
},
125+
});
126+
relationshipIdentifiable2 = {
127+
originalItem: currentItem,
128+
relatedItem: relationshipItem2,
129+
type: orgUnitToOrgUnitType,
130+
uuid: `1-${relationshipItem2.uuid}`,
131+
} as RelationshipIdentifiable;
132+
relationship2 = Object.assign(new Relationship(), {
133+
_links: {
134+
leftItem: currentItem._links.self,
135+
rightItem: relationshipItem2._links.self,
136+
},
137+
});
138+
139+
orgUnitType = Object.assign(new ItemType(), {
140+
id: '2',
141+
label: 'OrgUnit',
142+
});
143+
orgUnitToOrgUnitType = Object.assign(new RelationshipType(), {
144+
id: '1',
145+
leftMaxCardinality: null,
146+
leftMinCardinality: 0,
147+
leftType: createSuccessfulRemoteDataObject$(orgUnitType),
148+
leftwardType: 'isOrgUnitOfOrgUnit',
149+
rightMaxCardinality: null,
150+
rightMinCardinality: 0,
151+
rightType: createSuccessfulRemoteDataObject$(orgUnitType),
152+
rightwardType: 'isOrgUnitOfOrgUnit',
153+
uuid: 'relationshiptype-1',
154+
});
155+
});
156+
157+
describe('submit', () => {
158+
let fieldUpdateAddRelationship1: FieldUpdate;
159+
let fieldUpdateRemoveRelationship2: FieldUpdate;
160+
161+
beforeEach(() => {
162+
fieldUpdateAddRelationship1 = {
163+
changeType: FieldChangeType.ADD,
164+
field: relationshipIdentifiable1,
165+
};
166+
fieldUpdateRemoveRelationship2 = {
167+
changeType: FieldChangeType.REMOVE,
168+
field: relationshipIdentifiable2,
169+
};
170+
171+
spyOn(service, 'addRelationship').withArgs(relationshipIdentifiable1).and.returnValue(createSuccessfulRemoteDataObject$(relationship1));
172+
spyOn(service, 'deleteRelationship').withArgs(relationshipIdentifiable2 as DeleteRelationship).and.returnValue(createSuccessfulRemoteDataObject$({}));
173+
spyOn(itemService, 'invalidateByHref').and.callThrough();
174+
});
175+
176+
it('should support performing multiple relationships manipulations in one submit() call', () => {
177+
spyOn(objectUpdatesService, 'getFieldUpdates').and.returnValue(observableOf({
178+
[`1-${relationshipItem1.uuid}`]: fieldUpdateAddRelationship1,
179+
[`1-${relationshipItem2.uuid}`]: fieldUpdateRemoveRelationship2,
180+
} as FieldUpdates));
181+
service.submit(currentItem, `/entities/orgunit/${currentItem.uuid}/edit/relationships`);
182+
183+
expect(service.addRelationship).toHaveBeenCalledWith(relationshipIdentifiable1);
184+
expect(service.deleteRelationship).toHaveBeenCalledWith(relationshipIdentifiable2 as DeleteRelationship);
185+
186+
expect(itemService.invalidateByHref).toHaveBeenCalledWith(currentItem.self);
187+
expect(itemService.invalidateByHref).toHaveBeenCalledWith(relationshipItem1.self);
188+
// TODO currently this isn't done yet
189+
// expect(itemService.invalidateByHref).toHaveBeenCalledWith(relationshipItem2.self);
190+
191+
expect(notificationsService.success).toHaveBeenCalledTimes(1);
192+
});
193+
});
194+
195+
describe('deleteRelationship', () => {
196+
beforeEach(() => {
197+
spyOn(relationshipService, 'deleteRelationship').and.callThrough();
198+
});
199+
200+
it('should pass "all" as copyVirtualMetadata when the user want to keep the data on both sides', () => {
201+
service.deleteRelationship({
202+
uuid: relationshipItem1.uuid,
203+
keepLeftVirtualMetadata: true,
204+
keepRightVirtualMetadata: true,
205+
} as DeleteRelationship);
206+
207+
expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationshipItem1.uuid, 'all', false);
208+
});
209+
210+
it('should pass "left" as copyVirtualMetadata when the user only want to keep the data on the left side', () => {
211+
service.deleteRelationship({
212+
uuid: relationshipItem1.uuid,
213+
keepLeftVirtualMetadata: true,
214+
keepRightVirtualMetadata: false,
215+
} as DeleteRelationship);
216+
217+
expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationshipItem1.uuid, 'left', false);
218+
});
219+
220+
it('should pass "right" as copyVirtualMetadata when the user only want to keep the data on the right side', () => {
221+
service.deleteRelationship({
222+
uuid: relationshipItem1.uuid,
223+
keepLeftVirtualMetadata: false,
224+
keepRightVirtualMetadata: true,
225+
} as DeleteRelationship);
226+
227+
expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationshipItem1.uuid, 'right', false);
228+
});
229+
230+
it('should pass "none" as copyVirtualMetadata when the user doesn\'t want to keep the virtual metadata', () => {
231+
service.deleteRelationship({
232+
uuid: relationshipItem1.uuid,
233+
keepLeftVirtualMetadata: false,
234+
keepRightVirtualMetadata: false,
235+
} as DeleteRelationship);
236+
237+
expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationshipItem1.uuid, 'none', false);
238+
});
239+
});
240+
241+
describe('addRelationship', () => {
242+
beforeEach(() => {
243+
spyOn(relationshipService, 'addRelationship').and.callThrough();
244+
});
245+
246+
it('should call the addRelationship from relationshipService correctly when original item is on the right', () => {
247+
service.addRelationship({
248+
originalItem: currentItem,
249+
originalIsLeft: false,
250+
relatedItem: relationshipItem1,
251+
type: orgUnitToOrgUnitType,
252+
uuid: `1-${relationshipItem1.uuid}`,
253+
} as RelationshipIdentifiable);
254+
expect(relationshipService.addRelationship).toHaveBeenCalledWith(orgUnitToOrgUnitType.id, relationshipItem1, currentItem, undefined, null, false);
255+
});
256+
257+
it('should call the addRelationship from relationshipService correctly when original item is on the left', () => {
258+
service.addRelationship({
259+
originalItem: currentItem,
260+
originalIsLeft: true,
261+
relatedItem: relationshipItem1,
262+
type: orgUnitToOrgUnitType,
263+
uuid: `1-${relationshipItem1.uuid}`,
264+
} as RelationshipIdentifiable);
265+
266+
expect(relationshipService.addRelationship).toHaveBeenCalledWith(orgUnitToOrgUnitType.id, currentItem, relationshipItem1, null, undefined, false);
267+
});
268+
});
269+
270+
describe('displayNotifications', () => {
271+
it('should show one success notification when multiple requests succeeded', () => {
272+
service.displayNotifications([
273+
createSuccessfulRemoteDataObject({}),
274+
createSuccessfulRemoteDataObject({}),
275+
]);
276+
277+
expect(notificationsService.success).toHaveBeenCalledTimes(1);
278+
});
279+
280+
it('should show one success notification even when some requests failed', () => {
281+
service.displayNotifications([
282+
createSuccessfulRemoteDataObject({}),
283+
createFailedRemoteDataObject('Request Failed'),
284+
createSuccessfulRemoteDataObject({}),
285+
]);
286+
287+
expect(notificationsService.success).toHaveBeenCalledTimes(1);
288+
expect(notificationsService.error).toHaveBeenCalledTimes(1);
289+
});
290+
291+
it('should show a separate error notification for each failed request', () => {
292+
service.displayNotifications([
293+
createSuccessfulRemoteDataObject({}),
294+
createFailedRemoteDataObject('Request Failed 1'),
295+
createSuccessfulRemoteDataObject({}),
296+
createFailedRemoteDataObject('Request Failed 2'),
297+
]);
298+
299+
expect(notificationsService.success).toHaveBeenCalledTimes(1);
300+
expect(notificationsService.error).toHaveBeenCalledTimes(2);
301+
});
15302
});
16303
});

src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from '../../../core/data/object-updates/object-updates.reducer';
1111
import { RemoteData } from '../../../core/data/remote-data';
1212
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
13-
import { EMPTY, Observable, BehaviorSubject } from 'rxjs';
13+
import { EMPTY, Observable, BehaviorSubject, Subscription } from 'rxjs';
1414
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
1515
import { ItemDataService } from '../../../core/data/item-data.service';
1616
import { Item } from '../../../core/shared/item.model';
@@ -109,7 +109,7 @@ export class EditItemRelationshipsService {
109109
/**
110110
* Sends all initial values of this item to the object updates service
111111
*/
112-
public initializeOriginalFields(item: Item, url: string) {
112+
public initializeOriginalFields(item: Item, url: string): Subscription {
113113
return this.relationshipService.getRelatedItems(item).pipe(
114114
take(1),
115115
).subscribe((items: Item[]) => {
@@ -157,7 +157,7 @@ export class EditItemRelationshipsService {
157157
* - Success notification in case there's at least one successful response
158158
* @param responses
159159
*/
160-
displayNotifications(responses: RemoteData<NoContent>[]) {
160+
displayNotifications(responses: RemoteData<NoContent>[]): void {
161161
const failedResponses = responses.filter((response: RemoteData<NoContent>) => response.hasFailed);
162162
const successfulResponses = responses.filter((response: RemoteData<NoContent>) => response.hasSucceeded);
163163

@@ -175,15 +175,15 @@ export class EditItemRelationshipsService {
175175
* Get translated notification title
176176
* @param key
177177
*/
178-
getNotificationTitle(key: string) {
178+
getNotificationTitle(key: string): string {
179179
return this.translateService.instant(this.notificationsPrefix + key + '.title');
180180
}
181181

182182
/**
183183
* Get translated notification content
184184
* @param key
185185
*/
186-
getNotificationContent(key: string) {
186+
getNotificationContent(key: string): string {
187187
return this.translateService.instant(this.notificationsPrefix + key + '.content');
188188

189189
}

0 commit comments

Comments
 (0)