@truedat/lm 4.42.2 → 4.42.5

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.42.4] 2022-04-19
4
+
5
+ ### Changed
6
+
7
+ - [TD-4513] Limit related concepts graph depth
8
+
3
9
  ## [4.42.0] 2022-04-05
4
10
 
5
11
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/lm",
3
- "version": "4.42.2",
3
+ "version": "4.42.5",
4
4
  "description": "Truedat Link Manager",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -33,13 +33,12 @@
33
33
  "@testing-library/jest-dom": "^5.14.1",
34
34
  "@testing-library/react": "^12.0.0",
35
35
  "@testing-library/user-event": "^13.2.1",
36
- "@truedat/test": "4.42.0",
36
+ "@truedat/test": "4.42.5",
37
37
  "babel-jest": "^27.0.6",
38
38
  "babel-plugin-dynamic-import-node": "^2.3.3",
39
39
  "babel-plugin-lodash": "^3.3.4",
40
40
  "babel-plugin-react-intl": "^5.1.18",
41
41
  "babel-plugin-transform-semantic-ui-react-imports": "^1.4.1",
42
- "canvas": "^2.9.1",
43
42
  "enzyme": "^3.11.0",
44
43
  "enzyme-adapter-react-16": "^1.15.6",
45
44
  "enzyme-to-json": "^3.6.2",
@@ -84,12 +83,13 @@
84
83
  ]
85
84
  },
86
85
  "dependencies": {
87
- "@truedat/core": "4.42.2",
86
+ "@truedat/core": "4.42.5",
88
87
  "path-to-regexp": "^1.7.0",
89
88
  "prop-types": "^15.7.2",
90
89
  "react-graph-vis": "1.0.5",
91
90
  "react-hook-form": "^6.15.8",
92
91
  "react-intl": "^5.20.10",
92
+ "react-rangeslider": "^2.2.0",
93
93
  "react-redux": "^7.2.4",
94
94
  "react-router-dom": "^5.2.0",
95
95
  "redux": "^4.1.1",
@@ -101,5 +101,5 @@
101
101
  "react-dom": ">= 16.8.6 < 17",
102
102
  "semantic-ui-react": ">= 0.88.2 < 2.1"
103
103
  },
104
- "gitHead": "1ca6b75d5b0c80c8e2e931340d5efe4eeefb27de"
104
+ "gitHead": "77641b0e3a7555a6f6c4c0649ff7b6164292b4b0"
105
105
  }
@@ -1,27 +1,14 @@
1
1
  import _ from "lodash/fp";
2
- import React, { useEffect, useState } from "react";
3
- import { connect } from "react-redux";
2
+ import React, { useState } from "react";
4
3
  import { useIntl } from "react-intl";
5
4
  import PropTypes from "prop-types";
6
5
  import Graph from "react-graph-vis";
7
- import "../styles/relationGraph.less";
8
- import { dfsMaxDepth, dfs, getEdges } from "../services/relationGraphTraversal";
9
- import RelationGraphDepth from "./RelationGraphDepth";
10
6
 
11
- export const RelationGraph = ({
12
- navigate,
13
- currentId,
14
- relationsGraph,
15
- initialDepth,
16
- }) => {
7
+ export const RelationGraph = ({ navigate, currentId, relationsGraph }) => {
17
8
  const { formatMessage } = useIntl();
18
9
  const [network, setNetwork] = useState({});
19
- const [depth, setDepth] = useState(initialDepth || Infinity);
20
- const [maxDepth, setMaxDepth] = useState();
21
- const [limitedRelationsGraph, setLimitedRelationsGraph] = useState();
22
- const [start, setStart] = useState();
23
10
 
24
- const getVisNodes = _.flow(
11
+ const nodes = _.flow(
25
12
  _.get("nodes"),
26
13
  _.map(({ id, name: label }) => ({
27
14
  id,
@@ -44,9 +31,9 @@ export const RelationGraph = ({
44
31
  }
45
32
  : null,
46
33
  }))
47
- );
34
+ )(relationsGraph);
48
35
 
49
- const getVisEdges = _.flow(
36
+ const edges = _.flow(
50
37
  _.get("edges"),
51
38
  _.map(({ source_id: from, target_id: to, tags }) => ({
52
39
  from,
@@ -61,36 +48,7 @@ export const RelationGraph = ({
61
48
  _.join("\n")
62
49
  )(tags),
63
50
  }))
64
- );
65
-
66
- useEffect(() => {
67
- if (!_.isEmpty(relationsGraph)) {
68
- const start = _.flow(
69
- _.getOr([], "nodes"),
70
- _.find({ id: currentId })
71
- )(relationsGraph);
72
-
73
- const useEffectMaxDepth = dfsMaxDepth(relationsGraph, start, {}, 0);
74
-
75
- setStart(start);
76
- setMaxDepth(useEffectMaxDepth);
77
- if (depth === Infinity) {
78
- setDepth(useEffectMaxDepth);
79
- }
80
- }
81
- }, [relationsGraph]);
82
-
83
- useEffect(() => {
84
- if (!_.isEmpty(relationsGraph) && !_.isEmpty(start) && depth !== Infinity) {
85
- const nodes = dfs(relationsGraph, start, {}, depth);
86
- depth < maxDepth
87
- ? setLimitedRelationsGraph({
88
- nodes,
89
- edges: getEdges(relationsGraph, nodes),
90
- })
91
- : setLimitedRelationsGraph(relationsGraph);
92
- }
93
- }, [depth, start]);
51
+ )(relationsGraph);
94
52
 
95
53
  const options = {
96
54
  autoResize: true,
@@ -167,26 +125,14 @@ export const RelationGraph = ({
167
125
  };
168
126
  return (
169
127
  <>
170
- {!_.isEmpty(limitedRelationsGraph?.nodes) && (
171
- <>
172
- <RelationGraphDepth
173
- onClick={(_e) => setDepth(maxDepth)}
174
- onChange={(event) => setDepth(parseInt(event.target.value))}
175
- depth={depth}
176
- maxDepth={maxDepth}
177
- />
178
-
179
- <Graph
180
- graph={{
181
- nodes: getVisNodes(limitedRelationsGraph),
182
- edges: getVisEdges(limitedRelationsGraph),
183
- }}
184
- options={options}
185
- events={events}
186
- style={{ height: "640px" }}
187
- getNetwork={setNetwork}
188
- />
189
- </>
128
+ {!_.isEmpty(relationsGraph) && (
129
+ <Graph
130
+ graph={{ nodes, edges }}
131
+ options={options}
132
+ events={events}
133
+ style={{ height: "640px" }}
134
+ getNetwork={setNetwork}
135
+ />
190
136
  )}
191
137
  </>
192
138
  );
@@ -196,15 +142,6 @@ RelationGraph.propTypes = {
196
142
  navigate: PropTypes.func,
197
143
  currentId: PropTypes.string,
198
144
  relationsGraph: PropTypes.object,
199
- initialDepth: PropTypes.number,
200
145
  };
201
146
 
202
- export const mapStateToProps = ({
203
- relationsGraph,
204
- conceptGraphInitialDepth,
205
- }) => ({
206
- initialDepth: conceptGraphInitialDepth,
207
- relationsGraph,
208
- });
209
-
210
- export default connect(mapStateToProps)(RelationGraph);
147
+ export default RelationGraph;
@@ -1,32 +1,30 @@
1
+ import _ from "lodash/fp";
1
2
  import React from "react";
2
3
  import PropTypes from "prop-types";
3
- import { Button } from "semantic-ui-react";
4
+ import { Segment } from "semantic-ui-react";
5
+ import Slider from "react-rangeslider";
6
+ import "react-rangeslider/lib/index.css";
4
7
  import { FormattedMessage } from "react-intl";
5
8
 
6
- export const RelationGraphDepth = ({ onClick, onChange, depth, maxDepth }) => {
7
- return (
8
- <div className="graph">
9
- <label htmlFor="depth">
10
- <FormattedMessage id="relationGraph.depth" /> [1-{maxDepth}]
11
- </label>
12
- <input
13
- id="depth"
14
- aria-label="depth"
15
- type="number"
16
- min="1"
17
- max={maxDepth}
18
- value={depth === Infinity ? 0 : depth}
9
+ export const RelationGraphDepth = ({ onChange, depth, maxDepth }) => {
10
+ return _.isUndefined(maxDepth) ? null : (
11
+ <Segment className="graph-depth">
12
+ <FormattedMessage id="relationGraph.depth" />
13
+ <div>{depth}</div>
14
+ <Slider
15
+ value={depth}
16
+ orientation="vertical"
17
+ reverse={true}
19
18
  onChange={onChange}
20
- ></input>
21
- <Button disabled={depth === maxDepth} onClick={onClick}>
22
- <FormattedMessage id="relationGraph.all" />
23
- </Button>
24
- </div>
19
+ min={1}
20
+ max={maxDepth}
21
+ tooltip={false}
22
+ />
23
+ </Segment>
25
24
  );
26
25
  };
27
26
 
28
27
  RelationGraphDepth.propTypes = {
29
- onClick: PropTypes.func,
30
28
  onChange: PropTypes.func,
31
29
  depth: PropTypes.number,
32
30
  maxDepth: PropTypes.number,
@@ -12,6 +12,7 @@ import StructureLinks from "./StructureLinks";
12
12
  import StructureRelationForm from "./StructureRelationForm";
13
13
  import TagTypeSelector from "./TagTypeSelector";
14
14
  import RelationGraph from "./RelationGraph";
15
+ import RelationGraphDepth from "./RelationGraphDepth";
15
16
  import RelationRoutes from "./RelationRoutes";
16
17
  import RelationTags from "./RelationTags";
17
18
  import RelationTagCards from "./RelationTagCards";
@@ -32,6 +33,7 @@ export {
32
33
  StructureRelationForm,
33
34
  TagTypeSelector,
34
35
  RelationGraph,
36
+ RelationGraphDepth,
35
37
  RelationRoutes,
36
38
  RelationTags,
37
39
  RelationTagCards,
@@ -0,0 +1,188 @@
1
+ import _ from "lodash/fp";
2
+ import { selectLimitedGraph } from "..";
3
+ import { selectMaxDepth } from "../relationGraphTraversal";
4
+
5
+ const nodes = [
6
+ {
7
+ id: "business_concept:92",
8
+ name: "My Awesome Concept",
9
+ },
10
+ {
11
+ id: "business_concept:5",
12
+ name: "New Concept 4",
13
+ },
14
+ {
15
+ id: "business_concept:18",
16
+ name: "Float Concept",
17
+ },
18
+ ];
19
+
20
+ const edges = [
21
+ {
22
+ id: 1,
23
+ source_id: "business_concept:92",
24
+ target_id: "business_concept:5",
25
+ tags: [
26
+ {
27
+ id: 3,
28
+ value: {
29
+ target_type: "business_concept",
30
+ type: "bc_padre",
31
+ },
32
+ },
33
+ ],
34
+ },
35
+ {
36
+ id: 2,
37
+ source_id: "business_concept:5",
38
+ target_id: "business_concept:18",
39
+ tags: [
40
+ {
41
+ id: 3,
42
+ value: {
43
+ target_type: "business_concept",
44
+ type: "bc_padre",
45
+ },
46
+ },
47
+ ],
48
+ },
49
+ {
50
+ id: 3,
51
+ source_id: "business_concept:18",
52
+ target_id: "business_concept:5",
53
+ tags: [
54
+ {
55
+ id: 3,
56
+ value: {
57
+ target_type: "business_concept",
58
+ type: "bc_padre",
59
+ },
60
+ },
61
+ ],
62
+ },
63
+ ];
64
+
65
+ const relationsGraph = {
66
+ nodes,
67
+ edges,
68
+ };
69
+
70
+ describe("selectors: relationGraphTraversal", () => {
71
+ const currentId = "business_concept:92";
72
+
73
+ it("gets Graph on non-empty relationsGraph", () => {
74
+ const limitedGraph = selectLimitedGraph({ relationsGraph }, currentId, 3);
75
+
76
+ expect(
77
+ _.isUndefined(
78
+ _.find({ id: nodes[0].id, name: nodes[0].name }, limitedGraph.nodes)
79
+ ) &&
80
+ _.isUndefined(
81
+ _.find({ id: nodes[1].id, name: nodes[1].name }, limitedGraph.nodes)
82
+ ) &&
83
+ _.isUndefined(
84
+ _.find({ id: nodes[2].id, name: nodes[2].name }, limitedGraph.nodes)
85
+ )
86
+ ).toBeFalsy();
87
+
88
+ expect(
89
+ _.isUndefined(
90
+ _.find(
91
+ {
92
+ source_id: edges[0].source_id,
93
+ target_id: edges[0].target_id,
94
+ },
95
+ limitedGraph.edges
96
+ )
97
+ ) &&
98
+ _.isUndefined(
99
+ _.find(
100
+ {
101
+ source_id: edges[1].source_id,
102
+ target_id: edges[1].target_id,
103
+ },
104
+ limitedGraph.edges
105
+ )
106
+ ) &&
107
+ _.isUndefined(
108
+ _.find(
109
+ {
110
+ source_id: edges[2].source_id,
111
+ target_id: edges[2].target_id,
112
+ },
113
+ limitedGraph.edges
114
+ )
115
+ )
116
+ ).toBeFalsy();
117
+ });
118
+
119
+ it("gets Graph on empty relationsGraph", () => {
120
+ const limitedGraph = selectLimitedGraph(
121
+ { relationsGraph: {} },
122
+ currentId,
123
+ 3
124
+ );
125
+ expect(limitedGraph).toStrictEqual({});
126
+ });
127
+
128
+ it("gets depth limited graph", () => {
129
+ const limitedGraph = selectLimitedGraph({ relationsGraph }, currentId, 2);
130
+
131
+ expect(
132
+ _.isUndefined(
133
+ _.find({ id: nodes[0].id, name: nodes[0].name }, limitedGraph.nodes)
134
+ ) &&
135
+ _.isUndefined(
136
+ _.find({ id: nodes[1].id, name: nodes[1].name }, limitedGraph.nodes)
137
+ )
138
+ ).toBeFalsy();
139
+
140
+ expect(
141
+ _.isUndefined(
142
+ _.find({ id: nodes[2].id, name: nodes[2].name }, limitedGraph.nodes)
143
+ )
144
+ ).toBeTruthy();
145
+
146
+ expect(
147
+ _.isUndefined(
148
+ _.find(
149
+ {
150
+ source_id: edges[0].source_id,
151
+ target_id: edges[0].target_id,
152
+ },
153
+ limitedGraph.edges
154
+ )
155
+ )
156
+ ).toBeFalsy();
157
+
158
+ expect(
159
+ _.isUndefined(
160
+ _.find(
161
+ {
162
+ source_id: edges[1].source_id,
163
+ target_id: edges[1].target_id,
164
+ },
165
+ limitedGraph.edges
166
+ )
167
+ ) &&
168
+ _.isUndefined(
169
+ _.find(
170
+ {
171
+ source_id: edges[2].source_id,
172
+ target_id: edges[2].target_id,
173
+ },
174
+ limitedGraph.edges
175
+ )
176
+ )
177
+ ).toBeTruthy();
178
+ });
179
+
180
+ it("get graph max depth", () => {
181
+ const start = {
182
+ id: "business_concept:92",
183
+ name: "My Awesome Concept",
184
+ };
185
+
186
+ expect(selectMaxDepth({ relationsGraph }, currentId, start)).toBe(3);
187
+ });
188
+ });
@@ -1,2 +1,3 @@
1
1
  export * from "./getStructureLinks";
2
2
  export * from "./getImplementationToConceptLinks";
3
+ export * from "./relationGraphTraversal";
@@ -0,0 +1,113 @@
1
+ /* eslint-disable fp/no-mutation */
2
+ /* eslint-disable fp/no-let */
3
+ import _ from "lodash/fp";
4
+ import { createSelector } from "reselect";
5
+
6
+ const selectStart = createSelector(
7
+ (state) => state.relationsGraph,
8
+ (state, currentId) => currentId,
9
+ (relationsGraph, currentId) => {
10
+ return (
11
+ _.flow(_.getOr([], "nodes"), _.find({ id: currentId }))(relationsGraph) ||
12
+ {}
13
+ );
14
+ }
15
+ );
16
+
17
+ export const selectMaxDepth = createSelector(
18
+ (state) => state.relationsGraph,
19
+ (state, currentId) => selectStart(state, currentId),
20
+ (relationsGraph, start) => bfsMaxDepth(relationsGraph, start)
21
+ );
22
+
23
+ export const selectLimitedGraph = createSelector(
24
+ (state) => state.relationsGraph,
25
+ (state, currentId) => selectStart(state, currentId),
26
+ (state, currentId) => selectMaxDepth(state, currentId),
27
+ (state, currentId, depth) => depth,
28
+ (relationsGraph, start, maxDepth, depth) =>
29
+ getLimitedGraph(relationsGraph, start, maxDepth, depth)
30
+ );
31
+
32
+ const getLimitedGraph = (relationsGraph, start, maxDepth, depth) => {
33
+ if (!_.isEmpty(relationsGraph) && !_.isEmpty(start) && depth !== Infinity) {
34
+ const nodes = dfs(relationsGraph, start, {}, depth);
35
+ return depth < maxDepth
36
+ ? {
37
+ nodes,
38
+ edges: getEdges(relationsGraph, nodes),
39
+ }
40
+ : relationsGraph;
41
+ } else {
42
+ return relationsGraph;
43
+ }
44
+ };
45
+
46
+ const getNeighbours = (relationsGraph, start) => {
47
+ const neighbours = _.concat(
48
+ // Up
49
+ _.flow(
50
+ _.filter((edge) => edge.target_id === start.id),
51
+ _.flatMap((edge) => _.find({ id: edge.source_id }, relationsGraph.nodes))
52
+ )(relationsGraph.edges),
53
+ // Down
54
+ _.flow(
55
+ _.filter((edge) => edge.source_id === start.id),
56
+ _.flatMap((edge) => _.find({ id: edge.target_id }, relationsGraph.nodes))
57
+ )(relationsGraph.edges)
58
+ );
59
+ return neighbours;
60
+ };
61
+
62
+ export const getEdges = (relationsGraph, nodes) => {
63
+ const nodeIds = Object.keys(nodes);
64
+ return _.filter(
65
+ (edge) =>
66
+ nodeIds.includes(edge.source_id) && nodeIds.includes(edge.target_id),
67
+ relationsGraph.edges
68
+ );
69
+ };
70
+
71
+ export const dfs = (relationsGraph, start, visited, maxDepth) => {
72
+ if (Object.keys(visited).includes(start.id) || maxDepth === 0) return {};
73
+
74
+ return _.reduce(
75
+ (acc, neighbour) => ({
76
+ ...acc,
77
+ ...dfs(
78
+ relationsGraph,
79
+ neighbour,
80
+ { ...visited, [start.id]: start },
81
+ maxDepth - 1
82
+ ),
83
+ }),
84
+ { [start.id]: start }
85
+ )(getNeighbours(relationsGraph, start));
86
+ };
87
+
88
+ /*
89
+ * Shortest Path (Unweighted Graph)
90
+ * If the graph is unweighed, then finding the shortest path is easy: we can
91
+ * use the breadth-first search algorithm. For a weighted graph, we can use Dijkstra's algorithm.
92
+ * https://aquarchitect.github.io/swift-algorithm-club/Shortest%20Path%20%28Unweighted%29/
93
+ */
94
+ const bfsMaxDepth = (relationsGraph, start) => {
95
+ let queue = [];
96
+ let nodesDistance = {};
97
+
98
+ queue.push(start);
99
+ nodesDistance[start.id] = 1;
100
+
101
+ let current = start;
102
+ while (!_.isEmpty(current)) {
103
+ current = queue.shift() || {};
104
+ for (let neighbour of getNeighbours(relationsGraph, current)) {
105
+ if (!nodesDistance[neighbour.id]) {
106
+ queue.push(neighbour);
107
+ nodesDistance[neighbour.id] = nodesDistance[current.id] + 1;
108
+ }
109
+ }
110
+ }
111
+
112
+ return Math.max(...Object.values(nodesDistance));
113
+ };
@@ -1,34 +1,21 @@
1
+ .graph-depth {
2
+ position: absolute !important;
3
+ z-index: 1;
4
+ margin-right: 0px;
1
5
 
2
- .graph{
3
-
4
- div&{
5
- position: absolute;
6
- right: 0;
7
- z-index: 1;
8
- padding-right: 1rem;
9
-
10
- input[type=number] {
11
- vertical-align: top;
12
- line-height: 34px;
13
- width: 105px;
14
- font-size: 20px;
15
- text-align: center;
16
- }
17
-
18
- label {
19
- position: absolute;
20
- top: -20px;
21
- }
22
- .ui.button {
23
- vertical-align: top;
24
- width: 80px;
25
- border-radius: 0 4px 4px 0;
26
- border: 1px solid grey;
27
- border-left: 0;
28
- font-size: 14px;
29
- }
6
+ div {
7
+ text-align: center;
8
+ font-weight: bold;
9
+ }
10
+ }
30
11
 
12
+ .graph-depth .rangeslider-vertical {
13
+ .rangeslider__fill {
14
+ background-color: #ed5c17;
15
+ border: 1px solid gray;
31
16
  }
32
17
 
33
-
18
+ .rangeslider__handle {
19
+ border-radius: 4px;
20
+ }
34
21
  }
@@ -1,264 +0,0 @@
1
- import _ from "lodash/fp";
2
- import React from "react";
3
- import { mount } from "enzyme";
4
- import { intl } from "@truedat/test/intl-stub";
5
- import userEvent from "@testing-library/user-event";
6
- import { render } from "@truedat/test/render";
7
- import { waitFor } from "@testing-library/react";
8
- import { RelationGraph } from "../RelationGraph";
9
-
10
- jest.spyOn(React, "useContext").mockImplementation(() => intl);
11
-
12
- const renderOpts = {
13
- messages: {
14
- en: {
15
- "relationGraph.all": "All",
16
- "relationGraph.depth": "Depth",
17
- },
18
- },
19
- };
20
-
21
- describe("<RelationGraph />", () => {
22
- beforeEach(() => {
23
- // Avoid `attachTo: document.body` Warning
24
- const div = document.createElement("div");
25
- div.setAttribute("id", "container");
26
- document.body.appendChild(div);
27
- });
28
-
29
- afterEach(() => {
30
- const div = document.getElementById("container");
31
- if (div) {
32
- document.body.removeChild(div);
33
- }
34
- });
35
-
36
- const nodes = [
37
- {
38
- id: "business_concept:92",
39
- name: "My Awesome Concept",
40
- },
41
- {
42
- id: "business_concept:5",
43
- name: "New Concept 4",
44
- },
45
- {
46
- id: "business_concept:18",
47
- name: "Float Concept",
48
- },
49
- ];
50
-
51
- const edges = [
52
- {
53
- id: 1,
54
- source_id: "business_concept:92",
55
- target_id: "business_concept:5",
56
- tags: [
57
- {
58
- id: 3,
59
- value: {
60
- target_type: "business_concept",
61
- type: "bc_padre",
62
- },
63
- },
64
- ],
65
- },
66
- {
67
- id: 2,
68
- source_id: "business_concept:5",
69
- target_id: "business_concept:18",
70
- tags: [
71
- {
72
- id: 3,
73
- value: {
74
- target_type: "business_concept",
75
- type: "bc_padre",
76
- },
77
- },
78
- ],
79
- },
80
- {
81
- id: 3,
82
- source_id: "business_concept:18",
83
- target_id: "business_concept:5",
84
- tags: [
85
- {
86
- id: 3,
87
- value: {
88
- target_type: "business_concept",
89
- type: "bc_padre",
90
- },
91
- },
92
- ],
93
- },
94
- ];
95
-
96
- const relationsGraph = {
97
- nodes,
98
- edges,
99
- };
100
-
101
- const defaultProps = {
102
- currentId: "business_concept:92",
103
- navigate: jest.fn(),
104
- relationsGraph: {},
105
- };
106
-
107
- it("renders Graph on non-empty relationsGraph", () => {
108
- const props = { ...defaultProps, relationsGraph };
109
-
110
- const wrapper = mount(<RelationGraph {...props} />, {
111
- attachTo: document.getElementById("container"),
112
- });
113
-
114
- const graphComponent = wrapper.find("Graph");
115
- expect(graphComponent.length).toBe(1);
116
- const limitedGraph = graphComponent.prop("graph");
117
-
118
- expect(
119
- _.isUndefined(
120
- _.find({ id: nodes[0].id, label: nodes[0].name }, limitedGraph.nodes)
121
- ) &&
122
- _.isUndefined(
123
- _.find({ id: nodes[1].id, label: nodes[1].name }, limitedGraph.nodes)
124
- ) &&
125
- _.isUndefined(
126
- _.find({ id: nodes[2].id, label: nodes[2].name }, limitedGraph.nodes)
127
- )
128
- ).toBeFalsy();
129
-
130
- expect(
131
- _.isUndefined(
132
- _.find(
133
- {
134
- from: edges[0].source_id,
135
- to: edges[0].target_id,
136
- label: `source.${edges[0].tags[0].value.type}`,
137
- },
138
- limitedGraph.edges
139
- )
140
- ) &&
141
- _.isUndefined(
142
- _.find(
143
- {
144
- from: edges[1].source_id,
145
- to: edges[1].target_id,
146
- label: `source.${edges[1].tags[1].value.type}`,
147
- },
148
- limitedGraph.edges
149
- )
150
- ) &&
151
- _.isUndefined(
152
- _.find(
153
- {
154
- from: edges[2].source_id,
155
- to: edges[2].target_id,
156
- label: `source.${edges[2].tags[2].value.type}`,
157
- },
158
- limitedGraph.edges
159
- )
160
- )
161
- ).toBeFalsy();
162
- });
163
-
164
- it("does not render Graph on empty relationsGraph", () => {
165
- const wrapper = mount(<RelationGraph {...defaultProps} />, {
166
- attachTo: document.getElementById("container"),
167
- });
168
- expect(wrapper.find("Graph").length).toBe(0);
169
- });
170
-
171
- it("sets initial depth", () => {
172
- const initialDepth = 1;
173
- const props = { ...defaultProps, relationsGraph, initialDepth };
174
- const wrapper = mount(<RelationGraph {...props} />, {
175
- attachTo: document.getElementById("container"),
176
- });
177
- const input = wrapper.find("input");
178
- expect(input.prop("value")).toBe(initialDepth);
179
- });
180
-
181
- it("no initial depth defaults to max graph depth", () => {
182
- const props = { ...defaultProps, relationsGraph };
183
- const wrapper = mount(<RelationGraph {...props} />, {
184
- attachTo: document.getElementById("container"),
185
- });
186
- const input = wrapper.find("input");
187
- expect(input.prop("value")).toBe(3);
188
- });
189
-
190
- it("button sets max depth", async () => {
191
- const initialDepth = 1;
192
- const props = { ...defaultProps, relationsGraph, initialDepth };
193
- const { getByRole, getByLabelText } = render(
194
- <RelationGraph {...props} />,
195
- renderOpts
196
- );
197
-
198
- userEvent.click(await getByRole("button"));
199
- await waitFor(() => {
200
- expect(parseInt(getByLabelText("depth").value)).toBe(3);
201
- });
202
- });
203
-
204
- it("limits graph", async () => {
205
- const props = { ...defaultProps, relationsGraph };
206
- const wrapper = await mount(<RelationGraph {...props} />, {
207
- attachTo: document.getElementById("container"),
208
- });
209
- const input = wrapper.find("input");
210
- input.simulate("change", { target: { value: 2 } });
211
- const limitedGraph = wrapper.find("Graph").prop("graph");
212
-
213
- expect(
214
- _.isUndefined(
215
- _.find({ id: nodes[0].id, label: nodes[0].name }, limitedGraph.nodes)
216
- ) &&
217
- _.isUndefined(
218
- _.find({ id: nodes[1].id, label: nodes[1].name }, limitedGraph.nodes)
219
- )
220
- ).toBeFalsy();
221
-
222
- expect(
223
- _.isUndefined(
224
- _.find({ id: nodes[2].id, label: nodes[2].name }, limitedGraph.nodes)
225
- )
226
- ).toBeTruthy();
227
-
228
- expect(
229
- _.isUndefined(
230
- _.find(
231
- {
232
- from: edges[0].source_id,
233
- to: edges[0].target_id,
234
- label: `source.${edges[0].tags[0].value.type}`,
235
- },
236
- limitedGraph.edges
237
- )
238
- )
239
- ).toBeFalsy();
240
-
241
- expect(
242
- _.isUndefined(
243
- _.find(
244
- {
245
- from: edges[1].source_id,
246
- to: edges[1].target_id,
247
- label: `source.${edges[1].tags[0].value.type}`,
248
- },
249
- limitedGraph.edges
250
- )
251
- ) &&
252
- _.isUndefined(
253
- _.find(
254
- {
255
- from: edges[2].source_id,
256
- to: edges[2].target_id,
257
- label: `source.${edges[2].tags[0].value.type}`,
258
- },
259
- limitedGraph.edges
260
- )
261
- )
262
- ).toBeTruthy();
263
- });
264
- });