@nyaruka/temba-components 0.129.3 → 0.129.5

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.
Files changed (304) hide show
  1. package/.eslintrc.js +1 -0
  2. package/.github/workflows/build.yml +135 -3
  3. package/CHANGELOG.md +19 -0
  4. package/demo/data/flows/sample-flow.json +110 -87
  5. package/demo/field-config-demo.html +135 -0
  6. package/dist/temba-components.js +1257 -675
  7. package/dist/temba-components.js.map +1 -1
  8. package/docs/ActionEditor-Migration.md +118 -0
  9. package/out-tsc/src/events.js.map +1 -1
  10. package/out-tsc/src/flow/{EditorNode.js → CanvasNode.js} +345 -42
  11. package/out-tsc/src/flow/CanvasNode.js.map +1 -0
  12. package/out-tsc/src/flow/Editor.js +107 -3
  13. package/out-tsc/src/flow/Editor.js.map +1 -1
  14. package/out-tsc/src/flow/NodeEditor.js +1211 -0
  15. package/out-tsc/src/flow/NodeEditor.js.map +1 -0
  16. package/out-tsc/src/flow/Plumber.js +0 -6
  17. package/out-tsc/src/flow/Plumber.js.map +1 -1
  18. package/out-tsc/src/flow/actions/add_contact_groups.js +40 -0
  19. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -0
  20. package/out-tsc/src/flow/actions/add_contact_urn.js +16 -0
  21. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -0
  22. package/out-tsc/src/flow/actions/add_input_labels.js +11 -0
  23. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -0
  24. package/out-tsc/src/flow/actions/call_classifier.js +11 -0
  25. package/out-tsc/src/flow/actions/call_classifier.js.map +1 -0
  26. package/out-tsc/src/flow/actions/call_llm.js +11 -0
  27. package/out-tsc/src/flow/actions/call_llm.js.map +1 -0
  28. package/out-tsc/src/flow/actions/call_resthook.js +11 -0
  29. package/out-tsc/src/flow/actions/call_resthook.js.map +1 -0
  30. package/out-tsc/src/flow/actions/call_webhook.js +122 -0
  31. package/out-tsc/src/flow/actions/call_webhook.js.map +1 -0
  32. package/out-tsc/src/flow/actions/enter_flow.js +14 -0
  33. package/out-tsc/src/flow/actions/enter_flow.js.map +1 -0
  34. package/out-tsc/src/flow/actions/open_ticket.js +11 -0
  35. package/out-tsc/src/flow/actions/open_ticket.js.map +1 -0
  36. package/out-tsc/src/flow/actions/play_audio.js +11 -0
  37. package/out-tsc/src/flow/actions/play_audio.js.map +1 -0
  38. package/out-tsc/src/flow/actions/remove_contact_groups.js +62 -0
  39. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -0
  40. package/out-tsc/src/flow/actions/request_optin.js +11 -0
  41. package/out-tsc/src/flow/actions/request_optin.js.map +1 -0
  42. package/out-tsc/src/flow/actions/say_msg.js +11 -0
  43. package/out-tsc/src/flow/actions/say_msg.js.map +1 -0
  44. package/out-tsc/src/flow/actions/send_broadcast.js +33 -0
  45. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -0
  46. package/out-tsc/src/flow/actions/send_email.js +56 -0
  47. package/out-tsc/src/flow/actions/send_email.js.map +1 -0
  48. package/out-tsc/src/flow/actions/send_msg.js +55 -0
  49. package/out-tsc/src/flow/actions/send_msg.js.map +1 -0
  50. package/out-tsc/src/flow/actions/set_contact_channel.js +12 -0
  51. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -0
  52. package/out-tsc/src/flow/actions/set_contact_field.js +12 -0
  53. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -0
  54. package/out-tsc/src/flow/actions/set_contact_language.js +10 -0
  55. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -0
  56. package/out-tsc/src/flow/actions/set_contact_name.js +10 -0
  57. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -0
  58. package/out-tsc/src/flow/actions/set_contact_status.js +10 -0
  59. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -0
  60. package/out-tsc/src/flow/actions/set_run_result.js +10 -0
  61. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -0
  62. package/out-tsc/src/flow/actions/split_by_expression_example.js +77 -0
  63. package/out-tsc/src/flow/actions/split_by_expression_example.js.map +1 -0
  64. package/out-tsc/src/flow/actions/start_session.js +11 -0
  65. package/out-tsc/src/flow/actions/start_session.js.map +1 -0
  66. package/out-tsc/src/flow/actions/transfer_airtime.js +11 -0
  67. package/out-tsc/src/flow/actions/transfer_airtime.js.map +1 -0
  68. package/out-tsc/src/flow/config.js +88 -193
  69. package/out-tsc/src/flow/config.js.map +1 -1
  70. package/out-tsc/src/flow/nodes/execute_actions.js +4 -0
  71. package/out-tsc/src/flow/nodes/execute_actions.js.map +1 -0
  72. package/out-tsc/src/flow/nodes/split_by_airtime.js +9 -0
  73. package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -0
  74. package/out-tsc/src/flow/nodes/split_by_contact_field.js +7 -0
  75. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -0
  76. package/out-tsc/src/flow/nodes/split_by_expression.js +7 -0
  77. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -0
  78. package/out-tsc/src/flow/nodes/split_by_groups.js +7 -0
  79. package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -0
  80. package/out-tsc/src/flow/nodes/split_by_random.js +10 -0
  81. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -0
  82. package/out-tsc/src/flow/nodes/split_by_run_result.js +7 -0
  83. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -0
  84. package/out-tsc/src/flow/nodes/split_by_scheme.js +7 -0
  85. package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -0
  86. package/out-tsc/src/flow/nodes/split_by_subflow.js +9 -0
  87. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -0
  88. package/out-tsc/src/flow/nodes/split_by_webhook.js +18 -0
  89. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -0
  90. package/out-tsc/src/flow/nodes/wait_for_audio.js +7 -0
  91. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -0
  92. package/out-tsc/src/flow/nodes/wait_for_digits.js +7 -0
  93. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -0
  94. package/out-tsc/src/flow/nodes/wait_for_image.js +7 -0
  95. package/out-tsc/src/flow/nodes/wait_for_image.js.map +1 -0
  96. package/out-tsc/src/flow/nodes/wait_for_location.js +7 -0
  97. package/out-tsc/src/flow/nodes/wait_for_location.js.map +1 -0
  98. package/out-tsc/src/flow/nodes/wait_for_menu.js +7 -0
  99. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -0
  100. package/out-tsc/src/flow/nodes/wait_for_response.js +7 -0
  101. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -0
  102. package/out-tsc/src/flow/nodes/wait_for_video.js +7 -0
  103. package/out-tsc/src/flow/nodes/wait_for_video.js.map +1 -0
  104. package/out-tsc/src/flow/types.js +79 -0
  105. package/out-tsc/src/flow/types.js.map +1 -0
  106. package/out-tsc/src/flow/utils.js +65 -0
  107. package/out-tsc/src/flow/utils.js.map +1 -0
  108. package/out-tsc/src/form/ArrayEditor.js +199 -0
  109. package/out-tsc/src/form/ArrayEditor.js.map +1 -0
  110. package/out-tsc/src/form/BaseListEditor.js +128 -0
  111. package/out-tsc/src/form/BaseListEditor.js.map +1 -0
  112. package/out-tsc/src/form/Checkbox.js +17 -2
  113. package/out-tsc/src/form/Checkbox.js.map +1 -1
  114. package/out-tsc/src/form/Completion.js +6 -0
  115. package/out-tsc/src/form/Completion.js.map +1 -1
  116. package/out-tsc/src/form/FormField.js +110 -11
  117. package/out-tsc/src/form/FormField.js.map +1 -1
  118. package/out-tsc/src/form/KeyValueEditor.js +223 -0
  119. package/out-tsc/src/form/KeyValueEditor.js.map +1 -0
  120. package/out-tsc/src/form/select/Select.js +92 -32
  121. package/out-tsc/src/form/select/Select.js.map +1 -1
  122. package/out-tsc/src/interfaces.js +6 -0
  123. package/out-tsc/src/interfaces.js.map +1 -1
  124. package/out-tsc/src/live/ContactChat.js +2 -76
  125. package/out-tsc/src/live/ContactChat.js.map +1 -1
  126. package/out-tsc/temba-modules.js +9 -2
  127. package/out-tsc/temba-modules.js.map +1 -1
  128. package/out-tsc/test/ActionHelper.js +116 -0
  129. package/out-tsc/test/ActionHelper.js.map +1 -0
  130. package/out-tsc/test/actions/add_contact_groups.test.js +66 -0
  131. package/out-tsc/test/actions/add_contact_groups.test.js.map +1 -0
  132. package/out-tsc/test/actions/remove_contact_groups.test.js +226 -0
  133. package/out-tsc/test/actions/remove_contact_groups.test.js.map +1 -0
  134. package/out-tsc/test/actions/send_email.test.js +160 -0
  135. package/out-tsc/test/actions/send_email.test.js.map +1 -0
  136. package/out-tsc/test/actions/send_msg.test.js +95 -0
  137. package/out-tsc/test/actions/send_msg.test.js.map +1 -0
  138. package/out-tsc/test/temba-action-editing-integration.test.js +183 -0
  139. package/out-tsc/test/temba-action-editing-integration.test.js.map +1 -0
  140. package/out-tsc/test/temba-checkbox.test.js +1 -1
  141. package/out-tsc/test/temba-checkbox.test.js.map +1 -1
  142. package/out-tsc/test/temba-field-config.test.js +133 -0
  143. package/out-tsc/test/temba-field-config.test.js.map +1 -0
  144. package/out-tsc/test/temba-flow-editor-node.test.js +14 -14
  145. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  146. package/out-tsc/test/temba-node-editor.test.js +283 -0
  147. package/out-tsc/test/temba-node-editor.test.js.map +1 -0
  148. package/out-tsc/test/temba-select.test.js +158 -0
  149. package/out-tsc/test/temba-select.test.js.map +1 -1
  150. package/package.json +1 -1
  151. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  152. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  153. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  154. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  155. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  156. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  157. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  158. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  159. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  160. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  161. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  162. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  163. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  164. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  165. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  166. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  167. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  168. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  169. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  170. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  171. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  172. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  173. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  174. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  175. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  176. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  177. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  178. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  179. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  180. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  181. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  182. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  183. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  184. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  185. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  186. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  187. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  188. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  189. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  190. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  191. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  192. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  193. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  194. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  195. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  196. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  197. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  198. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  199. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  200. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  201. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  202. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  203. package/screenshots/truth/editor/router.png +0 -0
  204. package/screenshots/truth/editor/send_msg.png +0 -0
  205. package/screenshots/truth/editor/set_contact_language.png +0 -0
  206. package/screenshots/truth/editor/set_contact_name.png +0 -0
  207. package/screenshots/truth/editor/set_run_result.png +0 -0
  208. package/screenshots/truth/editor/wait.png +0 -0
  209. package/screenshots/truth/formfield/markdown-errors.png +0 -0
  210. package/screenshots/truth/formfield/plain-text-errors.png +0 -0
  211. package/screenshots/truth/formfield/widget-only-markdown-errors.png +0 -0
  212. package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
  213. package/src/events.ts +1 -40
  214. package/src/flow/{EditorNode.ts → CanvasNode.ts} +424 -48
  215. package/src/flow/Editor.ts +140 -4
  216. package/src/flow/NodeEditor.ts +1454 -0
  217. package/src/flow/Plumber.ts +0 -9
  218. package/src/flow/actions/add_contact_groups.ts +42 -0
  219. package/src/flow/actions/add_contact_urn.ts +17 -0
  220. package/src/flow/actions/add_input_labels.ts +12 -0
  221. package/src/flow/actions/call_classifier.ts +12 -0
  222. package/src/flow/actions/call_llm.ts +12 -0
  223. package/src/flow/actions/call_resthook.ts +12 -0
  224. package/src/flow/actions/call_webhook.ts +133 -0
  225. package/src/flow/actions/enter_flow.ts +15 -0
  226. package/src/flow/actions/open_ticket.ts +12 -0
  227. package/src/flow/actions/play_audio.ts +12 -0
  228. package/src/flow/actions/remove_contact_groups.ts +66 -0
  229. package/src/flow/actions/request_optin.ts +12 -0
  230. package/src/flow/actions/say_msg.ts +12 -0
  231. package/src/flow/actions/send_broadcast.ts +35 -0
  232. package/src/flow/actions/send_email.ts +60 -0
  233. package/src/flow/actions/send_msg.ts +58 -0
  234. package/src/flow/actions/set_contact_channel.ts +13 -0
  235. package/src/flow/actions/set_contact_field.ts +13 -0
  236. package/src/flow/actions/set_contact_language.ts +11 -0
  237. package/src/flow/actions/set_contact_name.ts +11 -0
  238. package/src/flow/actions/set_contact_status.ts +11 -0
  239. package/src/flow/actions/set_run_result.ts +11 -0
  240. package/src/flow/actions/split_by_expression_example.ts +88 -0
  241. package/src/flow/actions/start_session.ts +12 -0
  242. package/src/flow/actions/transfer_airtime.ts +12 -0
  243. package/src/flow/config.ts +93 -232
  244. package/src/flow/nodes/execute_actions.ts +5 -0
  245. package/src/flow/nodes/split_by_airtime.ts +9 -0
  246. package/src/flow/nodes/split_by_contact_field.ts +7 -0
  247. package/src/flow/nodes/split_by_expression.ts +7 -0
  248. package/src/flow/nodes/split_by_groups.ts +7 -0
  249. package/src/flow/nodes/split_by_random.ts +10 -0
  250. package/src/flow/nodes/split_by_run_result.ts +7 -0
  251. package/src/flow/nodes/split_by_scheme.ts +7 -0
  252. package/src/flow/nodes/split_by_subflow.ts +9 -0
  253. package/src/flow/nodes/split_by_webhook.ts +19 -0
  254. package/src/flow/nodes/wait_for_audio.ts +7 -0
  255. package/src/flow/nodes/wait_for_digits.ts +7 -0
  256. package/src/flow/nodes/wait_for_image.ts +7 -0
  257. package/src/flow/nodes/wait_for_location.ts +7 -0
  258. package/src/flow/nodes/wait_for_menu.ts +7 -0
  259. package/src/flow/nodes/wait_for_response.ts +7 -0
  260. package/src/flow/nodes/wait_for_video.ts +7 -0
  261. package/src/flow/types.ts +352 -0
  262. package/src/flow/utils.ts +76 -0
  263. package/src/form/ArrayEditor.ts +240 -0
  264. package/src/form/BaseListEditor.ts +177 -0
  265. package/src/form/Checkbox.ts +22 -3
  266. package/src/form/Completion.ts +6 -0
  267. package/src/form/FormField.ts +115 -11
  268. package/src/form/KeyValueEditor.ts +251 -0
  269. package/src/form/select/Select.ts +105 -32
  270. package/src/interfaces.ts +7 -2
  271. package/src/live/ContactChat.ts +3 -97
  272. package/src/store/flow-definition.d.ts +6 -1
  273. package/static/api/contacts.json +30 -0
  274. package/static/api/groups.json +4 -426
  275. package/static/api/locations.json +24 -0
  276. package/static/api/media.json +5 -0
  277. package/static/api/optins.json +16 -0
  278. package/static/api/orgs.json +13 -0
  279. package/static/api/topics.json +21 -0
  280. package/static/api/users.json +26 -0
  281. package/static/css/temba-components.css +3 -6
  282. package/temba-modules.ts +9 -2
  283. package/test/ActionHelper.ts +142 -0
  284. package/test/actions/add_contact_groups.test.ts +89 -0
  285. package/test/actions/remove_contact_groups.test.ts +265 -0
  286. package/test/actions/send_email.test.ts +214 -0
  287. package/test/actions/send_msg.test.ts +130 -0
  288. package/test/temba-action-editing-integration.test.ts +240 -0
  289. package/test/temba-checkbox.test.ts +1 -1
  290. package/test/temba-field-config.test.ts +152 -0
  291. package/test/temba-flow-editor-node.test.ts +18 -18
  292. package/test/temba-node-editor.test.ts +353 -0
  293. package/test/temba-select.test.ts +234 -0
  294. package/test-assets/contacts/history.json +11 -33
  295. package/web-dev-server.config.mjs +34 -0
  296. package/.github/workflows/coverage.yml +0 -80
  297. package/demo/sticky-note-demo.html +0 -155
  298. package/out-tsc/src/flow/EditorNode.js.map +0 -1
  299. package/out-tsc/src/flow/render.js +0 -358
  300. package/out-tsc/src/flow/render.js.map +0 -1
  301. package/out-tsc/test/temba-flow-render.test.js +0 -794
  302. package/out-tsc/test/temba-flow-render.test.js.map +0 -1
  303. package/src/flow/render.ts +0 -443
  304. package/test/temba-flow-render.test.ts +0 -1003
@@ -0,0 +1,95 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { send_msg } from '../../src/flow/actions/send_msg';
3
+ import { ActionTest } from '../ActionHelper';
4
+ /**
5
+ * Test suite for the send_msg action configuration.
6
+ */
7
+ describe('send_msg action config', () => {
8
+ const helper = new ActionTest(send_msg, 'send_msg');
9
+ describe('basic properties', () => {
10
+ helper.testBasicProperties();
11
+ it('has correct name', () => {
12
+ expect(send_msg.name).to.equal('Send Message');
13
+ });
14
+ });
15
+ describe('action scenarios', () => {
16
+ helper.testAction({
17
+ uuid: 'test-action-1',
18
+ type: 'send_msg',
19
+ text: 'Hello world!',
20
+ quick_replies: []
21
+ }, 'simple-text');
22
+ helper.testAction({
23
+ uuid: 'test-action-2',
24
+ type: 'send_msg',
25
+ text: 'Hello\nworld!\nHow are you?',
26
+ quick_replies: []
27
+ }, 'text-with-linebreaks');
28
+ helper.testAction({
29
+ uuid: 'test-action-3',
30
+ type: 'send_msg',
31
+ text: 'Choose an option:',
32
+ quick_replies: ['Yes', 'No', 'Maybe']
33
+ }, 'text-with-quick-replies');
34
+ helper.testAction({
35
+ uuid: 'test-action-4',
36
+ type: 'send_msg',
37
+ text: 'Rate our service:',
38
+ quick_replies: [
39
+ '⭐',
40
+ '⭐⭐',
41
+ '⭐⭐⭐',
42
+ '⭐⭐⭐⭐',
43
+ '⭐⭐⭐⭐⭐',
44
+ 'Not applicable'
45
+ ]
46
+ }, 'text-with-many-quick-replies');
47
+ helper.testAction({
48
+ uuid: 'test-action-5',
49
+ type: 'send_msg',
50
+ text: 'Welcome to our service!\n\nPlease choose from the following options:\n- Option A: Basic plan\n- Option B: Premium plan\n- Option C: Enterprise plan',
51
+ quick_replies: ['Basic', 'Premium', 'Enterprise']
52
+ }, 'multiline-text-with-replies');
53
+ helper.testAction({
54
+ uuid: 'test-action-6',
55
+ type: 'send_msg',
56
+ text: 'Which department would you like to contact?',
57
+ quick_replies: [
58
+ 'Customer Support Department',
59
+ 'Technical Support Team',
60
+ 'Billing and Accounts Department',
61
+ 'Sales and Marketing Division'
62
+ ]
63
+ }, 'long-quick-replies');
64
+ helper.testAction({
65
+ uuid: 'test-action-7',
66
+ type: 'send_msg',
67
+ text: 'This action definition is missing quick_replies altogether.'
68
+ }, 'text-without-quick-replies');
69
+ });
70
+ describe('validation edge cases', () => {
71
+ it('fails validation for empty text', () => {
72
+ const action = {
73
+ uuid: 'test-action',
74
+ type: 'send_msg',
75
+ text: '',
76
+ quick_replies: []
77
+ };
78
+ const result = send_msg.validate(action);
79
+ expect(result.valid).to.be.false;
80
+ expect(result.errors.text).to.equal('Message text is required');
81
+ });
82
+ it('fails validation for whitespace-only text', () => {
83
+ const action = {
84
+ uuid: 'test-action',
85
+ type: 'send_msg',
86
+ text: ' \n\t ',
87
+ quick_replies: []
88
+ };
89
+ const result = send_msg.validate(action);
90
+ expect(result.valid).to.be.false;
91
+ expect(result.errors.text).to.equal('Message text is required');
92
+ });
93
+ });
94
+ });
95
+ //# sourceMappingURL=send_msg.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send_msg.test.js","sourceRoot":"","sources":["../../../test/actions/send_msg.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C;;GAEG;AACH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEpD,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAE7B,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;YAC1B,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,cAAc;YACpB,aAAa,EAAE,EAAE;SACP,EACZ,aAAa,CACd,CAAC;QAEF,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,6BAA6B;YACnC,aAAa,EAAE,EAAE;SACP,EACZ,sBAAsB,CACvB,CAAC;QAEF,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,mBAAmB;YACzB,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC;SAC3B,EACZ,yBAAyB,CAC1B,CAAC;QAEF,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,mBAAmB;YACzB,aAAa,EAAE;gBACb,GAAG;gBACH,IAAI;gBACJ,KAAK;gBACL,MAAM;gBACN,OAAO;gBACP,gBAAgB;aACjB;SACS,EACZ,8BAA8B,CAC/B,CAAC;QAEF,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,qJAAqJ;YAC3J,aAAa,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC;SACvC,EACZ,6BAA6B,CAC9B,CAAC;QAEF,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,6CAA6C;YACnD,aAAa,EAAE;gBACb,6BAA6B;gBAC7B,wBAAwB;gBACxB,iCAAiC;gBACjC,8BAA8B;aAC/B;SACS,EACZ,oBAAoB,CACrB,CAAC;QAEF,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,6DAA6D;SACzD,EACZ,4BAA4B,CAC7B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,MAAM,MAAM,GAAY;gBACtB,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,EAAE;gBACR,aAAa,EAAE,EAAE;aAClB,CAAC;YAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC;YACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,MAAM,MAAM,GAAY;gBACtB,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,WAAW;gBACjB,aAAa,EAAE,EAAE;aAClB,CAAC;YAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC;YACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { send_msg } from '../../src/flow/actions/send_msg';\nimport { SendMsg } from '../../src/store/flow-definition';\nimport { ActionTest } from '../ActionHelper';\n\n/**\n * Test suite for the send_msg action configuration.\n */\ndescribe('send_msg action config', () => {\n const helper = new ActionTest(send_msg, 'send_msg');\n\n describe('basic properties', () => {\n helper.testBasicProperties();\n\n it('has correct name', () => {\n expect(send_msg.name).to.equal('Send Message');\n });\n });\n\n describe('action scenarios', () => {\n helper.testAction(\n {\n uuid: 'test-action-1',\n type: 'send_msg',\n text: 'Hello world!',\n quick_replies: []\n } as SendMsg,\n 'simple-text'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-2',\n type: 'send_msg',\n text: 'Hello\\nworld!\\nHow are you?',\n quick_replies: []\n } as SendMsg,\n 'text-with-linebreaks'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-3',\n type: 'send_msg',\n text: 'Choose an option:',\n quick_replies: ['Yes', 'No', 'Maybe']\n } as SendMsg,\n 'text-with-quick-replies'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-4',\n type: 'send_msg',\n text: 'Rate our service:',\n quick_replies: [\n '⭐',\n '⭐⭐',\n '⭐⭐⭐',\n '⭐⭐⭐⭐',\n '⭐⭐⭐⭐⭐',\n 'Not applicable'\n ]\n } as SendMsg,\n 'text-with-many-quick-replies'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-5',\n type: 'send_msg',\n text: 'Welcome to our service!\\n\\nPlease choose from the following options:\\n- Option A: Basic plan\\n- Option B: Premium plan\\n- Option C: Enterprise plan',\n quick_replies: ['Basic', 'Premium', 'Enterprise']\n } as SendMsg,\n 'multiline-text-with-replies'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-6',\n type: 'send_msg',\n text: 'Which department would you like to contact?',\n quick_replies: [\n 'Customer Support Department',\n 'Technical Support Team',\n 'Billing and Accounts Department',\n 'Sales and Marketing Division'\n ]\n } as SendMsg,\n 'long-quick-replies'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-7',\n type: 'send_msg',\n text: 'This action definition is missing quick_replies altogether.'\n } as SendMsg,\n 'text-without-quick-replies'\n );\n });\n\n describe('validation edge cases', () => {\n it('fails validation for empty text', () => {\n const action: SendMsg = {\n uuid: 'test-action',\n type: 'send_msg',\n text: '',\n quick_replies: []\n };\n\n const result = send_msg.validate(action);\n expect(result.valid).to.be.false;\n expect(result.errors.text).to.equal('Message text is required');\n });\n\n it('fails validation for whitespace-only text', () => {\n const action: SendMsg = {\n uuid: 'test-action',\n type: 'send_msg',\n text: ' \\n\\t ',\n quick_replies: []\n };\n\n const result = send_msg.validate(action);\n expect(result.valid).to.be.false;\n expect(result.errors.text).to.equal('Message text is required');\n });\n });\n});\n"]}
@@ -0,0 +1,183 @@
1
+ import { expect, fixture, html } from '@open-wc/testing';
2
+ import { CustomEventType } from '../src/interfaces';
3
+ import '../temba-modules';
4
+ describe('Action Editing Integration', () => {
5
+ it('should handle complete action editing workflow', async () => {
6
+ // Create a test node with a send_msg action
7
+ const testNode = {
8
+ uuid: 'test-node',
9
+ actions: [
10
+ {
11
+ type: 'send_msg',
12
+ uuid: 'test-action',
13
+ text: 'Hello world',
14
+ quick_replies: []
15
+ }
16
+ ],
17
+ exits: []
18
+ };
19
+ // Create EditorNode
20
+ const editorNode = await fixture(html `
21
+ <temba-flow-node
22
+ .node=${testNode}
23
+ .ui=${{ position: { left: 0, top: 0 } }}
24
+ ></temba-flow-node>
25
+ `);
26
+ await editorNode.updateComplete;
27
+ // Verify action is rendered
28
+ const actionElement = editorNode.querySelector('.action');
29
+ expect(actionElement).to.exist;
30
+ // Set up event listener to catch ActionEditRequested event
31
+ let editRequestedEvent = null;
32
+ editorNode.addEventListener(CustomEventType.ActionEditRequested, (event) => {
33
+ editRequestedEvent = event;
34
+ });
35
+ // Simulate clicking on the action content (not the remove button)
36
+ const actionContent = editorNode.querySelector('.action-content');
37
+ expect(actionContent).to.exist;
38
+ // Simulate a click (mousedown followed by mouseup at same position)
39
+ const mouseDownEvent = new MouseEvent('mousedown', {
40
+ clientX: 100,
41
+ clientY: 100,
42
+ bubbles: true
43
+ });
44
+ const mouseUpEvent = new MouseEvent('mouseup', {
45
+ clientX: 100,
46
+ clientY: 100,
47
+ bubbles: true
48
+ });
49
+ actionContent.dispatchEvent(mouseDownEvent);
50
+ actionContent.dispatchEvent(mouseUpEvent);
51
+ // Verify event was fired
52
+ expect(editRequestedEvent).to.exist;
53
+ expect(editRequestedEvent.detail.action).to.deep.equal(testNode.actions[0]);
54
+ expect(editRequestedEvent.detail.nodeUuid).to.equal('test-node');
55
+ });
56
+ it('should ignore clicks on remove button', async () => {
57
+ // Create a test node with a send_msg action
58
+ const testNode = {
59
+ uuid: 'test-node',
60
+ actions: [
61
+ {
62
+ type: 'send_msg',
63
+ uuid: 'test-action',
64
+ text: 'Hello world',
65
+ quick_replies: []
66
+ }
67
+ ],
68
+ exits: []
69
+ };
70
+ // Create EditorNode
71
+ const editorNode = await fixture(html `
72
+ <temba-flow-node
73
+ .node=${testNode}
74
+ .ui=${{ position: { left: 0, top: 0 } }}
75
+ ></temba-flow-node>
76
+ `);
77
+ await editorNode.updateComplete;
78
+ // Set up event listener to catch ActionEditRequested event
79
+ let editRequestedEvent = null;
80
+ editorNode.addEventListener(CustomEventType.ActionEditRequested, (event) => {
81
+ editRequestedEvent = event;
82
+ });
83
+ // Simulate clicking on the remove button
84
+ const removeButton = editorNode.querySelector('.remove-button');
85
+ expect(removeButton).to.exist;
86
+ removeButton.click();
87
+ // Verify NO ActionEditRequested event was fired (only remove action handling)
88
+ expect(editRequestedEvent).to.be.null;
89
+ });
90
+ it('should not open action editor when dragging beyond threshold', async () => {
91
+ // Create a test node with a send_msg action
92
+ const testNode = {
93
+ uuid: 'test-node',
94
+ actions: [
95
+ {
96
+ type: 'send_msg',
97
+ uuid: 'test-action',
98
+ text: 'Hello world',
99
+ quick_replies: []
100
+ }
101
+ ],
102
+ exits: []
103
+ };
104
+ // Create EditorNode
105
+ const editorNode = await fixture(html `
106
+ <temba-flow-node
107
+ .node=${testNode}
108
+ .ui=${{ position: { left: 0, top: 0 } }}
109
+ ></temba-flow-node>
110
+ `);
111
+ await editorNode.updateComplete;
112
+ // Set up event listener to catch ActionEditRequested event
113
+ let editRequestedEvent = null;
114
+ editorNode.addEventListener(CustomEventType.ActionEditRequested, (event) => {
115
+ editRequestedEvent = event;
116
+ });
117
+ // Simulate a drag operation (mousedown followed by mouseup at different position beyond threshold)
118
+ const actionContent = editorNode.querySelector('.action-content');
119
+ expect(actionContent).to.exist;
120
+ const mouseDownEvent = new MouseEvent('mousedown', {
121
+ clientX: 100,
122
+ clientY: 100,
123
+ bubbles: true
124
+ });
125
+ const mouseUpEvent = new MouseEvent('mouseup', {
126
+ clientX: 110, // 10 pixels away, beyond the 5 pixel threshold
127
+ clientY: 100,
128
+ bubbles: true
129
+ });
130
+ actionContent.dispatchEvent(mouseDownEvent);
131
+ actionContent.dispatchEvent(mouseUpEvent);
132
+ // Verify NO ActionEditRequested event was fired because we dragged beyond threshold
133
+ expect(editRequestedEvent).to.be.null;
134
+ });
135
+ it('should open action editor when clicking within threshold', async () => {
136
+ // Create a test node with a send_msg action
137
+ const testNode = {
138
+ uuid: 'test-node',
139
+ actions: [
140
+ {
141
+ type: 'send_msg',
142
+ uuid: 'test-action',
143
+ text: 'Hello world',
144
+ quick_replies: []
145
+ }
146
+ ],
147
+ exits: []
148
+ };
149
+ // Create EditorNode
150
+ const editorNode = await fixture(html `
151
+ <temba-flow-node
152
+ .node=${testNode}
153
+ .ui=${{ position: { left: 0, top: 0 } }}
154
+ ></temba-flow-node>
155
+ `);
156
+ await editorNode.updateComplete;
157
+ // Set up event listener to catch ActionEditRequested event
158
+ let editRequestedEvent = null;
159
+ editorNode.addEventListener(CustomEventType.ActionEditRequested, (event) => {
160
+ editRequestedEvent = event;
161
+ });
162
+ // Simulate a small movement (mousedown followed by mouseup at position within threshold)
163
+ const actionContent = editorNode.querySelector('.action-content');
164
+ expect(actionContent).to.exist;
165
+ const mouseDownEvent = new MouseEvent('mousedown', {
166
+ clientX: 100,
167
+ clientY: 100,
168
+ bubbles: true
169
+ });
170
+ const mouseUpEvent = new MouseEvent('mouseup', {
171
+ clientX: 103, // 3 pixels away, within the 5 pixel threshold
172
+ clientY: 102, // 2 pixels away
173
+ bubbles: true
174
+ });
175
+ actionContent.dispatchEvent(mouseDownEvent);
176
+ actionContent.dispatchEvent(mouseUpEvent);
177
+ // Verify ActionEditRequested event was fired because we stayed within threshold
178
+ expect(editRequestedEvent).to.exist;
179
+ expect(editRequestedEvent.detail.action).to.deep.equal(testNode.actions[0]);
180
+ expect(editRequestedEvent.detail.nodeUuid).to.equal('test-node');
181
+ });
182
+ });
183
+ //# sourceMappingURL=temba-action-editing-integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temba-action-editing-integration.test.js","sourceRoot":"","sources":["../../test/temba-action-editing-integration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGzD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,kBAAkB,CAAC;AAE1B,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,4CAA4C;QAC5C,MAAM,QAAQ,GAAS;YACrB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,aAAa;oBACnB,IAAI,EAAE,aAAa;oBACnB,aAAa,EAAE,EAAE;iBACP;aACb;YACD,KAAK,EAAE,EAAE;SACV,CAAC;QAEF,oBAAoB;QACpB,MAAM,UAAU,GAAe,MAAM,OAAO,CAAC,IAAI,CAAA;;gBAErC,QAAQ;cACV,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE;;KAE1C,CAAC,CAAC;QAEH,MAAM,UAAU,CAAC,cAAc,CAAC;QAEhC,4BAA4B;QAC5B,MAAM,aAAa,GAAG,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC1D,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QAE/B,2DAA2D;QAC3D,IAAI,kBAAkB,GAAuB,IAAI,CAAC;QAClD,UAAU,CAAC,gBAAgB,CACzB,eAAe,CAAC,mBAAmB,EACnC,CAAC,KAAkB,EAAE,EAAE;YACrB,kBAAkB,GAAG,KAAK,CAAC;QAC7B,CAAC,CACF,CAAC;QAEF,kEAAkE;QAClE,MAAM,aAAa,GAAG,UAAU,CAAC,aAAa,CAC5C,iBAAiB,CACH,CAAC;QACjB,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QAE/B,oEAAoE;QACpE,MAAM,cAAc,GAAG,IAAI,UAAU,CAAC,WAAW,EAAE;YACjD,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,MAAM,YAAY,GAAG,IAAI,UAAU,CAAC,SAAS,EAAE;YAC7C,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QAEH,aAAa,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QAC5C,aAAa,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;QAE1C,yBAAyB;QACzB,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QACpC,MAAM,CAAC,kBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CACrD,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CACpB,CAAC;QACF,MAAM,CAAC,kBAAmB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,4CAA4C;QAC5C,MAAM,QAAQ,GAAS;YACrB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,aAAa;oBACnB,IAAI,EAAE,aAAa;oBACnB,aAAa,EAAE,EAAE;iBACP;aACb;YACD,KAAK,EAAE,EAAE;SACV,CAAC;QAEF,oBAAoB;QACpB,MAAM,UAAU,GAAe,MAAM,OAAO,CAAC,IAAI,CAAA;;gBAErC,QAAQ;cACV,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE;;KAE1C,CAAC,CAAC;QAEH,MAAM,UAAU,CAAC,cAAc,CAAC;QAEhC,2DAA2D;QAC3D,IAAI,kBAAkB,GAAuB,IAAI,CAAC;QAClD,UAAU,CAAC,gBAAgB,CACzB,eAAe,CAAC,mBAAmB,EACnC,CAAC,KAAkB,EAAE,EAAE;YACrB,kBAAkB,GAAG,KAAK,CAAC;QAC7B,CAAC,CACF,CAAC;QAEF,yCAAyC;QACzC,MAAM,YAAY,GAAG,UAAU,CAAC,aAAa,CAC3C,gBAAgB,CACF,CAAC;QACjB,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QAE9B,YAAY,CAAC,KAAK,EAAE,CAAC;QAErB,8EAA8E;QAC9E,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,4CAA4C;QAC5C,MAAM,QAAQ,GAAS;YACrB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,aAAa;oBACnB,IAAI,EAAE,aAAa;oBACnB,aAAa,EAAE,EAAE;iBACP;aACb;YACD,KAAK,EAAE,EAAE;SACV,CAAC;QAEF,oBAAoB;QACpB,MAAM,UAAU,GAAe,MAAM,OAAO,CAAC,IAAI,CAAA;;gBAErC,QAAQ;cACV,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE;;KAE1C,CAAC,CAAC;QAEH,MAAM,UAAU,CAAC,cAAc,CAAC;QAEhC,2DAA2D;QAC3D,IAAI,kBAAkB,GAAuB,IAAI,CAAC;QAClD,UAAU,CAAC,gBAAgB,CACzB,eAAe,CAAC,mBAAmB,EACnC,CAAC,KAAkB,EAAE,EAAE;YACrB,kBAAkB,GAAG,KAAK,CAAC;QAC7B,CAAC,CACF,CAAC;QAEF,mGAAmG;QACnG,MAAM,aAAa,GAAG,UAAU,CAAC,aAAa,CAC5C,iBAAiB,CACH,CAAC;QACjB,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QAE/B,MAAM,cAAc,GAAG,IAAI,UAAU,CAAC,WAAW,EAAE;YACjD,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,MAAM,YAAY,GAAG,IAAI,UAAU,CAAC,SAAS,EAAE;YAC7C,OAAO,EAAE,GAAG,EAAE,+CAA+C;YAC7D,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QAEH,aAAa,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QAC5C,aAAa,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;QAE1C,oFAAoF;QACpF,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,4CAA4C;QAC5C,MAAM,QAAQ,GAAS;YACrB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,aAAa;oBACnB,IAAI,EAAE,aAAa;oBACnB,aAAa,EAAE,EAAE;iBACP;aACb;YACD,KAAK,EAAE,EAAE;SACV,CAAC;QAEF,oBAAoB;QACpB,MAAM,UAAU,GAAe,MAAM,OAAO,CAAC,IAAI,CAAA;;gBAErC,QAAQ;cACV,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE;;KAE1C,CAAC,CAAC;QAEH,MAAM,UAAU,CAAC,cAAc,CAAC;QAEhC,2DAA2D;QAC3D,IAAI,kBAAkB,GAAuB,IAAI,CAAC;QAClD,UAAU,CAAC,gBAAgB,CACzB,eAAe,CAAC,mBAAmB,EACnC,CAAC,KAAkB,EAAE,EAAE;YACrB,kBAAkB,GAAG,KAAK,CAAC;QAC7B,CAAC,CACF,CAAC;QAEF,yFAAyF;QACzF,MAAM,aAAa,GAAG,UAAU,CAAC,aAAa,CAC5C,iBAAiB,CACH,CAAC;QACjB,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QAE/B,MAAM,cAAc,GAAG,IAAI,UAAU,CAAC,WAAW,EAAE;YACjD,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,MAAM,YAAY,GAAG,IAAI,UAAU,CAAC,SAAS,EAAE;YAC7C,OAAO,EAAE,GAAG,EAAE,8CAA8C;YAC5D,OAAO,EAAE,GAAG,EAAE,gBAAgB;YAC9B,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QAEH,aAAa,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QAC5C,aAAa,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;QAE1C,gFAAgF;QAChF,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QACpC,MAAM,CAAC,kBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CACrD,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CACpB,CAAC;QACF,MAAM,CAAC,kBAAmB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { expect, fixture, html } from '@open-wc/testing';\nimport { CanvasNode } from '../src/flow/CanvasNode';\nimport { SendMsg, Node } from '../src/store/flow-definition';\nimport { CustomEventType } from '../src/interfaces';\nimport '../temba-modules';\n\ndescribe('Action Editing Integration', () => {\n it('should handle complete action editing workflow', async () => {\n // Create a test node with a send_msg action\n const testNode: Node = {\n uuid: 'test-node',\n actions: [\n {\n type: 'send_msg',\n uuid: 'test-action',\n text: 'Hello world',\n quick_replies: []\n } as SendMsg\n ],\n exits: []\n };\n\n // Create EditorNode\n const editorNode: CanvasNode = await fixture(html`\n <temba-flow-node\n .node=${testNode}\n .ui=${{ position: { left: 0, top: 0 } }}\n ></temba-flow-node>\n `);\n\n await editorNode.updateComplete;\n\n // Verify action is rendered\n const actionElement = editorNode.querySelector('.action');\n expect(actionElement).to.exist;\n\n // Set up event listener to catch ActionEditRequested event\n let editRequestedEvent: CustomEvent | null = null;\n editorNode.addEventListener(\n CustomEventType.ActionEditRequested,\n (event: CustomEvent) => {\n editRequestedEvent = event;\n }\n );\n\n // Simulate clicking on the action content (not the remove button)\n const actionContent = editorNode.querySelector(\n '.action-content'\n ) as HTMLElement;\n expect(actionContent).to.exist;\n\n // Simulate a click (mousedown followed by mouseup at same position)\n const mouseDownEvent = new MouseEvent('mousedown', {\n clientX: 100,\n clientY: 100,\n bubbles: true\n });\n const mouseUpEvent = new MouseEvent('mouseup', {\n clientX: 100,\n clientY: 100,\n bubbles: true\n });\n\n actionContent.dispatchEvent(mouseDownEvent);\n actionContent.dispatchEvent(mouseUpEvent);\n\n // Verify event was fired\n expect(editRequestedEvent).to.exist;\n expect(editRequestedEvent!.detail.action).to.deep.equal(\n testNode.actions[0]\n );\n expect(editRequestedEvent!.detail.nodeUuid).to.equal('test-node');\n });\n\n it('should ignore clicks on remove button', async () => {\n // Create a test node with a send_msg action\n const testNode: Node = {\n uuid: 'test-node',\n actions: [\n {\n type: 'send_msg',\n uuid: 'test-action',\n text: 'Hello world',\n quick_replies: []\n } as SendMsg\n ],\n exits: []\n };\n\n // Create EditorNode\n const editorNode: CanvasNode = await fixture(html`\n <temba-flow-node\n .node=${testNode}\n .ui=${{ position: { left: 0, top: 0 } }}\n ></temba-flow-node>\n `);\n\n await editorNode.updateComplete;\n\n // Set up event listener to catch ActionEditRequested event\n let editRequestedEvent: CustomEvent | null = null;\n editorNode.addEventListener(\n CustomEventType.ActionEditRequested,\n (event: CustomEvent) => {\n editRequestedEvent = event;\n }\n );\n\n // Simulate clicking on the remove button\n const removeButton = editorNode.querySelector(\n '.remove-button'\n ) as HTMLElement;\n expect(removeButton).to.exist;\n\n removeButton.click();\n\n // Verify NO ActionEditRequested event was fired (only remove action handling)\n expect(editRequestedEvent).to.be.null;\n });\n\n it('should not open action editor when dragging beyond threshold', async () => {\n // Create a test node with a send_msg action\n const testNode: Node = {\n uuid: 'test-node',\n actions: [\n {\n type: 'send_msg',\n uuid: 'test-action',\n text: 'Hello world',\n quick_replies: []\n } as SendMsg\n ],\n exits: []\n };\n\n // Create EditorNode\n const editorNode: CanvasNode = await fixture(html`\n <temba-flow-node\n .node=${testNode}\n .ui=${{ position: { left: 0, top: 0 } }}\n ></temba-flow-node>\n `);\n\n await editorNode.updateComplete;\n\n // Set up event listener to catch ActionEditRequested event\n let editRequestedEvent: CustomEvent | null = null;\n editorNode.addEventListener(\n CustomEventType.ActionEditRequested,\n (event: CustomEvent) => {\n editRequestedEvent = event;\n }\n );\n\n // Simulate a drag operation (mousedown followed by mouseup at different position beyond threshold)\n const actionContent = editorNode.querySelector(\n '.action-content'\n ) as HTMLElement;\n expect(actionContent).to.exist;\n\n const mouseDownEvent = new MouseEvent('mousedown', {\n clientX: 100,\n clientY: 100,\n bubbles: true\n });\n const mouseUpEvent = new MouseEvent('mouseup', {\n clientX: 110, // 10 pixels away, beyond the 5 pixel threshold\n clientY: 100,\n bubbles: true\n });\n\n actionContent.dispatchEvent(mouseDownEvent);\n actionContent.dispatchEvent(mouseUpEvent);\n\n // Verify NO ActionEditRequested event was fired because we dragged beyond threshold\n expect(editRequestedEvent).to.be.null;\n });\n\n it('should open action editor when clicking within threshold', async () => {\n // Create a test node with a send_msg action\n const testNode: Node = {\n uuid: 'test-node',\n actions: [\n {\n type: 'send_msg',\n uuid: 'test-action',\n text: 'Hello world',\n quick_replies: []\n } as SendMsg\n ],\n exits: []\n };\n\n // Create EditorNode\n const editorNode: CanvasNode = await fixture(html`\n <temba-flow-node\n .node=${testNode}\n .ui=${{ position: { left: 0, top: 0 } }}\n ></temba-flow-node>\n `);\n\n await editorNode.updateComplete;\n\n // Set up event listener to catch ActionEditRequested event\n let editRequestedEvent: CustomEvent | null = null;\n editorNode.addEventListener(\n CustomEventType.ActionEditRequested,\n (event: CustomEvent) => {\n editRequestedEvent = event;\n }\n );\n\n // Simulate a small movement (mousedown followed by mouseup at position within threshold)\n const actionContent = editorNode.querySelector(\n '.action-content'\n ) as HTMLElement;\n expect(actionContent).to.exist;\n\n const mouseDownEvent = new MouseEvent('mousedown', {\n clientX: 100,\n clientY: 100,\n bubbles: true\n });\n const mouseUpEvent = new MouseEvent('mouseup', {\n clientX: 103, // 3 pixels away, within the 5 pixel threshold\n clientY: 102, // 2 pixels away\n bubbles: true\n });\n\n actionContent.dispatchEvent(mouseDownEvent);\n actionContent.dispatchEvent(mouseUpEvent);\n\n // Verify ActionEditRequested event was fired because we stayed within threshold\n expect(editRequestedEvent).to.exist;\n expect(editRequestedEvent!.detail.action).to.deep.equal(\n testNode.actions[0]\n );\n expect(editRequestedEvent!.detail.nodeUuid).to.equal('test-node');\n });\n});\n"]}
@@ -55,7 +55,7 @@ describe('temba-checkbox', () => {
55
55
  const el = await fixture(html `
56
56
  <temba-checkbox name="My Checkbox"></temba-checkbox>
57
57
  `);
58
- expect(el.label).to.equal(null);
58
+ expect(el.label).to.equal(undefined);
59
59
  //the ".wrapper.label" style results in the background hover effect
60
60
  const wrapperDivEl = el.shadowRoot.querySelector('div.wrapper.label');
61
61
  expect(wrapperDivEl).to.equal(null);
@@ -1 +1 @@
1
- {"version":3,"file":"temba-checkbox.test.js","sourceRoot":"","sources":["../../test/temba-checkbox.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, fixture, expect } from '@open-wc/testing';\nimport { Checkbox } from '../src/form/Checkbox';\nimport { assertScreenshot, getClip } from './utils.test';\n\ndescribe('temba-checkbox', () => {\n it('renders default checkbox', async () => {\n const el: Checkbox = await fixture(html`\n <temba-checkbox label=\"My Checkbox\"></temba-checkbox>\n `);\n\n expect(el.label).to.equal('My Checkbox');\n await assertScreenshot('checkbox/default', getClip(el));\n });\n\n it('can select by clicking on the label', async () => {\n const el: Checkbox = await fixture(html`\n <temba-checkbox\n label=\"My Checkbox\"\n animatechange=\"false\"\n ></temba-checkbox>\n `);\n\n (el.shadowRoot.querySelector('.checkbox-label') as HTMLDivElement).click();\n expect(el.checked).to.equal(true);\n await assertScreenshot('checkbox/checked', getClip(el));\n });\n\n it('fires change event on click', async () => {\n // eslint-disable-next-line no-async-promise-executor\n return new Promise<void>(async (resolve) => {\n const checkbox: Checkbox = await fixture(html`\n <temba-checkbox label=\"My Checkbox\"></temba-checkbox>\n `);\n\n checkbox.addEventListener('change', () => {\n resolve();\n });\n\n click('temba-checkbox');\n });\n });\n\n it('checks via click method', async () => {\n const checkbox: Checkbox = await fixture(html`\n <temba-checkbox label=\"My Checkbox\"></temba-checkbox>\n `);\n checkbox.click();\n expect(checkbox.checked).to.equal(true);\n });\n\n it('has background hover effect when label is set', async () => {\n const el: Checkbox = await fixture(html`\n <temba-checkbox name=\"My Checkbox\" label=\"My Label\"></temba-checkbox>\n `);\n expect(el.label).to.equal('My Label');\n //the \".wrapper.label\" style results in the background hover effect\n const wrapperDivEl = el.shadowRoot.querySelector(\n 'div.wrapper.label'\n ) as HTMLDivElement;\n expect(wrapperDivEl).to.not.equal(null);\n await assertScreenshot(\n 'checkbox/checkbox-label-background-hover',\n getClip(el)\n );\n });\n\n //note: sometimes upstream logic sets an empty checkbox label to the name value,\n //but this is the expected behavior if the label value is still empty,\n //upon rendering the component\n it('has no background hover effect when label is empty', async () => {\n const el: Checkbox = await fixture(html`\n <temba-checkbox name=\"My Checkbox\"></temba-checkbox>\n `);\n expect(el.label).to.equal(null);\n //the \".wrapper.label\" style results in the background hover effect\n const wrapperDivEl = el.shadowRoot.querySelector(\n 'div.wrapper.label'\n ) as HTMLDivElement;\n expect(wrapperDivEl).to.equal(null);\n await assertScreenshot(\n 'checkbox/checkbox-no-label-no-background-hover',\n getClip(el)\n );\n });\n\n it('has no background hover effect when label is whitespace', async () => {\n const el: Checkbox = await fixture(html`\n <temba-checkbox name=\"My Checkbox\" label=\" \"></temba-checkbox>\n `);\n expect(el.label).to.equal('');\n //the \".wrapper.label\" style results in the background hover effect\n const wrapperDivEl = el.shadowRoot.querySelector(\n 'div.wrapper.label'\n ) as HTMLDivElement;\n expect(wrapperDivEl).to.equal(null);\n await assertScreenshot(\n 'checkbox/checkbox-whitespace-label-no-background-hover',\n getClip(el)\n );\n });\n\n it('submits as boolean without value', async () => {\n const form = (await fixture(html`\n <form>\n <temba-checkbox name=\"my-cb\"></temba-checkbox>\n </form>\n `)) as HTMLFormElement;\n\n // if we didn't click it, it shouldn't be in the form data\n let data = new FormData(form);\n expect(data.get('my-cb')).to.equal(null);\n\n // click our checkbox\n const checkbox = form.querySelector('temba-checkbox') as Checkbox;\n await click('temba-checkbox');\n expect(checkbox.checked).to.equal(true);\n\n // clicking a non-value checkbox should set it to 1\n data = new FormData(form);\n expect(data.get('my-cb')).to.equal('1');\n });\n\n it('supports custom values', async () => {\n const form = (await fixture(html`\n <form>\n <temba-checkbox name=\"my-cb\" value=\"3\"></temba-checkbox>\n </form>\n `)) as HTMLFormElement;\n\n // if we didn't click it, it shouldn't be in the form data\n let data = new FormData(form);\n expect(data.get('my-cb')).to.equal(null);\n\n // click our checkbox\n const checkbox = form.querySelector('temba-checkbox') as Checkbox;\n await click('temba-checkbox');\n expect(checkbox.checked).to.equal(true);\n\n // clicking a non-value checkbox should set it to 1\n data = new FormData(form);\n expect(data.get('my-cb')).to.equal('3');\n });\n\n it('supports programmtically updated values', async () => {\n // start with empty value\n const form = (await fixture(html`\n <form>\n <temba-checkbox name=\"my-cb\"></temba-checkbox>\n </form>\n `)) as HTMLFormElement;\n\n // update our value directly\n const checkbox = form.querySelector('temba-checkbox') as Checkbox;\n checkbox.value = '5';\n\n // we set a custom value, but we still aren't checked\n let data = new FormData(form);\n expect(data.get('my-cb')).to.equal(null);\n\n // click our checkbox\n await click('temba-checkbox');\n expect(checkbox.checked).to.equal(true);\n\n // clicking a non-value checkbox should set it to 1\n data = new FormData(form);\n expect(data.get('my-cb')).to.equal('5');\n });\n});\n"]}
1
+ {"version":3,"file":"temba-checkbox.test.js","sourceRoot":"","sources":["../../test/temba-checkbox.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, fixture, expect } from '@open-wc/testing';\nimport { Checkbox } from '../src/form/Checkbox';\nimport { assertScreenshot, getClip } from './utils.test';\n\ndescribe('temba-checkbox', () => {\n it('renders default checkbox', async () => {\n const el: Checkbox = await fixture(html`\n <temba-checkbox label=\"My Checkbox\"></temba-checkbox>\n `);\n\n expect(el.label).to.equal('My Checkbox');\n await assertScreenshot('checkbox/default', getClip(el));\n });\n\n it('can select by clicking on the label', async () => {\n const el: Checkbox = await fixture(html`\n <temba-checkbox\n label=\"My Checkbox\"\n animatechange=\"false\"\n ></temba-checkbox>\n `);\n\n (el.shadowRoot.querySelector('.checkbox-label') as HTMLDivElement).click();\n expect(el.checked).to.equal(true);\n await assertScreenshot('checkbox/checked', getClip(el));\n });\n\n it('fires change event on click', async () => {\n // eslint-disable-next-line no-async-promise-executor\n return new Promise<void>(async (resolve) => {\n const checkbox: Checkbox = await fixture(html`\n <temba-checkbox label=\"My Checkbox\"></temba-checkbox>\n `);\n\n checkbox.addEventListener('change', () => {\n resolve();\n });\n\n click('temba-checkbox');\n });\n });\n\n it('checks via click method', async () => {\n const checkbox: Checkbox = await fixture(html`\n <temba-checkbox label=\"My Checkbox\"></temba-checkbox>\n `);\n checkbox.click();\n expect(checkbox.checked).to.equal(true);\n });\n\n it('has background hover effect when label is set', async () => {\n const el: Checkbox = await fixture(html`\n <temba-checkbox name=\"My Checkbox\" label=\"My Label\"></temba-checkbox>\n `);\n expect(el.label).to.equal('My Label');\n //the \".wrapper.label\" style results in the background hover effect\n const wrapperDivEl = el.shadowRoot.querySelector(\n 'div.wrapper.label'\n ) as HTMLDivElement;\n expect(wrapperDivEl).to.not.equal(null);\n await assertScreenshot(\n 'checkbox/checkbox-label-background-hover',\n getClip(el)\n );\n });\n\n //note: sometimes upstream logic sets an empty checkbox label to the name value,\n //but this is the expected behavior if the label value is still empty,\n //upon rendering the component\n it('has no background hover effect when label is empty', async () => {\n const el: Checkbox = await fixture(html`\n <temba-checkbox name=\"My Checkbox\"></temba-checkbox>\n `);\n expect(el.label).to.equal(undefined);\n //the \".wrapper.label\" style results in the background hover effect\n const wrapperDivEl = el.shadowRoot.querySelector(\n 'div.wrapper.label'\n ) as HTMLDivElement;\n expect(wrapperDivEl).to.equal(null);\n await assertScreenshot(\n 'checkbox/checkbox-no-label-no-background-hover',\n getClip(el)\n );\n });\n\n it('has no background hover effect when label is whitespace', async () => {\n const el: Checkbox = await fixture(html`\n <temba-checkbox name=\"My Checkbox\" label=\" \"></temba-checkbox>\n `);\n expect(el.label).to.equal('');\n //the \".wrapper.label\" style results in the background hover effect\n const wrapperDivEl = el.shadowRoot.querySelector(\n 'div.wrapper.label'\n ) as HTMLDivElement;\n expect(wrapperDivEl).to.equal(null);\n await assertScreenshot(\n 'checkbox/checkbox-whitespace-label-no-background-hover',\n getClip(el)\n );\n });\n\n it('submits as boolean without value', async () => {\n const form = (await fixture(html`\n <form>\n <temba-checkbox name=\"my-cb\"></temba-checkbox>\n </form>\n `)) as HTMLFormElement;\n\n // if we didn't click it, it shouldn't be in the form data\n let data = new FormData(form);\n expect(data.get('my-cb')).to.equal(null);\n\n // click our checkbox\n const checkbox = form.querySelector('temba-checkbox') as Checkbox;\n await click('temba-checkbox');\n expect(checkbox.checked).to.equal(true);\n\n // clicking a non-value checkbox should set it to 1\n data = new FormData(form);\n expect(data.get('my-cb')).to.equal('1');\n });\n\n it('supports custom values', async () => {\n const form = (await fixture(html`\n <form>\n <temba-checkbox name=\"my-cb\" value=\"3\"></temba-checkbox>\n </form>\n `)) as HTMLFormElement;\n\n // if we didn't click it, it shouldn't be in the form data\n let data = new FormData(form);\n expect(data.get('my-cb')).to.equal(null);\n\n // click our checkbox\n const checkbox = form.querySelector('temba-checkbox') as Checkbox;\n await click('temba-checkbox');\n expect(checkbox.checked).to.equal(true);\n\n // clicking a non-value checkbox should set it to 1\n data = new FormData(form);\n expect(data.get('my-cb')).to.equal('3');\n });\n\n it('supports programmtically updated values', async () => {\n // start with empty value\n const form = (await fixture(html`\n <form>\n <temba-checkbox name=\"my-cb\"></temba-checkbox>\n </form>\n `)) as HTMLFormElement;\n\n // update our value directly\n const checkbox = form.querySelector('temba-checkbox') as Checkbox;\n checkbox.value = '5';\n\n // we set a custom value, but we still aren't checked\n let data = new FormData(form);\n expect(data.get('my-cb')).to.equal(null);\n\n // click our checkbox\n await click('temba-checkbox');\n expect(checkbox.checked).to.equal(true);\n\n // clicking a non-value checkbox should set it to 1\n data = new FormData(form);\n expect(data.get('my-cb')).to.equal('5');\n });\n});\n"]}
@@ -0,0 +1,133 @@
1
+ import { html, fixture, expect } from '@open-wc/testing';
2
+ import '../src/form/KeyValueEditor';
3
+ import '../src/form/ArrayEditor';
4
+ describe('Field Configuration System', () => {
5
+ describe('KeyValueEditor', () => {
6
+ it('should render with empty value and always show one empty row', async () => {
7
+ var _a, _b;
8
+ const el = await fixture(html `
9
+ <temba-key-value-editor></temba-key-value-editor>
10
+ `);
11
+ expect(el).to.exist;
12
+ // Should always have at least one row (empty row)
13
+ const rows = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.row');
14
+ expect(rows === null || rows === void 0 ? void 0 : rows.length).to.equal(1);
15
+ // Should not have add button anymore
16
+ expect((_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('.add-btn')).to.not.exist;
17
+ });
18
+ it('should render with initial values and maintain empty row', async () => {
19
+ var _a;
20
+ const initialValue = {
21
+ 'Content-Type': 'application/json',
22
+ Authorization: 'Bearer token123'
23
+ };
24
+ const el = await fixture(html `
25
+ <temba-key-value-editor .value=${initialValue}></temba-key-value-editor>
26
+ `);
27
+ expect(el).to.exist;
28
+ // Should have 2 data rows + 1 empty row = 3 total
29
+ const rows = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.row');
30
+ expect(rows === null || rows === void 0 ? void 0 : rows.length).to.equal(3);
31
+ });
32
+ it('should emit clean values without empty rows', async () => {
33
+ var _a;
34
+ const el = await fixture(html `
35
+ <temba-key-value-editor></temba-key-value-editor>
36
+ `);
37
+ let changeEvent = null;
38
+ el.addEventListener('change', (e) => {
39
+ changeEvent = e;
40
+ });
41
+ // Trigger a field change that should cause an update
42
+ const keyInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('temba-textinput');
43
+ if (keyInput) {
44
+ keyInput.value = 'test-key';
45
+ keyInput.dispatchEvent(new Event('change'));
46
+ }
47
+ await el.updateComplete;
48
+ expect(changeEvent).to.exist;
49
+ // Should emit the array format with key-value pairs
50
+ expect(changeEvent.detail.value).to.be.an('array');
51
+ expect(changeEvent.detail.value).to.deep.include({
52
+ key: 'test-key',
53
+ value: ''
54
+ });
55
+ });
56
+ it('should hide remove button for empty rows', async () => {
57
+ var _a, _b, _c, _d;
58
+ const initialValue = {
59
+ 'Content-Type': 'application/json'
60
+ };
61
+ const el = await fixture(html `
62
+ <temba-key-value-editor .value=${initialValue}></temba-key-value-editor>
63
+ `);
64
+ expect(el).to.exist;
65
+ const rows = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.row');
66
+ expect(rows === null || rows === void 0 ? void 0 : rows.length).to.equal(2); // 1 data row + 1 empty row
67
+ // First row (with data) should have remove button
68
+ const firstRowRemoveBtn = (_b = rows === null || rows === void 0 ? void 0 : rows[0]) === null || _b === void 0 ? void 0 : _b.querySelector('.remove-btn');
69
+ expect(firstRowRemoveBtn).to.exist;
70
+ // Second row (empty) should have spacer instead of remove button
71
+ const secondRowRemoveBtn = (_c = rows === null || rows === void 0 ? void 0 : rows[1]) === null || _c === void 0 ? void 0 : _c.querySelector('.remove-btn');
72
+ const secondRowSpacer = (_d = rows === null || rows === void 0 ? void 0 : rows[1]) === null || _d === void 0 ? void 0 : _d.querySelector('.remove-btn-spacer');
73
+ expect(secondRowRemoveBtn).to.not.exist;
74
+ expect(secondRowSpacer).to.exist;
75
+ });
76
+ it('should show remove button when empty row gets content', async () => {
77
+ var _a, _b, _c, _d, _e, _f;
78
+ const el = await fixture(html `
79
+ <temba-key-value-editor></temba-key-value-editor>
80
+ `);
81
+ // Initially should have no remove button (empty row)
82
+ let rows = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.row');
83
+ expect((_b = rows === null || rows === void 0 ? void 0 : rows[0]) === null || _b === void 0 ? void 0 : _b.querySelector('.remove-btn')).to.not.exist;
84
+ expect((_c = rows === null || rows === void 0 ? void 0 : rows[0]) === null || _c === void 0 ? void 0 : _c.querySelector('.remove-btn-spacer')).to.exist;
85
+ // Simulate adding content by setting value and triggering update
86
+ el.value = { 'test-key': '' };
87
+ el.requestUpdate();
88
+ await el.updateComplete;
89
+ // Now should have remove button for the row with content
90
+ rows = (_d = el.shadowRoot) === null || _d === void 0 ? void 0 : _d.querySelectorAll('.row');
91
+ expect(rows === null || rows === void 0 ? void 0 : rows.length).to.equal(2); // row with content + empty row
92
+ expect((_e = rows === null || rows === void 0 ? void 0 : rows[0]) === null || _e === void 0 ? void 0 : _e.querySelector('.remove-btn')).to.exist;
93
+ expect((_f = rows === null || rows === void 0 ? void 0 : rows[1]) === null || _f === void 0 ? void 0 : _f.querySelector('.remove-btn-spacer')).to.exist;
94
+ });
95
+ });
96
+ describe('ArrayEditor', () => {
97
+ it('should render with empty array', async () => {
98
+ var _a;
99
+ const itemConfig = {
100
+ name: { type: 'text', label: 'Name', required: true },
101
+ value: { type: 'text', label: 'Value' }
102
+ };
103
+ const el = await fixture(html `
104
+ <temba-array-editor .itemConfig=${itemConfig}></temba-array-editor>
105
+ `);
106
+ await el.updateComplete;
107
+ expect(el).to.exist;
108
+ expect((_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.add-btn')).to.exist;
109
+ });
110
+ it('should render with initial items', async () => {
111
+ var _a;
112
+ const itemConfig = {
113
+ operator: { type: 'text', label: 'Operator' },
114
+ value: { type: 'text', label: 'Value' }
115
+ };
116
+ const initialValue = [
117
+ { operator: 'equals', value: 'test' },
118
+ { operator: 'contains', value: 'example' }
119
+ ];
120
+ const el = await fixture(html `
121
+ <temba-array-editor
122
+ .value=${initialValue}
123
+ .itemConfig=${itemConfig}
124
+ itemLabel="Rule"
125
+ ></temba-array-editor>
126
+ `);
127
+ expect(el).to.exist;
128
+ const items = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.array-item');
129
+ expect(items === null || items === void 0 ? void 0 : items.length).to.equal(2);
130
+ });
131
+ });
132
+ });
133
+ //# sourceMappingURL=temba-field-config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temba-field-config.test.js","sourceRoot":"","sources":["../../test/temba-field-config.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, fixture, expect } from '@open-wc/testing';\nimport '../src/form/KeyValueEditor';\nimport '../src/form/ArrayEditor';\n\ndescribe('Field Configuration System', () => {\n describe('KeyValueEditor', () => {\n it('should render with empty value and always show one empty row', async () => {\n const el = await fixture(html`\n <temba-key-value-editor></temba-key-value-editor>\n `);\n\n expect(el).to.exist;\n // Should always have at least one row (empty row)\n const rows = el.shadowRoot?.querySelectorAll('.row');\n expect(rows?.length).to.equal(1);\n // Should not have add button anymore\n expect(el.shadowRoot?.querySelector('.add-btn')).to.not.exist;\n });\n\n it('should render with initial values and maintain empty row', async () => {\n const initialValue = {\n 'Content-Type': 'application/json',\n Authorization: 'Bearer token123'\n };\n\n const el = await fixture(html`\n <temba-key-value-editor .value=${initialValue}></temba-key-value-editor>\n `);\n\n expect(el).to.exist;\n // Should have 2 data rows + 1 empty row = 3 total\n const rows = el.shadowRoot?.querySelectorAll('.row');\n expect(rows?.length).to.equal(3);\n });\n\n it('should emit clean values without empty rows', async () => {\n const el = await fixture(html`\n <temba-key-value-editor></temba-key-value-editor>\n `);\n\n let changeEvent: any = null;\n el.addEventListener('change', (e) => {\n changeEvent = e;\n });\n\n // Trigger a field change that should cause an update\n const keyInput = el.shadowRoot?.querySelector('temba-textinput') as any;\n if (keyInput) {\n keyInput.value = 'test-key';\n keyInput.dispatchEvent(new Event('change'));\n }\n\n await (el as any).updateComplete;\n\n expect(changeEvent).to.exist;\n // Should emit the array format with key-value pairs\n expect(changeEvent.detail.value).to.be.an('array');\n expect(changeEvent.detail.value).to.deep.include({\n key: 'test-key',\n value: ''\n });\n });\n\n it('should hide remove button for empty rows', async () => {\n const initialValue = {\n 'Content-Type': 'application/json'\n };\n\n const el = await fixture(html`\n <temba-key-value-editor .value=${initialValue}></temba-key-value-editor>\n `);\n\n expect(el).to.exist;\n const rows = el.shadowRoot?.querySelectorAll('.row');\n expect(rows?.length).to.equal(2); // 1 data row + 1 empty row\n\n // First row (with data) should have remove button\n const firstRowRemoveBtn = rows?.[0]?.querySelector('.remove-btn');\n expect(firstRowRemoveBtn).to.exist;\n\n // Second row (empty) should have spacer instead of remove button\n const secondRowRemoveBtn = rows?.[1]?.querySelector('.remove-btn');\n const secondRowSpacer = rows?.[1]?.querySelector('.remove-btn-spacer');\n expect(secondRowRemoveBtn).to.not.exist;\n expect(secondRowSpacer).to.exist;\n });\n\n it('should show remove button when empty row gets content', async () => {\n const el = await fixture(html`\n <temba-key-value-editor></temba-key-value-editor>\n `);\n\n // Initially should have no remove button (empty row)\n let rows = el.shadowRoot?.querySelectorAll('.row');\n expect(rows?.[0]?.querySelector('.remove-btn')).to.not.exist;\n expect(rows?.[0]?.querySelector('.remove-btn-spacer')).to.exist;\n\n // Simulate adding content by setting value and triggering update\n (el as any).value = { 'test-key': '' };\n (el as any).requestUpdate();\n await (el as any).updateComplete;\n\n // Now should have remove button for the row with content\n rows = el.shadowRoot?.querySelectorAll('.row');\n expect(rows?.length).to.equal(2); // row with content + empty row\n expect(rows?.[0]?.querySelector('.remove-btn')).to.exist;\n expect(rows?.[1]?.querySelector('.remove-btn-spacer')).to.exist;\n });\n });\n\n describe('ArrayEditor', () => {\n it('should render with empty array', async () => {\n const itemConfig = {\n name: { type: 'text', label: 'Name', required: true },\n value: { type: 'text', label: 'Value' }\n };\n\n const el = await fixture(html`\n <temba-array-editor .itemConfig=${itemConfig}></temba-array-editor>\n `);\n\n await (el as any).updateComplete;\n\n expect(el).to.exist;\n expect(el.shadowRoot?.querySelector('.add-btn')).to.exist;\n });\n\n it('should render with initial items', async () => {\n const itemConfig = {\n operator: { type: 'text', label: 'Operator' },\n value: { type: 'text', label: 'Value' }\n };\n\n const initialValue = [\n { operator: 'equals', value: 'test' },\n { operator: 'contains', value: 'example' }\n ];\n\n const el = await fixture(html`\n <temba-array-editor\n .value=${initialValue}\n .itemConfig=${itemConfig}\n itemLabel=\"Rule\"\n ></temba-array-editor>\n `);\n\n expect(el).to.exist;\n const items = el.shadowRoot?.querySelectorAll('.array-item');\n expect(items?.length).to.equal(2);\n });\n });\n});\n"]}