@nyaruka/temba-components 0.140.0 → 0.141.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/.lintstagedrc.js +10 -0
- package/CHANGELOG.md +22 -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 +11 -2
- 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 +263 -156
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +1 -1
- package/out-tsc/src/display/FloatingTab.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +1 -1
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +239 -43
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/Plumber.js +61 -14
- package/out-tsc/src/flow/Plumber.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js +4 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +4 -1
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js +6 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/send_broadcast.js +6 -2
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +13 -0
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +7 -5
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +10 -3
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_contact_field.js +18 -5
- package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_expression.js +1 -1
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +0 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +0 -1
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_run_result.js +10 -4
- package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js +1 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +7 -0
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/layout/Dialog.js +0 -1
- package/out-tsc/src/layout/Dialog.js.map +1 -1
- package/out-tsc/src/layout/Modax.js +20 -2
- package/out-tsc/src/layout/Modax.js.map +1 -1
- package/out-tsc/src/list/ContentMenu.js +14 -1
- package/out-tsc/src/list/ContentMenu.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +10 -1
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/live/TembaChart.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 +11 -2
- 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 +11 -0
- package/out-tsc/src/simulator/Simulator.js.map +1 -1
- package/out-tsc/src/store/AppState.js +13 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/version.js +9 -0
- package/out-tsc/src/version.js.map +1 -0
- package/out-tsc/test/actions/add_contact_groups.test.js +35 -0
- package/out-tsc/test/actions/add_contact_groups.test.js.map +1 -1
- package/out-tsc/test/actions/add_input_labels.test.js +53 -0
- package/out-tsc/test/actions/add_input_labels.test.js.map +1 -0
- package/out-tsc/test/actions/enter_flow.test.js +71 -0
- package/out-tsc/test/actions/enter_flow.test.js.map +1 -0
- package/out-tsc/test/actions/remove_contact_groups.test.js +24 -0
- package/out-tsc/test/actions/remove_contact_groups.test.js.map +1 -1
- package/out-tsc/test/actions/send_broadcast.test.js +41 -0
- package/out-tsc/test/actions/send_broadcast.test.js.map +1 -1
- package/out-tsc/test/actions/set_contact_channel.test.js +67 -0
- package/out-tsc/test/actions/set_contact_channel.test.js.map +1 -0
- package/out-tsc/test/actions/set_contact_field.test.js +52 -0
- package/out-tsc/test/actions/set_contact_field.test.js.map +1 -0
- package/out-tsc/test/actions/set_contact_language.test.js +39 -0
- package/out-tsc/test/actions/set_contact_language.test.js.map +1 -0
- package/out-tsc/test/actions/set_contact_name.test.js +28 -0
- package/out-tsc/test/actions/set_contact_name.test.js.map +1 -0
- package/out-tsc/test/actions/set_contact_status.test.js +44 -0
- package/out-tsc/test/actions/set_contact_status.test.js.map +1 -0
- package/out-tsc/test/actions/set_run_result.test.js +47 -0
- package/out-tsc/test/actions/set_run_result.test.js.map +1 -0
- package/out-tsc/test/actions/start_session.test.js +76 -0
- package/out-tsc/test/actions/start_session.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_contact_field.test.js +50 -0
- package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_run_result.test.js +82 -0
- package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_ticket.test.js +139 -0
- package/out-tsc/test/nodes/split_by_ticket.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_webhook.test.js +111 -0
- package/out-tsc/test/nodes/split_by_webhook.test.js.map +1 -0
- package/out-tsc/test/temba-contact-chat.test.js +12 -0
- package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor.test.js +206 -0
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
- package/out-tsc/test/temba-flow-plumber.test.js +19 -0
- package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +4 -1
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +4 -2
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +3 -9
- package/rollup.components.mjs +7 -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/add_input_labels/editor/multiple-labels.png +0 -0
- package/screenshots/truth/actions/add_input_labels/editor/single-label.png +0 -0
- package/screenshots/truth/actions/add_input_labels/render/multiple-labels.png +0 -0
- package/screenshots/truth/actions/add_input_labels/render/single-label.png +0 -0
- package/screenshots/truth/actions/enter_flow/editor/basic-flow.png +0 -0
- package/screenshots/truth/actions/enter_flow/editor/long-flow-name.png +0 -0
- package/screenshots/truth/actions/enter_flow/render/basic-flow.png +0 -0
- package/screenshots/truth/actions/enter_flow/render/long-flow-name.png +0 -0
- package/screenshots/truth/actions/play_audio/render/expression-url.png +0 -0
- package/screenshots/truth/actions/play_audio/render/static-url.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/say_msg/render/multiline-text.png +0 -0
- package/screenshots/truth/actions/say_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/say_msg/render/text-with-audio-url.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/set_contact_channel/editor/sms-channel.png +0 -0
- package/screenshots/truth/actions/set_contact_channel/editor/whatsapp-channel.png +0 -0
- package/screenshots/truth/actions/set_contact_channel/render/sms-channel.png +0 -0
- package/screenshots/truth/actions/set_contact_channel/render/whatsapp-channel.png +0 -0
- package/screenshots/truth/actions/set_contact_field/editor/clear-value.png +0 -0
- package/screenshots/truth/actions/set_contact_field/editor/set-value.png +0 -0
- package/screenshots/truth/actions/set_contact_field/render/clear-value.png +0 -0
- package/screenshots/truth/actions/set_contact_field/render/set-value.png +0 -0
- package/screenshots/truth/actions/set_contact_language/editor/english.png +0 -0
- package/screenshots/truth/actions/set_contact_language/editor/french.png +0 -0
- package/screenshots/truth/actions/set_contact_language/render/english.png +0 -0
- package/screenshots/truth/actions/set_contact_language/render/french.png +0 -0
- package/screenshots/truth/actions/set_contact_name/editor/expression-name.png +0 -0
- package/screenshots/truth/actions/set_contact_name/editor/static-name.png +0 -0
- package/screenshots/truth/actions/set_contact_name/render/expression-name.png +0 -0
- package/screenshots/truth/actions/set_contact_name/render/static-name.png +0 -0
- package/screenshots/truth/actions/set_contact_status/editor/active.png +0 -0
- package/screenshots/truth/actions/set_contact_status/editor/archived.png +0 -0
- package/screenshots/truth/actions/set_contact_status/editor/blocked.png +0 -0
- package/screenshots/truth/actions/set_contact_status/render/active.png +0 -0
- package/screenshots/truth/actions/set_contact_status/render/archived.png +0 -0
- package/screenshots/truth/actions/set_contact_status/render/blocked.png +0 -0
- package/screenshots/truth/actions/set_run_result/editor/expression-value.png +0 -0
- package/screenshots/truth/actions/set_run_result/editor/with-category.png +0 -0
- package/screenshots/truth/actions/set_run_result/render/expression-value.png +0 -0
- package/screenshots/truth/actions/set_run_result/render/with-category.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/editor/wait.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/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_audio/render/basic-audio-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/render/basic-dial.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/render/dial-with-limits.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/digits-with-rules.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/digits-with-rules.png +0 -0
- package/screenshots/truth/nodes/wait_for_menu/render/menu-with-digits.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/src/display/FloatingTab.ts +1 -1
- package/src/flow/CanvasNode.ts +1 -1
- package/src/flow/Editor.ts +299 -88
- package/src/flow/Plumber.ts +89 -14
- package/src/flow/actions/add_contact_groups.ts +4 -1
- package/src/flow/actions/add_input_labels.ts +4 -1
- package/src/flow/actions/remove_contact_groups.ts +6 -1
- package/src/flow/actions/send_broadcast.ts +6 -2
- package/src/flow/actions/set_contact_channel.ts +13 -1
- package/src/flow/actions/set_contact_status.ts +7 -5
- package/src/flow/actions/start_session.ts +10 -3
- package/src/flow/nodes/split_by_contact_field.ts +16 -5
- package/src/flow/nodes/split_by_expression.ts +1 -1
- package/src/flow/nodes/split_by_llm_categorize.ts +0 -1
- package/src/flow/nodes/split_by_random.ts +0 -1
- package/src/flow/nodes/split_by_run_result.ts +10 -4
- package/src/flow/nodes/wait_for_digits.ts +2 -1
- package/src/flow/nodes/wait_for_response.ts +1 -1
- package/src/form/FieldRenderer.ts +7 -0
- package/src/layout/Dialog.ts +0 -1
- package/src/layout/Modax.ts +19 -2
- package/src/list/ContentMenu.ts +15 -1
- package/src/live/ContactChat.ts +10 -1
- package/src/live/TembaChart.ts +1 -1
- package/src/locales/es.ts +18 -13
- package/src/locales/fr.ts +18 -13
- package/src/locales/locale-codes.ts +11 -2
- package/src/locales/pt.ts +18 -13
- package/src/simulator/Simulator.ts +12 -0
- package/src/store/AppState.ts +15 -0
- package/src/store/flow-definition.d.ts +1 -0
- package/src/version.ts +10 -0
- package/test/actions/add_contact_groups.test.ts +38 -0
- package/test/actions/add_input_labels.test.ts +67 -0
- package/test/actions/enter_flow.test.ts +88 -0
- package/test/actions/remove_contact_groups.test.ts +29 -0
- package/test/actions/send_broadcast.test.ts +44 -0
- package/test/actions/set_contact_channel.test.ts +88 -0
- package/test/actions/set_contact_field.test.ts +68 -0
- package/test/actions/set_contact_language.test.ts +55 -0
- package/test/actions/set_contact_name.test.ts +39 -0
- package/test/actions/set_contact_status.test.ts +64 -0
- package/test/actions/set_run_result.test.ts +61 -0
- package/test/actions/start_session.test.ts +82 -0
- package/test/nodes/split_by_contact_field.test.ts +59 -0
- package/test/nodes/split_by_run_result.test.ts +100 -0
- package/test/nodes/split_by_ticket.test.ts +157 -0
- package/test/nodes/split_by_webhook.test.ts +131 -0
- package/test/temba-contact-chat.test.ts +17 -0
- package/test/temba-flow-editor.test.ts +264 -0
- package/test/temba-flow-plumber.test.ts +62 -0
- package/test/temba-select.test.ts +6 -1
- package/test/utils.test.ts +4 -2
- package/web-dev-server.config.mjs +5 -1
- package/web-test-runner.config.mjs +4 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing';
|
|
2
|
+
import { split_by_webhook } from '../../src/flow/nodes/split_by_webhook';
|
|
3
|
+
import { NodeTest } from '../NodeHelper';
|
|
4
|
+
/**
|
|
5
|
+
* Test suite for the split_by_webhook node configuration.
|
|
6
|
+
*/
|
|
7
|
+
describe('split_by_webhook node config', () => {
|
|
8
|
+
const helper = new NodeTest(split_by_webhook, 'split_by_webhook');
|
|
9
|
+
describe('basic properties', () => {
|
|
10
|
+
helper.testBasicProperties();
|
|
11
|
+
it('has correct type and name', () => {
|
|
12
|
+
expect(split_by_webhook.type).to.equal('split_by_webhook');
|
|
13
|
+
expect(split_by_webhook.name).to.equal('Call Webhook');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
describe('round-trip transformation', () => {
|
|
17
|
+
it('should transform from flow definition to form data', () => {
|
|
18
|
+
const node = {
|
|
19
|
+
uuid: 'test-node',
|
|
20
|
+
actions: [
|
|
21
|
+
{
|
|
22
|
+
type: 'call_webhook',
|
|
23
|
+
uuid: 'action-1',
|
|
24
|
+
method: 'POST',
|
|
25
|
+
url: 'https://example.com/webhook',
|
|
26
|
+
headers: { Authorization: 'Bearer token123' },
|
|
27
|
+
body: '{"key": "value"}'
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
exits: []
|
|
31
|
+
};
|
|
32
|
+
const formData = split_by_webhook.toFormData(node);
|
|
33
|
+
expect(formData.uuid).to.equal('test-node');
|
|
34
|
+
expect(formData.method).to.equal('POST');
|
|
35
|
+
expect(formData.url).to.equal('https://example.com/webhook');
|
|
36
|
+
expect(formData.headers).to.deep.equal({
|
|
37
|
+
Authorization: 'Bearer token123'
|
|
38
|
+
});
|
|
39
|
+
expect(formData.body).to.equal('{"key": "value"}');
|
|
40
|
+
});
|
|
41
|
+
it('should transform from form data to flow definition (GET)', () => {
|
|
42
|
+
const originalNode = {
|
|
43
|
+
uuid: 'test-node',
|
|
44
|
+
actions: [],
|
|
45
|
+
exits: []
|
|
46
|
+
};
|
|
47
|
+
const formData = {
|
|
48
|
+
uuid: 'test-node',
|
|
49
|
+
method: [{ value: 'GET', name: 'GET' }],
|
|
50
|
+
url: 'https://example.com/api',
|
|
51
|
+
headers: [],
|
|
52
|
+
body: ''
|
|
53
|
+
};
|
|
54
|
+
const resultNode = split_by_webhook.fromFormData(formData, originalNode);
|
|
55
|
+
expect(resultNode.uuid).to.equal('test-node');
|
|
56
|
+
expect(resultNode.actions).to.have.lengthOf(1);
|
|
57
|
+
expect(resultNode.actions[0].type).to.equal('call_webhook');
|
|
58
|
+
const action = resultNode.actions[0];
|
|
59
|
+
expect(action.method).to.equal('GET');
|
|
60
|
+
expect(action.url).to.equal('https://example.com/api');
|
|
61
|
+
// Should have Success/Failure router
|
|
62
|
+
expect(resultNode.router).to.exist;
|
|
63
|
+
expect(resultNode.router.type).to.equal('switch');
|
|
64
|
+
expect(resultNode.exits).to.have.lengthOf(2);
|
|
65
|
+
});
|
|
66
|
+
it('should transform from form data to flow definition (POST)', () => {
|
|
67
|
+
const originalNode = {
|
|
68
|
+
uuid: 'test-node',
|
|
69
|
+
actions: [],
|
|
70
|
+
exits: []
|
|
71
|
+
};
|
|
72
|
+
const formData = {
|
|
73
|
+
uuid: 'test-node',
|
|
74
|
+
method: [{ value: 'POST', name: 'POST' }],
|
|
75
|
+
url: 'https://example.com/webhook',
|
|
76
|
+
headers: [{ name: 'Content-Type', value: 'application/json' }],
|
|
77
|
+
body: '{"data": "@contact.name"}'
|
|
78
|
+
};
|
|
79
|
+
const resultNode = split_by_webhook.fromFormData(formData, originalNode);
|
|
80
|
+
const action = resultNode.actions[0];
|
|
81
|
+
expect(action.method).to.equal('POST');
|
|
82
|
+
expect(action.url).to.equal('https://example.com/webhook');
|
|
83
|
+
expect(action.headers).to.have.lengthOf(1);
|
|
84
|
+
expect(action.body).to.equal('{"data": "@contact.name"}');
|
|
85
|
+
});
|
|
86
|
+
it('should preserve action UUID on re-save', () => {
|
|
87
|
+
const originalNode = {
|
|
88
|
+
uuid: 'test-node',
|
|
89
|
+
actions: [
|
|
90
|
+
{
|
|
91
|
+
type: 'call_webhook',
|
|
92
|
+
uuid: 'existing-action-uuid',
|
|
93
|
+
method: 'GET',
|
|
94
|
+
url: 'https://example.com/old'
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
exits: []
|
|
98
|
+
};
|
|
99
|
+
const formData = {
|
|
100
|
+
uuid: 'test-node',
|
|
101
|
+
method: [{ value: 'GET', name: 'GET' }],
|
|
102
|
+
url: 'https://example.com/new',
|
|
103
|
+
headers: [],
|
|
104
|
+
body: ''
|
|
105
|
+
};
|
|
106
|
+
const resultNode = split_by_webhook.fromFormData(formData, originalNode);
|
|
107
|
+
expect(resultNode.actions[0].uuid).to.equal('existing-action-uuid');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=split_by_webhook.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"split_by_webhook.test.js","sourceRoot":"","sources":["../../../test/nodes/split_by_webhook.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { split_by_webhook } from '../../src/flow/nodes/split_by_webhook';\nimport { Node, CallWebhook } from '../../src/store/flow-definition';\nimport { NodeTest } from '../NodeHelper';\n\n/**\n * Test suite for the split_by_webhook node configuration.\n */\ndescribe('split_by_webhook node config', () => {\n const helper = new NodeTest(split_by_webhook, 'split_by_webhook');\n\n describe('basic properties', () => {\n helper.testBasicProperties();\n\n it('has correct type and name', () => {\n expect(split_by_webhook.type).to.equal('split_by_webhook');\n expect(split_by_webhook.name).to.equal('Call Webhook');\n });\n });\n\n describe('round-trip transformation', () => {\n it('should transform from flow definition to form data', () => {\n const node: Node = {\n uuid: 'test-node',\n actions: [\n {\n type: 'call_webhook',\n uuid: 'action-1',\n method: 'POST',\n url: 'https://example.com/webhook',\n headers: { Authorization: 'Bearer token123' },\n body: '{\"key\": \"value\"}'\n } as CallWebhook\n ],\n exits: []\n };\n\n const formData = split_by_webhook.toFormData!(node);\n\n expect(formData.uuid).to.equal('test-node');\n expect(formData.method).to.equal('POST');\n expect(formData.url).to.equal('https://example.com/webhook');\n expect(formData.headers).to.deep.equal({\n Authorization: 'Bearer token123'\n });\n expect(formData.body).to.equal('{\"key\": \"value\"}');\n });\n\n it('should transform from form data to flow definition (GET)', () => {\n const originalNode: Node = {\n uuid: 'test-node',\n actions: [],\n exits: []\n };\n\n const formData = {\n uuid: 'test-node',\n method: [{ value: 'GET', name: 'GET' }],\n url: 'https://example.com/api',\n headers: [],\n body: ''\n };\n\n const resultNode = split_by_webhook.fromFormData!(formData, originalNode);\n\n expect(resultNode.uuid).to.equal('test-node');\n expect(resultNode.actions).to.have.lengthOf(1);\n expect(resultNode.actions![0].type).to.equal('call_webhook');\n\n const action = resultNode.actions![0] as any;\n expect(action.method).to.equal('GET');\n expect(action.url).to.equal('https://example.com/api');\n\n // Should have Success/Failure router\n expect(resultNode.router).to.exist;\n expect(resultNode.router!.type).to.equal('switch');\n expect(resultNode.exits).to.have.lengthOf(2);\n });\n\n it('should transform from form data to flow definition (POST)', () => {\n const originalNode: Node = {\n uuid: 'test-node',\n actions: [],\n exits: []\n };\n\n const formData = {\n uuid: 'test-node',\n method: [{ value: 'POST', name: 'POST' }],\n url: 'https://example.com/webhook',\n headers: [{ name: 'Content-Type', value: 'application/json' }],\n body: '{\"data\": \"@contact.name\"}'\n };\n\n const resultNode = split_by_webhook.fromFormData!(formData, originalNode);\n\n const action = resultNode.actions![0] as any;\n expect(action.method).to.equal('POST');\n expect(action.url).to.equal('https://example.com/webhook');\n expect(action.headers).to.have.lengthOf(1);\n expect(action.body).to.equal('{\"data\": \"@contact.name\"}');\n });\n\n it('should preserve action UUID on re-save', () => {\n const originalNode: Node = {\n uuid: 'test-node',\n actions: [\n {\n type: 'call_webhook',\n uuid: 'existing-action-uuid',\n method: 'GET',\n url: 'https://example.com/old'\n } as CallWebhook\n ],\n exits: []\n };\n\n const formData = {\n uuid: 'test-node',\n method: [{ value: 'GET', name: 'GET' }],\n url: 'https://example.com/new',\n headers: [],\n body: ''\n };\n\n const resultNode = split_by_webhook.fromFormData!(formData, originalNode);\n\n expect(resultNode.actions![0].uuid).to.equal('existing-action-uuid');\n });\n });\n});\n"]}
|
|
@@ -70,6 +70,18 @@ describe('temba-contact-chat', () => {
|
|
|
70
70
|
});
|
|
71
71
|
await assertScreenshot('contacts/chat-for-stopped-contact', getClip(chat));
|
|
72
72
|
});
|
|
73
|
+
it('keeps flow footer from blocking scrollbar drag interactions', async () => {
|
|
74
|
+
await loadStore();
|
|
75
|
+
const chat = await getContactChat({
|
|
76
|
+
contact: 'contact-dave-active'
|
|
77
|
+
});
|
|
78
|
+
const flowFooter = chat.shadowRoot.querySelector('.flow-footer');
|
|
79
|
+
const inFlow = flowFooter.querySelector('.in-flow');
|
|
80
|
+
expect(flowFooter).to.exist;
|
|
81
|
+
expect(inFlow).to.exist;
|
|
82
|
+
expect(getComputedStyle(flowFooter).pointerEvents).to.equal('none');
|
|
83
|
+
expect(getComputedStyle(inFlow).pointerEvents).to.equal('auto');
|
|
84
|
+
});
|
|
73
85
|
it('sends text without attachments', async () => {
|
|
74
86
|
// we are a StoreElement, so load a store first
|
|
75
87
|
await loadStore();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temba-contact-chat.test.js","sourceRoot":"","sources":["../../test/temba-contact-chat.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { SinonStub, useFakeTimers } from 'sinon';\nimport { Compose } from '../src/form/Compose';\nimport { ContactChat } from '../src/live/ContactChat';\nimport { Attachment, CustomEventType } from '../src/interfaces';\nimport {\n assertScreenshot,\n clearMockPosts,\n getClip,\n getComponent,\n getValidAttachments,\n getValidText,\n loadStore,\n mockAPI,\n mockGET,\n mockNow,\n mockPOST,\n updateComponent\n} from '../test/utils.test';\n\nimport { expect, oneEvent } from '@open-wc/testing';\n\nlet clock: any;\n\nconst TAG = 'temba-contact-chat';\nconst getContactChat = async (attrs: any = {}) => {\n attrs['endpoint'] = '/test-assets/contacts/';\n // add some sizes and styles to force our chat history to scroll\n const chat = (await getComponent(\n TAG,\n attrs,\n '',\n 500,\n 500,\n 'display:flex;flex-direction:column;flex-grow:1;min-height:0;'\n )) as ContactChat;\n\n // TODO: this should be waiting for an event instead\n await waitFor(100);\n await clock.tick(0);\n return chat;\n};\n\nconst getResponseSuccessFiles = (attachments: Attachment[]) => {\n const response_attachments = attachments.map((attachment) => {\n return { content_type: attachment.content_type, url: attachment.url };\n });\n return response_attachments;\n};\n\ndescribe('temba-contact-chat', () => {\n let mockedNow: SinonStub;\n // map requests for contact history to our static files\n // we'll just us the same historylist for everybody for now\n beforeEach(() => {\n mockedNow = mockNow('2021-03-31T00:31:00.000-00:00');\n clearMockPosts();\n mockGET(\n /\\/contact\\/chat\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n\n mockGET(\n /\\/api\\/v2\\/users\\.json\\?email=admin1%40nyaruka\\.com/,\n '/test-assets/api/users/admin1.json'\n );\n\n mockAPI();\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n mockedNow.restore();\n });\n\n // temporarily disabled as it's too flaky in CI\n xit('show history and show chatbox if contact is active', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n showMessageLogsAfter: '2025-01-01T00:00:00.000Z'\n });\n\n await assertScreenshot('contacts/chat-for-active-contact', getClip(chat));\n });\n\n it('show history and hide chatbox if contact is archived', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-barack-archived',\n showMessageLogsAfter: '2025-01-01T00:00:00.000Z'\n });\n\n await assertScreenshot('contacts/chat-for-archived-contact', getClip(chat));\n });\n\n it('show history and hide chatbox if contact is blocked', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-michelle-blocked'\n });\n\n await assertScreenshot('contacts/chat-for-blocked-contact', getClip(chat));\n });\n\n it('show history and hide chatbox if contact is stopped', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-tim-stopped'\n });\n\n await assertScreenshot('contacts/chat-for-stopped-contact', getClip(chat));\n });\n\n it('sends text without attachments', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active'\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n const text = getValidText();\n await updateComponent(compose, text);\n\n const response_body = {\n event: {\n uuid: 'msg-uuid',\n contact: { uuid: 'contact-dave-active', name: 'Dave Matthews' },\n msg: {\n text: text,\n attachments: []\n }\n }\n };\n mockPOST(/contact\\/chat\\/contact-dave-active\\//, response_body);\n\n const listener = oneEvent(compose, CustomEventType.Submitted, false);\n await typeInto('temba-contact-chat:temba-compose', text, true, true);\n expect(await listener).to.exist;\n\n await assertScreenshot('contacts/chat-sends-text-only', getClip(chat));\n });\n\n it('sends attachments without text', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active'\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n const attachments = getValidAttachments();\n await updateComponent(compose, null, attachments);\n const response_attachments = getResponseSuccessFiles(attachments);\n const response_body = {\n event: {\n uuid: 'msg-uuid',\n contact: { uuid: 'contact-dave-active', name: 'Dave Matthews' },\n msg: {\n text: '',\n attachments: response_attachments\n }\n }\n };\n const response_headers = {};\n const response_status = '200';\n mockPOST(\n /contact\\/chat\\/contact-dave-active\\//,\n response_body,\n response_headers,\n response_status\n );\n\n const listener = oneEvent(compose, CustomEventType.Submitted, false);\n await typeInto('temba-contact-chat:temba-compose', '', false, true);\n expect(await listener).to.exist;\n\n await assertScreenshot(\n 'contacts/chat-sends-attachments-only',\n getClip(chat)\n );\n });\n\n it('sends text with attachments', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active'\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n const text = getValidText();\n const attachments = getValidAttachments();\n await updateComponent(compose, text, attachments);\n const response_attachments = getResponseSuccessFiles(attachments);\n const response_body = {\n event: {\n uuid: 'msg-uuid',\n contact: { uuid: 'contact-dave-active', name: 'Dave Matthews' },\n msg: {\n text,\n attachments: response_attachments\n }\n }\n };\n mockPOST(/contact\\/chat\\/contact-dave-active\\//, response_body);\n\n // press enter\n const listener = oneEvent(compose, CustomEventType.Submitted, false);\n await typeInto('temba-contact-chat:temba-compose', '', false, true);\n expect(await listener).to.exist;\n\n await assertScreenshot(\n 'contacts/chat-sends-text-and-attachments',\n getClip(chat)\n );\n });\n\n it('shows failure message with retry', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active'\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n await updateComponent(compose, getValidText(), getValidAttachments());\n\n const response_body = {};\n const response_headers = {};\n const response_status = '500';\n mockPOST(\n /api\\/v2\\/messages\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n // press\n const listener = oneEvent(compose, CustomEventType.Submitted, false);\n await typeInto('temba-contact-chat:temba-compose', '', false, true);\n expect(await listener).to.exist;\n\n await assertScreenshot('contacts/chat-failure', getClip(chat));\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"temba-contact-chat.test.js","sourceRoot":"","sources":["../../test/temba-contact-chat.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,aAAa,EAAE,MAAM,OAAO,CAAC;AAGjD,OAAO,EAAc,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,OAAO,EACP,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,SAAS,EACT,OAAO,EACP,OAAO,EACP,OAAO,EACP,QAAQ,EACR,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEpD,IAAI,KAAU,CAAC;AAEf,MAAM,GAAG,GAAG,oBAAoB,CAAC;AACjC,MAAM,cAAc,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE,EAAE;IAC/C,KAAK,CAAC,UAAU,CAAC,GAAG,wBAAwB,CAAC;IAC7C,gEAAgE;IAChE,MAAM,IAAI,GAAG,CAAC,MAAM,YAAY,CAC9B,GAAG,EACH,KAAK,EACL,EAAE,EACF,GAAG,EACH,GAAG,EACH,8DAA8D,CAC/D,CAAgB,CAAC;IAElB,oDAAoD;IACpD,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IACnB,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,uBAAuB,GAAG,CAAC,WAAyB,EAAE,EAAE;IAC5D,MAAM,oBAAoB,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;QAC1D,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,YAAY,EAAE,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,CAAC;IACxE,CAAC,CAAC,CAAC;IACH,OAAO,oBAAoB,CAAC;AAC9B,CAAC,CAAC;AAEF,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,IAAI,SAAoB,CAAC;IACzB,uDAAuD;IACvD,2DAA2D;IAC3D,UAAU,CAAC,GAAG,EAAE;QACd,SAAS,GAAG,OAAO,CAAC,+BAA+B,CAAC,CAAC;QACrD,cAAc,EAAE,CAAC;QACjB,OAAO,CACL,6BAA6B,EAC7B,oCAAoC,CACrC,CAAC;QAEF,OAAO,CACL,qDAAqD,EACrD,oCAAoC,CACrC,CAAC;QAEF,OAAO,EAAE,CAAC;QACV,KAAK,GAAG,aAAa,EAAE,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC;QACR,KAAK,CAAC,OAAO,EAAE,CAAC;QAChB,SAAS,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,+CAA+C;IAC/C,GAAG,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QACnE,+CAA+C;QAC/C,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,IAAI,GAAgB,MAAM,cAAc,CAAC;YAC7C,OAAO,EAAE,qBAAqB;YAC9B,oBAAoB,EAAE,0BAA0B;SACjD,CAAC,CAAC;QAEH,MAAM,gBAAgB,CAAC,kCAAkC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,+CAA+C;QAC/C,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,IAAI,GAAgB,MAAM,cAAc,CAAC;YAC7C,OAAO,EAAE,yBAAyB;YAClC,oBAAoB,EAAE,0BAA0B;SACjD,CAAC,CAAC;QAEH,MAAM,gBAAgB,CAAC,oCAAoC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,+CAA+C;QAC/C,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,IAAI,GAAgB,MAAM,cAAc,CAAC;YAC7C,OAAO,EAAE,0BAA0B;SACpC,CAAC,CAAC;QAEH,MAAM,gBAAgB,CAAC,mCAAmC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,+CAA+C;QAC/C,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,IAAI,GAAgB,MAAM,cAAc,CAAC;YAC7C,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;QAEH,MAAM,gBAAgB,CAAC,mCAAmC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,IAAI,GAAgB,MAAM,cAAc,CAAC;YAC7C,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAC9C,cAAc,CACA,CAAC;QACjB,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,CAAC,UAAU,CAAgB,CAAC;QAEnE,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QAC5B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QACxB,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACpE,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAC9C,+CAA+C;QAC/C,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,IAAI,GAAgB,MAAM,cAAc,CAAC;YAC7C,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,eAAe,CAAY,CAAC;QAC1E,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;QAC5B,MAAM,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAErC,MAAM,aAAa,GAAG;YACpB,KAAK,EAAE;gBACL,IAAI,EAAE,UAAU;gBAChB,OAAO,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,eAAe,EAAE;gBAC/D,GAAG,EAAE;oBACH,IAAI,EAAE,IAAI;oBACV,WAAW,EAAE,EAAE;iBAChB;aACF;SACF,CAAC;QACF,QAAQ,CAAC,sCAAsC,EAAE,aAAa,CAAC,CAAC;QAEhE,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACrE,MAAM,QAAQ,CAAC,kCAAkC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QAEhC,MAAM,gBAAgB,CAAC,+BAA+B,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAC9C,+CAA+C;QAC/C,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,IAAI,GAAgB,MAAM,cAAc,CAAC;YAC7C,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,eAAe,CAAY,CAAC;QAC1E,MAAM,WAAW,GAAG,mBAAmB,EAAE,CAAC;QAC1C,MAAM,eAAe,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QAClD,MAAM,oBAAoB,GAAG,uBAAuB,CAAC,WAAW,CAAC,CAAC;QAClE,MAAM,aAAa,GAAG;YACpB,KAAK,EAAE;gBACL,IAAI,EAAE,UAAU;gBAChB,OAAO,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,eAAe,EAAE;gBAC/D,GAAG,EAAE;oBACH,IAAI,EAAE,EAAE;oBACR,WAAW,EAAE,oBAAoB;iBAClC;aACF;SACF,CAAC;QACF,MAAM,gBAAgB,GAAG,EAAE,CAAC;QAC5B,MAAM,eAAe,GAAG,KAAK,CAAC;QAC9B,QAAQ,CACN,sCAAsC,EACtC,aAAa,EACb,gBAAgB,EAChB,eAAe,CAChB,CAAC;QAEF,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACrE,MAAM,QAAQ,CAAC,kCAAkC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QACpE,MAAM,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QAEhC,MAAM,gBAAgB,CACpB,sCAAsC,EACtC,OAAO,CAAC,IAAI,CAAC,CACd,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC3C,+CAA+C;QAC/C,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,IAAI,GAAgB,MAAM,cAAc,CAAC;YAC7C,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,eAAe,CAAY,CAAC;QAC1E,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,mBAAmB,EAAE,CAAC;QAC1C,MAAM,eAAe,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QAClD,MAAM,oBAAoB,GAAG,uBAAuB,CAAC,WAAW,CAAC,CAAC;QAClE,MAAM,aAAa,GAAG;YACpB,KAAK,EAAE;gBACL,IAAI,EAAE,UAAU;gBAChB,OAAO,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,eAAe,EAAE;gBAC/D,GAAG,EAAE;oBACH,IAAI;oBACJ,WAAW,EAAE,oBAAoB;iBAClC;aACF;SACF,CAAC;QACF,QAAQ,CAAC,sCAAsC,EAAE,aAAa,CAAC,CAAC;QAEhE,cAAc;QACd,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACrE,MAAM,QAAQ,CAAC,kCAAkC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QACpE,MAAM,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QAEhC,MAAM,gBAAgB,CACpB,0CAA0C,EAC1C,OAAO,CAAC,IAAI,CAAC,CACd,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,+CAA+C;QAC/C,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,IAAI,GAAgB,MAAM,cAAc,CAAC;YAC7C,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,eAAe,CAAY,CAAC;QAC1E,MAAM,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,mBAAmB,EAAE,CAAC,CAAC;QAEtE,MAAM,aAAa,GAAG,EAAE,CAAC;QACzB,MAAM,gBAAgB,GAAG,EAAE,CAAC;QAC5B,MAAM,eAAe,GAAG,KAAK,CAAC;QAC9B,QAAQ,CACN,yBAAyB,EACzB,aAAa,EACb,gBAAgB,EAChB,eAAe,CAChB,CAAC;QAEF,QAAQ;QACR,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACrE,MAAM,QAAQ,CAAC,kCAAkC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QACpE,MAAM,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QAEhC,MAAM,gBAAgB,CAAC,uBAAuB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { SinonStub, useFakeTimers } from 'sinon';\nimport { Compose } from '../src/form/Compose';\nimport { ContactChat } from '../src/live/ContactChat';\nimport { Attachment, CustomEventType } from '../src/interfaces';\nimport {\n assertScreenshot,\n clearMockPosts,\n getClip,\n getComponent,\n getValidAttachments,\n getValidText,\n loadStore,\n mockAPI,\n mockGET,\n mockNow,\n mockPOST,\n updateComponent\n} from '../test/utils.test';\n\nimport { expect, oneEvent } from '@open-wc/testing';\n\nlet clock: any;\n\nconst TAG = 'temba-contact-chat';\nconst getContactChat = async (attrs: any = {}) => {\n attrs['endpoint'] = '/test-assets/contacts/';\n // add some sizes and styles to force our chat history to scroll\n const chat = (await getComponent(\n TAG,\n attrs,\n '',\n 500,\n 500,\n 'display:flex;flex-direction:column;flex-grow:1;min-height:0;'\n )) as ContactChat;\n\n // TODO: this should be waiting for an event instead\n await waitFor(100);\n await clock.tick(0);\n return chat;\n};\n\nconst getResponseSuccessFiles = (attachments: Attachment[]) => {\n const response_attachments = attachments.map((attachment) => {\n return { content_type: attachment.content_type, url: attachment.url };\n });\n return response_attachments;\n};\n\ndescribe('temba-contact-chat', () => {\n let mockedNow: SinonStub;\n // map requests for contact history to our static files\n // we'll just us the same historylist for everybody for now\n beforeEach(() => {\n mockedNow = mockNow('2021-03-31T00:31:00.000-00:00');\n clearMockPosts();\n mockGET(\n /\\/contact\\/chat\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n\n mockGET(\n /\\/api\\/v2\\/users\\.json\\?email=admin1%40nyaruka\\.com/,\n '/test-assets/api/users/admin1.json'\n );\n\n mockAPI();\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n mockedNow.restore();\n });\n\n // temporarily disabled as it's too flaky in CI\n xit('show history and show chatbox if contact is active', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n showMessageLogsAfter: '2025-01-01T00:00:00.000Z'\n });\n\n await assertScreenshot('contacts/chat-for-active-contact', getClip(chat));\n });\n\n it('show history and hide chatbox if contact is archived', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-barack-archived',\n showMessageLogsAfter: '2025-01-01T00:00:00.000Z'\n });\n\n await assertScreenshot('contacts/chat-for-archived-contact', getClip(chat));\n });\n\n it('show history and hide chatbox if contact is blocked', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-michelle-blocked'\n });\n\n await assertScreenshot('contacts/chat-for-blocked-contact', getClip(chat));\n });\n\n it('show history and hide chatbox if contact is stopped', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-tim-stopped'\n });\n\n await assertScreenshot('contacts/chat-for-stopped-contact', getClip(chat));\n });\n\n it('keeps flow footer from blocking scrollbar drag interactions', async () => {\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active'\n });\n\n const flowFooter = chat.shadowRoot.querySelector(\n '.flow-footer'\n ) as HTMLElement;\n const inFlow = flowFooter.querySelector('.in-flow') as HTMLElement;\n\n expect(flowFooter).to.exist;\n expect(inFlow).to.exist;\n expect(getComputedStyle(flowFooter).pointerEvents).to.equal('none');\n expect(getComputedStyle(inFlow).pointerEvents).to.equal('auto');\n });\n\n it('sends text without attachments', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active'\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n const text = getValidText();\n await updateComponent(compose, text);\n\n const response_body = {\n event: {\n uuid: 'msg-uuid',\n contact: { uuid: 'contact-dave-active', name: 'Dave Matthews' },\n msg: {\n text: text,\n attachments: []\n }\n }\n };\n mockPOST(/contact\\/chat\\/contact-dave-active\\//, response_body);\n\n const listener = oneEvent(compose, CustomEventType.Submitted, false);\n await typeInto('temba-contact-chat:temba-compose', text, true, true);\n expect(await listener).to.exist;\n\n await assertScreenshot('contacts/chat-sends-text-only', getClip(chat));\n });\n\n it('sends attachments without text', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active'\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n const attachments = getValidAttachments();\n await updateComponent(compose, null, attachments);\n const response_attachments = getResponseSuccessFiles(attachments);\n const response_body = {\n event: {\n uuid: 'msg-uuid',\n contact: { uuid: 'contact-dave-active', name: 'Dave Matthews' },\n msg: {\n text: '',\n attachments: response_attachments\n }\n }\n };\n const response_headers = {};\n const response_status = '200';\n mockPOST(\n /contact\\/chat\\/contact-dave-active\\//,\n response_body,\n response_headers,\n response_status\n );\n\n const listener = oneEvent(compose, CustomEventType.Submitted, false);\n await typeInto('temba-contact-chat:temba-compose', '', false, true);\n expect(await listener).to.exist;\n\n await assertScreenshot(\n 'contacts/chat-sends-attachments-only',\n getClip(chat)\n );\n });\n\n it('sends text with attachments', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active'\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n const text = getValidText();\n const attachments = getValidAttachments();\n await updateComponent(compose, text, attachments);\n const response_attachments = getResponseSuccessFiles(attachments);\n const response_body = {\n event: {\n uuid: 'msg-uuid',\n contact: { uuid: 'contact-dave-active', name: 'Dave Matthews' },\n msg: {\n text,\n attachments: response_attachments\n }\n }\n };\n mockPOST(/contact\\/chat\\/contact-dave-active\\//, response_body);\n\n // press enter\n const listener = oneEvent(compose, CustomEventType.Submitted, false);\n await typeInto('temba-contact-chat:temba-compose', '', false, true);\n expect(await listener).to.exist;\n\n await assertScreenshot(\n 'contacts/chat-sends-text-and-attachments',\n getClip(chat)\n );\n });\n\n it('shows failure message with retry', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active'\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n await updateComponent(compose, getValidText(), getValidAttachments());\n\n const response_body = {};\n const response_headers = {};\n const response_status = '500';\n mockPOST(\n /api\\/v2\\/messages\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n // press\n const listener = oneEvent(compose, CustomEventType.Submitted, false);\n await typeInto('temba-contact-chat:temba-compose', '', false, true);\n expect(await listener).to.exist;\n\n await assertScreenshot('contacts/chat-failure', getClip(chat));\n });\n});\n"]}
|
|
@@ -2,6 +2,8 @@ import { html, fixture, expect } from '@open-wc/testing';
|
|
|
2
2
|
import { Editor } from '../src/flow/Editor';
|
|
3
3
|
import { Plumber } from '../src/flow/Plumber';
|
|
4
4
|
import { stub, restore } from 'sinon';
|
|
5
|
+
import { zustand } from '../src/store/AppState';
|
|
6
|
+
import { TEMBA_COMPONENTS_VERSION } from '../src/version';
|
|
5
7
|
// Register the component
|
|
6
8
|
customElements.define('temba-flow-editor', Editor);
|
|
7
9
|
describe('Editor', () => {
|
|
@@ -95,6 +97,41 @@ describe('Editor', () => {
|
|
|
95
97
|
consoleStub.restore();
|
|
96
98
|
});
|
|
97
99
|
});
|
|
100
|
+
describe('disconnectedCallback', () => {
|
|
101
|
+
it('clears flow data from the store when editor is removed', async () => {
|
|
102
|
+
// Set up some flow-specific state
|
|
103
|
+
zustand.setState({
|
|
104
|
+
flowDefinition: { nodes: [], _ui: { nodes: {} } },
|
|
105
|
+
activity: { nodes: {}, segments: {} },
|
|
106
|
+
simulatorActivity: { nodes: {}, segments: {} },
|
|
107
|
+
simulatorActive: true,
|
|
108
|
+
flowInfo: {
|
|
109
|
+
results: [],
|
|
110
|
+
dependencies: [],
|
|
111
|
+
counts: { nodes: 0, languages: 0 },
|
|
112
|
+
locals: []
|
|
113
|
+
},
|
|
114
|
+
dirtyDate: new Date()
|
|
115
|
+
});
|
|
116
|
+
editor = await fixture(html `
|
|
117
|
+
<temba-flow-editor>
|
|
118
|
+
<div id="canvas"></div>
|
|
119
|
+
</temba-flow-editor>
|
|
120
|
+
`);
|
|
121
|
+
// Verify state is populated
|
|
122
|
+
expect(zustand.getState().flowDefinition).to.not.be.null;
|
|
123
|
+
expect(zustand.getState().activity).to.not.be.null;
|
|
124
|
+
// Remove the editor from the DOM
|
|
125
|
+
editor.remove();
|
|
126
|
+
// Verify all flow-specific state has been cleared
|
|
127
|
+
expect(zustand.getState().flowDefinition).to.be.null;
|
|
128
|
+
expect(zustand.getState().activity).to.be.null;
|
|
129
|
+
expect(zustand.getState().simulatorActivity).to.be.null;
|
|
130
|
+
expect(zustand.getState().simulatorActive).to.be.false;
|
|
131
|
+
expect(zustand.getState().flowInfo).to.be.null;
|
|
132
|
+
expect(zustand.getState().dirtyDate).to.be.null;
|
|
133
|
+
});
|
|
134
|
+
});
|
|
98
135
|
describe('render method', () => {
|
|
99
136
|
it('renders loading when no definition', async () => {
|
|
100
137
|
editor = await fixture(html `
|
|
@@ -687,5 +724,174 @@ describe('Editor', () => {
|
|
|
687
724
|
expect(flowNodes[0].classList.contains('flow-start')).to.be.true;
|
|
688
725
|
});
|
|
689
726
|
});
|
|
727
|
+
describe('save feedback', () => {
|
|
728
|
+
let mockPostJSON;
|
|
729
|
+
let storeElement;
|
|
730
|
+
before(() => {
|
|
731
|
+
// Create a mock temba-store element that getStore() will find
|
|
732
|
+
// Use the real zustand getState so all store interactions work
|
|
733
|
+
storeElement = document.createElement('temba-store');
|
|
734
|
+
storeElement.getState = () => zustand.getState();
|
|
735
|
+
document.body.appendChild(storeElement);
|
|
736
|
+
});
|
|
737
|
+
after(() => {
|
|
738
|
+
storeElement.remove();
|
|
739
|
+
});
|
|
740
|
+
beforeEach(() => {
|
|
741
|
+
mockPostJSON = stub();
|
|
742
|
+
storeElement.postJSON = mockPostJSON;
|
|
743
|
+
});
|
|
744
|
+
afterEach(() => {
|
|
745
|
+
// Clean up any dialogs left in the DOM
|
|
746
|
+
document.querySelectorAll('temba-dialog').forEach((d) => d.remove());
|
|
747
|
+
});
|
|
748
|
+
it('sets isSaving when dirtyDate changes', async () => {
|
|
749
|
+
editor = await fixture(html `
|
|
750
|
+
<temba-flow-editor>
|
|
751
|
+
<div id="canvas"></div>
|
|
752
|
+
</temba-flow-editor>
|
|
753
|
+
`);
|
|
754
|
+
editor.isSaving = false;
|
|
755
|
+
// Simulate a dirtyDate change via updated()
|
|
756
|
+
editor.dirtyDate = new Date();
|
|
757
|
+
const changes = new Map();
|
|
758
|
+
changes.set('dirtyDate', null);
|
|
759
|
+
editor.updated(changes);
|
|
760
|
+
expect(editor.isSaving).to.be.true;
|
|
761
|
+
});
|
|
762
|
+
it('renders save indicator with visible class when saving', async () => {
|
|
763
|
+
editor = await fixture(html `
|
|
764
|
+
<temba-flow-editor>
|
|
765
|
+
<div id="canvas"></div>
|
|
766
|
+
</temba-flow-editor>
|
|
767
|
+
`);
|
|
768
|
+
editor.canvasSize = { width: 800, height: 600 };
|
|
769
|
+
editor.isSaving = true;
|
|
770
|
+
await editor.updateComplete;
|
|
771
|
+
const indicator = editor.querySelector('.save-indicator');
|
|
772
|
+
expect(indicator).to.exist;
|
|
773
|
+
expect(indicator.classList.contains('visible')).to.be.true;
|
|
774
|
+
});
|
|
775
|
+
it('save indicator is not visible when not saving', async () => {
|
|
776
|
+
editor = await fixture(html `
|
|
777
|
+
<temba-flow-editor>
|
|
778
|
+
<div id="canvas"></div>
|
|
779
|
+
</temba-flow-editor>
|
|
780
|
+
`);
|
|
781
|
+
editor.canvasSize = { width: 800, height: 600 };
|
|
782
|
+
editor.isSaving = false;
|
|
783
|
+
await editor.updateComplete;
|
|
784
|
+
const indicator = editor.querySelector('.save-indicator');
|
|
785
|
+
expect(indicator).to.exist;
|
|
786
|
+
expect(indicator.classList.contains('visible')).to.be.false;
|
|
787
|
+
});
|
|
788
|
+
it('clears isSaving after successful save', async () => {
|
|
789
|
+
editor = await fixture(html `
|
|
790
|
+
<temba-flow-editor>
|
|
791
|
+
<div id="canvas"></div>
|
|
792
|
+
</temba-flow-editor>
|
|
793
|
+
`);
|
|
794
|
+
editor.flow = 'test-flow';
|
|
795
|
+
editor.definition = { nodes: [], _ui: { nodes: {} } };
|
|
796
|
+
mockPostJSON.resolves({
|
|
797
|
+
status: 200,
|
|
798
|
+
json: {},
|
|
799
|
+
body: '{}',
|
|
800
|
+
headers: new Headers()
|
|
801
|
+
});
|
|
802
|
+
await editor.saveChanges();
|
|
803
|
+
expect(editor.isSaving).to.be.false;
|
|
804
|
+
});
|
|
805
|
+
it('adds temba-components version under _ui.editor when saving', async () => {
|
|
806
|
+
editor = await fixture(html `
|
|
807
|
+
<temba-flow-editor>
|
|
808
|
+
<div id="canvas"></div>
|
|
809
|
+
</temba-flow-editor>
|
|
810
|
+
`);
|
|
811
|
+
editor.flow = 'test-flow';
|
|
812
|
+
editor.definition = { nodes: [], _ui: { nodes: {} } };
|
|
813
|
+
mockPostJSON.resolves({
|
|
814
|
+
status: 200,
|
|
815
|
+
json: {},
|
|
816
|
+
body: '{}',
|
|
817
|
+
headers: new Headers()
|
|
818
|
+
});
|
|
819
|
+
await editor.saveChanges();
|
|
820
|
+
const payload = mockPostJSON.firstCall.args[1];
|
|
821
|
+
expect(payload._ui.editor).to.equal(TEMBA_COMPONENTS_VERSION);
|
|
822
|
+
});
|
|
823
|
+
it('shows error dialog on non-200 response', async () => {
|
|
824
|
+
editor = await fixture(html `
|
|
825
|
+
<temba-flow-editor>
|
|
826
|
+
<div id="canvas"></div>
|
|
827
|
+
</temba-flow-editor>
|
|
828
|
+
`);
|
|
829
|
+
editor.flow = 'test-flow';
|
|
830
|
+
editor.definition = { nodes: [], _ui: { nodes: {} } };
|
|
831
|
+
mockPostJSON.resolves({
|
|
832
|
+
status: 400,
|
|
833
|
+
json: { detail: 'Invalid flow definition' },
|
|
834
|
+
body: '{"detail":"Invalid flow definition"}',
|
|
835
|
+
headers: new Headers()
|
|
836
|
+
});
|
|
837
|
+
await editor.saveChanges();
|
|
838
|
+
await editor.updateComplete;
|
|
839
|
+
expect(editor.isSaving).to.be.false;
|
|
840
|
+
const dialog = document.querySelector('temba-dialog');
|
|
841
|
+
expect(dialog).to.exist;
|
|
842
|
+
expect(dialog.textContent).to.contain('Invalid flow definition');
|
|
843
|
+
});
|
|
844
|
+
it('shows error dialog on 500 server error', async () => {
|
|
845
|
+
editor = await fixture(html `
|
|
846
|
+
<temba-flow-editor>
|
|
847
|
+
<div id="canvas"></div>
|
|
848
|
+
</temba-flow-editor>
|
|
849
|
+
`);
|
|
850
|
+
editor.flow = 'test-flow';
|
|
851
|
+
editor.definition = { nodes: [], _ui: { nodes: {} } };
|
|
852
|
+
mockPostJSON.rejects(new Response(null, { status: 500 }));
|
|
853
|
+
await editor.saveChanges();
|
|
854
|
+
await editor.updateComplete;
|
|
855
|
+
expect(editor.isSaving).to.be.false;
|
|
856
|
+
const dialog = document.querySelector('temba-dialog');
|
|
857
|
+
expect(dialog).to.exist;
|
|
858
|
+
expect(dialog.textContent).to.contain('Server error');
|
|
859
|
+
});
|
|
860
|
+
it('shows error dialog on network failure', async () => {
|
|
861
|
+
editor = await fixture(html `
|
|
862
|
+
<temba-flow-editor>
|
|
863
|
+
<div id="canvas"></div>
|
|
864
|
+
</temba-flow-editor>
|
|
865
|
+
`);
|
|
866
|
+
editor.flow = 'test-flow';
|
|
867
|
+
editor.definition = { nodes: [], _ui: { nodes: {} } };
|
|
868
|
+
mockPostJSON.rejects(new Error('Network error'));
|
|
869
|
+
await editor.saveChanges();
|
|
870
|
+
await editor.updateComplete;
|
|
871
|
+
expect(editor.isSaving).to.be.false;
|
|
872
|
+
const dialog = document.querySelector('temba-dialog');
|
|
873
|
+
expect(dialog).to.exist;
|
|
874
|
+
expect(dialog.textContent).to.contain('Unable to reach the server');
|
|
875
|
+
});
|
|
876
|
+
it('extracts error message from response json fields', () => {
|
|
877
|
+
editor = new Editor();
|
|
878
|
+
expect(editor.extractErrorMessage({
|
|
879
|
+
status: 400,
|
|
880
|
+
json: { detail: 'Bad request' }
|
|
881
|
+
})).to.equal('Bad request');
|
|
882
|
+
expect(editor.extractErrorMessage({
|
|
883
|
+
status: 400,
|
|
884
|
+
json: { error: 'Something went wrong' }
|
|
885
|
+
})).to.equal('Something went wrong');
|
|
886
|
+
expect(editor.extractErrorMessage({
|
|
887
|
+
status: 400,
|
|
888
|
+
json: { description: 'Detailed error' }
|
|
889
|
+
})).to.equal('Detailed error');
|
|
890
|
+
expect(editor.extractErrorMessage({
|
|
891
|
+
status: 403,
|
|
892
|
+
json: {}
|
|
893
|
+
})).to.equal('Save failed with status 403.');
|
|
894
|
+
});
|
|
895
|
+
});
|
|
690
896
|
});
|
|
691
897
|
//# sourceMappingURL=temba-flow-editor.test.js.map
|