@nyaruka/temba-components 0.129.8 → 0.129.10
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 +37 -3
- package/demo/data/flows/sample-flow.json +186 -96
- package/demo/test-colorpicker.html +30 -0
- package/dist/temba-components.js +1126 -1111
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/excellent/helpers.js +2 -2
- package/out-tsc/src/excellent/helpers.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +25 -7
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +11 -1
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +133 -290
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +40 -0
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/call_llm.js +56 -3
- package/out-tsc/src/flow/actions/call_llm.js.map +1 -1
- package/out-tsc/src/flow/actions/call_webhook.js +1 -1
- package/out-tsc/src/flow/actions/call_webhook.js.map +1 -1
- package/out-tsc/src/flow/actions/open_ticket.js +65 -3
- package/out-tsc/src/flow/actions/open_ticket.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +75 -0
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/config.js +4 -0
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +227 -0
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_ticket.js +18 -0
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_response.js +27 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/types.js +0 -65
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +63 -117
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/BaseListEditor.js +4 -3
- package/out-tsc/src/form/BaseListEditor.js.map +1 -1
- package/out-tsc/src/form/Checkbox.js +77 -24
- package/out-tsc/src/form/Checkbox.js.map +1 -1
- package/out-tsc/src/form/ColorPicker.js +28 -40
- package/out-tsc/src/form/ColorPicker.js.map +1 -1
- package/out-tsc/src/form/Completion.js +44 -53
- package/out-tsc/src/form/Completion.js.map +1 -1
- package/out-tsc/src/form/Compose.js +7 -8
- package/out-tsc/src/form/Compose.js.map +1 -1
- package/out-tsc/src/form/ContactSearch.js +3 -4
- package/out-tsc/src/form/ContactSearch.js.map +1 -1
- package/out-tsc/src/form/DatePicker.js +29 -36
- package/out-tsc/src/form/DatePicker.js.map +1 -1
- package/out-tsc/src/form/{FormField.js → FieldElement.js} +81 -53
- package/out-tsc/src/form/FieldElement.js.map +1 -0
- package/out-tsc/src/form/FieldRenderer.js +306 -0
- package/out-tsc/src/form/FieldRenderer.js.map +1 -0
- package/out-tsc/src/form/ImagePicker.js +122 -126
- package/out-tsc/src/form/ImagePicker.js.map +1 -1
- package/out-tsc/src/form/KeyValueEditor.js +41 -37
- package/out-tsc/src/form/KeyValueEditor.js.map +1 -1
- package/out-tsc/src/form/MessageEditor.js +55 -63
- package/out-tsc/src/form/MessageEditor.js.map +1 -1
- package/out-tsc/src/form/TembaSlider.js +3 -3
- package/out-tsc/src/form/TembaSlider.js.map +1 -1
- package/out-tsc/src/form/TemplateEditor.js +3 -3
- package/out-tsc/src/form/TemplateEditor.js.map +1 -1
- package/out-tsc/src/form/TextInput.js +23 -27
- package/out-tsc/src/form/TextInput.js.map +1 -1
- package/out-tsc/src/form/select/Select.js +57 -35
- package/out-tsc/src/form/select/Select.js.map +1 -1
- package/out-tsc/src/form/select/UserSelect.js +8 -9
- package/out-tsc/src/form/select/UserSelect.js.map +1 -1
- package/out-tsc/src/form/select/WorkspaceSelect.js +7 -8
- package/out-tsc/src/form/select/WorkspaceSelect.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +62 -44
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/live/ContactFieldEditor.js.map +1 -1
- package/out-tsc/src/markdown.js +13 -11
- package/out-tsc/src/markdown.js.map +1 -1
- package/out-tsc/temba-modules.js +3 -2
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +2 -0
- package/out-tsc/test/ActionHelper.js.map +1 -1
- package/out-tsc/test/NodeHelper.js +148 -0
- package/out-tsc/test/NodeHelper.js.map +1 -0
- package/out-tsc/test/actions/call_llm.test.js +103 -0
- package/out-tsc/test/actions/call_llm.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_llm_categorize.test.js +532 -0
- package/out-tsc/test/nodes/split_by_llm_categorize.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_random.test.js +150 -0
- package/out-tsc/test/nodes/split_by_random.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js +150 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_response.test.js +171 -0
- package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -0
- package/out-tsc/test/temba-add-input-labels.test.js +70 -0
- package/out-tsc/test/temba-add-input-labels.test.js.map +1 -0
- package/out-tsc/test/temba-checkbox.test.js +16 -0
- package/out-tsc/test/temba-checkbox.test.js.map +1 -1
- package/out-tsc/test/temba-field-renderer.test.js +296 -0
- package/out-tsc/test/temba-field-renderer.test.js.map +1 -0
- package/out-tsc/test/temba-integration-markdown.test.js +2 -4
- package/out-tsc/test/temba-integration-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-markdown.test.js +1 -1
- package/out-tsc/test/temba-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-node-editor.test.js +400 -0
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +6 -3
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-slider.test.js +0 -1
- package/out-tsc/test/temba-slider.test.js.map +1 -1
- package/out-tsc/test/temba-webchat.test.js +1 -1
- package/out-tsc/test/temba-webchat.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/information-extraction.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/sentiment-analysis.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/summarization.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/translation-task.png +0 -0
- package/screenshots/truth/actions/call_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/actions/call_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/actions/call_llm/render/summarization.png +0 -0
- package/screenshots/truth/actions/call_llm/render/translation-task.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
- package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
- package/screenshots/truth/checkbox/checkbox-no-label-no-background-hover.png +0 -0
- package/screenshots/truth/checkbox/checkbox-with-help-text.png +0 -0
- package/screenshots/truth/checkbox/checked.png +0 -0
- package/screenshots/truth/checkbox/default.png +0 -0
- package/screenshots/truth/colorpicker/default.png +0 -0
- package/screenshots/truth/colorpicker/focused.png +0 -0
- package/screenshots/truth/colorpicker/initialized.png +0 -0
- package/screenshots/truth/colorpicker/selected.png +0 -0
- package/screenshots/truth/editor/router.png +0 -0
- package/screenshots/truth/editor/send_msg.png +0 -0
- package/screenshots/truth/editor/set_contact_language.png +0 -0
- package/screenshots/truth/editor/set_contact_name.png +0 -0
- package/screenshots/truth/editor/set_run_result.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-checked.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-unchecked.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-with-errors.png +0 -0
- package/screenshots/truth/field-renderer/context-comparison.png +0 -0
- package/screenshots/truth/field-renderer/key-value-with-label.png +0 -0
- package/screenshots/truth/field-renderer/message-editor-with-label.png +0 -0
- package/screenshots/truth/field-renderer/select-multi.png +0 -0
- package/screenshots/truth/field-renderer/select-no-label.png +0 -0
- package/screenshots/truth/field-renderer/select-with-label.png +0 -0
- package/screenshots/truth/field-renderer/text-evaluated.png +0 -0
- package/screenshots/truth/field-renderer/text-no-label.png +0 -0
- package/screenshots/truth/field-renderer/text-with-errors.png +0 -0
- package/screenshots/truth/field-renderer/text-with-label.png +0 -0
- package/screenshots/truth/field-renderer/textarea-evaluated.png +0 -0
- package/screenshots/truth/field-renderer/textarea-with-label.png +0 -0
- package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.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/editor/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.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/editor/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.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/editor/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.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/editor/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.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/omnibox/selected.png +0 -0
- package/screenshots/truth/run-list/basic.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/multi-with-endpoint.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/src/events.ts +12 -6
- package/src/excellent/helpers.ts +2 -2
- package/src/flow/CanvasNode.ts +22 -1
- package/src/flow/Editor.ts +12 -1
- package/src/flow/NodeEditor.ts +186 -374
- package/src/flow/actions/add_input_labels.ts +45 -0
- package/src/flow/actions/call_llm.ts +57 -3
- package/src/flow/actions/call_webhook.ts +1 -1
- package/src/flow/actions/open_ticket.ts +74 -3
- package/src/flow/actions/set_run_result.ts +83 -0
- package/src/flow/config.ts +4 -0
- package/src/flow/nodes/split_by_llm_categorize.ts +277 -0
- package/src/flow/nodes/split_by_ticket.ts +19 -0
- package/src/flow/nodes/wait_for_response.ts +28 -1
- package/src/flow/types.ts +26 -127
- package/src/form/ArrayEditor.ts +79 -139
- package/src/form/BaseListEditor.ts +4 -4
- package/src/form/Checkbox.ts +81 -24
- package/src/form/ColorPicker.ts +31 -43
- package/src/form/Completion.ts +49 -56
- package/src/form/Compose.ts +8 -8
- package/src/form/ContactSearch.ts +3 -4
- package/src/form/DatePicker.ts +32 -38
- package/src/form/{FormField.ts → FieldElement.ts} +108 -55
- package/src/form/FieldRenderer.ts +466 -0
- package/src/form/ImagePicker.ts +107 -110
- package/src/form/KeyValueEditor.ts +43 -39
- package/src/form/MessageEditor.ts +61 -67
- package/src/form/TembaSlider.ts +3 -3
- package/src/form/TemplateEditor.ts +3 -3
- package/src/form/TextInput.ts +26 -29
- package/src/form/select/Select.ts +63 -37
- package/src/form/select/UserSelect.ts +10 -11
- package/src/form/select/WorkspaceSelect.ts +9 -10
- package/src/live/ContactChat.ts +62 -47
- package/src/live/ContactFieldEditor.ts +2 -2
- package/src/markdown.ts +19 -11
- package/src/store/flow-definition.d.ts +5 -2
- package/static/api/labels.json +31 -0
- package/static/api/topics.json +24 -9
- package/static/api/users.json +35 -16
- package/static/css/temba-components.css +3 -3
- package/stress-test.js +18 -13
- package/temba-modules.ts +3 -2
- package/test/ActionHelper.ts +2 -0
- package/test/NodeHelper.ts +184 -0
- package/test/actions/call_llm.test.ts +137 -0
- package/test/nodes/README.md +78 -0
- package/test/nodes/split_by_llm_categorize.test.ts +698 -0
- package/test/nodes/split_by_random.test.ts +177 -0
- package/test/nodes/wait_for_digits.test.ts +176 -0
- package/test/nodes/wait_for_response.test.ts +206 -0
- package/test/temba-add-input-labels.test.ts +87 -0
- package/test/temba-checkbox.test.ts +26 -0
- package/test/temba-field-renderer.test.ts +482 -0
- package/test/temba-integration-markdown.test.ts +2 -4
- package/test/temba-markdown.test.ts +1 -1
- package/test/temba-node-editor.test.ts +496 -0
- package/test/temba-select.test.ts +6 -6
- package/test/temba-slider.test.ts +0 -1
- package/test/temba-webchat.test.ts +1 -1
- package/test-assets/contacts/history.json +7 -20
- package/test-assets/select/llms.json +18 -0
- package/web-dev-mock.mjs +96 -6
- package/web-dev-server.config.mjs +29 -7
- package/out-tsc/src/form/FormElement.js +0 -67
- package/out-tsc/src/form/FormElement.js.map +0 -1
- package/out-tsc/src/form/FormField.js.map +0 -1
- package/out-tsc/test/temba-formfield.test.js +0 -94
- package/out-tsc/test/temba-formfield.test.js.map +0 -1
- package/src/form/FormElement.ts +0 -69
- package/test/temba-flow-editor.test.ts.backup +0 -563
- package/test/temba-formfield.test.ts +0 -121
- package/test/temba-utils-index.test.ts.backup +0 -1737
package/static/api/users.json
CHANGED
|
@@ -3,24 +3,43 @@
|
|
|
3
3
|
"previous": null,
|
|
4
4
|
"results": [
|
|
5
5
|
{
|
|
6
|
-
"uuid": "
|
|
7
|
-
"email": "
|
|
8
|
-
"first_name": "
|
|
9
|
-
"last_name": "
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
6
|
+
"uuid": "c0f5b431-35e9-429c-9d57-5fee2fac46a3",
|
|
7
|
+
"email": "eric+marion+berry@textit.com",
|
|
8
|
+
"first_name": "Marion",
|
|
9
|
+
"last_name": "Berry",
|
|
10
|
+
"name": "Marion Berry",
|
|
11
|
+
"role": "agent",
|
|
12
|
+
"team": {
|
|
13
|
+
"uuid": "15236c1e-9375-4f84-bb48-ec64283d1eb9",
|
|
14
|
+
"name": "All Topics"
|
|
15
|
+
},
|
|
16
|
+
"created_on": "2023-04-05T21:11:31.909765Z",
|
|
17
|
+
"avatar": null
|
|
14
18
|
},
|
|
15
19
|
{
|
|
16
|
-
"uuid": "
|
|
17
|
-
"email": "
|
|
18
|
-
"first_name": "
|
|
19
|
-
"last_name": "
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
20
|
+
"uuid": "ae79dd5b-8c34-4602-a2c1-1e4db2419f0f",
|
|
21
|
+
"email": "eric@textit.com",
|
|
22
|
+
"first_name": "Eric",
|
|
23
|
+
"last_name": "Newcomer",
|
|
24
|
+
"name": "Eric Newcomer",
|
|
25
|
+
"role": "administrator",
|
|
26
|
+
"team": null,
|
|
27
|
+
"created_on": "2013-02-26T21:19:44Z",
|
|
28
|
+
"avatar": "https://dl-textit.s3.amazonaws.com/avatars/4/b6d756224c61435bb36b57ae03b83359.jpg"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"uuid": "f8e2a1b5-9c7d-4e6f-8a3b-1c5e9f2d4a6b",
|
|
32
|
+
"email": "sarah.smith@textit.com",
|
|
33
|
+
"first_name": "Sarah",
|
|
34
|
+
"last_name": "Smith",
|
|
35
|
+
"name": "Sarah Smith",
|
|
36
|
+
"role": "agent",
|
|
37
|
+
"team": {
|
|
38
|
+
"uuid": "15236c1e-9375-4f84-bb48-ec64283d1eb9",
|
|
39
|
+
"name": "All Topics"
|
|
40
|
+
},
|
|
41
|
+
"created_on": "2023-06-15T14:22:18.456789Z",
|
|
42
|
+
"avatar": null
|
|
24
43
|
}
|
|
25
44
|
]
|
|
26
45
|
}
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
--color-text-light: rgba(255, 255, 255, 1);
|
|
62
62
|
--color-text-dark: rgba(0, 0, 0, 0.8);
|
|
63
63
|
--color-text-dark-secondary: rgba(0, 0, 0, 0.25);
|
|
64
|
-
--color-text-help:
|
|
64
|
+
--color-text-help: rgb(120, 120, 120);
|
|
65
65
|
--color-tertiary: rgb(var(--tertiary-rgb));
|
|
66
66
|
|
|
67
67
|
--help-text-size: 0.85em;
|
|
@@ -125,8 +125,8 @@
|
|
|
125
125
|
--header-bg: var(--color-primary-dark);
|
|
126
126
|
--header-text: var(--color-text-light);
|
|
127
127
|
|
|
128
|
-
--temba-textinput-padding: 9px;
|
|
129
|
-
--temba-textinput-font-size:
|
|
128
|
+
--temba-textinput-padding: 9px 14px;
|
|
129
|
+
--temba-textinput-font-size: 14px;
|
|
130
130
|
|
|
131
131
|
--options-block-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.03);
|
|
132
132
|
--options-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
package/stress-test.js
CHANGED
|
@@ -11,7 +11,7 @@ let maxRuns = 10;
|
|
|
11
11
|
// Parse arguments
|
|
12
12
|
for (let i = 0; i < args.length; i++) {
|
|
13
13
|
const arg = args[i];
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
if (arg.startsWith('--runs=')) {
|
|
16
16
|
maxRuns = parseInt(arg.split('=')[1]);
|
|
17
17
|
if (isNaN(maxRuns) || maxRuns <= 0) {
|
|
@@ -28,12 +28,16 @@ for (let i = 0; i < args.length; i++) {
|
|
|
28
28
|
// Validate test file
|
|
29
29
|
if (!testFile) {
|
|
30
30
|
console.error('❌ Usage: yarn stress-test <test-file> [--runs=N]');
|
|
31
|
-
console.error(
|
|
31
|
+
console.error(
|
|
32
|
+
' Example: yarn stress-test test/temba-webchat.test.ts --runs=100'
|
|
33
|
+
);
|
|
32
34
|
process.exit(1);
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
if (!testFile.startsWith('test/') || !testFile.endsWith('.test.ts')) {
|
|
36
|
-
console.error(
|
|
38
|
+
console.error(
|
|
39
|
+
'❌ Test file must be in the test/ directory and end with .test.ts'
|
|
40
|
+
);
|
|
37
41
|
process.exit(1);
|
|
38
42
|
}
|
|
39
43
|
|
|
@@ -51,21 +55,21 @@ const startTime = performance.now();
|
|
|
51
55
|
try {
|
|
52
56
|
while (run <= maxRuns) {
|
|
53
57
|
const runStartTime = performance.now();
|
|
54
|
-
|
|
58
|
+
|
|
55
59
|
process.stdout.write(`Run ${run.toString().padStart(3)}/${maxRuns}: `);
|
|
56
|
-
|
|
60
|
+
|
|
57
61
|
try {
|
|
58
62
|
// Run the test with minimal output
|
|
59
|
-
const result = execSync(`yarn test ${testFile}`, {
|
|
63
|
+
const result = execSync(`yarn test ${testFile}`, {
|
|
60
64
|
encoding: 'utf-8',
|
|
61
65
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
62
66
|
});
|
|
63
|
-
|
|
67
|
+
|
|
64
68
|
const runEndTime = performance.now();
|
|
65
69
|
const runTime = runEndTime - runStartTime;
|
|
66
70
|
runTimes.push(runTime);
|
|
67
71
|
totalTime += runTime;
|
|
68
|
-
|
|
72
|
+
|
|
69
73
|
// Check if the test actually passed by looking for success indicators
|
|
70
74
|
if (result.includes('all tests passed') || result.includes('0 failed')) {
|
|
71
75
|
console.log(`✅ PASS (${(runTime / 1000).toFixed(2)}s)`);
|
|
@@ -75,11 +79,10 @@ try {
|
|
|
75
79
|
failures++;
|
|
76
80
|
break;
|
|
77
81
|
}
|
|
78
|
-
|
|
79
82
|
} catch (error) {
|
|
80
83
|
const runEndTime = performance.now();
|
|
81
84
|
const runTime = runEndTime - runStartTime;
|
|
82
|
-
|
|
85
|
+
|
|
83
86
|
console.log(`❌ FAIL (${(runTime / 1000).toFixed(2)}s)`);
|
|
84
87
|
console.log('');
|
|
85
88
|
console.log('💥 Test failed on run', run);
|
|
@@ -94,7 +97,7 @@ try {
|
|
|
94
97
|
failures++;
|
|
95
98
|
break;
|
|
96
99
|
}
|
|
97
|
-
|
|
100
|
+
|
|
98
101
|
run++;
|
|
99
102
|
}
|
|
100
103
|
} catch (error) {
|
|
@@ -112,14 +115,16 @@ console.log('==================');
|
|
|
112
115
|
console.log(`Test file: ${testFile}`);
|
|
113
116
|
console.log(`Completed runs: ${run - 1}/${maxRuns}`);
|
|
114
117
|
console.log(`Failures: ${failures}`);
|
|
115
|
-
console.log(
|
|
118
|
+
console.log(
|
|
119
|
+
`Success rate: ${(((run - 1 - failures) / (run - 1)) * 100).toFixed(1)}%`
|
|
120
|
+
);
|
|
116
121
|
console.log('');
|
|
117
122
|
|
|
118
123
|
if (runTimes.length > 0) {
|
|
119
124
|
const avgTime = runTimes.reduce((a, b) => a + b, 0) / runTimes.length;
|
|
120
125
|
const minTime = Math.min(...runTimes);
|
|
121
126
|
const maxTime = Math.max(...runTimes);
|
|
122
|
-
|
|
127
|
+
|
|
123
128
|
console.log('⏱️ Timing Statistics');
|
|
124
129
|
console.log('=====================');
|
|
125
130
|
console.log(`Total time: ${(totalTestTime / 1000).toFixed(2)}s`);
|
package/temba-modules.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Completion } from './src/form/Completion';
|
|
|
6
6
|
import { Modax } from './src/layout/Modax';
|
|
7
7
|
import { Dialog } from './src/layout/Dialog';
|
|
8
8
|
import { Button } from './src/display/Button';
|
|
9
|
-
import {
|
|
9
|
+
import { FieldElement } from './src/form/FieldElement';
|
|
10
10
|
import { Loading } from './src/display/Loading';
|
|
11
11
|
import { CharCount } from './src/display/CharCount';
|
|
12
12
|
import { Options } from './src/display/Options';
|
|
@@ -103,7 +103,8 @@ addCustomElement('temba-field-manager', FieldManager);
|
|
|
103
103
|
addCustomElement('temba-urn', ContactUrn);
|
|
104
104
|
addCustomElement('temba-content-menu', ContentMenu);
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
// Note: FieldElement is a base class and not directly instantiated as a custom element
|
|
107
|
+
export { FieldElement };
|
|
107
108
|
addCustomElement('temba-dialog', Dialog);
|
|
108
109
|
addCustomElement('temba-modax', Modax);
|
|
109
110
|
addCustomElement('temba-charcount', CharCount);
|
package/test/ActionHelper.ts
CHANGED
|
@@ -8,6 +8,8 @@ import '../temba-modules';
|
|
|
8
8
|
/**
|
|
9
9
|
* Generic action test framework
|
|
10
10
|
* Tests the complete action lifecycle: render → edit → save → validate
|
|
11
|
+
*
|
|
12
|
+
* For node configuration testing, see NodeHelper.ts
|
|
11
13
|
*/
|
|
12
14
|
export class ActionTest<T extends Action> {
|
|
13
15
|
constructor(private actionConfig: any, private actionName: string) {}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { fixture, expect } from '@open-wc/testing';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { Node } from '../src/store/flow-definition';
|
|
4
|
+
import { assertScreenshot, getClip } from './utils.test';
|
|
5
|
+
import { Editor } from '../src/flow/Editor';
|
|
6
|
+
import '../temba-modules';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generic node test framework
|
|
10
|
+
* Tests the complete node lifecycle: render → edit → save → validate
|
|
11
|
+
*
|
|
12
|
+
* This is the node configuration equivalent of ActionHelper.ts for action configurations.
|
|
13
|
+
* It provides uniform testing for all types of nodes: simple wait nodes, router-based
|
|
14
|
+
* split nodes, and complex form-configured nodes.
|
|
15
|
+
*/
|
|
16
|
+
export class NodeTest<T extends Node> {
|
|
17
|
+
constructor(private nodeConfig: any, private nodeName: string) {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Renders a node in the flow editor and returns the flow node
|
|
21
|
+
*/
|
|
22
|
+
private async renderNode(node: T, nodeUI: any): Promise<HTMLElement> {
|
|
23
|
+
const mockDefinition = {
|
|
24
|
+
nodes: [node],
|
|
25
|
+
_ui: {
|
|
26
|
+
nodes: {
|
|
27
|
+
[node.uuid]: {
|
|
28
|
+
type: nodeUI.type,
|
|
29
|
+
position: { left: 50, top: 50 },
|
|
30
|
+
...nodeUI
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const editor = (await fixture(html`
|
|
37
|
+
<temba-flow-editor>
|
|
38
|
+
<div id="canvas"></div>
|
|
39
|
+
</temba-flow-editor>
|
|
40
|
+
`)) as Editor;
|
|
41
|
+
|
|
42
|
+
(editor as any).definition = mockDefinition;
|
|
43
|
+
(editor as any).canvasSize = { width: 400, height: 300 };
|
|
44
|
+
await editor.updateComplete;
|
|
45
|
+
|
|
46
|
+
const flowNode = editor.querySelector('temba-flow-node') as HTMLElement;
|
|
47
|
+
expect(flowNode).to.exist;
|
|
48
|
+
|
|
49
|
+
return flowNode;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Opens the node editor for a node and returns the editor element
|
|
54
|
+
*/
|
|
55
|
+
private async openNodeEditor(node: T, nodeUI: any): Promise<HTMLElement> {
|
|
56
|
+
const nodeEditor = (await fixture(html`
|
|
57
|
+
<temba-node-editor
|
|
58
|
+
.node=${node}
|
|
59
|
+
.nodeUI=${nodeUI}
|
|
60
|
+
.isOpen=${true}
|
|
61
|
+
></temba-node-editor>
|
|
62
|
+
`)) as HTMLElement;
|
|
63
|
+
|
|
64
|
+
await (nodeEditor as any).updateComplete;
|
|
65
|
+
|
|
66
|
+
// Wait for form data initialization if needed
|
|
67
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
68
|
+
await (nodeEditor as any).updateComplete;
|
|
69
|
+
|
|
70
|
+
expect(nodeEditor).to.exist;
|
|
71
|
+
|
|
72
|
+
return nodeEditor;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Takes a screenshot of the dialog container within a node editor
|
|
77
|
+
*/
|
|
78
|
+
private async assertDialogScreenshot(
|
|
79
|
+
el: HTMLElement,
|
|
80
|
+
screenshotName: string
|
|
81
|
+
) {
|
|
82
|
+
const dialog = el.shadowRoot
|
|
83
|
+
?.querySelector('temba-dialog')
|
|
84
|
+
?.shadowRoot?.querySelector('.dialog-container') as HTMLElement;
|
|
85
|
+
await assertScreenshot(screenshotName, getClip(dialog));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Complete test for a node configuration
|
|
90
|
+
* 1. Renders the node in a flow node (with screenshot)
|
|
91
|
+
* 2. Opens the node editor (with screenshot)
|
|
92
|
+
* 3. Simulates save and validates round-trip conversion
|
|
93
|
+
*/
|
|
94
|
+
async testNode(node: T, nodeUI: any, testName: string) {
|
|
95
|
+
it(`${testName}`, async () => {
|
|
96
|
+
// Step 1: Render node in flow node
|
|
97
|
+
const flowNode = await this.renderNode(node, nodeUI);
|
|
98
|
+
|
|
99
|
+
// For execute_actions nodes, check for .body, for router nodes check for .router or .categories
|
|
100
|
+
const hasContent =
|
|
101
|
+
flowNode.querySelector('.body') ||
|
|
102
|
+
flowNode.querySelector('.router') ||
|
|
103
|
+
flowNode.querySelector('.categories') ||
|
|
104
|
+
flowNode.querySelector('.action') ||
|
|
105
|
+
flowNode.textContent?.trim();
|
|
106
|
+
|
|
107
|
+
expect(hasContent).to.exist;
|
|
108
|
+
await assertScreenshot(
|
|
109
|
+
`nodes/${this.nodeName}/render/${testName}`,
|
|
110
|
+
getClip(flowNode)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Step 2: Open node editor
|
|
114
|
+
const nodeEditor = await this.openNodeEditor(node, nodeUI);
|
|
115
|
+
await this.assertDialogScreenshot(
|
|
116
|
+
nodeEditor,
|
|
117
|
+
`nodes/${this.nodeName}/editor/${testName}`
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Step 3: Test round-trip conversion (simulates save workflow)
|
|
121
|
+
if (this.nodeConfig.toFormData && this.nodeConfig.fromFormData) {
|
|
122
|
+
const formData = this.nodeConfig.toFormData(node);
|
|
123
|
+
const convertedNode = this.nodeConfig.fromFormData(formData, node) as T;
|
|
124
|
+
|
|
125
|
+
// Validate the round trip worked
|
|
126
|
+
expect(convertedNode.uuid).to.equal(node.uuid);
|
|
127
|
+
|
|
128
|
+
// Validate the converted node has expected structure
|
|
129
|
+
expect(convertedNode).to.have.property('actions');
|
|
130
|
+
expect(convertedNode).to.have.property('exits');
|
|
131
|
+
|
|
132
|
+
expect(convertedNode).to.deep.equal(node);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Run basic property tests
|
|
139
|
+
*/
|
|
140
|
+
testBasicProperties() {
|
|
141
|
+
it('has correct basic properties', () => {
|
|
142
|
+
expect(this.nodeConfig.type).to.be.a('string');
|
|
143
|
+
|
|
144
|
+
// Name is optional - only some node configs have it
|
|
145
|
+
if (this.nodeConfig.name) {
|
|
146
|
+
expect(this.nodeConfig.name).to.be.a('string');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Color is optional
|
|
150
|
+
if (this.nodeConfig.color) {
|
|
151
|
+
expect(this.nodeConfig.color).to.be.a('string');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// toFormData and fromFormData are optional - only needed for complex data transformations
|
|
155
|
+
if (this.nodeConfig.toFormData) {
|
|
156
|
+
expect(this.nodeConfig.toFormData).to.be.a('function');
|
|
157
|
+
}
|
|
158
|
+
if (this.nodeConfig.fromFormData) {
|
|
159
|
+
expect(this.nodeConfig.fromFormData).to.be.a('function');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Form configuration is optional
|
|
163
|
+
if (this.nodeConfig.form) {
|
|
164
|
+
expect(this.nodeConfig.form).to.be.an('object');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Layout is optional
|
|
168
|
+
if (this.nodeConfig.layout) {
|
|
169
|
+
expect(this.nodeConfig.layout).to.be.an('array');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Router config is optional
|
|
173
|
+
if (this.nodeConfig.router) {
|
|
174
|
+
expect(this.nodeConfig.router).to.be.an('object');
|
|
175
|
+
expect(this.nodeConfig.router.type).to.exist;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Render function is optional
|
|
179
|
+
if (this.nodeConfig.render) {
|
|
180
|
+
expect(this.nodeConfig.render).to.be.a('function');
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing';
|
|
2
|
+
import { call_llm } from '../../src/flow/actions/call_llm';
|
|
3
|
+
import { CallLLM } from '../../src/store/flow-definition';
|
|
4
|
+
import { ActionTest } from '../ActionHelper';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Test suite for the call_llm action configuration.
|
|
8
|
+
*/
|
|
9
|
+
describe('call_llm action config', () => {
|
|
10
|
+
const helper = new ActionTest(call_llm, 'call_llm');
|
|
11
|
+
|
|
12
|
+
describe('basic properties', () => {
|
|
13
|
+
helper.testBasicProperties();
|
|
14
|
+
|
|
15
|
+
it('has correct name', () => {
|
|
16
|
+
expect(call_llm.name).to.equal('Call AI');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('has form configuration', () => {
|
|
20
|
+
expect(call_llm.form).to.exist;
|
|
21
|
+
expect(call_llm.form.llm).to.exist;
|
|
22
|
+
expect(call_llm.form.instructions).to.exist;
|
|
23
|
+
expect(call_llm.form.input).to.exist;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('has layout configuration', () => {
|
|
27
|
+
expect(call_llm.layout).to.exist;
|
|
28
|
+
expect(call_llm.layout).to.deep.equal(['llm', 'input', 'instructions']);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('has data transformation functions', () => {
|
|
32
|
+
expect(call_llm.toFormData).to.be.a('function');
|
|
33
|
+
expect(call_llm.fromFormData).to.be.a('function');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('data transformations', () => {
|
|
38
|
+
it('converts action to form data correctly', () => {
|
|
39
|
+
const action: CallLLM = {
|
|
40
|
+
uuid: 'test-llm-1',
|
|
41
|
+
type: 'call_llm',
|
|
42
|
+
input: '@input',
|
|
43
|
+
llm: { uuid: 'gpt-4', name: 'GPT 4.1' },
|
|
44
|
+
instructions: 'Translate to French',
|
|
45
|
+
result_name: 'translated_text'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const formData = call_llm.toFormData(action);
|
|
49
|
+
|
|
50
|
+
expect(formData.uuid).to.equal('test-llm-1');
|
|
51
|
+
expect(formData.llm).to.deep.equal([{ value: 'gpt-4', name: 'GPT 4.1' }]);
|
|
52
|
+
expect(formData.instructions).to.equal('Translate to French');
|
|
53
|
+
expect(formData.input).to.equal('@input');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('converts form data to action correctly', () => {
|
|
57
|
+
const formData = {
|
|
58
|
+
uuid: 'test-llm-2',
|
|
59
|
+
llm: [{ value: 'gpt-5', name: 'GPT 5' }],
|
|
60
|
+
instructions: 'Summarize the following text',
|
|
61
|
+
input: '@input'
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const action = call_llm.fromFormData(formData) as CallLLM;
|
|
65
|
+
|
|
66
|
+
expect(action.uuid).to.equal('test-llm-2');
|
|
67
|
+
expect(action.type).to.equal('call_llm');
|
|
68
|
+
expect(action.llm).to.deep.equal({ uuid: 'gpt-5', name: 'GPT 5' });
|
|
69
|
+
expect(action.instructions).to.equal('Summarize the following text');
|
|
70
|
+
expect(action.input).to.equal('@input');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('handles empty form data', () => {
|
|
74
|
+
const formData = {
|
|
75
|
+
uuid: 'test-llm-3',
|
|
76
|
+
llm: [],
|
|
77
|
+
instructions: '',
|
|
78
|
+
input: ''
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const action = call_llm.fromFormData(formData) as CallLLM;
|
|
82
|
+
|
|
83
|
+
expect(action.llm).to.deep.equal({ uuid: '', name: '' });
|
|
84
|
+
expect(action.instructions).to.equal('');
|
|
85
|
+
expect(action.input).to.equal('@input');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('action scenarios', () => {
|
|
90
|
+
helper.testAction(
|
|
91
|
+
{
|
|
92
|
+
uuid: 'test-action-1',
|
|
93
|
+
type: 'call_llm',
|
|
94
|
+
llm: { uuid: 'gpt-4', name: 'GPT 4.1' },
|
|
95
|
+
instructions: 'Translate to French',
|
|
96
|
+
input: '@input'
|
|
97
|
+
} as CallLLM,
|
|
98
|
+
'translation-task'
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
helper.testAction(
|
|
102
|
+
{
|
|
103
|
+
uuid: 'test-action-2',
|
|
104
|
+
type: 'call_llm',
|
|
105
|
+
llm: { uuid: 'gpt-5', name: 'GPT 5' },
|
|
106
|
+
instructions:
|
|
107
|
+
'Analyze the sentiment of the following message and classify it as positive, negative, or neutral. Provide a brief explanation for your classification.',
|
|
108
|
+
input: '@input'
|
|
109
|
+
} as CallLLM,
|
|
110
|
+
'sentiment-analysis'
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
helper.testAction(
|
|
114
|
+
{
|
|
115
|
+
uuid: 'test-action-3',
|
|
116
|
+
type: 'call_llm',
|
|
117
|
+
llm: { uuid: 'gpt-4', name: 'GPT 4.1' },
|
|
118
|
+
instructions:
|
|
119
|
+
'Summarize the key points from the conversation above in bullet format.',
|
|
120
|
+
input: '@input'
|
|
121
|
+
} as CallLLM,
|
|
122
|
+
'summarization'
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
helper.testAction(
|
|
126
|
+
{
|
|
127
|
+
uuid: 'test-action-4',
|
|
128
|
+
type: 'call_llm',
|
|
129
|
+
llm: { uuid: 'gpt-5', name: 'GPT 5' },
|
|
130
|
+
instructions:
|
|
131
|
+
'Extract any contact information (phone numbers, email addresses) from the text and format them as a JSON object.',
|
|
132
|
+
input: '@input'
|
|
133
|
+
} as CallLLM,
|
|
134
|
+
'information-extraction'
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Node Configuration Tests
|
|
2
|
+
|
|
3
|
+
This directory contains tests for node configurations using the `NodeHelper` testing framework.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Similar to how the `ActionHelper` provides a uniform testing strategy for action configurations, the `NodeHelper` class provides a structured approach to testing node configurations. It handles:
|
|
8
|
+
|
|
9
|
+
1. **Rendering Tests**: Verifies nodes render correctly in the flow editor
|
|
10
|
+
2. **Editor Tests**: Verifies the node editor opens and displays correctly
|
|
11
|
+
3. **Round-trip Conversion**: Tests form data conversion for nodes with `toFormData`/`fromFormData`
|
|
12
|
+
4. **Screenshot Testing**: Captures visual screenshots for regression testing
|
|
13
|
+
|
|
14
|
+
## Test Structure
|
|
15
|
+
|
|
16
|
+
Each node test file follows this pattern:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { expect } from '@open-wc/testing';
|
|
20
|
+
import { node_config } from '../../src/flow/nodes/node_config';
|
|
21
|
+
import { Node } from '../../src/store/flow-definition';
|
|
22
|
+
import { NodeTest } from '../NodeHelper';
|
|
23
|
+
|
|
24
|
+
describe('node_config node config', () => {
|
|
25
|
+
const helper = new NodeTest(node_config, 'node_config');
|
|
26
|
+
|
|
27
|
+
describe('basic properties', () => {
|
|
28
|
+
helper.testBasicProperties();
|
|
29
|
+
|
|
30
|
+
// Additional property tests...
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('node scenarios', () => {
|
|
34
|
+
helper.testNode(nodeData, nodeUI, 'test-name');
|
|
35
|
+
|
|
36
|
+
// More scenarios...
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Optional: data transformation tests for nodes with form configuration
|
|
40
|
+
describe('data transformation', () => {
|
|
41
|
+
// Round-trip conversion tests...
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Node Types Covered
|
|
47
|
+
|
|
48
|
+
### Simple Nodes
|
|
49
|
+
|
|
50
|
+
- `wait_for_digits`: Basic node with no form configuration
|
|
51
|
+
- `wait_for_audio`, `wait_for_image`, etc.: Similar wait nodes
|
|
52
|
+
|
|
53
|
+
### Form-Configured Nodes
|
|
54
|
+
|
|
55
|
+
- `wait_for_response`: Node with form fields and data transformation
|
|
56
|
+
- `split_by_llm_categorize`: Complex node with form configuration
|
|
57
|
+
|
|
58
|
+
### Router-Based Nodes
|
|
59
|
+
|
|
60
|
+
- `split_by_random`: Random distribution node
|
|
61
|
+
- Other split nodes with router configurations
|
|
62
|
+
|
|
63
|
+
## Screenshots
|
|
64
|
+
|
|
65
|
+
Screenshots are automatically generated and stored in:
|
|
66
|
+
|
|
67
|
+
- `screenshots/nodes/{node_name}/render/{test_name}.png` - Flow editor rendering
|
|
68
|
+
- `screenshots/nodes/{node_name}/editor/{test_name}.png` - Node editor dialog
|
|
69
|
+
|
|
70
|
+
## Running Tests
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Run all node tests
|
|
74
|
+
yarn test test/nodes/*.test.ts --no-watch
|
|
75
|
+
|
|
76
|
+
# Run specific node test
|
|
77
|
+
yarn test test/nodes/wait_for_response.test.ts --no-watch
|
|
78
|
+
```
|