@reltio/graph 1.4.1584 → 1.4.1586-mui5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/index.ts +1 -0
  2. package/package.json +26 -9
  3. package/public/bundle.js +205 -0
  4. package/public/bundle.js.LICENSE.txt +88 -0
  5. package/public/package.json +29 -0
  6. package/scripts/build/index.js +20 -0
  7. package/src/__tests__/GraphView.test.tsx +25 -0
  8. package/src/components/CircleLayout/CircleLayout.tsx +76 -0
  9. package/src/components/CircleLayout/__tests__/CircleLayout.test.tsx +194 -0
  10. package/src/components/GotoButton/GotoButton.tsx +23 -0
  11. package/src/components/GotoButton/__tests__/GotoButton.test.tsx +33 -0
  12. package/src/components/GotoButton/styles.ts +15 -0
  13. package/src/components/GraphArea/GraphArea.tsx +113 -0
  14. package/src/components/GraphArea/__tests__/GraphArea.test.tsx +202 -0
  15. package/src/components/GraphArea/styles.ts +30 -0
  16. package/src/components/GraphLayoutSelector/GraphLayoutSelector.tsx +43 -0
  17. package/src/components/GraphLayoutSelector/__tests__/GraphLayoutSelector.spec.tsx +44 -0
  18. package/src/components/GraphLayoutSelector/styles.ts +15 -0
  19. package/src/components/GraphPerspectiveView/GraphPerspectiveView.tsx +58 -0
  20. package/src/components/GraphPerspectiveView/__tests__/GraphPerspectiveView.test.tsx +115 -0
  21. package/src/components/GraphPerspectiveView/__tests__/useGraphLoader.test.tsx +1519 -0
  22. package/src/components/GraphPerspectiveView/__tests__/useRelationshipTable.test.tsx +1003 -0
  23. package/src/components/GraphPerspectiveView/__tests__/useSelectedEntity.test.tsx +373 -0
  24. package/src/components/GraphPerspectiveView/styles.ts +34 -0
  25. package/src/components/GraphRightSidePanel/GraphRightSidePanel.tsx +27 -0
  26. package/src/components/GraphRightSidePanel/__tests__/GraphRightSidePanel.test.tsx +18 -0
  27. package/src/components/GraphRightSidePanel/icons/relationship-icon.svg +3 -0
  28. package/src/components/GraphRightSidePanel/styles.ts +8 -0
  29. package/src/components/GraphTypeSelector/GraphTypeSelector.tsx +56 -0
  30. package/src/components/GraphTypeSelector/__tests__/GraphTypeSelector.test.tsx +109 -0
  31. package/src/components/GraphTypeSelector/styles.ts +17 -0
  32. package/src/components/HierarchyLayout/HierarchyLayout.ts +102 -0
  33. package/src/components/HierarchyLayout/__tests__/HierarchyLayout.spec.tsx +241 -0
  34. package/src/components/HierarchyLayout/__tests__/buildHierarchy.spec.ts +130 -0
  35. package/src/components/HierarchyLayout/__tests__/calculations.spec.ts +230 -0
  36. package/src/components/HierarchyLayout/buildHierarchy.ts +109 -0
  37. package/src/components/HierarchyLayout/calculations.ts +86 -0
  38. package/src/components/RightPanelEntityDetails/RightPanelEntityDetails.tsx +33 -0
  39. package/src/components/RightPanelEntityDetails/__tests__/RightPanelEntityDetails.test.tsx +39 -0
  40. package/src/components/RightPanelEntityDetails/styles.ts +12 -0
  41. package/src/components/RightPanelProfileBand/RightPanelProfileBand.tsx +33 -0
  42. package/src/components/RightPanelProfileBand/__tests__/RightPanelProfileBand.test.tsx +67 -0
  43. package/src/components/RightPanelProfileBand/styles.ts +8 -0
  44. package/src/components/RightPanelRelationship/RightPanelRelationship.tsx +25 -0
  45. package/src/components/RightPanelRelationship/__tests__/RightPanelRelationship.test.tsx +18 -0
  46. package/src/components/RightPanelRelationship/components/CollapsibleTable/CollapsibleTable.tsx +100 -0
  47. package/src/components/RightPanelRelationship/components/CollapsibleTable/TableHead.tsx +38 -0
  48. package/src/components/RightPanelRelationship/components/CollapsibleTable/__tests__/CollapsibleTable.test.tsx +169 -0
  49. package/src/components/RightPanelRelationship/components/CollapsibleTable/__tests__/TableHead.test.tsx +107 -0
  50. package/src/components/RightPanelRelationship/components/CollapsibleTable/styles.ts +38 -0
  51. package/src/components/RightPanelRelationship/components/CollapsibleTable/types.ts +20 -0
  52. package/src/components/RightPanelRelationship/components/DeleteRelationDialog/DeleteRelationDialog.tsx +34 -0
  53. package/src/components/RightPanelRelationship/components/DeleteRelationDialog/__tests__/DeleteRelationDialog.test.tsx +46 -0
  54. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/GraphAddRelationButton.tsx +43 -0
  55. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/GraphAddRelationDialog.tsx +121 -0
  56. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/__tests__/GraphAddRelationButton.test.tsx +80 -0
  57. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/__tests__/GraphAddRelationDialog.test.tsx +262 -0
  58. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/__tests__/mockdata.ts +75 -0
  59. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/styles.ts +25 -0
  60. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/useCreatableInOutRelationTypes.ts +13 -0
  61. package/src/components/RightPanelRelationship/components/RelationshipTable/RelationshipTable.tsx +126 -0
  62. package/src/components/RightPanelRelationship/components/RelationshipTable/__tests__/RelationshipTable.test.tsx +176 -0
  63. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/ControlsCellRenderer.tsx +59 -0
  64. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/DefaultCellRenderer.tsx +29 -0
  65. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/ExpandedRowRenderer.tsx +94 -0
  66. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/HeadCellRenderer.tsx +34 -0
  67. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/ProfileCellRenderer.tsx +41 -0
  68. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/__tests__/ControlsCellRenderer.test.tsx +80 -0
  69. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/__tests__/DefaultCellRenderer.test.tsx +47 -0
  70. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/__tests__/ExpandedRowRenderer.test.tsx +166 -0
  71. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/__tests__/HeadCellRenderer.test.tsx +23 -0
  72. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/__tests__/ProfileCellRenderer.test.tsx +47 -0
  73. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/components/DeleteRelationButton.tsx +32 -0
  74. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/components/__tests__/DeleteRelationButton.test.tsx +28 -0
  75. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/styles.ts +113 -0
  76. package/src/components/RightPanelRelationship/components/RelationshipTable/styles.ts +36 -0
  77. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsFilters.tsx +41 -0
  78. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsFiltersDialog/RelationshipsFiltersDialog.tsx +91 -0
  79. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsFiltersDialog/__tests__/RelationshipsFilterDialog.test.tsx +192 -0
  80. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsFiltersDialog/styles.ts +34 -0
  81. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsSearchTextField/RelationshipsSearchTextField.tsx +31 -0
  82. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsSearchTextField/__tests__/RelationshipsSearchTextField.test.tsx +43 -0
  83. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsSearchTextField/styles.ts +17 -0
  84. package/src/components/RightPanelRelationship/components/RelationshipsFilters/__tests__/RelationshipsFilters.test.tsx +76 -0
  85. package/src/components/RightPanelRelationship/components/RelationshipsFilters/styles.ts +19 -0
  86. package/src/components/RightPanelRelationship/styles.ts +20 -0
  87. package/src/components/SigmaCustomRenderers/SigmaCustomRenderersContainer.tsx +16 -0
  88. package/src/components/SigmaGraphContainer/SigmaGraphContainer.tsx +55 -0
  89. package/src/components/SigmaGraphContainer/__tests__/SigmaGraphContainer.test.tsx +25 -0
  90. package/src/components/SigmaGraphResizer/SigmaGraphResizer.tsx +13 -0
  91. package/src/components/SigmaGraphResizer/__tests__/SigmaGraphResizer.test.tsx +28 -0
  92. package/src/components/TreeLayout/TreeLayout.tsx +33 -0
  93. package/src/components/TreeLayout/__tests__/TreeLayout.test.tsx +42 -0
  94. package/src/components/TreeLayout/styles.ts +10 -0
  95. package/src/components/ZoomSlider/ZoomSlider.tsx +90 -0
  96. package/src/components/ZoomSlider/__tests__/ZoomSlider.test.tsx +84 -0
  97. package/src/components/ZoomSlider/styles.ts +57 -0
  98. package/src/constants/index.ts +1 -0
  99. package/src/contexts/GraphStateContext.tsx +19 -0
  100. package/src/contexts/SigmaCustomRenderersContext.ts +20 -0
  101. package/src/helpers/relationsHelpers.ts +9 -0
  102. package/src/hooks/__tests__/useGraphLayout.test.tsx +825 -0
  103. package/src/hooks/__tests__/useGraphLayoutSelector.test.tsx +19 -0
  104. package/src/hooks/__tests__/useSelfRelationLoopsRenderer.test.ts +198 -0
  105. package/src/hooks/__tests__/useSigmaCustomRenderers.test.ts +273 -0
  106. package/src/hooks/__tests__/useTooltipRenderer.test.ts +289 -0
  107. package/src/hooks/useGraphLayout.ts +308 -0
  108. package/src/hooks/useGraphLayoutSelector.ts +19 -0
  109. package/src/hooks/useGraphLoader.ts +279 -0
  110. package/src/hooks/useGraphRightSidePanelElements.tsx +36 -0
  111. package/src/hooks/useGraphState.ts +40 -0
  112. package/src/hooks/useGraphType.ts +32 -0
  113. package/src/hooks/useRelationshipTable.ts +212 -0
  114. package/src/hooks/useRelationshipTableFilters.ts +43 -0
  115. package/src/hooks/useSelectedEntity.ts +89 -0
  116. package/src/hooks/useSelfRelationLoopsRenderer.ts +52 -0
  117. package/src/hooks/useSigmaCustomRenderers.ts +112 -0
  118. package/src/hooks/useTooltipRenderer.ts +80 -0
  119. package/src/index.tsx +37 -0
  120. package/src/rendering/canvas/__tests__/hover.spec.ts +31 -0
  121. package/src/rendering/canvas/__tests__/label.spec.ts +64 -0
  122. package/src/rendering/canvas/__tests__/nodeBackground.spec.ts +99 -0
  123. package/src/rendering/canvas/__tests__/selfRelationLoop.spec.ts +88 -0
  124. package/src/rendering/canvas/__tests__/tooltip.spec.ts +33 -0
  125. package/src/rendering/canvas/hover.ts +13 -0
  126. package/src/rendering/canvas/label.ts +33 -0
  127. package/src/rendering/canvas/nodeBackground.ts +61 -0
  128. package/src/rendering/canvas/selfRelationLoop.ts +108 -0
  129. package/src/rendering/canvas/tooltip.ts +38 -0
  130. package/src/rendering/icons/collapseIcon.inline.svg.ts +6 -0
  131. package/src/rendering/icons/expandIcon.inline.svg.ts +5 -0
  132. package/src/rendering/icons/no_photo.inline.svg.ts +13 -0
  133. package/src/rendering/webgl/edge.arrowHead.ts +87 -0
  134. package/src/rendering/webgl/edge.clamped.ts +246 -0
  135. package/src/rendering/webgl/edge.reversedArrowHead.ts +14 -0
  136. package/src/rendering/webgl/helpers/__tests__/imageHelper.spec.ts +36 -0
  137. package/src/rendering/webgl/helpers/imageHelper.ts +13 -0
  138. package/src/rendering/webgl/image.ts +115 -0
  139. package/src/rendering/webgl/node.border.ts +78 -0
  140. package/src/rendering/webgl/node.buttons.ts +241 -0
  141. package/src/rendering/webgl/node.image.ts +169 -0
  142. package/src/rendering/webgl/shaders/edge.clamped.vert.glsl.ts +55 -0
  143. package/src/rendering/webgl/shaders/node.border.frag.glsl.ts +30 -0
  144. package/src/rendering/webgl/shaders/node.border.vert.glsl.ts +37 -0
  145. package/src/rendering/webgl/shaders/node.buttons.frag.glsl.ts +32 -0
  146. package/src/rendering/webgl/shaders/node.buttons.vert.glsl.ts +49 -0
  147. package/src/rendering/webgl/shaders/node.image.frag.glsl.ts +46 -0
  148. package/src/rendering/webgl/shaders/node.image.vert.glsl.ts +42 -0
  149. package/src/types/graphDataTypes.ts +143 -0
  150. package/src/types/sigmaCustomRenderersTypes.ts +13 -0
  151. package/src/utils/__tests__/graph.spec.ts +152 -0
  152. package/src/utils/__tests__/graphLayout.spec.ts +125 -0
  153. package/src/utils/__tests__/hopsData.spec.ts +456 -0
  154. package/src/utils/__tests__/processResponceToGraph.spec.ts +165 -0
  155. package/src/utils/graph.ts +83 -0
  156. package/src/utils/graphLayout.ts +70 -0
  157. package/src/utils/hopsData.ts +198 -0
  158. package/src/utils/processResponseToGraph.ts +32 -0
  159. package/tsconfig.json +4 -0
  160. package/webpack.config.js +10 -0
  161. package/bundle.js +0 -2
  162. package/bundle.js.LICENSE.txt +0 -54
  163. /package/{5271a9e7b6651c852e93.png → public/5271a9e7b6651c852e93.png} +0 -0
  164. /package/{main.css → public/main.css} +0 -0
@@ -0,0 +1,1519 @@
1
+ import React from 'react';
2
+ import {render} from '@testing-library/react';
3
+ import {renderHook} from '@testing-library/react-hooks';
4
+ import {mount, shallow} from 'enzyme';
5
+ import * as reactRedux from 'react-redux';
6
+ import {act} from 'react-dom/test-utils';
7
+ import {LinearLoadIndicator, PageRequestsAbortingContext} from '@reltio/components';
8
+ import {
9
+ createRelation,
10
+ Entity,
11
+ getHops,
12
+ getEntity,
13
+ HopsResponse,
14
+ Metadata,
15
+ RelationTypeDirection,
16
+ removeRelation,
17
+ GraphLayout
18
+ } from '@reltio/mdm-sdk';
19
+ import {ui} from '@reltio/mdm-module';
20
+ import * as processResponseToGraphExport from '../../../utils/processResponseToGraph';
21
+ import * as hopsDataExport from '../../../utils/hopsData';
22
+ import {GraphStateContext} from '../../../contexts/GraphStateContext';
23
+ import {GraphPathType} from '../../../types/graphDataTypes';
24
+ import GraphPerspectiveView from '../GraphPerspectiveView';
25
+ import {useGraphState} from '../../../hooks/useGraphState';
26
+
27
+ jest.mock('../../GraphArea/GraphArea', () => () => null);
28
+ jest.mock('../../GraphRightSidePanel/GraphRightSidePanel', () => ({
29
+ ...(jest.requireActual('../../GraphRightSidePanel/GraphRightSidePanel') as Record<string, unknown>),
30
+ default: () => null
31
+ }));
32
+ jest.mock('../../../contexts/GraphStateContext', () => ({
33
+ GraphStateContext: {
34
+ Provider: jest.fn().mockImplementation(({children}) => <div id="graphStateContextMock">{children}</div>)
35
+ }
36
+ }));
37
+
38
+ jest.mock('@reltio/components', () => ({
39
+ ...jest.requireActual<Record<string, unknown>>('@reltio/components'),
40
+ ScreenProfileBand: () => null
41
+ }));
42
+
43
+ jest.mock('@reltio/mdm-sdk', () => ({
44
+ ...jest.requireActual<Record<string, unknown>>('@reltio/mdm-sdk'),
45
+ removeRelation: jest.fn().mockReturnValue(Promise.resolve({})),
46
+ createRelation: jest.fn().mockReturnValue(Promise.resolve({})),
47
+ getHops: jest.fn().mockReturnValue(Promise.resolve({})),
48
+ getEntity: jest.fn().mockReturnValue(Promise.resolve({})),
49
+ getRelation: jest.fn().mockReturnValue(Promise.resolve({})),
50
+ updateRelation: jest.fn().mockReturnValue(Promise.resolve())
51
+ }));
52
+
53
+ type CreateStoreProps = {
54
+ entity?: Entity;
55
+ metadata?: Metadata;
56
+ };
57
+ const createStore = ({entity, metadata}: CreateStoreProps = {}) => ({
58
+ profile: {
59
+ entity: entity || {
60
+ uri: 'entities/uri1',
61
+ type: 'configuration/entityTypes/HCO'
62
+ }
63
+ },
64
+ tenant: {
65
+ id: 't1'
66
+ },
67
+ metadata: {
68
+ t1: metadata
69
+ }
70
+ });
71
+
72
+ describe('useGraphLoader behavior', () => {
73
+ const dispatch = jest.fn();
74
+ let store;
75
+ const signal = new AbortController().signal;
76
+ const setUp = (
77
+ method: typeof shallow | typeof mount = shallow,
78
+ customStore?,
79
+ graphTypeUri?,
80
+ graphLayout = GraphLayout.SIMPLE_NETWORK
81
+ ) => {
82
+ store = customStore || createStore({metadata: {} as Metadata});
83
+
84
+ return (method as typeof shallow)(
85
+ <PageRequestsAbortingContext.Provider value={signal}>
86
+ <GraphPerspectiveView graphTypeUri={graphTypeUri} graphLayout={graphLayout} />
87
+ </PageRequestsAbortingContext.Provider>
88
+ );
89
+ };
90
+
91
+ beforeAll(() => {
92
+ jest.spyOn(reactRedux, 'useDispatch').mockReturnValue(dispatch);
93
+ jest.spyOn(reactRedux, 'useSelector').mockImplementation((selector) => selector(store));
94
+ });
95
+
96
+ afterEach(() => {
97
+ jest.clearAllMocks();
98
+ });
99
+
100
+ it('should request graph data and set value to GraphStateContext.Provider', async () => {
101
+ const processResponseToGraphSpy = jest.spyOn(processResponseToGraphExport, 'default');
102
+ const data: HopsResponse = {
103
+ entities: [
104
+ {uri: 'e1', type: ''},
105
+ {uri: 'e2', type: ''},
106
+ {uri: 'e3', type: ''}
107
+ ],
108
+ relations: [
109
+ {
110
+ uri: 'r1',
111
+ type: '',
112
+ startObject: {
113
+ objectURI: 'e1'
114
+ },
115
+ endObject: {
116
+ objectURI: 'e2'
117
+ },
118
+ direction: RelationTypeDirection.directed
119
+ },
120
+ {
121
+ uri: 'r2',
122
+ type: '',
123
+ startObject: {
124
+ objectURI: 'e2'
125
+ },
126
+ endObject: {
127
+ objectURI: 'e3'
128
+ },
129
+ direction: RelationTypeDirection.directed
130
+ },
131
+ {
132
+ uri: 'r3',
133
+ type: '',
134
+ startObject: {
135
+ objectURI: 'e3'
136
+ },
137
+ endObject: {
138
+ objectURI: 'e1'
139
+ },
140
+ direction: RelationTypeDirection.bidirectional
141
+ }
142
+ ]
143
+ };
144
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
145
+ const component = setUp(mount);
146
+ expect(component.find(GraphStateContext.Provider)).toHaveLength(1);
147
+ expect(component.find(GraphStateContext.Provider).prop('value')).toMatchObject({
148
+ data: null,
149
+ graphLoading: true,
150
+ graphTypeUri: null
151
+ });
152
+ expect(component.find(LinearLoadIndicator)).toHaveLength(1);
153
+ expect(getHops).toHaveBeenCalledWith({
154
+ uri: 'entities/uri1',
155
+ deep: 1,
156
+ max: 3000,
157
+ limitCreditsConsumption: true,
158
+ graphTypes: null,
159
+ activenessDate: null,
160
+ signal
161
+ });
162
+ await act(async () => {
163
+ await Promise.resolve();
164
+ });
165
+ component.update();
166
+ const contextValue = component.find(GraphStateContext.Provider).prop('value');
167
+ expect(contextValue).toMatchObject({
168
+ data,
169
+ graphTypeUri: null,
170
+ graphLoading: false
171
+ });
172
+ const graph = contextValue.graphologyGraph;
173
+ expect(graph.hasNode('e1')).toBe(true);
174
+ expect(graph.hasNode('e2')).toBe(true);
175
+ expect(graph.hasNode('e3')).toBe(true);
176
+ expect(graph.hasEdge('r1')).toBe(true);
177
+ expect(graph.source('r1')).toBe('e1');
178
+ expect(graph.target('r1')).toBe('e2');
179
+ expect(graph.hasEdge('r2')).toBe(true);
180
+ expect(graph.source('r2')).toBe('e2');
181
+ expect(graph.target('r2')).toBe('e3');
182
+ expect(graph.hasEdge('r3')).toBe(true);
183
+ expect(graph.source('r3')).toBe('e3');
184
+ expect(graph.target('r3')).toBe('e1');
185
+ expect(graph.getEdgeAttribute('r1', 'direction')).toBe(RelationTypeDirection.directed);
186
+ expect(graph.getEdgeAttribute('r2', 'direction')).toBe(RelationTypeDirection.directed);
187
+ expect(graph.getEdgeAttribute('r3', 'direction')).toBe(RelationTypeDirection.bidirectional);
188
+ expect(processResponseToGraphSpy).toHaveBeenCalled();
189
+ expect(component.find(LinearLoadIndicator)).toHaveLength(0);
190
+ });
191
+
192
+ it('should not request graph data for dtss entity', () => {
193
+ const data = {entities: [], relations: []};
194
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
195
+ const component = setUp(
196
+ mount,
197
+ createStore({
198
+ entity: {
199
+ uri: 'entities/uri1',
200
+ type: 'configuration/entityTypes/HCO',
201
+ dataTenant: 'dt1'
202
+ },
203
+ metadata: {} as Metadata
204
+ })
205
+ );
206
+ expect(component.find(GraphStateContext.Provider)).toHaveLength(1);
207
+ expect(component.find(GraphStateContext.Provider).prop('value')).toMatchObject({
208
+ data: null,
209
+ graphLoading: false,
210
+ graphTypeUri: null
211
+ });
212
+ expect(getHops).not.toHaveBeenCalled();
213
+ });
214
+
215
+ it('should not request graph data for temp entity', () => {
216
+ const data = {entities: [], relations: []};
217
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
218
+ const component = setUp(
219
+ mount,
220
+ createStore({
221
+ entity: {
222
+ uri: 'entities/uri$$1',
223
+ type: 'configuration/entityTypes/HCO'
224
+ },
225
+ metadata: {} as Metadata
226
+ })
227
+ );
228
+ expect(component.find(GraphStateContext.Provider)).toHaveLength(1);
229
+ expect(component.find(GraphStateContext.Provider).prop('value')).toMatchObject({
230
+ data: null,
231
+ graphTypeUri: null,
232
+ graphLoading: false
233
+ });
234
+ expect(getHops).not.toHaveBeenCalled();
235
+ });
236
+
237
+ it('should request hops in case when we change graph type with tree layout and then we change layout', async () => {
238
+ const component = setUp(mount);
239
+ const contextValue = component.find(GraphStateContext.Provider).prop('value');
240
+ await act(async () => {
241
+ await Promise.resolve();
242
+ });
243
+ component.update();
244
+ contextValue.setLayout(GraphLayout.TREE);
245
+ await act(async () => {
246
+ await Promise.resolve();
247
+ });
248
+ component.update();
249
+
250
+ contextValue.setGraphTypeUri('newGraphTypeUri');
251
+ await act(async () => {
252
+ await Promise.resolve();
253
+ });
254
+ component.update();
255
+
256
+ contextValue.setLayout(GraphLayout.SIMPLE_NETWORK);
257
+ await act(async () => {
258
+ await Promise.resolve();
259
+ });
260
+ component.update();
261
+ expect(getHops).toHaveBeenCalledTimes(2);
262
+ });
263
+
264
+ it('should dispatch an error in case of failed graph request', async () => {
265
+ const error = {errorMessage: 'Error!!!'};
266
+ (getHops as jest.Mock).mockReturnValue(Promise.reject(error));
267
+ const component = setUp(mount);
268
+ expect(component.find(GraphStateContext.Provider)).toHaveLength(1);
269
+ expect(component.find(GraphStateContext.Provider).prop('value')).toMatchObject({
270
+ data: null,
271
+ graphLoading: true,
272
+ graphTypeUri: null
273
+ });
274
+ await act(async () => {
275
+ await Promise.resolve();
276
+ });
277
+ component.update();
278
+ expect(component.find(GraphStateContext.Provider).prop('value')).toMatchObject({
279
+ data: null,
280
+ graphLoading: false,
281
+ graphTypeUri: null
282
+ });
283
+ expect(dispatch).toHaveBeenCalledWith(ui.actions.errorSet(error.errorMessage));
284
+ });
285
+
286
+ it('should not dispatch an error in case of cancel graph request', async () => {
287
+ const error = {errorMessage: 'Error!!!', name: 'AbortError'};
288
+ (getHops as jest.Mock).mockReturnValue(Promise.reject(error));
289
+ render(
290
+ <PageRequestsAbortingContext.Provider value={signal}>
291
+ <GraphPerspectiveView />
292
+ </PageRequestsAbortingContext.Provider>
293
+ );
294
+ await act(async () => {
295
+ await Promise.resolve();
296
+ });
297
+
298
+ expect(dispatch).not.toHaveBeenCalled();
299
+ });
300
+
301
+ it('should request graph data after changing graphType', async () => {
302
+ const data = {entities: [], relations: []};
303
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
304
+ const component = setUp(mount);
305
+ expect(component.find(GraphStateContext.Provider)).toHaveLength(1);
306
+ expect(component.find(GraphStateContext.Provider).prop('value')).toMatchObject({
307
+ data: null,
308
+ graphLoading: true,
309
+ graphTypeUri: null
310
+ });
311
+ expect(getHops).toHaveBeenCalledWith({
312
+ uri: 'entities/uri1',
313
+ deep: 1,
314
+ max: 3000,
315
+ limitCreditsConsumption: true,
316
+ graphTypes: null,
317
+ activenessDate: null,
318
+ signal
319
+ });
320
+ await act(async () => {
321
+ await Promise.resolve();
322
+ });
323
+ component.update();
324
+ expect(component.find(LinearLoadIndicator)).toHaveLength(0);
325
+ component
326
+ .find(GraphStateContext.Provider)
327
+ .prop('value')
328
+ .setGraphTypeUri('configuration/graphTypes/FamilyGraph');
329
+ await act(async () => {
330
+ await Promise.resolve();
331
+ });
332
+ component.update();
333
+ expect(component.find(GraphStateContext.Provider).prop('value')).toMatchObject({
334
+ data,
335
+ graphLoading: false,
336
+ graphTypeUri: 'configuration/graphTypes/FamilyGraph'
337
+ });
338
+ expect(getHops).toHaveBeenCalledWith({
339
+ uri: 'entities/uri1',
340
+ deep: 2,
341
+ max: 200,
342
+ limitCreditsConsumption: true,
343
+ graphTypes: ['FamilyGraph'],
344
+ activenessDate: null,
345
+ signal
346
+ });
347
+ });
348
+
349
+ describe('external graphtype uri', () => {
350
+ const store = {
351
+ profile: {
352
+ entity: {
353
+ uri: 'entities/uri1',
354
+ type: 'configuration/entityTypes/HCO'
355
+ }
356
+ },
357
+ tenant: {
358
+ id: 't1'
359
+ },
360
+ metadata: {
361
+ t1: {
362
+ entityTypes: [
363
+ {
364
+ uri: 'configuration/entityTypes/HCO'
365
+ }
366
+ ],
367
+ relationTypes: [
368
+ {
369
+ uri: 'configuration/relationTypes/ReportsTo',
370
+ startObject: {objectTypeURI: 'configuration/entityTypes/HCO'},
371
+ endObject: {objectTypeURI: 'configuration/entityTypes/HCO'}
372
+ }
373
+ ],
374
+ graphTypes: [
375
+ {
376
+ uri: 'configuration/graphTypes/FamilyGraph',
377
+ label: 'Company Hierarchy',
378
+ relationshipTypeURIs: ['configuration/relationTypes/ReportsTo']
379
+ }
380
+ ]
381
+ }
382
+ }
383
+ };
384
+ it('should use external graphtype uri if it is valid', async () => {
385
+ const data = {entities: [], relations: []};
386
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
387
+ setUp(mount, store, 'configuration/graphTypes/FamilyGraph');
388
+ expect(getHops).toHaveBeenCalledWith({
389
+ uri: 'entities/uri1',
390
+ deep: 2,
391
+ max: 200,
392
+ limitCreditsConsumption: true,
393
+ graphTypes: ['FamilyGraph'],
394
+ activenessDate: null,
395
+ signal
396
+ });
397
+ });
398
+
399
+ it('should use filter out invalid graph types', async () => {
400
+ const data = {entities: [], relations: []};
401
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
402
+ setUp(mount, store, 'configuration/graphTypes/NonexistingGraphType');
403
+ expect(getHops).toHaveBeenCalledWith({
404
+ uri: 'entities/uri1',
405
+ deep: 1,
406
+ max: 3000,
407
+ limitCreditsConsumption: true,
408
+ graphTypes: null,
409
+ activenessDate: null,
410
+ signal
411
+ });
412
+ });
413
+ });
414
+
415
+ describe('onDeleteRelation behavior', () => {
416
+ it('should call removeRelation API and update data and graphologyGraph in GraphStateContext.Provider', async () => {
417
+ const removeRelationFromHopsDataSpy = jest.spyOn(hopsDataExport, 'removeRelationFromHopsData');
418
+ const data: HopsResponse = {
419
+ entities: [
420
+ {uri: 'e1', type: '', traversedRelations: 1, untraversedRelations: 0},
421
+ {uri: 'e2', type: '', traversedRelations: 2, untraversedRelations: 1},
422
+ {uri: 'e3', type: '', traversedRelations: 1, untraversedRelations: 3}
423
+ ],
424
+ relations: [
425
+ {
426
+ uri: 'r1',
427
+ type: '',
428
+ startObject: {
429
+ objectURI: 'e1'
430
+ },
431
+ endObject: {
432
+ objectURI: 'e2'
433
+ }
434
+ },
435
+ {
436
+ uri: 'r2',
437
+ type: '',
438
+ startObject: {
439
+ objectURI: 'e2'
440
+ },
441
+ endObject: {
442
+ objectURI: 'e3'
443
+ }
444
+ }
445
+ ]
446
+ };
447
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
448
+ const component = setUp(
449
+ mount,
450
+ createStore({
451
+ entity: {
452
+ uri: 'e1',
453
+ type: ''
454
+ },
455
+ metadata: {entityTypes: [], relationTypes: []}
456
+ })
457
+ );
458
+ await act(async () => {
459
+ await Promise.resolve();
460
+ });
461
+ component.update();
462
+ let contextValue = component.find(GraphStateContext.Provider).prop('value');
463
+ expect(contextValue).toMatchObject({
464
+ data,
465
+ graphTypeUri: null,
466
+ graphLoading: false
467
+ });
468
+
469
+ contextValue.onDeleteRelation('r1');
470
+ component.update();
471
+ expect(component.find(LinearLoadIndicator)).toHaveLength(1);
472
+ expect(removeRelation).toHaveBeenCalledWith('r1');
473
+ await act(async () => {
474
+ await Promise.resolve();
475
+ });
476
+ component.update();
477
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
478
+ const graph = contextValue.graphologyGraph;
479
+ expect(graph.hasNode('e1')).toBe(true);
480
+ expect(graph.hasNode('e2')).toBe(false);
481
+ expect(graph.hasNode('e3')).toBe(false);
482
+ expect(graph.hasEdge('r1')).toBe(false);
483
+ expect(graph.hasEdge('r2')).toBe(false);
484
+ expect(graph.getNodeAttribute('e1', 'traversedRelationsCount')).toBe(0);
485
+ expect(removeRelationFromHopsDataSpy).toHaveBeenCalled();
486
+ expect(component.find(LinearLoadIndicator)).toHaveLength(0);
487
+ });
488
+
489
+ it('should change traversedRelationsCount in graph', async () => {
490
+ const removeRelationFromHopsDataSpy = jest.spyOn(hopsDataExport, 'removeRelationFromHopsData');
491
+ const data: HopsResponse = {
492
+ entities: [
493
+ {uri: 'e1', type: '', traversedRelations: 2, untraversedRelations: 0},
494
+ {uri: 'e2', type: '', traversedRelations: 2, untraversedRelations: 1},
495
+ {uri: 'e3', type: '', traversedRelations: 2, untraversedRelations: 3}
496
+ ],
497
+ relations: [
498
+ {
499
+ uri: 'r1',
500
+ type: '',
501
+ startObject: {
502
+ objectURI: 'e1'
503
+ },
504
+ endObject: {
505
+ objectURI: 'e2'
506
+ }
507
+ },
508
+ {
509
+ uri: 'r2',
510
+ type: '',
511
+ startObject: {
512
+ objectURI: 'e2'
513
+ },
514
+ endObject: {
515
+ objectURI: 'e3'
516
+ }
517
+ },
518
+ {
519
+ uri: 'r3',
520
+ type: '',
521
+ startObject: {
522
+ objectURI: 'e1'
523
+ },
524
+ endObject: {
525
+ objectURI: 'e3'
526
+ }
527
+ }
528
+ ]
529
+ };
530
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
531
+ const component = setUp(
532
+ mount,
533
+ createStore({
534
+ entity: {
535
+ uri: 'e1',
536
+ type: ''
537
+ },
538
+ metadata: {entityTypes: [], relationTypes: []}
539
+ })
540
+ );
541
+ await act(async () => {
542
+ await Promise.resolve();
543
+ });
544
+ component.update();
545
+ let contextValue = component.find(GraphStateContext.Provider).prop('value');
546
+ expect(contextValue).toMatchObject({
547
+ data,
548
+ graphTypeUri: null,
549
+ graphLoading: false
550
+ });
551
+
552
+ contextValue.onDeleteRelation('r1');
553
+ component.update();
554
+ expect(component.find(LinearLoadIndicator)).toHaveLength(1);
555
+ expect(removeRelation).toHaveBeenCalledWith('r1');
556
+ await act(async () => {
557
+ await Promise.resolve();
558
+ });
559
+ component.update();
560
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
561
+ const graph = contextValue.graphologyGraph;
562
+ expect(graph.hasNode('e1')).toBe(true);
563
+ expect(graph.hasNode('e2')).toBe(true);
564
+ expect(graph.hasNode('e3')).toBe(true);
565
+ expect(graph.hasEdge('r1')).toBe(false);
566
+ expect(graph.hasEdge('r2')).toBe(true);
567
+ expect(graph.hasEdge('r3')).toBe(true);
568
+ expect(removeRelationFromHopsDataSpy).toHaveBeenCalled();
569
+ expect(component.find(LinearLoadIndicator)).toHaveLength(0);
570
+
571
+ expect(graph.getNodeAttribute('e1', 'traversedRelationsCount')).toBe(1);
572
+ expect(graph.getNodeAttribute('e2', 'traversedRelationsCount')).toBe(1);
573
+ expect(graph.getNodeAttribute('e3', 'traversedRelationsCount')).toBe(2);
574
+ });
575
+ });
576
+
577
+ describe('onAddRelation behavior', () => {
578
+ it('should call createRelation API and update data and graphologyGraph in GraphStateContext.Provider', async () => {
579
+ const addRelationToHopsDataSpy = jest.spyOn(hopsDataExport, 'addRelationToHopsData');
580
+ const data: HopsResponse = {
581
+ entities: [
582
+ {uri: 'e1', type: '', traversedRelations: 1, untraversedRelations: 0},
583
+ {uri: 'e2', type: '', traversedRelations: 1, untraversedRelations: 2}
584
+ ],
585
+ relations: [
586
+ {
587
+ uri: 'r1',
588
+ type: '',
589
+ startObject: {
590
+ objectURI: 'e1'
591
+ },
592
+ endObject: {
593
+ objectURI: 'e2'
594
+ },
595
+ direction: RelationTypeDirection.directed
596
+ }
597
+ ]
598
+ };
599
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
600
+ (createRelation as jest.Mock).mockReturnValue(
601
+ Promise.resolve([
602
+ {
603
+ successful: true,
604
+ object: {
605
+ uri: 'r2',
606
+ type: 'relations/type2',
607
+ startObject: {
608
+ objectURI: 'e1',
609
+ type: ''
610
+ },
611
+ endObject: {
612
+ objectURI: 'e3',
613
+ type: 'entities/type3'
614
+ }
615
+ }
616
+ }
617
+ ])
618
+ );
619
+ (getEntity as jest.Mock).mockReturnValueOnce(Promise.resolve({label: 'correctLabel'}));
620
+ const component = setUp(
621
+ mount,
622
+ createStore({
623
+ entity: {
624
+ uri: 'e1',
625
+ type: ''
626
+ },
627
+ metadata: {
628
+ entityTypes: [],
629
+ relationTypes: [
630
+ {
631
+ uri: 'relations/type2',
632
+ direction: RelationTypeDirection.bidirectional,
633
+ attributes: [],
634
+ label: ''
635
+ }
636
+ ]
637
+ }
638
+ })
639
+ );
640
+ await act(async () => {
641
+ await Promise.resolve();
642
+ });
643
+ component.update();
644
+ let contextValue = component.find(GraphStateContext.Provider).prop('value');
645
+ expect(contextValue).toMatchObject({
646
+ data,
647
+ graphTypeUri: null,
648
+ graphLoading: false
649
+ });
650
+
651
+ const newRelation = {
652
+ startEntityUri: 'e1',
653
+ endEntityUri: 'e3',
654
+ relationType: 'relations/type2',
655
+ attributes: {
656
+ FirstName: [
657
+ {
658
+ type: 'configuration/entityTypes/HCP/attributes/FirstName',
659
+ ov: true,
660
+ value: 'Name',
661
+ uri: 'uri/FirstName'
662
+ }
663
+ ]
664
+ },
665
+ startDate: 8908907878,
666
+ endDate: 568787879090
667
+ };
668
+ contextValue.onAddRelation(newRelation);
669
+ component.update();
670
+ expect(component.find(LinearLoadIndicator)).toHaveLength(1);
671
+ expect(createRelation).toHaveBeenCalledWith(newRelation);
672
+ await act(async () => {
673
+ await Promise.resolve();
674
+ });
675
+ component.update();
676
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
677
+ const graph = contextValue.graphologyGraph;
678
+ expect(graph.hasNode('e1')).toBe(true);
679
+ expect(graph.hasNode('e2')).toBe(true);
680
+ expect(graph.hasNode('e3')).toBe(true);
681
+ expect(graph.hasEdge('r1')).toBe(true);
682
+ expect(graph.getEdgeAttribute('r1', 'direction')).toBe(RelationTypeDirection.directed);
683
+ expect(graph.hasEdge('r2')).toBe(true);
684
+ expect(graph.getEdgeAttribute('r2', 'direction')).toBe(RelationTypeDirection.bidirectional);
685
+ expect(graph.getNodeAttribute('e1', 'traversedRelationsCount')).toBe(2);
686
+ expect(graph.getNodeAttribute('e2', 'traversedRelationsCount')).toBe(1);
687
+ expect(graph.getNodeAttribute('e3', 'traversedRelationsCount')).toBe(1);
688
+ expect(graph.getNodeAttribute('e3', 'untraversedRelationsCount')).toBe(1);
689
+ expect(graph.getNodeAttribute('e3', 'label')).toBe('correctLabel');
690
+ expect(addRelationToHopsDataSpy).toHaveBeenCalledWith(
691
+ data,
692
+ {
693
+ endObject: {objectURI: 'e3', type: 'entities/type3', label: 'correctLabel'},
694
+ startObject: {objectURI: 'e1', type: ''},
695
+ type: 'relations/type2',
696
+ uri: 'r2'
697
+ },
698
+ store.metadata.t1
699
+ );
700
+ expect(contextValue.data).toEqual({
701
+ entities: [
702
+ {uri: 'e1', type: '', traversedRelations: 2, untraversedRelations: 0},
703
+ {uri: 'e2', type: '', traversedRelations: 1, untraversedRelations: 2},
704
+ {
705
+ uri: 'e3',
706
+ type: 'entities/type3',
707
+ label: 'correctLabel',
708
+ traversedRelations: 1,
709
+ untraversedRelations: 1
710
+ }
711
+ ],
712
+ relations: [
713
+ {
714
+ uri: 'r1',
715
+ type: '',
716
+ startObject: {
717
+ objectURI: 'e1'
718
+ },
719
+ endObject: {
720
+ objectURI: 'e2'
721
+ },
722
+ direction: RelationTypeDirection.directed
723
+ },
724
+ {
725
+ uri: 'r2',
726
+ type: 'relations/type2',
727
+ startObject: {
728
+ objectURI: 'e1'
729
+ },
730
+ endObject: {
731
+ objectURI: 'e3'
732
+ },
733
+ direction: RelationTypeDirection.bidirectional
734
+ }
735
+ ]
736
+ });
737
+ expect(component.find(LinearLoadIndicator)).toHaveLength(0);
738
+ });
739
+
740
+ it('should not fail if initially there were no relations', async () => {
741
+ const addRelationToHopsDataSpy = jest.spyOn(hopsDataExport, 'addRelationToHopsData');
742
+ const data: HopsResponse = {
743
+ entities: [{uri: 'e1', type: '', traversedRelations: 1, untraversedRelations: 0}]
744
+ };
745
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
746
+ (createRelation as jest.Mock).mockReturnValue(
747
+ Promise.resolve([
748
+ {
749
+ successful: true,
750
+ object: {
751
+ uri: 'r2',
752
+ type: 'relations/type2',
753
+ startObject: {
754
+ objectURI: 'e1',
755
+ type: ''
756
+ },
757
+ endObject: {
758
+ objectURI: 'e3',
759
+ type: 'entities/type3'
760
+ }
761
+ }
762
+ }
763
+ ])
764
+ );
765
+ const component = setUp(
766
+ mount,
767
+ createStore({
768
+ entity: {
769
+ uri: 'e1',
770
+ type: ''
771
+ },
772
+ metadata: {
773
+ entityTypes: [],
774
+ relationTypes: [
775
+ {
776
+ uri: 'relations/type2',
777
+ direction: RelationTypeDirection.bidirectional,
778
+ attributes: [],
779
+ label: ''
780
+ }
781
+ ]
782
+ }
783
+ })
784
+ );
785
+ await act(async () => {
786
+ await Promise.resolve();
787
+ });
788
+ component.update();
789
+ let contextValue = component.find(GraphStateContext.Provider).prop('value');
790
+ expect(contextValue).toMatchObject({
791
+ data,
792
+ graphTypeUri: null,
793
+ graphLoading: false
794
+ });
795
+
796
+ const newRelation = {
797
+ startEntityUri: 'e1',
798
+ endEntityUri: 'e3',
799
+ relationType: 'relations/type2',
800
+ attributes: {
801
+ FirstName: [
802
+ {
803
+ type: 'configuration/entityTypes/HCP/attributes/FirstName',
804
+ ov: true,
805
+ value: 'Name',
806
+ uri: 'uri/FirstName'
807
+ }
808
+ ]
809
+ },
810
+ startDate: 8908907878,
811
+ endDate: 568787879090
812
+ };
813
+ contextValue.onAddRelation(newRelation);
814
+ component.update();
815
+ expect(component.find(LinearLoadIndicator)).toHaveLength(1);
816
+ expect(createRelation).toHaveBeenCalledWith(newRelation);
817
+ await act(async () => {
818
+ await Promise.resolve();
819
+ });
820
+ component.update();
821
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
822
+ const graph = contextValue.graphologyGraph;
823
+ expect(graph.hasNode('e1')).toBe(true);
824
+ expect(graph.hasNode('e3')).toBe(true);
825
+ expect(graph.hasEdge('r2')).toBe(true);
826
+ expect(graph.getEdgeAttribute('r2', 'direction')).toBe(RelationTypeDirection.bidirectional);
827
+ expect(graph.getNodeAttribute('e1', 'traversedRelationsCount')).toBe(2);
828
+ expect(graph.getNodeAttribute('e3', 'traversedRelationsCount')).toBe(1);
829
+ expect(graph.getNodeAttribute('e3', 'untraversedRelationsCount')).toBe(1);
830
+ expect(addRelationToHopsDataSpy).toHaveBeenCalledWith(
831
+ data,
832
+ {
833
+ endObject: {objectURI: 'e3', type: 'entities/type3'},
834
+ startObject: {objectURI: 'e1', type: ''},
835
+ type: 'relations/type2',
836
+ uri: 'r2'
837
+ },
838
+ store.metadata.t1
839
+ );
840
+ expect(contextValue.data).toEqual({
841
+ entities: [
842
+ {uri: 'e1', type: '', traversedRelations: 2, untraversedRelations: 0},
843
+ {
844
+ uri: 'e3',
845
+ type: 'entities/type3',
846
+ label: undefined,
847
+ traversedRelations: 1,
848
+ untraversedRelations: 1
849
+ }
850
+ ],
851
+ relations: [
852
+ {
853
+ uri: 'r2',
854
+ type: 'relations/type2',
855
+ startObject: {
856
+ objectURI: 'e1'
857
+ },
858
+ endObject: {
859
+ objectURI: 'e3'
860
+ },
861
+ direction: RelationTypeDirection.bidirectional
862
+ }
863
+ ]
864
+ });
865
+ expect(component.find(LinearLoadIndicator)).toHaveLength(0);
866
+ });
867
+
868
+ it('should change traversedRelationsCount for existing nodes', async () => {
869
+ const addRelationToHopsDataSpy = jest.spyOn(hopsDataExport, 'addRelationToHopsData');
870
+ const data: HopsResponse = {
871
+ entities: [
872
+ {uri: 'e1', type: '', traversedRelations: 1, untraversedRelations: 0},
873
+ {uri: 'e2', type: '', traversedRelations: 2, untraversedRelations: 2},
874
+ {uri: 'e3', type: '', traversedRelations: 1, untraversedRelations: 2}
875
+ ],
876
+ relations: [
877
+ {
878
+ uri: 'r1',
879
+ type: '',
880
+ startObject: {
881
+ objectURI: 'e1'
882
+ },
883
+ endObject: {
884
+ objectURI: 'e2'
885
+ }
886
+ },
887
+ {
888
+ uri: 'r2',
889
+ type: '',
890
+ startObject: {
891
+ objectURI: 'e2'
892
+ },
893
+ endObject: {
894
+ objectURI: 'e3'
895
+ }
896
+ }
897
+ ]
898
+ };
899
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
900
+ (createRelation as jest.Mock).mockReturnValue(
901
+ Promise.resolve([
902
+ {
903
+ successful: true,
904
+ object: {
905
+ uri: 'r3',
906
+ type: 'relations/type2',
907
+ startObject: {
908
+ objectURI: 'e1',
909
+ type: ''
910
+ },
911
+ endObject: {
912
+ objectURI: 'e3',
913
+ type: 'entities/type3'
914
+ }
915
+ }
916
+ }
917
+ ])
918
+ );
919
+ const component = setUp(
920
+ mount,
921
+ createStore({
922
+ entity: {
923
+ uri: 'e1',
924
+ type: ''
925
+ },
926
+ metadata: {entityTypes: [], relationTypes: []}
927
+ })
928
+ );
929
+ await act(async () => {
930
+ await Promise.resolve();
931
+ });
932
+ component.update();
933
+ let contextValue = component.find(GraphStateContext.Provider).prop('value');
934
+ expect(contextValue).toMatchObject({
935
+ data,
936
+ graphTypeUri: null,
937
+ graphLoading: false
938
+ });
939
+
940
+ const newRelation = {
941
+ startEntityUri: 'e1',
942
+ endEntityUri: 'e3',
943
+ relationType: 'relations/type2',
944
+ attributes: {
945
+ FirstName: [
946
+ {
947
+ type: 'configuration/entityTypes/HCP/attributes/FirstName',
948
+ ov: true,
949
+ value: 'Name',
950
+ uri: 'uri/FirstName'
951
+ }
952
+ ]
953
+ },
954
+ startDate: 8908907878,
955
+ endDate: 568787879090
956
+ };
957
+ contextValue.onAddRelation(newRelation);
958
+ component.update();
959
+ expect(component.find(LinearLoadIndicator)).toHaveLength(1);
960
+ expect(createRelation).toHaveBeenCalledWith(newRelation);
961
+ await act(async () => {
962
+ await Promise.resolve();
963
+ });
964
+ component.update();
965
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
966
+ const graph = contextValue.graphologyGraph;
967
+ expect(graph.hasNode('e1')).toBe(true);
968
+ expect(graph.hasNode('e2')).toBe(true);
969
+ expect(graph.hasNode('e3')).toBe(true);
970
+ expect(graph.hasEdge('r1')).toBe(true);
971
+ expect(graph.hasEdge('r2')).toBe(true);
972
+ expect(graph.hasEdge('r3')).toBe(true);
973
+ expect(graph.getNodeAttribute('e1', 'traversedRelationsCount')).toBe(2);
974
+ expect(graph.getNodeAttribute('e2', 'traversedRelationsCount')).toBe(2);
975
+ expect(graph.getNodeAttribute('e3', 'traversedRelationsCount')).toBe(2);
976
+ expect(addRelationToHopsDataSpy).toHaveBeenCalledWith(
977
+ data,
978
+ {
979
+ endObject: {objectURI: 'e3', type: 'entities/type3'},
980
+ startObject: {objectURI: 'e1', type: ''},
981
+ type: 'relations/type2',
982
+ uri: 'r3'
983
+ },
984
+ store.metadata.t1
985
+ );
986
+ expect(contextValue.data).toEqual({
987
+ entities: [
988
+ {uri: 'e1', type: '', traversedRelations: 2, untraversedRelations: 0},
989
+ {uri: 'e2', type: '', traversedRelations: 2, untraversedRelations: 2},
990
+ {uri: 'e3', type: '', traversedRelations: 2, untraversedRelations: 2}
991
+ ],
992
+ relations: [
993
+ {
994
+ uri: 'r1',
995
+ type: '',
996
+ startObject: {
997
+ objectURI: 'e1'
998
+ },
999
+ endObject: {
1000
+ objectURI: 'e2'
1001
+ }
1002
+ },
1003
+ {
1004
+ uri: 'r2',
1005
+ type: '',
1006
+ startObject: {
1007
+ objectURI: 'e2'
1008
+ },
1009
+ endObject: {
1010
+ objectURI: 'e3'
1011
+ }
1012
+ },
1013
+ {
1014
+ uri: 'r3',
1015
+ type: 'relations/type2',
1016
+ startObject: {
1017
+ objectURI: 'e1'
1018
+ },
1019
+ endObject: {
1020
+ objectURI: 'e3'
1021
+ }
1022
+ }
1023
+ ]
1024
+ });
1025
+ expect(component.find(LinearLoadIndicator)).toHaveLength(0);
1026
+ });
1027
+ });
1028
+
1029
+ describe('onCollapseEntity behavior', () => {
1030
+ const data: HopsResponse = {
1031
+ entities: [
1032
+ {uri: 'e1', type: ''},
1033
+ {uri: 'e2', type: ''},
1034
+ {uri: 'e3', type: ''},
1035
+ {uri: 'e4', type: ''},
1036
+ {uri: 'e5', type: ''},
1037
+ {uri: 'e6', type: ''}
1038
+ ],
1039
+ relations: [
1040
+ {
1041
+ uri: 'r1',
1042
+ type: '',
1043
+ startObject: {
1044
+ objectURI: 'e1'
1045
+ },
1046
+ endObject: {
1047
+ objectURI: 'e2'
1048
+ }
1049
+ },
1050
+ {
1051
+ uri: 'r2',
1052
+ type: '',
1053
+ startObject: {
1054
+ objectURI: 'e2'
1055
+ },
1056
+ endObject: {
1057
+ objectURI: 'e3'
1058
+ }
1059
+ },
1060
+ {
1061
+ uri: 'r3',
1062
+ type: '',
1063
+ startObject: {
1064
+ objectURI: 'e1'
1065
+ },
1066
+ endObject: {
1067
+ objectURI: 'e4'
1068
+ }
1069
+ },
1070
+ {
1071
+ uri: 'r4',
1072
+ type: '',
1073
+ startObject: {
1074
+ objectURI: 'e4'
1075
+ },
1076
+ endObject: {
1077
+ objectURI: 'e3'
1078
+ }
1079
+ },
1080
+ {
1081
+ uri: 'r5',
1082
+ type: '',
1083
+ startObject: {
1084
+ objectURI: 'e2'
1085
+ },
1086
+ endObject: {
1087
+ objectURI: 'e5'
1088
+ }
1089
+ },
1090
+ {
1091
+ uri: 'r6',
1092
+ type: '',
1093
+ startObject: {
1094
+ objectURI: 'e3'
1095
+ },
1096
+ endObject: {
1097
+ objectURI: 'e5'
1098
+ }
1099
+ },
1100
+ {
1101
+ uri: 'r7',
1102
+ type: '',
1103
+ startObject: {
1104
+ objectURI: 'e1'
1105
+ },
1106
+ endObject: {
1107
+ objectURI: 'e3'
1108
+ }
1109
+ },
1110
+ {
1111
+ uri: 'r8',
1112
+ type: '',
1113
+ startObject: {
1114
+ objectURI: 'e5'
1115
+ },
1116
+ endObject: {
1117
+ objectURI: 'e6'
1118
+ }
1119
+ }
1120
+ ]
1121
+ };
1122
+
1123
+ beforeAll(() => {
1124
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
1125
+ });
1126
+
1127
+ it('should update data and graphologyGraph in GraphStateContext.Provider', async () => {
1128
+ const component = setUp(
1129
+ mount,
1130
+ createStore({
1131
+ entity: {
1132
+ uri: 'e1',
1133
+ type: ''
1134
+ },
1135
+ metadata: {entityTypes: [], relationTypes: []}
1136
+ })
1137
+ );
1138
+
1139
+ await act(async () => {
1140
+ await Promise.resolve();
1141
+ });
1142
+ component.update();
1143
+ let contextValue = component.find(GraphStateContext.Provider).prop('value');
1144
+ expect(contextValue).toMatchObject({
1145
+ data,
1146
+ graphTypeUri: null,
1147
+ graphLoading: false
1148
+ });
1149
+
1150
+ contextValue.onCollapseEntity('e1', {});
1151
+ component.update();
1152
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
1153
+ const graph = contextValue.graphologyGraph;
1154
+ expect(
1155
+ graph
1156
+ .nodes()
1157
+ .map((uri) => graph.getNodeAttribute(uri, 'hidden'))
1158
+ .filter((x) => !!x).length
1159
+ ).toBe(5);
1160
+ expect(graph.getNodeAttribute('e1', 'hidden')).toBeUndefined();
1161
+ expect(graph.getNodeAttribute('e2', 'hidden')).toBe(true);
1162
+ expect(graph.getNodeAttribute('e3', 'hidden')).toBe(true);
1163
+ expect(graph.getNodeAttribute('e4', 'hidden')).toBe(true);
1164
+ expect(graph.getNodeAttribute('e5', 'hidden')).toBe(true);
1165
+ expect(graph.getNodeAttribute('e6', 'hidden')).toBe(true);
1166
+ });
1167
+
1168
+ it('should hide neighbor nodes, except for nodes in the path', async () => {
1169
+ const component = setUp(
1170
+ mount,
1171
+ createStore({
1172
+ entity: {
1173
+ uri: 'e1',
1174
+ type: ''
1175
+ },
1176
+ metadata: {entityTypes: [], relationTypes: []}
1177
+ })
1178
+ );
1179
+
1180
+ await act(async () => {
1181
+ await Promise.resolve();
1182
+ });
1183
+ component.update();
1184
+ let contextValue = component.find(GraphStateContext.Provider).prop('value');
1185
+ expect(contextValue).toMatchObject({
1186
+ data,
1187
+ graphTypeUri: null,
1188
+ graphLoading: false
1189
+ });
1190
+
1191
+ const path: GraphPathType = {
1192
+ nodes: ['e1', 'e2', 'e3'],
1193
+ edges: ['r1', 'r2']
1194
+ };
1195
+
1196
+ contextValue.onCollapseEntity('e3', path);
1197
+ component.update();
1198
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
1199
+ const graph = contextValue.graphologyGraph;
1200
+ expect(
1201
+ graph
1202
+ .nodes()
1203
+ .map((uri) => graph.getNodeAttribute(uri, 'hidden'))
1204
+ .filter((x) => !!x).length
1205
+ ).toBe(3);
1206
+ expect(graph.getNodeAttribute('e1', 'hidden')).toBeUndefined();
1207
+ expect(graph.getNodeAttribute('e2', 'hidden')).toBeUndefined();
1208
+ expect(graph.getNodeAttribute('e3', 'hidden')).toBeUndefined();
1209
+ expect(graph.getNodeAttribute('e4', 'hidden')).toBe(true);
1210
+ expect(graph.getNodeAttribute('e5', 'hidden')).toBe(true);
1211
+ expect(graph.getNodeAttribute('e6', 'hidden')).toBe(true);
1212
+ });
1213
+ });
1214
+
1215
+ describe('onExpandEntity behavior', () => {
1216
+ const data: HopsResponse = {
1217
+ entities: [
1218
+ {uri: 'e1', type: '', untraversedRelations: 0, traversedRelations: 2},
1219
+ {uri: 'e2', type: '', untraversedRelations: 1, traversedRelations: 1},
1220
+ {uri: 'e3', type: '', untraversedRelations: 2, traversedRelations: 1},
1221
+ {uri: 'e4', type: '', untraversedRelations: 0, traversedRelations: 1}
1222
+ ],
1223
+ relations: [
1224
+ {
1225
+ uri: 'r1',
1226
+ type: '',
1227
+ startObject: {
1228
+ objectURI: 'e1'
1229
+ },
1230
+ endObject: {
1231
+ objectURI: 'e2'
1232
+ }
1233
+ },
1234
+ {
1235
+ uri: 'r2',
1236
+ type: '',
1237
+ startObject: {
1238
+ objectURI: 'e1'
1239
+ },
1240
+ endObject: {
1241
+ objectURI: 'e3'
1242
+ }
1243
+ },
1244
+ {
1245
+ uri: 'r3',
1246
+ type: '',
1247
+ startObject: {
1248
+ objectURI: 'e3'
1249
+ },
1250
+ endObject: {
1251
+ objectURI: 'e4'
1252
+ }
1253
+ }
1254
+ ]
1255
+ };
1256
+
1257
+ it('should dispatch an error in case of failed graph request', async () => {
1258
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
1259
+ const error = {errorMessage: 'Error!!!'};
1260
+
1261
+ const component = setUp(
1262
+ mount,
1263
+ createStore({
1264
+ entity: {
1265
+ uri: 'e1',
1266
+ type: ''
1267
+ },
1268
+ metadata: {entityTypes: [], relationTypes: []}
1269
+ })
1270
+ );
1271
+
1272
+ await act(async () => {
1273
+ await Promise.resolve();
1274
+ });
1275
+ component.update();
1276
+ let contextValue = component.find(GraphStateContext.Provider).prop('value');
1277
+ expect(contextValue).toMatchObject({
1278
+ data,
1279
+ graphTypeUri: null,
1280
+ graphLoading: false
1281
+ });
1282
+
1283
+ (getHops as jest.Mock).mockReturnValue(Promise.reject(error));
1284
+
1285
+ act(() => {
1286
+ contextValue.onExpandEntity('e2');
1287
+ });
1288
+ component.update();
1289
+ expect(component.find(LinearLoadIndicator)).toHaveLength(1);
1290
+ expect(getHops).toHaveBeenCalledWith({
1291
+ uri: 'e2',
1292
+ deep: 1,
1293
+ max: 3000,
1294
+ limitCreditsConsumption: true,
1295
+ graphTypes: null,
1296
+ activenessDate: null,
1297
+ signal
1298
+ });
1299
+ await act(async () => {
1300
+ await Promise.resolve();
1301
+ });
1302
+ component.update();
1303
+
1304
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
1305
+ expect(contextValue).toMatchObject({
1306
+ data,
1307
+ graphLoading: false,
1308
+ graphTypeUri: null
1309
+ });
1310
+ expect(dispatch).toHaveBeenCalledWith(ui.actions.errorSet(error.errorMessage));
1311
+ expect(component.find(LinearLoadIndicator)).toHaveLength(0);
1312
+ });
1313
+
1314
+ it('should not dispatch an error in case of cancel graph request', async () => {
1315
+ const initialProps = {
1316
+ entity: {
1317
+ uri: 'e1',
1318
+ type: ''
1319
+ },
1320
+ graphTypeUri: 'graphType',
1321
+ graphLayout: GraphLayout.SIMPLE_NETWORK
1322
+ };
1323
+ store = createStore({
1324
+ entity: {
1325
+ uri: 'e1',
1326
+ type: ''
1327
+ },
1328
+ metadata: {entityTypes: [], relationTypes: []}
1329
+ });
1330
+
1331
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
1332
+
1333
+ const {result} = renderHook(
1334
+ ({entity, graphTypeUri, graphLayout}) => useGraphState(entity, graphTypeUri, graphLayout),
1335
+ {initialProps}
1336
+ );
1337
+
1338
+ await act(async () => {
1339
+ await Promise.resolve();
1340
+ });
1341
+
1342
+ const error = {errorMessage: 'Error!!!', name: 'AbortError'};
1343
+ (getHops as jest.Mock).mockReturnValue(Promise.reject(error));
1344
+
1345
+ act(() => {
1346
+ result.current.onExpandEntity('e2');
1347
+ });
1348
+ await act(async () => {
1349
+ await Promise.resolve();
1350
+ });
1351
+
1352
+ expect(dispatch).not.toHaveBeenCalled();
1353
+ });
1354
+
1355
+ it('should not update data when untraversedRelation is 0', async () => {
1356
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
1357
+ const component = setUp(
1358
+ mount,
1359
+ createStore({
1360
+ entity: {
1361
+ uri: 'e1',
1362
+ type: ''
1363
+ },
1364
+ metadata: {entityTypes: [], relationTypes: []}
1365
+ })
1366
+ );
1367
+
1368
+ await act(async () => {
1369
+ await Promise.resolve();
1370
+ });
1371
+ component.update();
1372
+ let contextValue = component.find(GraphStateContext.Provider).prop('value');
1373
+ expect(contextValue).toMatchObject({
1374
+ data,
1375
+ graphTypeUri: null,
1376
+ graphLoading: false
1377
+ });
1378
+
1379
+ contextValue.onCollapseEntity('e1', {});
1380
+ component.update();
1381
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
1382
+ expect(contextValue).toMatchObject({
1383
+ data,
1384
+ graphTypeUri: null,
1385
+ graphLoading: false
1386
+ });
1387
+ let graph = contextValue.graphologyGraph;
1388
+ expect(graph.getNodeAttribute('e1', 'hidden')).toBeUndefined();
1389
+ expect(graph.getNodeAttribute('e2', 'hidden')).toBe(true);
1390
+ expect(graph.getNodeAttribute('e3', 'hidden')).toBe(true);
1391
+
1392
+ act(() => {
1393
+ contextValue.onExpandEntity('e1');
1394
+ });
1395
+ component.update();
1396
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
1397
+ expect(contextValue).toMatchObject({
1398
+ data,
1399
+ graphTypeUri: null,
1400
+ graphLoading: false
1401
+ });
1402
+ graph = contextValue.graphologyGraph;
1403
+ expect(graph.getNodeAttribute('e1', 'hidden')).toBeUndefined();
1404
+ expect(graph.getNodeAttribute('e2', 'hidden')).toBe(false);
1405
+ expect(graph.getNodeAttribute('e3', 'hidden')).toBe(false);
1406
+ });
1407
+
1408
+ it('should call getHops, update data and graphologyGraph in GraphStateContext.Provider', async () => {
1409
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(data));
1410
+ const processResponseToGraphSpy = jest.spyOn(processResponseToGraphExport, 'default');
1411
+ const component = setUp(
1412
+ mount,
1413
+ createStore({
1414
+ entity: {
1415
+ uri: 'e1',
1416
+ type: ''
1417
+ },
1418
+ metadata: {entityTypes: [], relationTypes: []}
1419
+ })
1420
+ );
1421
+
1422
+ await act(async () => {
1423
+ await Promise.resolve();
1424
+ });
1425
+ component.update();
1426
+ let contextValue = component.find(GraphStateContext.Provider).prop('value');
1427
+ expect(contextValue).toMatchObject({
1428
+ data,
1429
+ graphTypeUri: null,
1430
+ graphLoading: false
1431
+ });
1432
+
1433
+ const hopsData = {
1434
+ entities: [
1435
+ {uri: 'e2', type: '', untraversedRelations: 0, traversedRelations: 2},
1436
+ {uri: 'e1', type: '', untraversedRelations: 1, traversedRelations: 1},
1437
+ {uri: 'e5', type: '', untraversedRelations: 5, traversedRelations: 1}
1438
+ ],
1439
+ relations: [
1440
+ {
1441
+ uri: 'r1',
1442
+ type: '',
1443
+ startObject: {
1444
+ objectURI: 'e1'
1445
+ },
1446
+ endObject: {
1447
+ objectURI: 'e2'
1448
+ }
1449
+ },
1450
+ {
1451
+ uri: 'r4',
1452
+ type: '',
1453
+ startObject: {
1454
+ objectURI: 'e2'
1455
+ },
1456
+ endObject: {
1457
+ objectURI: 'e5'
1458
+ }
1459
+ }
1460
+ ]
1461
+ };
1462
+
1463
+ (getHops as jest.Mock).mockReturnValue(Promise.resolve(hopsData));
1464
+
1465
+ act(() => {
1466
+ contextValue.onExpandEntity('e2');
1467
+ });
1468
+ component.update();
1469
+ expect(component.find(LinearLoadIndicator)).toHaveLength(1);
1470
+ expect(getHops).toHaveBeenCalledWith({
1471
+ uri: 'e2',
1472
+ deep: 1,
1473
+ max: 3000,
1474
+ limitCreditsConsumption: true,
1475
+ graphTypes: null,
1476
+ activenessDate: null,
1477
+ signal
1478
+ });
1479
+ await act(async () => {
1480
+ await Promise.resolve();
1481
+ });
1482
+ component.update();
1483
+
1484
+ contextValue = component.find(GraphStateContext.Provider).prop('value');
1485
+ expect(contextValue).toMatchObject({
1486
+ data: {
1487
+ entities: [
1488
+ {uri: 'e1', type: '', untraversedRelations: 0, traversedRelations: 2},
1489
+ {uri: 'e2', type: '', untraversedRelations: 0, traversedRelations: 2},
1490
+ {uri: 'e3', type: '', untraversedRelations: 2, traversedRelations: 1},
1491
+ {uri: 'e4', type: '', untraversedRelations: 0, traversedRelations: 1},
1492
+ {uri: 'e5', type: '', untraversedRelations: 5, traversedRelations: 1}
1493
+ ],
1494
+ relations: [
1495
+ ...data.relations,
1496
+ {
1497
+ uri: 'r4',
1498
+ type: '',
1499
+ startObject: {
1500
+ objectURI: 'e2'
1501
+ },
1502
+ endObject: {
1503
+ objectURI: 'e5'
1504
+ }
1505
+ }
1506
+ ]
1507
+ },
1508
+ graphLoading: false,
1509
+ graphTypeUri: null
1510
+ });
1511
+ const graph = contextValue.graphologyGraph;
1512
+ expect(graph.nodes()).toEqual(['e1', 'e2', 'e3', 'e4', 'e5']);
1513
+ expect(graph.edges()).toEqual(['r1', 'r2', 'r3', 'r4']);
1514
+ expect(component.find(LinearLoadIndicator)).toHaveLength(0);
1515
+ expect(processResponseToGraphSpy).toHaveBeenCalledTimes(2);
1516
+ expect(getHops).toHaveBeenCalledTimes(2);
1517
+ });
1518
+ });
1519
+ });