@nyaruka/temba-components 0.135.9 → 0.136.1
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 +25 -0
- package/demo/components/webchat/example.html +4 -2
- package/dist/static/svg/index.svg +1 -1
- package/dist/temba-components.js +1351 -322
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/Icons.js +2 -1
- package/out-tsc/src/Icons.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +2 -6
- package/out-tsc/src/display/FloatingTab.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +29 -1
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +229 -5
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/Plumber.js +320 -1
- package/out-tsc/src/flow/Plumber.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/layout/FloatingWindow.js +30 -8
- package/out-tsc/src/layout/FloatingWindow.js.map +1 -1
- package/out-tsc/src/simulator/Simulator.js +1861 -0
- package/out-tsc/src/simulator/Simulator.js.map +1 -0
- package/out-tsc/src/store/AppState.js +66 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/utils.js +48 -0
- package/out-tsc/src/utils.js.map +1 -1
- package/out-tsc/temba-modules.js +2 -0
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/temba-appstate-node-sorting.test.js +430 -0
- package/out-tsc/test/temba-appstate-node-sorting.test.js.map +1 -0
- package/out-tsc/test/temba-floating-tab.test.js +0 -9
- package/out-tsc/test/temba-floating-tab.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor.test.js +262 -1
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
- package/out-tsc/test/temba-flow-plumber-connections.test.js +3 -1
- package/out-tsc/test/temba-flow-plumber-connections.test.js.map +1 -1
- package/out-tsc/test/temba-flow-plumber.test.js +3 -1
- package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
- package/out-tsc/test/temba-simulator.test.js +642 -0
- package/out-tsc/test/temba-simulator.test.js.map +1 -0
- package/out-tsc/test/utils.test.js +1 -1
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
- package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
- package/screenshots/truth/floating-tab/gray.png +0 -0
- package/screenshots/truth/floating-tab/green.png +0 -0
- package/screenshots/truth/floating-tab/purple.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/screenshots/truth/simulator/after-message-sent.png +0 -0
- package/screenshots/truth/simulator/after-reset.png +0 -0
- package/screenshots/truth/simulator/attachment-menu.png +0 -0
- package/screenshots/truth/simulator/context-expanded.png +0 -0
- package/screenshots/truth/simulator/context-explorer-open.png +0 -0
- package/screenshots/truth/simulator/event-info.png +0 -0
- package/screenshots/truth/simulator/image-attachment.png +0 -0
- package/screenshots/truth/simulator/open-initial.png +0 -0
- package/screenshots/truth/simulator/quick-replies.png +0 -0
- package/src/Icons.ts +2 -1
- package/src/display/FloatingTab.ts +2 -7
- package/src/flow/CanvasNode.ts +30 -1
- package/src/flow/Editor.ts +246 -4
- package/src/flow/Plumber.ts +371 -2
- package/src/interfaces.ts +2 -1
- package/src/layout/FloatingWindow.ts +37 -12
- package/src/simulator/Simulator.ts +2061 -0
- package/src/store/AppState.ts +109 -0
- package/src/utils.ts +53 -0
- package/static/svg/index.svg +1 -1
- package/static/svg/work/traced/route.svg +1 -0
- package/static/svg/work/used/route.svg +3 -0
- package/temba-modules.ts +2 -0
- package/test/temba-appstate-node-sorting.test.ts +506 -0
- package/test/temba-floating-tab.test.ts +0 -11
- package/test/temba-flow-editor.test.ts +298 -1
- package/test/temba-flow-plumber-connections.test.ts +4 -1
- package/test/temba-flow-plumber.test.ts +4 -1
- package/test/temba-simulator.test.ts +866 -0
- package/test/utils.test.ts +1 -1
|
@@ -79,7 +79,7 @@ describe('Editor', () => {
|
|
|
79
79
|
// Test that calling firstUpdated doesn't throw (without getStore)
|
|
80
80
|
expect(() => {
|
|
81
81
|
// Only test the plumber initialization part
|
|
82
|
-
(editor as any).plumber = new Plumber(mockCanvas);
|
|
82
|
+
(editor as any).plumber = new Plumber(mockCanvas, editor);
|
|
83
83
|
}).to.not.throw();
|
|
84
84
|
});
|
|
85
85
|
|
|
@@ -521,4 +521,301 @@ describe('Editor', () => {
|
|
|
521
521
|
expect(expectedPosition).to.deep.equal({ left: 260, top: 160 });
|
|
522
522
|
});
|
|
523
523
|
});
|
|
524
|
+
|
|
525
|
+
describe('flow-start indicator', () => {
|
|
526
|
+
it('should mark the first node as flow-start', async () => {
|
|
527
|
+
const { zustand } = await import('../src/store/AppState');
|
|
528
|
+
|
|
529
|
+
// create a flow definition with multiple nodes
|
|
530
|
+
const mockFlowDefinition = {
|
|
531
|
+
language: 'en',
|
|
532
|
+
localization: {},
|
|
533
|
+
name: 'Test Flow',
|
|
534
|
+
nodes: [
|
|
535
|
+
{
|
|
536
|
+
uuid: 'node-1',
|
|
537
|
+
actions: [
|
|
538
|
+
{ type: 'send_msg', uuid: 'action-1', text: 'Message 1' }
|
|
539
|
+
],
|
|
540
|
+
exits: [{ uuid: 'exit-1', destination_uuid: null }]
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
uuid: 'node-2',
|
|
544
|
+
actions: [
|
|
545
|
+
{ type: 'send_msg', uuid: 'action-2', text: 'Message 2' }
|
|
546
|
+
],
|
|
547
|
+
exits: [{ uuid: 'exit-2', destination_uuid: null }]
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
uuid: 'node-3',
|
|
551
|
+
actions: [
|
|
552
|
+
{ type: 'send_msg', uuid: 'action-3', text: 'Message 3' }
|
|
553
|
+
],
|
|
554
|
+
exits: [{ uuid: 'exit-3', destination_uuid: null }]
|
|
555
|
+
}
|
|
556
|
+
],
|
|
557
|
+
uuid: 'test-uuid',
|
|
558
|
+
type: 'messaging' as const,
|
|
559
|
+
revision: 1,
|
|
560
|
+
spec_version: '14.3',
|
|
561
|
+
_ui: {
|
|
562
|
+
nodes: {
|
|
563
|
+
'node-1': { position: { left: 100, top: 100 }, type: 'send_msg' },
|
|
564
|
+
'node-2': { position: { left: 200, top: 200 }, type: 'send_msg' },
|
|
565
|
+
'node-3': { position: { left: 300, top: 300 }, type: 'send_msg' }
|
|
566
|
+
},
|
|
567
|
+
languages: []
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
zustand.getState().setFlowContents({
|
|
572
|
+
definition: mockFlowDefinition as any,
|
|
573
|
+
info: {
|
|
574
|
+
results: [],
|
|
575
|
+
dependencies: [],
|
|
576
|
+
counts: { nodes: 3, languages: 1 },
|
|
577
|
+
locals: []
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
editor = await fixture(html`
|
|
582
|
+
<temba-flow-editor>
|
|
583
|
+
<div id="canvas"></div>
|
|
584
|
+
</temba-flow-editor>
|
|
585
|
+
`);
|
|
586
|
+
|
|
587
|
+
await editor.updateComplete;
|
|
588
|
+
|
|
589
|
+
// get all flow nodes
|
|
590
|
+
const flowNodes = editor.querySelectorAll('temba-flow-node');
|
|
591
|
+
expect(flowNodes.length).to.equal(3);
|
|
592
|
+
|
|
593
|
+
// first node should have flow-start class
|
|
594
|
+
expect(flowNodes[0].classList.contains('flow-start')).to.be.true;
|
|
595
|
+
|
|
596
|
+
// other nodes should not have flow-start class
|
|
597
|
+
expect(flowNodes[1].classList.contains('flow-start')).to.be.false;
|
|
598
|
+
expect(flowNodes[2].classList.contains('flow-start')).to.be.false;
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
it('should update flow-start when node positions change', async () => {
|
|
602
|
+
const { zustand } = await import('../src/store/AppState');
|
|
603
|
+
|
|
604
|
+
// create a flow with nodes in a specific order
|
|
605
|
+
const mockFlowDefinition = {
|
|
606
|
+
language: 'en',
|
|
607
|
+
localization: {},
|
|
608
|
+
name: 'Test Flow',
|
|
609
|
+
nodes: [
|
|
610
|
+
{
|
|
611
|
+
uuid: 'node-1',
|
|
612
|
+
actions: [
|
|
613
|
+
{ type: 'send_msg', uuid: 'action-1', text: 'Message 1' }
|
|
614
|
+
],
|
|
615
|
+
exits: [{ uuid: 'exit-1', destination_uuid: null }]
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
uuid: 'node-2',
|
|
619
|
+
actions: [
|
|
620
|
+
{ type: 'send_msg', uuid: 'action-2', text: 'Message 2' }
|
|
621
|
+
],
|
|
622
|
+
exits: [{ uuid: 'exit-2', destination_uuid: null }]
|
|
623
|
+
}
|
|
624
|
+
],
|
|
625
|
+
uuid: 'test-uuid',
|
|
626
|
+
type: 'messaging' as const,
|
|
627
|
+
revision: 1,
|
|
628
|
+
spec_version: '14.3',
|
|
629
|
+
_ui: {
|
|
630
|
+
nodes: {
|
|
631
|
+
'node-1': { position: { left: 100, top: 200 }, type: 'send_msg' },
|
|
632
|
+
'node-2': { position: { left: 100, top: 100 }, type: 'send_msg' }
|
|
633
|
+
},
|
|
634
|
+
languages: []
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
zustand.getState().setFlowContents({
|
|
639
|
+
definition: mockFlowDefinition as any,
|
|
640
|
+
info: {
|
|
641
|
+
results: [],
|
|
642
|
+
dependencies: [],
|
|
643
|
+
counts: { nodes: 2, languages: 1 },
|
|
644
|
+
locals: []
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
editor = await fixture(html`
|
|
649
|
+
<temba-flow-editor>
|
|
650
|
+
<div id="canvas"></div>
|
|
651
|
+
</temba-flow-editor>
|
|
652
|
+
`);
|
|
653
|
+
|
|
654
|
+
await editor.updateComplete;
|
|
655
|
+
|
|
656
|
+
// node-2 should be first (top: 100 < top: 200)
|
|
657
|
+
const flowNodes = editor.querySelectorAll('temba-flow-node');
|
|
658
|
+
expect(flowNodes[0].getAttribute('uuid')).to.equal('node-2');
|
|
659
|
+
expect(flowNodes[0].classList.contains('flow-start')).to.be.true;
|
|
660
|
+
expect(flowNodes[1].classList.contains('flow-start')).to.be.false;
|
|
661
|
+
|
|
662
|
+
// move node-1 to the top
|
|
663
|
+
zustand.getState().updateCanvasPositions({
|
|
664
|
+
'node-1': { left: 100, top: 50 }
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
await editor.updateComplete;
|
|
668
|
+
|
|
669
|
+
// now node-1 should be first
|
|
670
|
+
const updatedFlowNodes = editor.querySelectorAll('temba-flow-node');
|
|
671
|
+
expect(updatedFlowNodes[0].getAttribute('uuid')).to.equal('node-1');
|
|
672
|
+
expect(updatedFlowNodes[0].classList.contains('flow-start')).to.be.true;
|
|
673
|
+
expect(updatedFlowNodes[1].classList.contains('flow-start')).to.be.false;
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it('should maintain flow-start when nodes are added', async () => {
|
|
677
|
+
const { zustand } = await import('../src/store/AppState');
|
|
678
|
+
|
|
679
|
+
// start with one node
|
|
680
|
+
const mockFlowDefinition = {
|
|
681
|
+
language: 'en',
|
|
682
|
+
localization: {},
|
|
683
|
+
name: 'Test Flow',
|
|
684
|
+
nodes: [
|
|
685
|
+
{
|
|
686
|
+
uuid: 'node-1',
|
|
687
|
+
actions: [
|
|
688
|
+
{ type: 'send_msg', uuid: 'action-1', text: 'Message 1' }
|
|
689
|
+
],
|
|
690
|
+
exits: [{ uuid: 'exit-1', destination_uuid: null }]
|
|
691
|
+
}
|
|
692
|
+
],
|
|
693
|
+
uuid: 'test-uuid',
|
|
694
|
+
type: 'messaging' as const,
|
|
695
|
+
revision: 1,
|
|
696
|
+
spec_version: '14.3',
|
|
697
|
+
_ui: {
|
|
698
|
+
nodes: {
|
|
699
|
+
'node-1': { position: { left: 100, top: 200 }, type: 'send_msg' }
|
|
700
|
+
},
|
|
701
|
+
languages: []
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
zustand.getState().setFlowContents({
|
|
706
|
+
definition: mockFlowDefinition as any,
|
|
707
|
+
info: {
|
|
708
|
+
results: [],
|
|
709
|
+
dependencies: [],
|
|
710
|
+
counts: { nodes: 1, languages: 1 },
|
|
711
|
+
locals: []
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
editor = await fixture(html`
|
|
716
|
+
<temba-flow-editor>
|
|
717
|
+
<div id="canvas"></div>
|
|
718
|
+
</temba-flow-editor>
|
|
719
|
+
`);
|
|
720
|
+
|
|
721
|
+
await editor.updateComplete;
|
|
722
|
+
|
|
723
|
+
let flowNodes = editor.querySelectorAll('temba-flow-node');
|
|
724
|
+
expect(flowNodes[0].classList.contains('flow-start')).to.be.true;
|
|
725
|
+
|
|
726
|
+
// add a new node at the top
|
|
727
|
+
const newNode = {
|
|
728
|
+
uuid: 'node-2',
|
|
729
|
+
actions: [
|
|
730
|
+
{ type: 'send_msg' as const, uuid: 'action-2', text: 'Message 2' }
|
|
731
|
+
],
|
|
732
|
+
exits: [{ uuid: 'exit-2', destination_uuid: null }]
|
|
733
|
+
};
|
|
734
|
+
const newNodeUI = {
|
|
735
|
+
position: { left: 100, top: 100 },
|
|
736
|
+
type: 'send_msg' as const
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
zustand.getState().addNode(newNode, newNodeUI);
|
|
740
|
+
|
|
741
|
+
await editor.updateComplete;
|
|
742
|
+
|
|
743
|
+
// new node should now be the flow-start
|
|
744
|
+
flowNodes = editor.querySelectorAll('temba-flow-node');
|
|
745
|
+
expect(flowNodes[0].getAttribute('uuid')).to.equal('node-2');
|
|
746
|
+
expect(flowNodes[0].classList.contains('flow-start')).to.be.true;
|
|
747
|
+
expect(flowNodes[1].classList.contains('flow-start')).to.be.false;
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('should handle flow-start when first node is removed', async () => {
|
|
751
|
+
const { zustand } = await import('../src/store/AppState');
|
|
752
|
+
|
|
753
|
+
// create flow with multiple nodes
|
|
754
|
+
const mockFlowDefinition = {
|
|
755
|
+
language: 'en',
|
|
756
|
+
localization: {},
|
|
757
|
+
name: 'Test Flow',
|
|
758
|
+
nodes: [
|
|
759
|
+
{
|
|
760
|
+
uuid: 'node-1',
|
|
761
|
+
actions: [
|
|
762
|
+
{ type: 'send_msg', uuid: 'action-1', text: 'Message 1' }
|
|
763
|
+
],
|
|
764
|
+
exits: [{ uuid: 'exit-1', destination_uuid: null }]
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
uuid: 'node-2',
|
|
768
|
+
actions: [
|
|
769
|
+
{ type: 'send_msg', uuid: 'action-2', text: 'Message 2' }
|
|
770
|
+
],
|
|
771
|
+
exits: [{ uuid: 'exit-2', destination_uuid: null }]
|
|
772
|
+
}
|
|
773
|
+
],
|
|
774
|
+
uuid: 'test-uuid',
|
|
775
|
+
type: 'messaging' as const,
|
|
776
|
+
revision: 1,
|
|
777
|
+
spec_version: '14.3',
|
|
778
|
+
_ui: {
|
|
779
|
+
nodes: {
|
|
780
|
+
'node-1': { position: { left: 100, top: 100 }, type: 'send_msg' },
|
|
781
|
+
'node-2': { position: { left: 100, top: 200 }, type: 'send_msg' }
|
|
782
|
+
},
|
|
783
|
+
languages: []
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
zustand.getState().setFlowContents({
|
|
788
|
+
definition: mockFlowDefinition as any,
|
|
789
|
+
info: {
|
|
790
|
+
results: [],
|
|
791
|
+
dependencies: [],
|
|
792
|
+
counts: { nodes: 2, languages: 1 },
|
|
793
|
+
locals: []
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
editor = await fixture(html`
|
|
798
|
+
<temba-flow-editor>
|
|
799
|
+
<div id="canvas"></div>
|
|
800
|
+
</temba-flow-editor>
|
|
801
|
+
`);
|
|
802
|
+
|
|
803
|
+
await editor.updateComplete;
|
|
804
|
+
|
|
805
|
+
let flowNodes = editor.querySelectorAll('temba-flow-node');
|
|
806
|
+
expect(flowNodes[0].getAttribute('uuid')).to.equal('node-1');
|
|
807
|
+
expect(flowNodes[0].classList.contains('flow-start')).to.be.true;
|
|
808
|
+
|
|
809
|
+
// remove the first node
|
|
810
|
+
zustand.getState().removeNodes(['node-1']);
|
|
811
|
+
|
|
812
|
+
await editor.updateComplete;
|
|
813
|
+
|
|
814
|
+
// node-2 should now be the flow-start
|
|
815
|
+
flowNodes = editor.querySelectorAll('temba-flow-node');
|
|
816
|
+
expect(flowNodes.length).to.equal(1);
|
|
817
|
+
expect(flowNodes[0].getAttribute('uuid')).to.equal('node-2');
|
|
818
|
+
expect(flowNodes[0].classList.contains('flow-start')).to.be.true;
|
|
819
|
+
});
|
|
820
|
+
});
|
|
524
821
|
});
|
|
@@ -16,8 +16,11 @@ describe('Plumber - Connection Management', () => {
|
|
|
16
16
|
const mockElement = document.createElement('div');
|
|
17
17
|
stub(document, 'getElementById').returns(mockElement);
|
|
18
18
|
|
|
19
|
+
// Create a mock editor with fireCustomEvent
|
|
20
|
+
const mockEditor = { fireCustomEvent: stub() };
|
|
21
|
+
|
|
19
22
|
// Create a new plumber instance
|
|
20
|
-
plumber = new Plumber(mockCanvas);
|
|
23
|
+
plumber = new Plumber(mockCanvas, mockEditor);
|
|
21
24
|
|
|
22
25
|
// Replace the internal jsPlumb instance with mocks
|
|
23
26
|
(plumber as any).jsPlumb = {
|
|
@@ -17,8 +17,11 @@ describe('Plumber', () => {
|
|
|
17
17
|
const mockElement = document.createElement('div');
|
|
18
18
|
stub(document, 'getElementById').returns(mockElement);
|
|
19
19
|
|
|
20
|
+
// Create a mock editor with fireCustomEvent
|
|
21
|
+
const mockEditor = { fireCustomEvent: stub() };
|
|
22
|
+
|
|
20
23
|
// Create a new plumber instance
|
|
21
|
-
plumber = new Plumber(mockCanvas);
|
|
24
|
+
plumber = new Plumber(mockCanvas, mockEditor);
|
|
22
25
|
|
|
23
26
|
// Replace the internal jsPlumb instance with mocks
|
|
24
27
|
mockJsPlumb = {
|