Catalog alignment
[sdc.git] / catalog-ui / src / app / ng2 / pages / composition / graph / composition-graph.component.spec.ts
1 import {NO_ERRORS_SCHEMA} from '@angular/core';
2 import {async, ComponentFixture} from '@angular/core/testing';
3 import {SdcUiServices} from 'onap-ui-angular';
4 import 'rxjs/add/observable/of';
5 import {ConfigureFn, configureTests} from '../../../../../jest/test-config.helper';
6 import {CompositionGraphComponent} from "./composition-graph.component";
7 import {WorkspaceService} from "../../workspace/workspace.service";
8 import {ComponentInstance, GroupInstance, NodesFactory, ZoneInstance, ZoneInstanceMode} from "../../../../models";
9 import {EventListenerService} from "../../../../services";
10 import {
11     CompositionGraphGeneralUtils,
12     CompositionGraphNodesUtils,
13     CompositionGraphZoneUtils,
14     MatchCapabilitiesRequirementsUtils, ServicePathGraphUtils
15 } from "./utils";
16 import {CompositionGraphLinkUtils} from "./utils/composition-graph-links-utils";
17 import {ConnectionWizardService} from "./connection-wizard/connection-wizard.service";
18 import {CommonGraphUtils} from "./common/common-graph-utils";
19 import {CompositionGraphPaletteUtils} from "./utils/composition-graph-palette-utils";
20 import {TopologyTemplateService} from "../../../services/component-services/topology-template.service";
21 import {ComponentInstanceServiceNg2} from "../../../services/component-instance-services/component-instance.service";
22 import {CompositionService} from "../composition.service";
23 import {ModalService} from '../../../services/modal.service';
24 import {Store} from '@ngxs/store';
25 import {PoliciesService} from '../../../services/policies.service';
26 import {GroupsService} from '../../../services/groups.service';
27 import {PolicyInstance} from "../../../../models/graph/zones/policy-instance";
28 import {ZoneInstanceType} from "../../../../models/graph/zones/zone-instance";
29 import {GRAPH_EVENTS} from "../../../../utils/constants";
30 import * as cytoscape from "cytoscape";
31 import {ComponentMetadata} from "../../../../models/component-metadata";
32 import {Zone} from "../../../../models/graph/zones/zone";
33 import {SelectedComponentType, SetSelectedComponentAction} from "../common/store/graph.actions";
34
35 describe('composition graph component', () => {
36
37     let fixture: ComponentFixture<CompositionGraphComponent>;
38     let instance: CompositionGraphComponent;
39     let eventServiceMock: Partial<EventListenerService>;
40     let compositionGraphZoneUtils: Partial<CompositionGraphZoneUtils>;
41     let generalGraphUtils: Partial<CompositionGraphGeneralUtils>;
42     let workspaceServiceMock: Partial<WorkspaceService>;
43     let policyService: Partial<PoliciesService>;
44     let storeStub;
45     let compositionGraphLinkUtils: Partial<CompositionGraphLinkUtils>;
46     let nodesGraphUtils: Partial<CompositionGraphNodesUtils>;
47
48     let createPolicyInstance = () => {
49         let policy = new PolicyInstance();
50         policy.targets = {COMPONENT_INSTANCES: [], GROUPS: []};
51         return new ZoneInstance(policy, '', '');
52     }
53
54     beforeEach(
55         async(() => {
56
57             eventServiceMock = {
58                 notifyObservers: jest.fn(),
59                 unRegisterObserver: jest.fn()
60             }
61
62             compositionGraphZoneUtils = {
63                 endCyTagMode: jest.fn(),
64                 showZoneTagIndications: jest.fn(),
65                 hideZoneTagIndications: jest.fn(),
66                 hideGroupZoneIndications: jest.fn(),
67                 showGroupZoneIndications: jest.fn(),
68                 startCyTagMode: jest.fn()
69             }
70
71             workspaceServiceMock = {
72                 metadata: <ComponentMetadata>{
73                     uniqueId: 'service_unique_id',
74                     componentType: 'SERVICE'
75                 }
76             }
77
78             compositionGraphLinkUtils = {
79                 handleLinkClick: jest.fn(),
80                 getModifyLinkMenu: jest.fn()
81             }
82
83             storeStub = {
84                 dispatch: jest.fn()
85             }
86             policyService = {
87                 getSpecificPolicy: jest.fn()
88             }
89
90             generalGraphUtils = {
91                 zoomGraphTo: jest.fn()
92             }
93
94             nodesGraphUtils = {
95                 onNodesPositionChanged: jest.fn()
96             }
97
98             const configure: ConfigureFn = (testBed) => {
99                 testBed.configureTestingModule({
100                     declarations: [CompositionGraphComponent],
101                     imports: [],
102                     schemas: [NO_ERRORS_SCHEMA],
103                     providers: [
104                         {provide: NodesFactory, useValue: {}},
105                         {provide: EventListenerService, useValue: eventServiceMock},
106                         {provide: CompositionGraphZoneUtils, useValue: compositionGraphZoneUtils},
107                         {provide: CompositionGraphGeneralUtils, useValue: generalGraphUtils},
108                         {provide: CompositionGraphLinkUtils, useValue: compositionGraphLinkUtils},
109                         {provide: CompositionGraphNodesUtils, useValue: nodesGraphUtils},
110                         {provide: ConnectionWizardService, useValue: {}},
111                         {provide: CommonGraphUtils, useValue: {}},
112                         {provide: CompositionGraphPaletteUtils, useValue: {}},
113                         {provide: TopologyTemplateService, useValue: {}},
114                         {provide: ComponentInstanceServiceNg2, useValue: {}},
115                         {provide: MatchCapabilitiesRequirementsUtils, useValue: {}},
116                         {provide: CompositionService, useValue: {}},
117                         {provide: SdcUiServices.LoaderService, useValue: {}},
118                         {provide: WorkspaceService, useValue: workspaceServiceMock},
119                         {provide: SdcUiServices.NotificationsService, useValue: {}},
120                         {provide: SdcUiServices.simplePopupMenuService, useValue: {}},
121                         {provide: ServicePathGraphUtils, useValue: {}},
122                         {provide: ModalService, useValue: {}},
123                         {provide: PoliciesService, useValue: policyService},
124                         {provide: GroupsService, useValue: {}},
125                         {provide: Store, useValue: storeStub},
126                     ],
127                 });
128             };
129
130             configureTests(configure).then((testBed) => {
131                 fixture = testBed.createComponent(CompositionGraphComponent);
132                 instance = fixture.componentInstance;
133                 instance._cy = cytoscape({});
134             });
135         })
136     );
137
138     it('composition graph component should be defined', () => {
139         expect(fixture).toBeDefined();
140     });
141
142     describe('on zone instance mode changed', () => {
143         let newZoneInstance: ZoneInstance;
144
145         beforeEach(
146             async(() => {
147                 newZoneInstance = createPolicyInstance();
148                 instance.zoneTagMode = null;
149                 instance.zones = [];
150                 instance.zones[ZoneInstanceType.POLICY] = new Zone('Policies', 'P', ZoneInstanceType.POLICY);
151                 instance.zones[ZoneInstanceType.GROUP] = new Zone('Groups', 'G', ZoneInstanceType.GROUP);
152                 instance.activeZoneInstance = createPolicyInstance();
153             }))
154
155         it('zone instance in tag mode and we want to turn tag mode off', () => {
156             instance.zoneTagMode = 'some_zone_id';
157             instance.activeZoneInstance = newZoneInstance;
158             instance.zoneInstanceModeChanged(ZoneInstanceMode.NONE, newZoneInstance, ZoneInstanceType.POLICY);
159             expect(instance.eventListenerService.notifyObservers).toHaveBeenCalledWith(GRAPH_EVENTS.ON_CANVAS_TAG_END, newZoneInstance);
160             expect(instance.activeZoneInstance.mode).toBe(ZoneInstanceMode.SELECTED)
161         })
162
163         it('we are not in tag mode and policy instance mode changed to NONE - group and zone tag indication need to be removed', () => {
164             instance.zoneInstanceModeChanged(ZoneInstanceMode.NONE, newZoneInstance, ZoneInstanceType.POLICY);
165             expect(instance.compositionGraphZoneUtils.hideZoneTagIndications).toHaveBeenCalledWith(instance._cy);
166             expect(instance.compositionGraphZoneUtils.hideGroupZoneIndications).toHaveBeenCalledWith(instance.zones[ZoneInstanceType.GROUP].instances);
167         })
168
169         it('we are not in tag mode and active zone instance gets hover/none - we dont actually change mode', () => {
170             let newMode = ZoneInstanceMode.SELECTED;
171             instance.zoneInstanceModeChanged(newMode, newZoneInstance, ZoneInstanceType.POLICY);
172             expect(newZoneInstance.mode).toBe(newMode);
173         })
174
175         it('we are not in tag mode and zone instance mode changed to HOVER mode', () => {
176             instance.zoneInstanceModeChanged(ZoneInstanceMode.HOVER, newZoneInstance, ZoneInstanceType.POLICY);
177             expect(instance.compositionGraphZoneUtils.showZoneTagIndications).toHaveBeenCalledWith(instance._cy, newZoneInstance);
178             expect(instance.compositionGraphZoneUtils.showGroupZoneIndications).toHaveBeenCalledWith(instance.zones[ZoneInstanceType.GROUP].instances, newZoneInstance);
179             expect(instance.eventListenerService.notifyObservers).not.toHaveBeenCalled();
180         })
181
182         it('we are not in tag mode and mode changed to SELECTED', () => {
183             instance.zoneInstanceModeChanged(ZoneInstanceMode.SELECTED, newZoneInstance, ZoneInstanceType.POLICY);
184             expect(instance.compositionGraphZoneUtils.showZoneTagIndications).toHaveBeenCalledWith(instance._cy, newZoneInstance);
185             expect(instance.compositionGraphZoneUtils.showGroupZoneIndications).toHaveBeenCalledWith(instance.zones[ZoneInstanceType.GROUP].instances, newZoneInstance);
186             expect(instance.activeZoneInstance).toBe(newZoneInstance);
187             expect(instance.eventListenerService.notifyObservers).toHaveBeenCalledWith(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, newZoneInstance);
188             expect(instance.store.dispatch).toHaveBeenCalledWith(new SetSelectedComponentAction({
189                 component: newZoneInstance.instanceData,
190                 type: SelectedComponentType[ZoneInstanceType[newZoneInstance.type]]
191             }));
192             expect(instance.eventListenerService.notifyObservers).not.toHaveBeenCalledWith(GRAPH_EVENTS.ON_CANVAS_TAG_START, ZoneInstanceType.POLICY);
193         })
194
195
196         it('we are not in tag mode and and zone instance mode changed to TAG', () => {
197             instance.zoneInstanceModeChanged(ZoneInstanceMode.TAG, newZoneInstance, ZoneInstanceType.POLICY);
198             expect(instance.compositionGraphZoneUtils.showZoneTagIndications).toHaveBeenCalledWith(instance._cy, newZoneInstance);
199             expect(instance.compositionGraphZoneUtils.showGroupZoneIndications).toHaveBeenCalledWith(instance.zones[ZoneInstanceType.GROUP].instances, newZoneInstance);
200             expect(instance.activeZoneInstance).toBe(newZoneInstance);
201             expect(instance.eventListenerService.notifyObservers).toHaveBeenCalledWith(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, newZoneInstance);
202             expect(instance.store.dispatch).toHaveBeenCalledWith(new SetSelectedComponentAction({
203                 component: newZoneInstance.instanceData,
204                 type: SelectedComponentType[ZoneInstanceType[newZoneInstance.type]]
205             }));
206             expect(instance.compositionGraphZoneUtils.startCyTagMode).toHaveBeenCalledWith(instance._cy);
207             expect(instance.eventListenerService.notifyObservers).toHaveBeenCalledWith(GRAPH_EVENTS.ON_CANVAS_TAG_START, ZoneInstanceType.POLICY);
208         })
209     })
210
211     it('unset active zone instance', () => {
212         instance.activeZoneInstance = createPolicyInstance();
213         instance.unsetActiveZoneInstance();
214         expect(instance.activeZoneInstance).toBeNull();
215         expect(instance.zoneTagMode).toBeNull();
216     })
217
218     it('zone background clicked - we are not in tag mode and active zone instance exist', () => {
219         instance.activeZoneInstance = createPolicyInstance();
220         jest.spyOn(instance, 'unsetActiveZoneInstance');
221         jest.spyOn(instance, 'selectTopologyTemplate');
222         instance.zoneBackgroundClicked();
223         expect(instance.unsetActiveZoneInstance).toHaveBeenCalled();
224         expect(instance.selectTopologyTemplate).toHaveBeenCalled();
225     })
226
227     it('zone background clicked - we are not in tag mode and no active zone instance exist', () => {
228         jest.spyOn(instance, 'unsetActiveZoneInstance');
229         jest.spyOn(instance, 'selectTopologyTemplate');
230         instance.zoneBackgroundClicked();
231         expect(instance.unsetActiveZoneInstance).not.toHaveBeenCalled();
232         expect(instance.selectTopologyTemplate).not.toHaveBeenCalled();
233     })
234
235     it('on zoom in', () => {
236         jest.spyOn(instance, 'zoom');
237         instance.zoom(true);
238         expect(instance.generalGraphUtils.zoomGraphTo).toHaveBeenCalledWith(instance._cy, instance._cy.zoom() + .1);
239     })
240
241     it('on zoom out', () => {
242         jest.spyOn(instance, 'zoom');
243         instance.zoom(false);
244         expect(instance.generalGraphUtils.zoomGraphTo).toHaveBeenCalledWith(instance._cy, instance._cy.zoom() - .1);
245     })
246
247     describe('cytoscape tap end event have been called', () => {
248
249         it('canvas background was clicked while zone instance in tag mode, zone instance still selected in tag mode)', () => {
250             let event = <Cy.EventObject>{cyTarget: instance._cy};
251             instance.zoneTagMode = 'instance_in_tag'
252             instance.onTapEnd(event);
253             expect(instance.zoneTagMode).toBe('instance_in_tag');
254         })
255
256         it('canvas background was clicked and no zone instance selected, topology template is now selected', () => {
257             let event = <Cy.EventObject>{cyTarget: instance._cy};
258             jest.spyOn(instance, 'selectTopologyTemplate');
259             instance.onTapEnd(event);
260             expect(instance.selectTopologyTemplate).toHaveBeenCalled();
261             expect(instance.eventListenerService.notifyObservers).toHaveBeenCalledWith(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
262         })
263
264         it('canvas background was clicked and zone instance was selected, topology template is now selected and zone instance is unselected', () => {
265             let event = <Cy.EventObject>{cyTarget: instance._cy};
266             instance.activeZoneInstance = createPolicyInstance();
267             jest.spyOn(instance, 'selectTopologyTemplate');
268             jest.spyOn(instance, 'unsetActiveZoneInstance');
269             instance.onTapEnd(event);
270             expect(instance.selectTopologyTemplate).toHaveBeenCalled();
271             expect(instance.unsetActiveZoneInstance).toHaveBeenCalled();
272         })
273
274
275         it('canvas background was clicked and zone instance was selected, topology template is now selected and zone instance is unselected', () => {
276             let event = <Cy.EventObject>{cyTarget: instance._cy};
277             instance.activeZoneInstance = createPolicyInstance();
278             jest.spyOn(instance, 'selectTopologyTemplate');
279             jest.spyOn(instance, 'unsetActiveZoneInstance');
280             instance.onTapEnd(event);
281             expect(instance.selectTopologyTemplate).toHaveBeenCalled();
282             expect(instance.unsetActiveZoneInstance).toHaveBeenCalled();
283         })
284
285         it('on simple edge clicked, open link menu and handle link click', () => {
286             let event = <Cy.EventObject>{
287                 cyTarget: [{
288                     isEdge: jest.fn().mockReturnValue(true),
289                     data: jest.fn().mockReturnValue({type: 'simple'})
290                 }
291             }];
292             instance.openModifyLinkMenu = jest.fn();
293             instance.onTapEnd(event);
294             expect(instance.compositionGraphLinkUtils.handleLinkClick).toHaveBeenCalledWith(instance._cy, event);
295             expect(instance.openModifyLinkMenu).toHaveBeenCalled();
296         })
297
298         it('on service path edge clicked, no menu is opened', () => {
299             let event = <Cy.EventObject>{
300                 cyTarget: [{
301                     isEdge: jest.fn().mockReturnValue(true),
302                     data: jest.fn().mockReturnValue({type: 'service-path-link'})
303                 }]
304             };
305             instance.openModifyLinkMenu = jest.fn();
306             instance.onTapEnd(event);
307             expect(instance.compositionGraphLinkUtils.handleLinkClick).toHaveBeenCalledWith(instance._cy, event);
308             expect(instance.openModifyLinkMenu).not.toHaveBeenCalled();
309         })
310
311         it('on drop after drag event (position has changed), call onNodesPositionChanged to update node position', () => {
312             let event = <Cy.EventObject>{
313                 cyTarget: [{
314                     isEdge: jest.fn().mockReturnValue(false),
315                     position: jest.fn().mockReturnValue({x:2.11, y:2.44})
316                 }]
317             };
318             instance.currentlyClickedNodePosition = <Cy.Position>{x:2.33, y:2.44};
319             instance.onTapEnd(event);
320             let nodesMoved: Cy.CollectionNodes = instance._cy.$(':grabbed');
321             expect(instance.nodesGraphUtils.onNodesPositionChanged).toHaveBeenCalledWith(instance._cy, instance.topologyTemplate, nodesMoved);
322
323         })
324
325         it('on node clicked (position not changed) while zone instance selected, unset active zone and call set selected instance', () => {
326             let event = <Cy.EventObject>{
327                 cyTarget: [{
328                     isEdge: jest.fn().mockReturnValue(false),
329                     position: jest.fn().mockReturnValue({x:2.11, y:2.44}),
330                     data: jest.fn().mockReturnValue({componentInstance: new ComponentInstance()})
331                 }],
332             };
333             instance.currentlyClickedNodePosition = <Cy.Position>{x:2.11, y:2.44};
334             instance.activeZoneInstance = createPolicyInstance();
335             jest.spyOn(instance, 'unsetActiveZoneInstance');
336             jest.spyOn(instance, 'selectComponentInstance');
337             instance.onTapEnd(event);
338             expect(instance.unsetActiveZoneInstance).toHaveBeenCalled();
339             expect(instance.selectComponentInstance).toHaveBeenCalledWith(event.cyTarget[0].data().componentInstance);
340         })
341     })
342
343     it('initial view mode will turn off all cytoscape events', () => {
344         jest.spyOn(instance, 'isViewOnly').mockReturnValue(true);
345         jest.spyOn(instance._cy, 'off');
346         instance.initViewMode();
347         expect(instance._cy.off).toHaveBeenCalledWith('drag');
348         expect(instance._cy.off).toHaveBeenCalledWith('handlemouseout');
349         expect(instance._cy.off).toHaveBeenCalledWith('handlemouseover');
350         expect(instance._cy.off).toHaveBeenCalledWith('canvasredraw');
351         expect(instance._cy.off).toHaveBeenCalledWith('handletagclick');
352
353     })
354 });