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.
Files changed (88) hide show
  1. package/.claude/launch.json +1 -1
  2. package/README.md +176 -0
  3. package/docs/.nojekyll +0 -0
  4. package/docs/Architecture.md +303 -0
  5. package/docs/Custom-Styling.md +275 -0
  6. package/docs/Data_Model.md +158 -0
  7. package/docs/Event_System.md +156 -0
  8. package/docs/Getting_Started.md +237 -0
  9. package/docs/Implementation_Reference.md +528 -0
  10. package/docs/Layout_Persistence.md +117 -0
  11. package/docs/README.md +115 -52
  12. package/docs/_cover.md +11 -0
  13. package/docs/_sidebar.md +52 -0
  14. package/docs/_topbar.md +8 -0
  15. package/docs/api/PictFlowCard.md +216 -0
  16. package/docs/api/PictFlowCardPropertiesPanel.md +235 -0
  17. package/docs/api/addConnection.md +101 -0
  18. package/docs/api/addNode.md +137 -0
  19. package/docs/api/autoLayout.md +77 -0
  20. package/docs/api/getFlowData.md +112 -0
  21. package/docs/api/marshalToView.md +95 -0
  22. package/docs/api/openPanel.md +128 -0
  23. package/docs/api/registerHandler.md +174 -0
  24. package/docs/api/registerNodeType.md +142 -0
  25. package/docs/api/removeConnection.md +57 -0
  26. package/docs/api/removeNode.md +80 -0
  27. package/docs/api/saveLayout.md +152 -0
  28. package/docs/api/screenToSVGCoords.md +68 -0
  29. package/docs/api/selectNode.md +116 -0
  30. package/docs/api/setTheme.md +168 -0
  31. package/docs/api/setZoom.md +97 -0
  32. package/docs/api/toggleFullscreen.md +68 -0
  33. package/docs/card-help/EACH.md +19 -0
  34. package/docs/card-help/FREAD.md +24 -0
  35. package/docs/card-help/FWRITE.md +24 -0
  36. package/docs/card-help/GET.md +22 -0
  37. package/docs/card-help/ITE.md +23 -0
  38. package/docs/card-help/LOG.md +23 -0
  39. package/docs/card-help/NOTE.md +17 -0
  40. package/docs/card-help/PREV.md +18 -0
  41. package/docs/card-help/SET.md +27 -0
  42. package/docs/card-help/SPKL.md +22 -0
  43. package/docs/card-help/STAT.md +23 -0
  44. package/docs/card-help/SW.md +25 -0
  45. package/docs/css/docuserve.css +73 -0
  46. package/docs/index.html +39 -0
  47. package/docs/retold-catalog.json +169 -0
  48. package/docs/retold-keyword-index.json +13942 -0
  49. package/example_applications/simple_cards/package.json +1 -0
  50. package/example_applications/simple_cards/source/card-help-content.js +16 -0
  51. package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +2 -0
  52. package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +2 -0
  53. package/example_applications/simple_cards/source/cards/FlowCard-Each.js +2 -0
  54. package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +2 -0
  55. package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +2 -0
  56. package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +2 -0
  57. package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +2 -0
  58. package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +2 -0
  59. package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +2 -0
  60. package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +2 -0
  61. package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +2 -0
  62. package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +2 -0
  63. package/package.json +11 -7
  64. package/scripts/generate-card-help.js +214 -0
  65. package/source/Pict-Section-Flow.js +4 -0
  66. package/source/PictFlowCard.js +3 -1
  67. package/source/providers/PictProvider-Flow-CSS.js +245 -152
  68. package/source/providers/PictProvider-Flow-ConnectorShapes.js +24 -0
  69. package/source/providers/PictProvider-Flow-Geometry.js +195 -38
  70. package/source/providers/PictProvider-Flow-PanelChrome.js +14 -12
  71. package/source/services/PictService-Flow-ConnectionHandleManager.js +263 -0
  72. package/source/services/PictService-Flow-ConnectionRenderer.js +134 -183
  73. package/source/services/PictService-Flow-DataManager.js +338 -0
  74. package/source/services/PictService-Flow-InteractionManager.js +165 -7
  75. package/source/services/PictService-Flow-PathGenerator.js +282 -0
  76. package/source/services/PictService-Flow-PortRenderer.js +269 -0
  77. package/source/services/PictService-Flow-RenderManager.js +281 -0
  78. package/source/services/PictService-Flow-Tether.js +6 -42
  79. package/source/views/PictView-Flow-Node.js +2 -220
  80. package/source/views/PictView-Flow-PropertiesPanel.js +89 -44
  81. package/source/views/PictView-Flow.js +130 -882
  82. package/test/ConnectionHandleManager_tests.js +717 -0
  83. package/test/ConnectionRenderer_tests.js +591 -0
  84. package/test/DataManager_tests.js +859 -0
  85. package/test/Geometry_tests.js +767 -0
  86. package/test/PathGenerator_tests.js +978 -0
  87. package/test/PortRenderer_tests.js +367 -0
  88. 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
+ );