@nyaruka/temba-components 0.138.4 → 0.139.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.
- package/CHANGELOG.md +15 -0
- package/dist/locales/es.js +5 -5
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fr.js +5 -5
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/locale-codes.js +2 -11
- package/dist/locales/locale-codes.js.map +1 -1
- package/dist/locales/pt.js +5 -5
- package/dist/locales/pt.js.map +1 -1
- package/dist/temba-components.js +816 -852
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +23 -30
- package/out-tsc/src/display/FloatingTab.js.map +1 -1
- package/out-tsc/src/flow/CanvasMenu.js +5 -3
- package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +6 -7
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +152 -235
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/Plumber.js +757 -403
- package/out-tsc/src/flow/Plumber.js.map +1 -1
- package/out-tsc/src/flow/utils.js +138 -66
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/interfaces.js +1 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/list/TicketList.js +4 -1
- package/out-tsc/src/list/TicketList.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +18 -1
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/locales/es.js +5 -5
- package/out-tsc/src/locales/es.js.map +1 -1
- package/out-tsc/src/locales/fr.js +5 -5
- package/out-tsc/src/locales/fr.js.map +1 -1
- package/out-tsc/src/locales/locale-codes.js +2 -11
- package/out-tsc/src/locales/locale-codes.js.map +1 -1
- package/out-tsc/src/locales/pt.js +5 -5
- package/out-tsc/src/locales/pt.js.map +1 -1
- package/out-tsc/src/simulator/Simulator.js +1 -0
- package/out-tsc/src/simulator/Simulator.js.map +1 -1
- package/out-tsc/test/temba-floating-tab.test.js +4 -6
- package/out-tsc/test/temba-floating-tab.test.js.map +1 -1
- package/out-tsc/test/temba-flow-collision.test.js +221 -223
- package/out-tsc/test/temba-flow-collision.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor.test.js +0 -2
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
- package/out-tsc/test/temba-flow-plumber-connections.test.js +83 -84
- package/out-tsc/test/temba-flow-plumber-connections.test.js.map +1 -1
- package/out-tsc/test/temba-flow-plumber.test.js +102 -93
- package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
- package/package.json +1 -1
- package/src/display/FloatingTab.ts +22 -31
- package/src/flow/CanvasMenu.ts +8 -3
- package/src/flow/CanvasNode.ts +6 -7
- package/src/flow/Editor.ts +184 -279
- package/src/flow/Plumber.ts +1011 -457
- package/src/flow/utils.ts +162 -84
- package/src/interfaces.ts +2 -1
- package/src/list/TicketList.ts +4 -1
- package/src/live/ContactChat.ts +19 -1
- package/src/locales/es.ts +13 -18
- package/src/locales/fr.ts +13 -18
- package/src/locales/locale-codes.ts +2 -11
- package/src/locales/pt.ts +13 -18
- package/src/simulator/Simulator.ts +1 -0
- package/test/temba-floating-tab.test.ts +4 -6
- package/test/temba-flow-collision.test.ts +225 -303
- package/test/temba-flow-editor.test.ts +0 -2
- package/test/temba-flow-plumber-connections.test.ts +97 -97
- package/test/temba-flow-plumber.test.ts +116 -103
|
@@ -6,115 +6,114 @@ describe('Plumber - Connection Management', () => {
|
|
|
6
6
|
let mockCanvas;
|
|
7
7
|
let clock;
|
|
8
8
|
beforeEach(() => {
|
|
9
|
-
// Use fake timers to control setTimeout
|
|
10
9
|
clock = useFakeTimers();
|
|
11
|
-
// Create mock canvas and make getElementById return a mock element
|
|
12
10
|
mockCanvas = document.createElement('div');
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// Create a mock editor with fireCustomEvent
|
|
11
|
+
mockCanvas.id = 'canvas';
|
|
12
|
+
document.body.appendChild(mockCanvas);
|
|
16
13
|
const mockEditor = { fireCustomEvent: stub() };
|
|
17
|
-
// Create a new plumber instance
|
|
18
14
|
plumber = new Plumber(mockCanvas, mockEditor);
|
|
19
|
-
// Replace the internal jsPlumb instance with mocks
|
|
20
|
-
plumber.jsPlumb = {
|
|
21
|
-
getConnections: stub().returns([]),
|
|
22
|
-
addClass: stub(),
|
|
23
|
-
removeClass: stub(),
|
|
24
|
-
batch: stub().callsFake((fn) => fn()),
|
|
25
|
-
addEndpoint: stub().returns({}),
|
|
26
|
-
revalidate: stub(),
|
|
27
|
-
connect: stub(),
|
|
28
|
-
getEndpoints: stub().returns([
|
|
29
|
-
{ elementId: 'test-from', addClass: stub() }
|
|
30
|
-
]),
|
|
31
|
-
select: stub().returns({
|
|
32
|
-
deleteAll: stub()
|
|
33
|
-
}),
|
|
34
|
-
selectEndpoints: stub().returns({
|
|
35
|
-
deleteAll: stub()
|
|
36
|
-
}),
|
|
37
|
-
deleteConnection: stub(),
|
|
38
|
-
removeAllEndpoints: stub(),
|
|
39
|
-
repaintEverything: stub()
|
|
40
|
-
};
|
|
41
15
|
});
|
|
42
16
|
afterEach(() => {
|
|
43
|
-
|
|
44
|
-
// Restore the original document.getElementById
|
|
45
|
-
(_b = (_a = document.getElementById).restore) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
17
|
+
mockCanvas.remove();
|
|
46
18
|
clock.restore();
|
|
47
19
|
});
|
|
48
20
|
describe('setConnectionRemovingState', () => {
|
|
49
|
-
it('returns false when no
|
|
50
|
-
const result = plumber.setConnectionRemovingState('
|
|
21
|
+
it('returns false when no connection exists for the exit', () => {
|
|
22
|
+
const result = plumber.setConnectionRemovingState('nonexistent', true);
|
|
51
23
|
expect(result).to.be.false;
|
|
52
|
-
expect(plumber.jsPlumb.getConnections).to.have.been.called;
|
|
53
24
|
});
|
|
54
|
-
it('
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
25
|
+
it('adds removing class when isRemoving is true', () => {
|
|
26
|
+
// Create mock elements for a connection
|
|
27
|
+
const exitEl = document.createElement('div');
|
|
28
|
+
exitEl.id = 'exit-1';
|
|
29
|
+
const targetEl = document.createElement('div');
|
|
30
|
+
targetEl.id = 'target-1';
|
|
31
|
+
mockCanvas.appendChild(exitEl);
|
|
32
|
+
mockCanvas.appendChild(targetEl);
|
|
33
|
+
// Create a connection
|
|
34
|
+
plumber.connectIds('node-1', 'exit-1', 'target-1');
|
|
35
|
+
clock.tick(16);
|
|
36
|
+
const result = plumber.setConnectionRemovingState('exit-1', true);
|
|
61
37
|
expect(result).to.be.true;
|
|
62
|
-
|
|
63
|
-
expect(
|
|
38
|
+
const conn = plumber.connections.get('exit-1');
|
|
39
|
+
expect(conn.svgEl.classList.contains('removing')).to.be.true;
|
|
40
|
+
exitEl.remove();
|
|
41
|
+
targetEl.remove();
|
|
64
42
|
});
|
|
65
|
-
it('removes removing class
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
43
|
+
it('removes removing class when isRemoving is false', () => {
|
|
44
|
+
const exitEl = document.createElement('div');
|
|
45
|
+
exitEl.id = 'exit-2';
|
|
46
|
+
const targetEl = document.createElement('div');
|
|
47
|
+
targetEl.id = 'target-2';
|
|
48
|
+
mockCanvas.appendChild(exitEl);
|
|
49
|
+
mockCanvas.appendChild(targetEl);
|
|
50
|
+
plumber.connectIds('node-1', 'exit-2', 'target-2');
|
|
51
|
+
clock.tick(16);
|
|
52
|
+
plumber.setConnectionRemovingState('exit-2', true);
|
|
53
|
+
plumber.setConnectionRemovingState('exit-2', false);
|
|
54
|
+
const conn = plumber.connections.get('exit-2');
|
|
55
|
+
expect(conn.svgEl.classList.contains('removing')).to.be.false;
|
|
56
|
+
exitEl.remove();
|
|
57
|
+
targetEl.remove();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('removeExitConnection', () => {
|
|
61
|
+
it('removes a connection for an exit', () => {
|
|
62
|
+
const exitEl = document.createElement('div');
|
|
63
|
+
exitEl.id = 'exit-3';
|
|
64
|
+
const targetEl = document.createElement('div');
|
|
65
|
+
targetEl.id = 'target-3';
|
|
66
|
+
mockCanvas.appendChild(exitEl);
|
|
67
|
+
mockCanvas.appendChild(targetEl);
|
|
68
|
+
plumber.connectIds('node-1', 'exit-3', 'target-3');
|
|
69
|
+
clock.tick(16);
|
|
70
|
+
expect(plumber.connections.has('exit-3')).to.be.true;
|
|
71
|
+
const result = plumber.removeExitConnection('exit-3');
|
|
72
72
|
expect(result).to.be.true;
|
|
73
|
-
expect(
|
|
74
|
-
|
|
73
|
+
expect(plumber.connections.has('exit-3')).to.be.false;
|
|
74
|
+
exitEl.remove();
|
|
75
|
+
targetEl.remove();
|
|
76
|
+
});
|
|
77
|
+
it('returns false when no connection exists', () => {
|
|
78
|
+
const result = plumber.removeExitConnection('nonexistent');
|
|
79
|
+
expect(result).to.be.false;
|
|
75
80
|
});
|
|
76
81
|
});
|
|
77
82
|
describe('connectIds and processPendingConnections', () => {
|
|
78
83
|
it('adds connection to pending connections', () => {
|
|
79
|
-
// Call connectIds which should add to pending connections
|
|
80
84
|
plumber.connectIds('test-node', 'test-from', 'test-to');
|
|
81
|
-
// Verify pendingConnections has the new connection
|
|
82
85
|
expect(plumber.pendingConnections.length).to.equal(1);
|
|
83
|
-
// Advance timer to trigger the timeout
|
|
84
|
-
clock.tick(51); // Just past the 50ms timeout
|
|
85
|
-
// Now the batch should have been called
|
|
86
|
-
expect(plumber.jsPlumb.batch).to.have.been.called;
|
|
87
|
-
expect(plumber.jsPlumb.addEndpoint).to.have.been.called;
|
|
88
|
-
expect(plumber.jsPlumb.connect).to.have.been.called;
|
|
89
86
|
});
|
|
90
|
-
it('clears previous
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
const setTimeoutSpy = stub(window, 'setTimeout').returns(123);
|
|
94
|
-
// Call twice
|
|
87
|
+
it('clears previous rAF when called multiple times', () => {
|
|
88
|
+
const cancelSpy = stub(window, 'cancelAnimationFrame');
|
|
89
|
+
const rafSpy = stub(window, 'requestAnimationFrame').returns(123);
|
|
95
90
|
plumber.processPendingConnections();
|
|
96
91
|
plumber.processPendingConnections();
|
|
97
|
-
|
|
98
|
-
expect(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
clearTimeoutSpy.restore();
|
|
102
|
-
setTimeoutSpy.restore();
|
|
92
|
+
expect(cancelSpy).to.have.been.calledOnce;
|
|
93
|
+
expect(rafSpy).to.have.been.calledTwice;
|
|
94
|
+
cancelSpy.restore();
|
|
95
|
+
rafSpy.restore();
|
|
103
96
|
});
|
|
104
97
|
});
|
|
105
|
-
describe('
|
|
106
|
-
it('removes connections for
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
98
|
+
describe('removeNodeConnections', () => {
|
|
99
|
+
it('removes inbound and outbound connections for a node', () => {
|
|
100
|
+
const exitEl = document.createElement('div');
|
|
101
|
+
exitEl.id = 'exit-4';
|
|
102
|
+
exitEl.classList.add('exit');
|
|
103
|
+
const nodeEl = document.createElement('div');
|
|
104
|
+
nodeEl.id = 'node-1';
|
|
105
|
+
nodeEl.appendChild(exitEl);
|
|
106
|
+
const targetEl = document.createElement('div');
|
|
107
|
+
targetEl.id = 'target-4';
|
|
108
|
+
mockCanvas.appendChild(nodeEl);
|
|
109
|
+
mockCanvas.appendChild(targetEl);
|
|
110
|
+
plumber.connectIds('node-1', 'exit-4', 'target-4');
|
|
111
|
+
clock.tick(16);
|
|
112
|
+
expect(plumber.connections.size).to.equal(1);
|
|
113
|
+
plumber.removeNodeConnections('node-1', ['exit-4']);
|
|
114
|
+
expect(plumber.connections.size).to.equal(0);
|
|
115
|
+
nodeEl.remove();
|
|
116
|
+
targetEl.remove();
|
|
118
117
|
});
|
|
119
118
|
});
|
|
120
119
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temba-flow-plumber-connections.test.js","sourceRoot":"","sources":["../../test/temba-flow-plumber-connections.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { stub, useFakeTimers, SinonFakeTimers } from 'sinon';\nimport { Plumber } from '../src/flow/Plumber';\n\ndescribe('Plumber - Connection Management', () => {\n let plumber: Plumber;\n let mockCanvas: HTMLElement;\n let clock: SinonFakeTimers;\n\n beforeEach(() => {\n // Use fake timers to control setTimeout\n clock = useFakeTimers();\n\n // Create mock canvas and make getElementById return a mock element\n mockCanvas = document.createElement('div');\n const mockElement = document.createElement('div');\n stub(document, 'getElementById').returns(mockElement);\n\n // Create a mock editor with fireCustomEvent\n const mockEditor = { fireCustomEvent: stub() };\n\n // Create a new plumber instance\n plumber = new Plumber(mockCanvas, mockEditor);\n\n // Replace the internal jsPlumb instance with mocks\n (plumber as any).jsPlumb = {\n getConnections: stub().returns([]),\n addClass: stub(),\n removeClass: stub(),\n batch: stub().callsFake((fn: any) => fn()),\n addEndpoint: stub().returns({}),\n revalidate: stub(),\n connect: stub(),\n getEndpoints: stub().returns([\n { elementId: 'test-from', addClass: stub() }\n ]),\n select: stub().returns({\n deleteAll: stub()\n }),\n selectEndpoints: stub().returns({\n deleteAll: stub()\n }),\n deleteConnection: stub(),\n removeAllEndpoints: stub(),\n repaintEverything: stub()\n };\n });\n\n afterEach(() => {\n // Restore the original document.getElementById\n (document.getElementById as any).restore?.();\n clock.restore();\n });\n\n describe('setConnectionRemovingState', () => {\n it('returns false when no connections are found', () => {\n const result = plumber.setConnectionRemovingState('test-exit', true);\n expect(result).to.be.false;\n expect((plumber as any).jsPlumb.getConnections).to.have.been.called;\n });\n\n it('sets removing class on connections when isRemoving is true', () => {\n const mockConnections = [\n { id: 'conn1', addClass: stub() },\n { id: 'conn2', addClass: stub() }\n ];\n\n (plumber as any).jsPlumb.getConnections = stub().returns(mockConnections);\n\n const result = plumber.setConnectionRemovingState('test-exit', true);\n expect(result).to.be.true;\n expect(mockConnections[0].addClass).to.have.been.calledWith('removing');\n expect(mockConnections[1].addClass).to.have.been.calledWith('removing');\n });\n\n it('removes removing class from connections when isRemoving is false', () => {\n const mockConnections = [\n { id: 'conn1', removeClass: stub() },\n { id: 'conn2', removeClass: stub() }\n ];\n\n (plumber as any).jsPlumb.getConnections = stub().returns(mockConnections);\n\n const result = plumber.setConnectionRemovingState('test-exit', false);\n expect(result).to.be.true;\n expect(mockConnections[0].removeClass).to.have.been.calledWith(\n 'removing'\n );\n expect(mockConnections[1].removeClass).to.have.been.calledWith(\n 'removing'\n );\n });\n });\n\n describe('connectIds and processPendingConnections', () => {\n it('adds connection to pending connections', () => {\n // Call connectIds which should add to pending connections\n plumber.connectIds('test-node', 'test-from', 'test-to');\n\n // Verify pendingConnections has the new connection\n expect((plumber as any).pendingConnections.length).to.equal(1);\n\n // Advance timer to trigger the timeout\n clock.tick(51); // Just past the 50ms timeout\n\n // Now the batch should have been called\n expect((plumber as any).jsPlumb.batch).to.have.been.called;\n expect((plumber as any).jsPlumb.addEndpoint).to.have.been.called;\n expect((plumber as any).jsPlumb.connect).to.have.been.called;\n });\n\n it('clears previous timeout when called multiple times', () => {\n // Set up spies for window.setTimeout and window.clearTimeout instead of global\n const clearTimeoutSpy = stub(window, 'clearTimeout');\n const setTimeoutSpy = stub(window, 'setTimeout').returns(123 as any);\n\n // Call twice\n plumber.processPendingConnections();\n plumber.processPendingConnections();\n\n // Should have called clearTimeout once and setTimeout twice\n expect(clearTimeoutSpy).to.have.been.calledOnce;\n expect(setTimeoutSpy).to.have.been.calledTwice;\n\n // Clean up\n clearTimeoutSpy.restore();\n setTimeoutSpy.restore();\n });\n });\n\n describe('removeExitConnection', () => {\n it('removes connections for an exit', () => {\n const mockConnections = [{ id: 'conn1' }, { id: 'conn2' }];\n (plumber as any).jsPlumb.getConnections = stub().returns(mockConnections);\n\n const result = plumber.removeExitConnection('test-exit');\n\n expect(result).to.be.true;\n expect((plumber as any).jsPlumb.deleteConnection).to.have.been\n .calledTwice;\n });\n\n it('returns false when no connections exist', () => {\n (plumber as any).jsPlumb.getConnections = stub().returns([]);\n\n const result = plumber.removeExitConnection('test-exit');\n\n expect(result).to.be.false;\n });\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"temba-flow-plumber-connections.test.js","sourceRoot":"","sources":["../../test/temba-flow-plumber-connections.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { stub, useFakeTimers, SinonFakeTimers } from 'sinon';\nimport { Plumber } from '../src/flow/Plumber';\n\ndescribe('Plumber - Connection Management', () => {\n let plumber: Plumber;\n let mockCanvas: HTMLElement;\n let clock: SinonFakeTimers;\n\n beforeEach(() => {\n clock = useFakeTimers();\n\n mockCanvas = document.createElement('div');\n mockCanvas.id = 'canvas';\n document.body.appendChild(mockCanvas);\n\n const mockEditor = { fireCustomEvent: stub() };\n plumber = new Plumber(mockCanvas, mockEditor);\n });\n\n afterEach(() => {\n mockCanvas.remove();\n clock.restore();\n });\n\n describe('setConnectionRemovingState', () => {\n it('returns false when no connection exists for the exit', () => {\n const result = plumber.setConnectionRemovingState('nonexistent', true);\n expect(result).to.be.false;\n });\n\n it('adds removing class when isRemoving is true', () => {\n // Create mock elements for a connection\n const exitEl = document.createElement('div');\n exitEl.id = 'exit-1';\n const targetEl = document.createElement('div');\n targetEl.id = 'target-1';\n mockCanvas.appendChild(exitEl);\n mockCanvas.appendChild(targetEl);\n\n // Create a connection\n plumber.connectIds('node-1', 'exit-1', 'target-1');\n clock.tick(16);\n\n const result = plumber.setConnectionRemovingState('exit-1', true);\n expect(result).to.be.true;\n\n const conn = (plumber as any).connections.get('exit-1');\n expect(conn.svgEl.classList.contains('removing')).to.be.true;\n\n exitEl.remove();\n targetEl.remove();\n });\n\n it('removes removing class when isRemoving is false', () => {\n const exitEl = document.createElement('div');\n exitEl.id = 'exit-2';\n const targetEl = document.createElement('div');\n targetEl.id = 'target-2';\n mockCanvas.appendChild(exitEl);\n mockCanvas.appendChild(targetEl);\n\n plumber.connectIds('node-1', 'exit-2', 'target-2');\n clock.tick(16);\n\n plumber.setConnectionRemovingState('exit-2', true);\n plumber.setConnectionRemovingState('exit-2', false);\n\n const conn = (plumber as any).connections.get('exit-2');\n expect(conn.svgEl.classList.contains('removing')).to.be.false;\n\n exitEl.remove();\n targetEl.remove();\n });\n });\n\n describe('removeExitConnection', () => {\n it('removes a connection for an exit', () => {\n const exitEl = document.createElement('div');\n exitEl.id = 'exit-3';\n const targetEl = document.createElement('div');\n targetEl.id = 'target-3';\n mockCanvas.appendChild(exitEl);\n mockCanvas.appendChild(targetEl);\n\n plumber.connectIds('node-1', 'exit-3', 'target-3');\n clock.tick(16);\n\n expect((plumber as any).connections.has('exit-3')).to.be.true;\n\n const result = plumber.removeExitConnection('exit-3');\n expect(result).to.be.true;\n expect((plumber as any).connections.has('exit-3')).to.be.false;\n\n exitEl.remove();\n targetEl.remove();\n });\n\n it('returns false when no connection exists', () => {\n const result = plumber.removeExitConnection('nonexistent');\n expect(result).to.be.false;\n });\n });\n\n describe('connectIds and processPendingConnections', () => {\n it('adds connection to pending connections', () => {\n plumber.connectIds('test-node', 'test-from', 'test-to');\n expect((plumber as any).pendingConnections.length).to.equal(1);\n });\n\n it('clears previous rAF when called multiple times', () => {\n const cancelSpy = stub(window, 'cancelAnimationFrame');\n const rafSpy = stub(window, 'requestAnimationFrame').returns(123 as any);\n\n plumber.processPendingConnections();\n plumber.processPendingConnections();\n\n expect(cancelSpy).to.have.been.calledOnce;\n expect(rafSpy).to.have.been.calledTwice;\n\n cancelSpy.restore();\n rafSpy.restore();\n });\n });\n\n describe('removeNodeConnections', () => {\n it('removes inbound and outbound connections for a node', () => {\n const exitEl = document.createElement('div');\n exitEl.id = 'exit-4';\n exitEl.classList.add('exit');\n const nodeEl = document.createElement('div');\n nodeEl.id = 'node-1';\n nodeEl.appendChild(exitEl);\n const targetEl = document.createElement('div');\n targetEl.id = 'target-4';\n mockCanvas.appendChild(nodeEl);\n mockCanvas.appendChild(targetEl);\n\n plumber.connectIds('node-1', 'exit-4', 'target-4');\n clock.tick(16);\n\n expect((plumber as any).connections.size).to.equal(1);\n\n plumber.removeNodeConnections('node-1', ['exit-4']);\n expect((plumber as any).connections.size).to.equal(0);\n\n nodeEl.remove();\n targetEl.remove();\n });\n });\n});\n"]}
|
|
@@ -1,53 +1,24 @@
|
|
|
1
1
|
import { expect } from '@open-wc/testing';
|
|
2
|
-
import { Plumber,
|
|
2
|
+
import { Plumber, calculateFlowchartPath } from '../src/flow/Plumber';
|
|
3
3
|
import { stub, useFakeTimers } from 'sinon';
|
|
4
4
|
describe('Plumber', () => {
|
|
5
5
|
let plumber;
|
|
6
|
-
let mockJsPlumb;
|
|
7
6
|
let mockCanvas;
|
|
8
7
|
let clock;
|
|
8
|
+
let mockElement;
|
|
9
9
|
beforeEach(() => {
|
|
10
|
-
// Use fake timers to control setTimeout
|
|
11
10
|
clock = useFakeTimers();
|
|
12
|
-
// Create mock canvas and make getElementById return a mock element
|
|
13
11
|
mockCanvas = document.createElement('div');
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
mockCanvas.id = 'canvas';
|
|
13
|
+
document.body.appendChild(mockCanvas);
|
|
14
|
+
mockElement = document.createElement('div');
|
|
15
|
+
mockElement.id = 'test-exit';
|
|
16
|
+
mockCanvas.appendChild(mockElement);
|
|
17
17
|
const mockEditor = { fireCustomEvent: stub() };
|
|
18
|
-
// Create a new plumber instance
|
|
19
18
|
plumber = new Plumber(mockCanvas, mockEditor);
|
|
20
|
-
// Replace the internal jsPlumb instance with mocks
|
|
21
|
-
mockJsPlumb = {
|
|
22
|
-
getConnections: stub().returns([]),
|
|
23
|
-
addClass: stub(),
|
|
24
|
-
removeClass: stub(),
|
|
25
|
-
batch: stub().callsFake((fn) => fn()),
|
|
26
|
-
addEndpoint: stub().returns({}),
|
|
27
|
-
connect: stub(),
|
|
28
|
-
getEndpoints: stub().returns([
|
|
29
|
-
{ elementId: 'test-from', addClass: stub() }
|
|
30
|
-
]),
|
|
31
|
-
select: stub().returns({
|
|
32
|
-
deleteAll: stub()
|
|
33
|
-
}),
|
|
34
|
-
selectEndpoints: stub().returns({
|
|
35
|
-
deleteAll: stub()
|
|
36
|
-
}),
|
|
37
|
-
deleteConnection: stub(),
|
|
38
|
-
removeAllEndpoints: stub(),
|
|
39
|
-
repaintEverything: stub(),
|
|
40
|
-
revalidate: stub(),
|
|
41
|
-
bind: stub()
|
|
42
|
-
};
|
|
43
|
-
plumber.jsPlumb = mockJsPlumb;
|
|
44
|
-
// Reset the connectionWait to avoid timing issues
|
|
45
|
-
plumber.connectionWait = null;
|
|
46
19
|
});
|
|
47
20
|
afterEach(() => {
|
|
48
|
-
|
|
49
|
-
// Restore the original document.getElementById
|
|
50
|
-
(_b = (_a = document.getElementById).restore) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
21
|
+
mockCanvas.remove();
|
|
51
22
|
clock.restore();
|
|
52
23
|
});
|
|
53
24
|
describe('constructor', () => {
|
|
@@ -55,79 +26,117 @@ describe('Plumber', () => {
|
|
|
55
26
|
expect(plumber).to.be.instanceOf(Plumber);
|
|
56
27
|
});
|
|
57
28
|
});
|
|
58
|
-
describe('
|
|
59
|
-
it('
|
|
60
|
-
|
|
61
|
-
|
|
29
|
+
describe('makeSource', () => {
|
|
30
|
+
it('registers a mousedown listener on the exit element', () => {
|
|
31
|
+
const exitEl = document.createElement('div');
|
|
32
|
+
exitEl.id = 'exit-1';
|
|
33
|
+
mockCanvas.appendChild(exitEl);
|
|
34
|
+
plumber.makeSource('exit-1');
|
|
35
|
+
// Source should be tracked
|
|
36
|
+
expect(plumber.sources.has('exit-1')).to.be.true;
|
|
37
|
+
exitEl.remove();
|
|
38
|
+
});
|
|
39
|
+
it('cleans up previous listener when called again', () => {
|
|
40
|
+
const exitEl = document.createElement('div');
|
|
41
|
+
exitEl.id = 'exit-2';
|
|
42
|
+
mockCanvas.appendChild(exitEl);
|
|
43
|
+
plumber.makeSource('exit-2');
|
|
44
|
+
plumber.makeSource('exit-2');
|
|
45
|
+
expect(plumber.sources.has('exit-2')).to.be.true;
|
|
46
|
+
exitEl.remove();
|
|
62
47
|
});
|
|
63
48
|
});
|
|
64
|
-
describe('
|
|
65
|
-
it('
|
|
66
|
-
|
|
67
|
-
|
|
49
|
+
describe('makeTarget', () => {
|
|
50
|
+
it('is a no-op', () => {
|
|
51
|
+
// Should not throw
|
|
52
|
+
plumber.makeTarget('test-node');
|
|
68
53
|
});
|
|
69
54
|
});
|
|
70
55
|
describe('connectIds', () => {
|
|
71
56
|
it('adds connection to pending connections and processes them', () => {
|
|
72
57
|
plumber.connectIds('test-node', 'test-from', 'test-to');
|
|
73
|
-
// Verify pendingConnections has the new connection
|
|
74
58
|
expect(plumber.pendingConnections.length).to.equal(1);
|
|
75
|
-
// Advance timer to trigger the timeout
|
|
76
|
-
clock.tick(51); // Just past the 50ms timeout
|
|
77
|
-
// Now the batch should have been called
|
|
78
|
-
expect(mockJsPlumb.batch).to.have.been.called;
|
|
79
59
|
});
|
|
80
60
|
});
|
|
81
61
|
describe('processPendingConnections', () => {
|
|
82
|
-
it('
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// Fast-forward clock past the timeout
|
|
86
|
-
clock.tick(51); // Just past the 50ms timeout
|
|
87
|
-
expect(mockJsPlumb.batch).to.have.been.called;
|
|
88
|
-
});
|
|
89
|
-
it('creates endpoints and connections for pending connections', () => {
|
|
90
|
-
plumber.connectIds('test-node', 'test-from', 'test-to');
|
|
91
|
-
// Fast-forward clock past the timeout
|
|
92
|
-
clock.tick(51); // Just past the 50ms timeout
|
|
93
|
-
expect(mockJsPlumb.addEndpoint).to.have.been.called;
|
|
94
|
-
expect(mockJsPlumb.connect).to.have.been.called;
|
|
95
|
-
});
|
|
96
|
-
it('clears existing timeout when called multiple times', () => {
|
|
97
|
-
// Set up spies for window.setTimeout and window.clearTimeout
|
|
98
|
-
const clearTimeoutSpy = stub(window, 'clearTimeout');
|
|
99
|
-
const setTimeoutSpy = stub(window, 'setTimeout').returns(123);
|
|
100
|
-
// Call twice
|
|
62
|
+
it('clears existing rAF when called multiple times', () => {
|
|
63
|
+
const cancelSpy = stub(window, 'cancelAnimationFrame');
|
|
64
|
+
const rafSpy = stub(window, 'requestAnimationFrame').returns(123);
|
|
101
65
|
plumber.processPendingConnections();
|
|
102
66
|
plumber.processPendingConnections();
|
|
103
|
-
|
|
104
|
-
expect(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
clearTimeoutSpy.restore();
|
|
108
|
-
setTimeoutSpy.restore();
|
|
109
|
-
});
|
|
110
|
-
it('handles empty pending connections', () => {
|
|
111
|
-
// Call without adding any connections
|
|
112
|
-
plumber.processPendingConnections();
|
|
113
|
-
// Fast-forward clock past the timeout
|
|
114
|
-
clock.tick(51); // Just past the 50ms timeout
|
|
115
|
-
expect(mockJsPlumb.batch).to.have.been.called;
|
|
67
|
+
expect(cancelSpy).to.have.been.calledOnce;
|
|
68
|
+
expect(rafSpy).to.have.been.calledTwice;
|
|
69
|
+
cancelSpy.restore();
|
|
70
|
+
rafSpy.restore();
|
|
116
71
|
});
|
|
117
72
|
});
|
|
118
|
-
describe('
|
|
119
|
-
it('
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
73
|
+
describe('event system', () => {
|
|
74
|
+
it('supports on/off/notify pattern', () => {
|
|
75
|
+
let received = null;
|
|
76
|
+
const handler = (info) => {
|
|
77
|
+
received = info;
|
|
78
|
+
};
|
|
79
|
+
plumber.on('test-event', handler);
|
|
80
|
+
plumber.notifyListeners('test-event', { data: 'test' });
|
|
81
|
+
expect(received).to.deep.equal({ data: 'test' });
|
|
82
|
+
received = null;
|
|
83
|
+
plumber.off('test-event', handler);
|
|
84
|
+
plumber.notifyListeners('test-event', { data: 'test2' });
|
|
85
|
+
expect(received).to.be.null;
|
|
124
86
|
});
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
87
|
+
});
|
|
88
|
+
describe('reset', () => {
|
|
89
|
+
it('clears all state', () => {
|
|
90
|
+
plumber.connectIds('test-node', 'test-from', 'test-to');
|
|
91
|
+
plumber.reset();
|
|
92
|
+
expect(plumber.pendingConnections.length).to.equal(0);
|
|
93
|
+
expect(plumber.connections.size).to.equal(0);
|
|
94
|
+
expect(plumber.sources.size).to.equal(0);
|
|
130
95
|
});
|
|
131
96
|
});
|
|
132
97
|
});
|
|
98
|
+
describe('calculateFlowchartPath', () => {
|
|
99
|
+
it('generates a straight vertical path when source and target are aligned', () => {
|
|
100
|
+
const path = calculateFlowchartPath(100, 0, 100, 100);
|
|
101
|
+
expect(path).to.include('M 100 0');
|
|
102
|
+
expect(path).to.include('L 100 100');
|
|
103
|
+
// Should not contain Q (quadratic curve) for aligned points
|
|
104
|
+
expect(path).to.not.include('Q');
|
|
105
|
+
});
|
|
106
|
+
it('generates a path with corners when source and target are offset', () => {
|
|
107
|
+
const path = calculateFlowchartPath(50, 0, 150, 200);
|
|
108
|
+
expect(path).to.include('M 50 0');
|
|
109
|
+
expect(path).to.include('Q'); // Should have rounded corners
|
|
110
|
+
expect(path).to.include('L 150 200');
|
|
111
|
+
});
|
|
112
|
+
it('handles custom stub and corner radius', () => {
|
|
113
|
+
const path = calculateFlowchartPath(0, 0, 100, 100, 30, 15, 10);
|
|
114
|
+
expect(path).to.include('M 0 0');
|
|
115
|
+
expect(path).to.include('L 100 100');
|
|
116
|
+
});
|
|
117
|
+
it('handles cases where vertical space is tight by using reduced-radius corners', () => {
|
|
118
|
+
// With stubs of 20+10=30, and only 35 total vertical space, there's only 5px for corners
|
|
119
|
+
const path = calculateFlowchartPath(50, 0, 150, 35);
|
|
120
|
+
expect(path).to.include('M 50 0');
|
|
121
|
+
// Should still use rounded corners (L-shape with curves)
|
|
122
|
+
expect(path).to.include('Q');
|
|
123
|
+
});
|
|
124
|
+
it('enforces midY is always below source exit for top face', () => {
|
|
125
|
+
// Target above source — midY should not go above sourceY + stubStart
|
|
126
|
+
const path = calculateFlowchartPath(50, 100, 150, 50);
|
|
127
|
+
expect(path).to.include('M 50 100');
|
|
128
|
+
// Should still exit downward with a curve at exitY (120)
|
|
129
|
+
expect(path).to.include('Q');
|
|
130
|
+
});
|
|
131
|
+
it('generates a path entering from the left face', () => {
|
|
132
|
+
const path = calculateFlowchartPath(50, 0, 150, 100, 20, 10, 5, 'left');
|
|
133
|
+
expect(path).to.include('M 50 0');
|
|
134
|
+
expect(path).to.include('L 150 100'); // ends at target
|
|
135
|
+
});
|
|
136
|
+
it('generates a path entering from the right face', () => {
|
|
137
|
+
const path = calculateFlowchartPath(150, 0, 50, 100, 20, 10, 5, 'right');
|
|
138
|
+
expect(path).to.include('M 150 0');
|
|
139
|
+
expect(path).to.include('L 50 100'); // ends at target
|
|
140
|
+
});
|
|
141
|
+
});
|
|
133
142
|
//# sourceMappingURL=temba-flow-plumber.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temba-flow-plumber.test.js","sourceRoot":"","sources":["../../test/temba-flow-plumber.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { Plumber, SOURCE_DEFAULTS, TARGET_DEFAULTS } from '../src/flow/Plumber';\nimport { stub, useFakeTimers, SinonFakeTimers } from 'sinon';\n\ndescribe('Plumber', () => {\n let plumber: Plumber;\n let mockJsPlumb: any;\n let mockCanvas: HTMLElement;\n let clock: SinonFakeTimers;\n\n beforeEach(() => {\n // Use fake timers to control setTimeout\n clock = useFakeTimers();\n\n // Create mock canvas and make getElementById return a mock element\n mockCanvas = document.createElement('div');\n const mockElement = document.createElement('div');\n stub(document, 'getElementById').returns(mockElement);\n\n // Create a mock editor with fireCustomEvent\n const mockEditor = { fireCustomEvent: stub() };\n\n // Create a new plumber instance\n plumber = new Plumber(mockCanvas, mockEditor);\n\n // Replace the internal jsPlumb instance with mocks\n mockJsPlumb = {\n getConnections: stub().returns([]),\n addClass: stub(),\n removeClass: stub(),\n batch: stub().callsFake((fn) => fn()),\n addEndpoint: stub().returns({}),\n connect: stub(),\n getEndpoints: stub().returns([\n { elementId: 'test-from', addClass: stub() }\n ]),\n select: stub().returns({\n deleteAll: stub()\n }),\n selectEndpoints: stub().returns({\n deleteAll: stub()\n }),\n deleteConnection: stub(),\n removeAllEndpoints: stub(),\n repaintEverything: stub(),\n revalidate: stub(),\n bind: stub()\n };\n\n (plumber as any).jsPlumb = mockJsPlumb;\n // Reset the connectionWait to avoid timing issues\n (plumber as any).connectionWait = null;\n });\n\n afterEach(() => {\n // Restore the original document.getElementById\n (document.getElementById as any).restore?.();\n clock.restore();\n });\n\n describe('constructor', () => {\n it('creates a new plumber instance', () => {\n expect(plumber).to.be.instanceOf(Plumber);\n });\n });\n\n describe('makeTarget', () => {\n it('creates a target endpoint for the specified element', () => {\n plumber.makeTarget('test-target');\n expect(mockJsPlumb.addEndpoint).to.have.been.called;\n });\n });\n\n describe('makeSource', () => {\n it('creates a source endpoint for the specified element', () => {\n plumber.makeSource('test-source');\n expect(mockJsPlumb.addEndpoint).to.have.been.called;\n });\n });\n\n describe('connectIds', () => {\n it('adds connection to pending connections and processes them', () => {\n plumber.connectIds('test-node', 'test-from', 'test-to');\n\n // Verify pendingConnections has the new connection\n expect((plumber as any).pendingConnections.length).to.equal(1);\n\n // Advance timer to trigger the timeout\n clock.tick(51); // Just past the 50ms timeout\n\n // Now the batch should have been called\n expect(mockJsPlumb.batch).to.have.been.called;\n });\n });\n\n describe('processPendingConnections', () => {\n it('processes pending connections with timeout', () => {\n // Add a connection to pending connections\n plumber.connectIds('test-node', 'test-from', 'test-to');\n\n // Fast-forward clock past the timeout\n clock.tick(51); // Just past the 50ms timeout\n\n expect(mockJsPlumb.batch).to.have.been.called;\n });\n\n it('creates endpoints and connections for pending connections', () => {\n plumber.connectIds('test-node', 'test-from', 'test-to');\n\n // Fast-forward clock past the timeout\n clock.tick(51); // Just past the 50ms timeout\n\n expect(mockJsPlumb.addEndpoint).to.have.been.called;\n expect(mockJsPlumb.connect).to.have.been.called;\n });\n\n it('clears existing timeout when called multiple times', () => {\n // Set up spies for window.setTimeout and window.clearTimeout\n const clearTimeoutSpy = stub(window, 'clearTimeout');\n const setTimeoutSpy = stub(window, 'setTimeout').returns(123 as any);\n\n // Call twice\n plumber.processPendingConnections();\n plumber.processPendingConnections();\n\n // Should have called clearTimeout once and setTimeout twice\n expect(clearTimeoutSpy).to.have.been.calledOnce;\n expect(setTimeoutSpy).to.have.been.calledTwice;\n\n // Clean up\n clearTimeoutSpy.restore();\n setTimeoutSpy.restore();\n });\n\n it('handles empty pending connections', () => {\n // Call without adding any connections\n plumber.processPendingConnections();\n\n // Fast-forward clock past the timeout\n clock.tick(51); // Just past the 50ms timeout\n\n expect(mockJsPlumb.batch).to.have.been.called;\n });\n });\n\n describe('constants', () => {\n it('has correct properties in SOURCE_DEFAULTS', () => {\n expect(SOURCE_DEFAULTS).to.have.property('endpoint');\n expect(SOURCE_DEFAULTS).to.have.property('anchors');\n expect(SOURCE_DEFAULTS).to.have.property('maxConnections');\n expect(SOURCE_DEFAULTS).to.have.property('source');\n });\n\n it('has correct properties in TARGET_DEFAULTS', () => {\n expect(TARGET_DEFAULTS).to.have.property('endpoint');\n expect(TARGET_DEFAULTS).to.have.property('anchor');\n expect(TARGET_DEFAULTS).to.have.property('maxConnections');\n expect(TARGET_DEFAULTS).to.have.property('target');\n });\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"temba-flow-plumber.test.js","sourceRoot":"","sources":["../../test/temba-flow-plumber.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { Plumber, calculateFlowchartPath } from '../src/flow/Plumber';\nimport { stub, useFakeTimers, SinonFakeTimers } from 'sinon';\n\ndescribe('Plumber', () => {\n let plumber: Plumber;\n let mockCanvas: HTMLElement;\n let clock: SinonFakeTimers;\n let mockElement: HTMLElement;\n\n beforeEach(() => {\n clock = useFakeTimers();\n\n mockCanvas = document.createElement('div');\n mockCanvas.id = 'canvas';\n document.body.appendChild(mockCanvas);\n\n mockElement = document.createElement('div');\n mockElement.id = 'test-exit';\n mockCanvas.appendChild(mockElement);\n\n const mockEditor = { fireCustomEvent: stub() };\n plumber = new Plumber(mockCanvas, mockEditor);\n });\n\n afterEach(() => {\n mockCanvas.remove();\n clock.restore();\n });\n\n describe('constructor', () => {\n it('creates a new plumber instance', () => {\n expect(plumber).to.be.instanceOf(Plumber);\n });\n });\n\n describe('makeSource', () => {\n it('registers a mousedown listener on the exit element', () => {\n const exitEl = document.createElement('div');\n exitEl.id = 'exit-1';\n mockCanvas.appendChild(exitEl);\n\n plumber.makeSource('exit-1');\n\n // Source should be tracked\n expect((plumber as any).sources.has('exit-1')).to.be.true;\n\n exitEl.remove();\n });\n\n it('cleans up previous listener when called again', () => {\n const exitEl = document.createElement('div');\n exitEl.id = 'exit-2';\n mockCanvas.appendChild(exitEl);\n\n plumber.makeSource('exit-2');\n plumber.makeSource('exit-2');\n\n expect((plumber as any).sources.has('exit-2')).to.be.true;\n\n exitEl.remove();\n });\n });\n\n describe('makeTarget', () => {\n it('is a no-op', () => {\n // Should not throw\n plumber.makeTarget('test-node');\n });\n });\n\n describe('connectIds', () => {\n it('adds connection to pending connections and processes them', () => {\n plumber.connectIds('test-node', 'test-from', 'test-to');\n expect((plumber as any).pendingConnections.length).to.equal(1);\n });\n });\n\n describe('processPendingConnections', () => {\n it('clears existing rAF when called multiple times', () => {\n const cancelSpy = stub(window, 'cancelAnimationFrame');\n const rafSpy = stub(window, 'requestAnimationFrame').returns(123 as any);\n\n plumber.processPendingConnections();\n plumber.processPendingConnections();\n\n expect(cancelSpy).to.have.been.calledOnce;\n expect(rafSpy).to.have.been.calledTwice;\n\n cancelSpy.restore();\n rafSpy.restore();\n });\n });\n\n describe('event system', () => {\n it('supports on/off/notify pattern', () => {\n let received = null;\n const handler = (info: any) => {\n received = info;\n };\n\n plumber.on('test-event', handler);\n (plumber as any).notifyListeners('test-event', { data: 'test' });\n expect(received).to.deep.equal({ data: 'test' });\n\n received = null;\n plumber.off('test-event', handler);\n (plumber as any).notifyListeners('test-event', { data: 'test2' });\n expect(received).to.be.null;\n });\n });\n\n describe('reset', () => {\n it('clears all state', () => {\n plumber.connectIds('test-node', 'test-from', 'test-to');\n plumber.reset();\n\n expect((plumber as any).pendingConnections.length).to.equal(0);\n expect((plumber as any).connections.size).to.equal(0);\n expect((plumber as any).sources.size).to.equal(0);\n });\n });\n});\n\ndescribe('calculateFlowchartPath', () => {\n it('generates a straight vertical path when source and target are aligned', () => {\n const path = calculateFlowchartPath(100, 0, 100, 100);\n expect(path).to.include('M 100 0');\n expect(path).to.include('L 100 100');\n // Should not contain Q (quadratic curve) for aligned points\n expect(path).to.not.include('Q');\n });\n\n it('generates a path with corners when source and target are offset', () => {\n const path = calculateFlowchartPath(50, 0, 150, 200);\n expect(path).to.include('M 50 0');\n expect(path).to.include('Q'); // Should have rounded corners\n expect(path).to.include('L 150 200');\n });\n\n it('handles custom stub and corner radius', () => {\n const path = calculateFlowchartPath(0, 0, 100, 100, 30, 15, 10);\n expect(path).to.include('M 0 0');\n expect(path).to.include('L 100 100');\n });\n\n it('handles cases where vertical space is tight by using reduced-radius corners', () => {\n // With stubs of 20+10=30, and only 35 total vertical space, there's only 5px for corners\n const path = calculateFlowchartPath(50, 0, 150, 35);\n expect(path).to.include('M 50 0');\n // Should still use rounded corners (L-shape with curves)\n expect(path).to.include('Q');\n });\n\n it('enforces midY is always below source exit for top face', () => {\n // Target above source — midY should not go above sourceY + stubStart\n const path = calculateFlowchartPath(50, 100, 150, 50);\n expect(path).to.include('M 50 100');\n // Should still exit downward with a curve at exitY (120)\n expect(path).to.include('Q');\n });\n\n it('generates a path entering from the left face', () => {\n const path = calculateFlowchartPath(50, 0, 150, 100, 20, 10, 5, 'left');\n expect(path).to.include('M 50 0');\n expect(path).to.include('L 150 100'); // ends at target\n });\n\n it('generates a path entering from the right face', () => {\n const path = calculateFlowchartPath(150, 0, 50, 100, 20, 10, 5, 'right');\n expect(path).to.include('M 150 0');\n expect(path).to.include('L 50 100'); // ends at target\n });\n});\n"]}
|