pict-section-flow 0.0.10 → 0.0.13
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/.claude/launch.json +1 -1
- package/README.md +176 -0
- package/docs/.nojekyll +0 -0
- package/docs/Architecture.md +303 -0
- package/docs/Custom-Styling.md +275 -0
- package/docs/Data_Model.md +158 -0
- package/docs/Event_System.md +156 -0
- package/docs/Getting_Started.md +237 -0
- package/docs/Implementation_Reference.md +528 -0
- package/docs/Layout_Persistence.md +117 -0
- package/docs/README.md +115 -52
- package/docs/_cover.md +11 -0
- package/docs/_sidebar.md +52 -0
- package/docs/_topbar.md +8 -0
- package/docs/api/PictFlowCard.md +216 -0
- package/docs/api/PictFlowCardPropertiesPanel.md +235 -0
- package/docs/api/addConnection.md +101 -0
- package/docs/api/addNode.md +137 -0
- package/docs/api/autoLayout.md +77 -0
- package/docs/api/getFlowData.md +112 -0
- package/docs/api/marshalToView.md +95 -0
- package/docs/api/openPanel.md +128 -0
- package/docs/api/registerHandler.md +174 -0
- package/docs/api/registerNodeType.md +142 -0
- package/docs/api/removeConnection.md +57 -0
- package/docs/api/removeNode.md +80 -0
- package/docs/api/saveLayout.md +152 -0
- package/docs/api/screenToSVGCoords.md +68 -0
- package/docs/api/selectNode.md +116 -0
- package/docs/api/setTheme.md +168 -0
- package/docs/api/setZoom.md +97 -0
- package/docs/api/toggleFullscreen.md +68 -0
- package/docs/card-help/EACH.md +19 -0
- package/docs/card-help/FREAD.md +24 -0
- package/docs/card-help/FWRITE.md +24 -0
- package/docs/card-help/GET.md +22 -0
- package/docs/card-help/ITE.md +23 -0
- package/docs/card-help/LOG.md +23 -0
- package/docs/card-help/NOTE.md +17 -0
- package/docs/card-help/PREV.md +18 -0
- package/docs/card-help/SET.md +27 -0
- package/docs/card-help/SPKL.md +22 -0
- package/docs/card-help/STAT.md +23 -0
- package/docs/card-help/SW.md +25 -0
- package/docs/css/docuserve.css +73 -0
- package/docs/index.html +39 -0
- package/docs/retold-catalog.json +169 -0
- package/docs/retold-keyword-index.json +13942 -0
- package/example_applications/simple_cards/package.json +1 -0
- package/example_applications/simple_cards/source/card-help-content.js +16 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Each.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +2 -0
- package/package.json +11 -7
- package/scripts/generate-card-help.js +214 -0
- package/source/Pict-Section-Flow.js +4 -0
- package/source/PictFlowCard.js +3 -1
- package/source/providers/PictProvider-Flow-CSS.js +245 -152
- package/source/providers/PictProvider-Flow-ConnectorShapes.js +24 -0
- package/source/providers/PictProvider-Flow-Geometry.js +195 -38
- package/source/providers/PictProvider-Flow-PanelChrome.js +14 -12
- package/source/services/PictService-Flow-ConnectionHandleManager.js +263 -0
- package/source/services/PictService-Flow-ConnectionRenderer.js +134 -183
- package/source/services/PictService-Flow-DataManager.js +338 -0
- package/source/services/PictService-Flow-InteractionManager.js +165 -7
- package/source/services/PictService-Flow-PathGenerator.js +282 -0
- package/source/services/PictService-Flow-PortRenderer.js +269 -0
- package/source/services/PictService-Flow-RenderManager.js +281 -0
- package/source/services/PictService-Flow-Tether.js +6 -42
- package/source/views/PictView-Flow-Node.js +2 -220
- package/source/views/PictView-Flow-PropertiesPanel.js +89 -44
- package/source/views/PictView-Flow.js +130 -882
- package/test/ConnectionHandleManager_tests.js +717 -0
- package/test/ConnectionRenderer_tests.js +591 -0
- package/test/DataManager_tests.js +859 -0
- package/test/Geometry_tests.js +767 -0
- package/test/PathGenerator_tests.js +978 -0
- package/test/PortRenderer_tests.js +367 -0
- package/test/RenderManager_tests.js +756 -0
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
const libFable = require('fable');
|
|
2
|
+
const libChai = require('chai');
|
|
3
|
+
const libExpect = libChai.expect;
|
|
4
|
+
|
|
5
|
+
const libRenderManager = require('../source/services/PictService-Flow-RenderManager.js');
|
|
6
|
+
|
|
7
|
+
suite
|
|
8
|
+
(
|
|
9
|
+
'PictService-Flow-RenderManager',
|
|
10
|
+
function ()
|
|
11
|
+
{
|
|
12
|
+
let _Fable;
|
|
13
|
+
let _RenderManager;
|
|
14
|
+
let _MockFlowView;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a minimal mock DOM element that supports the operations
|
|
18
|
+
* used by RenderManager (firstChild, removeChild, appendChild,
|
|
19
|
+
* querySelectorAll, setAttribute, querySelector).
|
|
20
|
+
*/
|
|
21
|
+
function createMockElement()
|
|
22
|
+
{
|
|
23
|
+
let tmpChildren = [];
|
|
24
|
+
return {
|
|
25
|
+
_children: tmpChildren,
|
|
26
|
+
get firstChild() { return tmpChildren.length > 0 ? tmpChildren[0] : null; },
|
|
27
|
+
removeChild: function (pChild)
|
|
28
|
+
{
|
|
29
|
+
let tmpIdx = tmpChildren.indexOf(pChild);
|
|
30
|
+
if (tmpIdx >= 0) tmpChildren.splice(tmpIdx, 1);
|
|
31
|
+
},
|
|
32
|
+
appendChild: function (pChild)
|
|
33
|
+
{
|
|
34
|
+
tmpChildren.push(pChild);
|
|
35
|
+
},
|
|
36
|
+
querySelectorAll: function (pSelector)
|
|
37
|
+
{
|
|
38
|
+
// Simple mock: match data-connection-hash, data-panel-hash, data-node-hash
|
|
39
|
+
let tmpMatch = pSelector.match(/\[data-(connection|panel|node)-hash="([^"]+)"\]/);
|
|
40
|
+
if (!tmpMatch) return [];
|
|
41
|
+
let tmpType = tmpMatch[1];
|
|
42
|
+
let tmpHash = tmpMatch[2];
|
|
43
|
+
return tmpChildren.filter((c) => c._dataHash === tmpHash && c._dataType === tmpType);
|
|
44
|
+
},
|
|
45
|
+
querySelector: function (pSelector)
|
|
46
|
+
{
|
|
47
|
+
let tmpResults = this.querySelectorAll(pSelector);
|
|
48
|
+
return tmpResults.length > 0 ? tmpResults[0] : null;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createMockSVGChild(pDataType, pDataHash, pParent)
|
|
54
|
+
{
|
|
55
|
+
let tmpSelf =
|
|
56
|
+
{
|
|
57
|
+
_dataType: pDataType,
|
|
58
|
+
_dataHash: pDataHash,
|
|
59
|
+
remove: function ()
|
|
60
|
+
{
|
|
61
|
+
if (pParent)
|
|
62
|
+
{
|
|
63
|
+
let tmpIdx = pParent._children.indexOf(tmpSelf);
|
|
64
|
+
if (tmpIdx >= 0) pParent._children.splice(tmpIdx, 1);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
setAttribute: function () {}
|
|
68
|
+
};
|
|
69
|
+
return tmpSelf;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setup
|
|
73
|
+
(
|
|
74
|
+
function ()
|
|
75
|
+
{
|
|
76
|
+
_Fable = new libFable({});
|
|
77
|
+
|
|
78
|
+
_MockFlowView =
|
|
79
|
+
{
|
|
80
|
+
fable: _Fable,
|
|
81
|
+
log: _Fable.log,
|
|
82
|
+
options:
|
|
83
|
+
{
|
|
84
|
+
ViewIdentifier: 'test-flow',
|
|
85
|
+
EnableGridSnap: false,
|
|
86
|
+
GridSnapSize: 20
|
|
87
|
+
},
|
|
88
|
+
_FlowData:
|
|
89
|
+
{
|
|
90
|
+
Nodes:
|
|
91
|
+
[
|
|
92
|
+
{
|
|
93
|
+
Hash: 'n1',
|
|
94
|
+
Type: 'generic',
|
|
95
|
+
X: 100, Y: 100,
|
|
96
|
+
Width: 160, Height: 80,
|
|
97
|
+
Ports:
|
|
98
|
+
[
|
|
99
|
+
{ Hash: 'p-out', Direction: 'output', Side: 'right', Label: 'Out' }
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
Hash: 'n2',
|
|
104
|
+
Type: 'generic',
|
|
105
|
+
X: 400, Y: 100,
|
|
106
|
+
Width: 160, Height: 80,
|
|
107
|
+
Ports:
|
|
108
|
+
[
|
|
109
|
+
{ Hash: 'p-in', Direction: 'input', Side: 'left', Label: 'In' }
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
Connections:
|
|
114
|
+
[
|
|
115
|
+
{
|
|
116
|
+
Hash: 'c1',
|
|
117
|
+
SourceNodeHash: 'n1',
|
|
118
|
+
SourcePortHash: 'p-out',
|
|
119
|
+
TargetNodeHash: 'n2',
|
|
120
|
+
TargetPortHash: 'p-in',
|
|
121
|
+
Data: {}
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
OpenPanels: [],
|
|
125
|
+
ViewState:
|
|
126
|
+
{
|
|
127
|
+
SelectedNodeHash: null,
|
|
128
|
+
SelectedConnectionHash: null,
|
|
129
|
+
SelectedTetherHash: null
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
_NodesLayer: createMockElement(),
|
|
133
|
+
_ConnectionsLayer: createMockElement(),
|
|
134
|
+
_PanelsLayer: createMockElement(),
|
|
135
|
+
_TethersLayer: createMockElement(),
|
|
136
|
+
_SVGElement: null,
|
|
137
|
+
_ConnectionRenderer:
|
|
138
|
+
{
|
|
139
|
+
renderConnection: function (pConn, pLayer, pIsSelected)
|
|
140
|
+
{
|
|
141
|
+
let tmpEl = createMockSVGChild('connection', pConn.Hash, pLayer);
|
|
142
|
+
pLayer.appendChild(tmpEl);
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
_NodeView:
|
|
146
|
+
{
|
|
147
|
+
renderNode: function (pNode, pLayer, pIsSelected, pConfig)
|
|
148
|
+
{
|
|
149
|
+
let tmpEl = createMockSVGChild('node', pNode.Hash, pLayer);
|
|
150
|
+
pLayer.appendChild(tmpEl);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
_NodeTypeProvider:
|
|
154
|
+
{
|
|
155
|
+
getNodeType: function () { return null; }
|
|
156
|
+
},
|
|
157
|
+
_PropertiesPanelView: null,
|
|
158
|
+
_TetherService:
|
|
159
|
+
{
|
|
160
|
+
renderTether: function (pPanel, pNodeData, pLayer, pIsSelected, pViewId)
|
|
161
|
+
{
|
|
162
|
+
let tmpEl = createMockSVGChild('panel', pPanel.Hash, pLayer);
|
|
163
|
+
pLayer.appendChild(tmpEl);
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
_LayoutService:
|
|
167
|
+
{
|
|
168
|
+
snapToGrid: function (pVal, pSize) { return Math.round(pVal / pSize) * pSize; }
|
|
169
|
+
},
|
|
170
|
+
_ConnectorShapesProvider: null,
|
|
171
|
+
getNode: function (pHash)
|
|
172
|
+
{
|
|
173
|
+
return _MockFlowView._FlowData.Nodes.find((n) => n.Hash === pHash) || null;
|
|
174
|
+
},
|
|
175
|
+
getConnection: function (pHash)
|
|
176
|
+
{
|
|
177
|
+
return _MockFlowView._FlowData.Connections.find((c) => c.Hash === pHash) || null;
|
|
178
|
+
},
|
|
179
|
+
updateViewportTransform: function () {},
|
|
180
|
+
_resetHandlesForNode: function () {}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
_RenderManager = new libRenderManager(_Fable, { FlowView: _MockFlowView }, 'RM-Test');
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// ---- Constructor ----
|
|
188
|
+
|
|
189
|
+
suite
|
|
190
|
+
(
|
|
191
|
+
'Constructor',
|
|
192
|
+
function ()
|
|
193
|
+
{
|
|
194
|
+
test
|
|
195
|
+
(
|
|
196
|
+
'should instantiate with correct serviceType',
|
|
197
|
+
function (fDone)
|
|
198
|
+
{
|
|
199
|
+
libExpect(_RenderManager).to.be.an('object');
|
|
200
|
+
libExpect(_RenderManager.serviceType).to.equal('PictServiceFlowRenderManager');
|
|
201
|
+
fDone();
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
test
|
|
206
|
+
(
|
|
207
|
+
'should store FlowView reference from options',
|
|
208
|
+
function (fDone)
|
|
209
|
+
{
|
|
210
|
+
libExpect(_RenderManager._FlowView).to.equal(_MockFlowView);
|
|
211
|
+
fDone();
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
test
|
|
216
|
+
(
|
|
217
|
+
'should handle missing FlowView',
|
|
218
|
+
function (fDone)
|
|
219
|
+
{
|
|
220
|
+
let tmpManager = new libRenderManager(_Fable, {}, 'NoView');
|
|
221
|
+
libExpect(tmpManager._FlowView).to.be.null;
|
|
222
|
+
fDone();
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// ---- renderFlow ----
|
|
229
|
+
|
|
230
|
+
suite
|
|
231
|
+
(
|
|
232
|
+
'renderFlow',
|
|
233
|
+
function ()
|
|
234
|
+
{
|
|
235
|
+
test
|
|
236
|
+
(
|
|
237
|
+
'should clear layers and re-render all nodes and connections',
|
|
238
|
+
function (fDone)
|
|
239
|
+
{
|
|
240
|
+
// Pre-populate layers
|
|
241
|
+
_MockFlowView._NodesLayer.appendChild({ mock: true });
|
|
242
|
+
_MockFlowView._ConnectionsLayer.appendChild({ mock: true });
|
|
243
|
+
|
|
244
|
+
_RenderManager.renderFlow();
|
|
245
|
+
|
|
246
|
+
// Connections should be rendered (1 connection)
|
|
247
|
+
libExpect(_MockFlowView._ConnectionsLayer._children).to.have.length(1);
|
|
248
|
+
// Nodes should be rendered (2 nodes)
|
|
249
|
+
libExpect(_MockFlowView._NodesLayer._children).to.have.length(2);
|
|
250
|
+
fDone();
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
test
|
|
255
|
+
(
|
|
256
|
+
'should call updateViewportTransform',
|
|
257
|
+
function (fDone)
|
|
258
|
+
{
|
|
259
|
+
let tmpViewportCalled = false;
|
|
260
|
+
_MockFlowView.updateViewportTransform = function () { tmpViewportCalled = true; };
|
|
261
|
+
|
|
262
|
+
_RenderManager.renderFlow();
|
|
263
|
+
|
|
264
|
+
libExpect(tmpViewportCalled).to.be.true;
|
|
265
|
+
fDone();
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
test
|
|
270
|
+
(
|
|
271
|
+
'should do nothing when no FlowView',
|
|
272
|
+
function (fDone)
|
|
273
|
+
{
|
|
274
|
+
let tmpManager = new libRenderManager(_Fable, {}, 'NoView');
|
|
275
|
+
tmpManager.renderFlow();
|
|
276
|
+
fDone();
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
test
|
|
281
|
+
(
|
|
282
|
+
'should do nothing when layers are missing',
|
|
283
|
+
function (fDone)
|
|
284
|
+
{
|
|
285
|
+
_MockFlowView._NodesLayer = null;
|
|
286
|
+
_RenderManager.renderFlow();
|
|
287
|
+
// Should not throw
|
|
288
|
+
fDone();
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
test
|
|
293
|
+
(
|
|
294
|
+
'should enrich port data from node type config',
|
|
295
|
+
function (fDone)
|
|
296
|
+
{
|
|
297
|
+
_MockFlowView._NodeTypeProvider.getNodeType = function (pType)
|
|
298
|
+
{
|
|
299
|
+
if (pType === 'action')
|
|
300
|
+
{
|
|
301
|
+
return {
|
|
302
|
+
DefaultPorts:
|
|
303
|
+
[
|
|
304
|
+
{ Direction: 'input', Label: 'In', PortType: 'data', Side: 'left' },
|
|
305
|
+
{ Direction: 'output', Label: 'Out', PortType: 'data', Side: 'right' }
|
|
306
|
+
]
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
_MockFlowView._FlowData.Nodes = [
|
|
313
|
+
{
|
|
314
|
+
Hash: 'n-action',
|
|
315
|
+
Type: 'action',
|
|
316
|
+
X: 100, Y: 100,
|
|
317
|
+
Width: 160, Height: 80,
|
|
318
|
+
Ports:
|
|
319
|
+
[
|
|
320
|
+
{ Hash: 'p1', Direction: 'input', Label: 'In' },
|
|
321
|
+
{ Hash: 'p2', Direction: 'output', Label: 'Out' }
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
];
|
|
325
|
+
_MockFlowView._FlowData.Connections = [];
|
|
326
|
+
|
|
327
|
+
_RenderManager.renderFlow();
|
|
328
|
+
|
|
329
|
+
// Ports should now have enriched PortType and Side
|
|
330
|
+
let tmpNode = _MockFlowView._FlowData.Nodes[0];
|
|
331
|
+
libExpect(tmpNode.Ports[0].PortType).to.equal('data');
|
|
332
|
+
libExpect(tmpNode.Ports[0].Side).to.equal('left');
|
|
333
|
+
libExpect(tmpNode.Ports[1].PortType).to.equal('data');
|
|
334
|
+
libExpect(tmpNode.Ports[1].Side).to.equal('right');
|
|
335
|
+
fDone();
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
test
|
|
340
|
+
(
|
|
341
|
+
'should render panels and tethers when PropertiesPanelView exists',
|
|
342
|
+
function (fDone)
|
|
343
|
+
{
|
|
344
|
+
let tmpPanelsRendered = false;
|
|
345
|
+
_MockFlowView._PropertiesPanelView =
|
|
346
|
+
{
|
|
347
|
+
renderPanels: function () { tmpPanelsRendered = true; }
|
|
348
|
+
};
|
|
349
|
+
_MockFlowView._FlowData.OpenPanels = [{ Hash: 'panel-1', NodeHash: 'n1' }];
|
|
350
|
+
|
|
351
|
+
_RenderManager.renderFlow();
|
|
352
|
+
|
|
353
|
+
libExpect(tmpPanelsRendered).to.be.true;
|
|
354
|
+
fDone();
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// ---- renderSingleConnection ----
|
|
361
|
+
|
|
362
|
+
suite
|
|
363
|
+
(
|
|
364
|
+
'renderSingleConnection',
|
|
365
|
+
function ()
|
|
366
|
+
{
|
|
367
|
+
test
|
|
368
|
+
(
|
|
369
|
+
'should remove and re-render a single connection',
|
|
370
|
+
function (fDone)
|
|
371
|
+
{
|
|
372
|
+
// Pre-render
|
|
373
|
+
_RenderManager.renderFlow();
|
|
374
|
+
libExpect(_MockFlowView._ConnectionsLayer._children).to.have.length(1);
|
|
375
|
+
|
|
376
|
+
// Re-render single
|
|
377
|
+
_RenderManager.renderSingleConnection('c1');
|
|
378
|
+
|
|
379
|
+
// Should still have 1 connection element
|
|
380
|
+
libExpect(_MockFlowView._ConnectionsLayer._children).to.have.length(1);
|
|
381
|
+
fDone();
|
|
382
|
+
}
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
test
|
|
386
|
+
(
|
|
387
|
+
'should do nothing for non-existent connection',
|
|
388
|
+
function (fDone)
|
|
389
|
+
{
|
|
390
|
+
_RenderManager.renderSingleConnection('non-existent');
|
|
391
|
+
fDone();
|
|
392
|
+
}
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
test
|
|
396
|
+
(
|
|
397
|
+
'should do nothing when ConnectionsLayer is null',
|
|
398
|
+
function (fDone)
|
|
399
|
+
{
|
|
400
|
+
_MockFlowView._ConnectionsLayer = null;
|
|
401
|
+
_RenderManager.renderSingleConnection('c1');
|
|
402
|
+
fDone();
|
|
403
|
+
}
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
// ---- renderSingleTether ----
|
|
409
|
+
|
|
410
|
+
suite
|
|
411
|
+
(
|
|
412
|
+
'renderSingleTether',
|
|
413
|
+
function ()
|
|
414
|
+
{
|
|
415
|
+
test
|
|
416
|
+
(
|
|
417
|
+
'should render a tether for an open panel',
|
|
418
|
+
function (fDone)
|
|
419
|
+
{
|
|
420
|
+
_MockFlowView._FlowData.OpenPanels = [{ Hash: 'panel-1', NodeHash: 'n1' }];
|
|
421
|
+
|
|
422
|
+
_RenderManager.renderSingleTether('panel-1');
|
|
423
|
+
|
|
424
|
+
libExpect(_MockFlowView._TethersLayer._children).to.have.length(1);
|
|
425
|
+
fDone();
|
|
426
|
+
}
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
test
|
|
430
|
+
(
|
|
431
|
+
'should do nothing for non-existent panel',
|
|
432
|
+
function (fDone)
|
|
433
|
+
{
|
|
434
|
+
_RenderManager.renderSingleTether('non-existent');
|
|
435
|
+
fDone();
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
test
|
|
440
|
+
(
|
|
441
|
+
'should do nothing when TethersLayer is null',
|
|
442
|
+
function (fDone)
|
|
443
|
+
{
|
|
444
|
+
_MockFlowView._TethersLayer = null;
|
|
445
|
+
_RenderManager.renderSingleTether('panel-1');
|
|
446
|
+
fDone();
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
test
|
|
451
|
+
(
|
|
452
|
+
'should do nothing when TetherService is null',
|
|
453
|
+
function (fDone)
|
|
454
|
+
{
|
|
455
|
+
_MockFlowView._TetherService = null;
|
|
456
|
+
_RenderManager.renderSingleTether('panel-1');
|
|
457
|
+
fDone();
|
|
458
|
+
}
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
// ---- updateNodePosition ----
|
|
464
|
+
|
|
465
|
+
suite
|
|
466
|
+
(
|
|
467
|
+
'updateNodePosition',
|
|
468
|
+
function ()
|
|
469
|
+
{
|
|
470
|
+
test
|
|
471
|
+
(
|
|
472
|
+
'should update node X and Y',
|
|
473
|
+
function (fDone)
|
|
474
|
+
{
|
|
475
|
+
_RenderManager.renderFlow();
|
|
476
|
+
|
|
477
|
+
_RenderManager.updateNodePosition('n1', 500, 300);
|
|
478
|
+
|
|
479
|
+
let tmpNode = _MockFlowView._FlowData.Nodes.find((n) => n.Hash === 'n1');
|
|
480
|
+
libExpect(tmpNode.X).to.equal(500);
|
|
481
|
+
libExpect(tmpNode.Y).to.equal(300);
|
|
482
|
+
fDone();
|
|
483
|
+
}
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
test
|
|
487
|
+
(
|
|
488
|
+
'should snap to grid when enabled',
|
|
489
|
+
function (fDone)
|
|
490
|
+
{
|
|
491
|
+
_MockFlowView.options.EnableGridSnap = true;
|
|
492
|
+
_MockFlowView.options.GridSnapSize = 20;
|
|
493
|
+
|
|
494
|
+
_RenderManager.renderFlow();
|
|
495
|
+
|
|
496
|
+
_RenderManager.updateNodePosition('n1', 513, 307);
|
|
497
|
+
|
|
498
|
+
let tmpNode = _MockFlowView._FlowData.Nodes.find((n) => n.Hash === 'n1');
|
|
499
|
+
libExpect(tmpNode.X).to.equal(520);
|
|
500
|
+
libExpect(tmpNode.Y).to.equal(300);
|
|
501
|
+
fDone();
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
test
|
|
506
|
+
(
|
|
507
|
+
'should call _resetHandlesForNode',
|
|
508
|
+
function (fDone)
|
|
509
|
+
{
|
|
510
|
+
let tmpResetHash = null;
|
|
511
|
+
_MockFlowView._resetHandlesForNode = function (pHash) { tmpResetHash = pHash; };
|
|
512
|
+
|
|
513
|
+
_RenderManager.renderFlow();
|
|
514
|
+
_RenderManager.updateNodePosition('n1', 200, 200);
|
|
515
|
+
|
|
516
|
+
libExpect(tmpResetHash).to.equal('n1');
|
|
517
|
+
fDone();
|
|
518
|
+
}
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
test
|
|
522
|
+
(
|
|
523
|
+
'should re-render connections for the moved node',
|
|
524
|
+
function (fDone)
|
|
525
|
+
{
|
|
526
|
+
let tmpRenderedConnHashes = [];
|
|
527
|
+
_MockFlowView._ConnectionRenderer.renderConnection = function (pConn, pLayer)
|
|
528
|
+
{
|
|
529
|
+
tmpRenderedConnHashes.push(pConn.Hash);
|
|
530
|
+
let tmpEl = createMockSVGChild('connection', pConn.Hash);
|
|
531
|
+
pLayer.appendChild(tmpEl);
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
_RenderManager.renderFlow();
|
|
535
|
+
tmpRenderedConnHashes = [];
|
|
536
|
+
|
|
537
|
+
_RenderManager.updateNodePosition('n1', 200, 200);
|
|
538
|
+
|
|
539
|
+
// c1 connects n1 and n2, so it should be re-rendered
|
|
540
|
+
libExpect(tmpRenderedConnHashes).to.include('c1');
|
|
541
|
+
fDone();
|
|
542
|
+
}
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
test
|
|
546
|
+
(
|
|
547
|
+
'should do nothing for non-existent node',
|
|
548
|
+
function (fDone)
|
|
549
|
+
{
|
|
550
|
+
_RenderManager.updateNodePosition('non-existent', 200, 200);
|
|
551
|
+
fDone();
|
|
552
|
+
}
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
test
|
|
556
|
+
(
|
|
557
|
+
'should do nothing when no FlowView',
|
|
558
|
+
function (fDone)
|
|
559
|
+
{
|
|
560
|
+
let tmpManager = new libRenderManager(_Fable, {}, 'NoView');
|
|
561
|
+
tmpManager.updateNodePosition('n1', 200, 200);
|
|
562
|
+
fDone();
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
// ---- renderConnectionsForNode ----
|
|
569
|
+
|
|
570
|
+
suite
|
|
571
|
+
(
|
|
572
|
+
'renderConnectionsForNode',
|
|
573
|
+
function ()
|
|
574
|
+
{
|
|
575
|
+
test
|
|
576
|
+
(
|
|
577
|
+
'should re-render connections involving the specified node',
|
|
578
|
+
function (fDone)
|
|
579
|
+
{
|
|
580
|
+
_RenderManager.renderFlow();
|
|
581
|
+
|
|
582
|
+
let tmpRenderCount = 0;
|
|
583
|
+
_MockFlowView._ConnectionRenderer.renderConnection = function (pConn, pLayer)
|
|
584
|
+
{
|
|
585
|
+
tmpRenderCount++;
|
|
586
|
+
let tmpEl = createMockSVGChild('connection', pConn.Hash);
|
|
587
|
+
pLayer.appendChild(tmpEl);
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
_RenderManager.renderConnectionsForNode('n1');
|
|
591
|
+
|
|
592
|
+
libExpect(tmpRenderCount).to.equal(1);
|
|
593
|
+
fDone();
|
|
594
|
+
}
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
test
|
|
598
|
+
(
|
|
599
|
+
'should not re-render connections for unrelated nodes',
|
|
600
|
+
function (fDone)
|
|
601
|
+
{
|
|
602
|
+
_RenderManager.renderFlow();
|
|
603
|
+
|
|
604
|
+
let tmpRenderCount = 0;
|
|
605
|
+
_MockFlowView._ConnectionRenderer.renderConnection = function ()
|
|
606
|
+
{
|
|
607
|
+
tmpRenderCount++;
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
_RenderManager.renderConnectionsForNode('unrelated-node');
|
|
611
|
+
|
|
612
|
+
libExpect(tmpRenderCount).to.equal(0);
|
|
613
|
+
fDone();
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
test
|
|
618
|
+
(
|
|
619
|
+
'should do nothing when ConnectionsLayer is null',
|
|
620
|
+
function (fDone)
|
|
621
|
+
{
|
|
622
|
+
_MockFlowView._ConnectionsLayer = null;
|
|
623
|
+
_RenderManager.renderConnectionsForNode('n1');
|
|
624
|
+
fDone();
|
|
625
|
+
}
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
// ---- renderTethersForNode ----
|
|
631
|
+
|
|
632
|
+
suite
|
|
633
|
+
(
|
|
634
|
+
'renderTethersForNode',
|
|
635
|
+
function ()
|
|
636
|
+
{
|
|
637
|
+
test
|
|
638
|
+
(
|
|
639
|
+
'should re-render tethers for panels attached to the node',
|
|
640
|
+
function (fDone)
|
|
641
|
+
{
|
|
642
|
+
_MockFlowView._FlowData.OpenPanels = [{ Hash: 'panel-1', NodeHash: 'n1' }];
|
|
643
|
+
|
|
644
|
+
let tmpRenderCount = 0;
|
|
645
|
+
_MockFlowView._TetherService.renderTether = function (pPanel, pNodeData, pLayer)
|
|
646
|
+
{
|
|
647
|
+
tmpRenderCount++;
|
|
648
|
+
let tmpEl = createMockSVGChild('panel', pPanel.Hash);
|
|
649
|
+
pLayer.appendChild(tmpEl);
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
_RenderManager.renderTethersForNode('n1');
|
|
653
|
+
|
|
654
|
+
libExpect(tmpRenderCount).to.equal(1);
|
|
655
|
+
fDone();
|
|
656
|
+
}
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
test
|
|
660
|
+
(
|
|
661
|
+
'should skip panels for other nodes',
|
|
662
|
+
function (fDone)
|
|
663
|
+
{
|
|
664
|
+
_MockFlowView._FlowData.OpenPanels = [{ Hash: 'panel-1', NodeHash: 'n2' }];
|
|
665
|
+
|
|
666
|
+
let tmpRenderCount = 0;
|
|
667
|
+
_MockFlowView._TetherService.renderTether = function ()
|
|
668
|
+
{
|
|
669
|
+
tmpRenderCount++;
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
_RenderManager.renderTethersForNode('n1');
|
|
673
|
+
|
|
674
|
+
libExpect(tmpRenderCount).to.equal(0);
|
|
675
|
+
fDone();
|
|
676
|
+
}
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
test
|
|
680
|
+
(
|
|
681
|
+
'should do nothing when no panels for node',
|
|
682
|
+
function (fDone)
|
|
683
|
+
{
|
|
684
|
+
_MockFlowView._FlowData.OpenPanels = [];
|
|
685
|
+
_RenderManager.renderTethersForNode('n1');
|
|
686
|
+
fDone();
|
|
687
|
+
}
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
test
|
|
691
|
+
(
|
|
692
|
+
'should do nothing when TethersLayer is null',
|
|
693
|
+
function (fDone)
|
|
694
|
+
{
|
|
695
|
+
_MockFlowView._TethersLayer = null;
|
|
696
|
+
_RenderManager.renderTethersForNode('n1');
|
|
697
|
+
fDone();
|
|
698
|
+
}
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
test
|
|
702
|
+
(
|
|
703
|
+
'should do nothing when TetherService is null',
|
|
704
|
+
function (fDone)
|
|
705
|
+
{
|
|
706
|
+
_MockFlowView._TetherService = null;
|
|
707
|
+
_RenderManager.renderTethersForNode('n1');
|
|
708
|
+
fDone();
|
|
709
|
+
}
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
);
|
|
713
|
+
|
|
714
|
+
// ---- reinjectMarkerDefs ----
|
|
715
|
+
|
|
716
|
+
suite
|
|
717
|
+
(
|
|
718
|
+
'reinjectMarkerDefs',
|
|
719
|
+
function ()
|
|
720
|
+
{
|
|
721
|
+
test
|
|
722
|
+
(
|
|
723
|
+
'should do nothing when ConnectorShapesProvider is null',
|
|
724
|
+
function (fDone)
|
|
725
|
+
{
|
|
726
|
+
_MockFlowView._ConnectorShapesProvider = null;
|
|
727
|
+
_RenderManager.reinjectMarkerDefs();
|
|
728
|
+
fDone();
|
|
729
|
+
}
|
|
730
|
+
);
|
|
731
|
+
|
|
732
|
+
test
|
|
733
|
+
(
|
|
734
|
+
'should do nothing when SVGElement is null',
|
|
735
|
+
function (fDone)
|
|
736
|
+
{
|
|
737
|
+
_MockFlowView._SVGElement = null;
|
|
738
|
+
_RenderManager.reinjectMarkerDefs();
|
|
739
|
+
fDone();
|
|
740
|
+
}
|
|
741
|
+
);
|
|
742
|
+
|
|
743
|
+
test
|
|
744
|
+
(
|
|
745
|
+
'should do nothing when no FlowView',
|
|
746
|
+
function (fDone)
|
|
747
|
+
{
|
|
748
|
+
let tmpManager = new libRenderManager(_Fable, {}, 'NoView');
|
|
749
|
+
tmpManager.reinjectMarkerDefs();
|
|
750
|
+
fDone();
|
|
751
|
+
}
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
);
|