pict-section-flow 1.2.0 → 1.4.0

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.
@@ -0,0 +1,138 @@
1
+ const libChai = require('chai');
2
+ const libExpect = libChai.expect;
3
+
4
+ const libPictViewFlow = require('../source/views/PictView-Flow.js');
5
+ const libPictViewFlowToolbar = require('../source/views/PictView-Flow-Toolbar.js');
6
+ const libPictViewFlowFloatingToolbar = require('../source/views/PictView-Flow-FloatingToolbar.js');
7
+
8
+ // The toolbar custom-button API (ToolbarExtraButtons + onToolbarButton) lets a host add its own buttons
9
+ // to the flow toolbar. They render in both the docked and the floating toolbar (so they survive every
10
+ // toolbar mode) and, on click, fire the FlowView's onToolbarButton hook. These are harness-free unit
11
+ // tests: the render-time stamping and the click dispatch are prototype methods called against light
12
+ // stubs, so no DOM or full Pict app is needed.
13
+ suite('Flow Toolbar custom buttons (ToolbarExtraButtons)',
14
+ function ()
15
+ {
16
+ suite('default_configuration is additive + backward compatible',
17
+ function ()
18
+ {
19
+ test('the flow view defaults to no extra buttons and no hook',
20
+ function ()
21
+ {
22
+ libExpect(libPictViewFlow.default_configuration.ToolbarExtraButtons).to.be.an('array');
23
+ libExpect(libPictViewFlow.default_configuration.ToolbarExtraButtons.length).to.equal(0);
24
+ libExpect(libPictViewFlow.default_configuration.onToolbarButton).to.equal(false);
25
+ });
26
+
27
+ test('both toolbar views default to an empty extra-button list',
28
+ function ()
29
+ {
30
+ libExpect(libPictViewFlowToolbar.default_configuration.ToolbarExtraButtons).to.be.an('array');
31
+ libExpect(libPictViewFlowToolbar.default_configuration.ToolbarExtraButtons.length).to.equal(0);
32
+ libExpect(libPictViewFlowFloatingToolbar.default_configuration.ToolbarExtraButtons).to.be.an('array');
33
+ libExpect(libPictViewFlowFloatingToolbar.default_configuration.ToolbarExtraButtons.length).to.equal(0);
34
+ });
35
+
36
+ test('both toolbar templates render the extra-button group + row template',
37
+ function ()
38
+ {
39
+ let tmpDockedTemplates = libPictViewFlowToolbar.default_configuration.Templates;
40
+ let tmpDockedBar = tmpDockedTemplates.find((pT) => pT.Hash === 'Flow-Toolbar-Template');
41
+ let tmpDockedRow = tmpDockedTemplates.find((pT) => pT.Hash === 'Flow-Toolbar-Extra-Button');
42
+ libExpect(tmpDockedBar.Template).to.contain('{~TS:Flow-Toolbar-Extra-Button:Record.ToolbarExtraButtons~}');
43
+ libExpect(tmpDockedRow).to.be.an('object');
44
+ libExpect(tmpDockedRow.Template).to.contain('_handleExtraAction');
45
+
46
+ let tmpFloatTemplates = libPictViewFlowFloatingToolbar.default_configuration.Templates;
47
+ let tmpFloatBar = tmpFloatTemplates.find((pT) => pT.Hash === 'Flow-FloatingToolbar-Template');
48
+ let tmpFloatRow = tmpFloatTemplates.find((pT) => pT.Hash === 'Flow-FloatingToolbar-Extra-Button');
49
+ libExpect(tmpFloatBar.Template).to.contain('{~TS:Flow-FloatingToolbar-Extra-Button:Record.ToolbarExtraButtons~}');
50
+ libExpect(tmpFloatRow).to.be.an('object');
51
+ libExpect(tmpFloatRow.Template).to.contain('_handleExtraClick');
52
+ });
53
+ });
54
+
55
+ suite('_stampExtraButtons (render-time per-row fields)',
56
+ function ()
57
+ {
58
+ test('stamps FlowViewIdentifier and an empty ActiveClass onto each button',
59
+ function ()
60
+ {
61
+ let tmpStub =
62
+ {
63
+ options:
64
+ {
65
+ FlowViewIdentifier: 'MB-FlowView-7',
66
+ ToolbarExtraButtons: [ { Hash: 'background', Icon: 'background' }, { Hash: 'done', Icon: 'check' } ]
67
+ }
68
+ };
69
+ libPictViewFlowToolbar.prototype._stampExtraButtons.call(tmpStub);
70
+ libExpect(tmpStub.options.ToolbarExtraButtons[0].FlowViewIdentifier).to.equal('MB-FlowView-7');
71
+ libExpect(tmpStub.options.ToolbarExtraButtons[0].ActiveClass).to.equal('');
72
+ libExpect(tmpStub.options.ToolbarExtraButtons[1].FlowViewIdentifier).to.equal('MB-FlowView-7');
73
+ });
74
+
75
+ test('an Active button gets the active class',
76
+ function ()
77
+ {
78
+ let tmpStub =
79
+ {
80
+ options:
81
+ {
82
+ FlowViewIdentifier: 'Pict-Flow',
83
+ ToolbarExtraButtons: [ { Hash: 'edit', Icon: 'edit', Active: true } ]
84
+ }
85
+ };
86
+ libPictViewFlowToolbar.prototype._stampExtraButtons.call(tmpStub);
87
+ libExpect(tmpStub.options.ToolbarExtraButtons[0].ActiveClass).to.equal(' pict-flow-toolbar-btn-active');
88
+ });
89
+
90
+ test('a non-array extra-button option is tolerated',
91
+ function ()
92
+ {
93
+ let tmpStub = { options: { FlowViewIdentifier: 'Pict-Flow', ToolbarExtraButtons: false } };
94
+ libExpect(function () { libPictViewFlowToolbar.prototype._stampExtraButtons.call(tmpStub); }).to.not.throw();
95
+ });
96
+ });
97
+
98
+ suite('click dispatch fires onToolbarButton(hash, element)',
99
+ function ()
100
+ {
101
+ test('the docked toolbar routes a click to the FlowView hook with the element',
102
+ function ()
103
+ {
104
+ let tmpFired = [];
105
+ let tmpElement = { id: 'the-button' };
106
+ let tmpStub =
107
+ {
108
+ _FlowView: { options: { onToolbarButton: (pHash, pEl) => { tmpFired.push({ Hash: pHash, El: pEl }); } } }
109
+ };
110
+ libPictViewFlowToolbar.prototype._handleExtraAction.call(tmpStub, 'background', tmpElement);
111
+ libExpect(tmpFired.length).to.equal(1);
112
+ libExpect(tmpFired[0].Hash).to.equal('background');
113
+ libExpect(tmpFired[0].El).to.equal(tmpElement);
114
+ });
115
+
116
+ test('no hook configured is a no-op (does not throw)',
117
+ function ()
118
+ {
119
+ let tmpStub = { _FlowView: { options: { onToolbarButton: false } } };
120
+ libExpect(function () { libPictViewFlowToolbar.prototype._handleExtraAction.call(tmpStub, 'edit', {}); }).to.not.throw();
121
+ });
122
+
123
+ test('the floating toolbar routes its click through the docked toolbar dispatch',
124
+ function ()
125
+ {
126
+ let tmpSeen = [];
127
+ let tmpElement = { id: 'floating-button' };
128
+ let tmpFloatStub =
129
+ {
130
+ _ToolbarView: { _handleExtraAction: (pHash, pEl) => { tmpSeen.push({ Hash: pHash, El: pEl }); } }
131
+ };
132
+ libPictViewFlowFloatingToolbar.prototype._handleExtraClick.call(tmpFloatStub, 'done', tmpElement);
133
+ libExpect(tmpSeen.length).to.equal(1);
134
+ libExpect(tmpSeen[0].Hash).to.equal('done');
135
+ libExpect(tmpSeen[0].El).to.equal(tmpElement);
136
+ });
137
+ });
138
+ });
@@ -0,0 +1,70 @@
1
+ const libFable = require('fable');
2
+ const libChai = require('chai');
3
+ const libExpect = libChai.expect;
4
+
5
+ const libInteractionManager = require('../source/services/PictService-Flow-InteractionManager.js');
6
+ const STATES = libInteractionManager.INTERACTION_STATES;
7
+
8
+ // EnableUndirectedConnections lets a connection be drawn between ANY two ports rather than only
9
+ // output -> input. A free-form canvas (a moodboard) turns it on so a card's ports connect in any
10
+ // direction; directed graphs (workflows) leave it off. These tests cover the start side of the drag
11
+ // (the gate that previously hard-required an output port). The completion side uses
12
+ // document.elementFromPoint, so it is exercised in the browser rather than here.
13
+ function makeFlowView(pUndirected)
14
+ {
15
+ return {
16
+ options: { EnableConnectionCreation: true, EnableUndirectedConnections: pUndirected },
17
+ // Returning null skips the drag-line DOM creation, so no document is needed.
18
+ getPortPosition: function () { return null; },
19
+ _ViewportElement: { appendChild: function () {} }
20
+ };
21
+ }
22
+
23
+ function makeManager(pFable, pFlowView)
24
+ {
25
+ let tmpManager = new libInteractionManager(pFable, { FlowView: pFlowView }, 'IM-Test');
26
+ tmpManager._SVGElement = { classList: { add: function () {}, remove: function () {} } };
27
+ return tmpManager;
28
+ }
29
+
30
+ function makePort(pNodeHash, pPortHash, pDirection)
31
+ {
32
+ let tmpAttrs = { 'data-node-hash': pNodeHash, 'data-port-hash': pPortHash, 'data-port-direction': pDirection };
33
+ return { getAttribute: function (pName) { return Object.prototype.hasOwnProperty.call(tmpAttrs, pName) ? tmpAttrs[pName] : null; } };
34
+ }
35
+
36
+ suite('Flow undirected connections (EnableUndirectedConnections)',
37
+ function ()
38
+ {
39
+ let _Fable;
40
+ setup(function () { _Fable = new libFable({}); });
41
+
42
+ test('directed mode (default): an input port does NOT start a connection',
43
+ function ()
44
+ {
45
+ let tmpManager = makeManager(_Fable, makeFlowView(false));
46
+ tmpManager._startConnection({ stopPropagation: function () {} }, makePort('n1', 'p1', 'input'));
47
+ libExpect(tmpManager._State).to.not.equal(STATES.CONNECTING);
48
+ });
49
+
50
+ test('undirected mode: an input port DOES start a connection',
51
+ function ()
52
+ {
53
+ let tmpManager = makeManager(_Fable, makeFlowView(true));
54
+ tmpManager._startConnection({ stopPropagation: function () {} }, makePort('n1', 'p1', 'input'));
55
+ libExpect(tmpManager._State).to.equal(STATES.CONNECTING);
56
+ libExpect(tmpManager._ConnectSourceNodeHash).to.equal('n1');
57
+ libExpect(tmpManager._ConnectSourcePortHash).to.equal('p1');
58
+ });
59
+
60
+ test('an output port starts a connection in either mode',
61
+ function ()
62
+ {
63
+ [ false, true ].forEach(function (pUndirected)
64
+ {
65
+ let tmpManager = makeManager(_Fable, makeFlowView(pUndirected));
66
+ tmpManager._startConnection({ stopPropagation: function () {} }, makePort('n1', 'p1', 'output'));
67
+ libExpect(tmpManager._State).to.equal(STATES.CONNECTING);
68
+ });
69
+ });
70
+ });