@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,116 @@
1
+ import { fixture, expect } from '@open-wc/testing';
2
+ import { html } from 'lit';
3
+ import { assertScreenshot, getClip } from './utils.test';
4
+ import '../temba-modules';
5
+ /**
6
+ * Generic action test framework
7
+ * Tests the complete action lifecycle: render → edit → save → validate
8
+ */
9
+ export class ActionTest {
10
+ constructor(actionConfig, actionName) {
11
+ this.actionConfig = actionConfig;
12
+ this.actionName = actionName;
13
+ }
14
+ /**
15
+ * Renders an action in the flow editor and returns the flow node
16
+ */
17
+ async renderAction(action) {
18
+ const node = {
19
+ uuid: 'test-node',
20
+ actions: [action],
21
+ exits: []
22
+ };
23
+ const mockDefinition = {
24
+ nodes: [node],
25
+ _ui: {
26
+ nodes: {
27
+ [node.uuid]: {
28
+ type: 'execute_actions',
29
+ position: { left: 50, top: 50 }
30
+ }
31
+ }
32
+ }
33
+ };
34
+ const editor = (await fixture(html `
35
+ <temba-flow-editor>
36
+ <div id="canvas"></div>
37
+ </temba-flow-editor>
38
+ `));
39
+ editor.definition = mockDefinition;
40
+ editor.canvasSize = { width: 400, height: 300 };
41
+ await editor.updateComplete;
42
+ const flowNode = editor.querySelector('temba-flow-node');
43
+ expect(flowNode).to.exist;
44
+ return flowNode;
45
+ }
46
+ /**
47
+ * Opens the node editor for an action and returns the editor element
48
+ */
49
+ async openNodeEditor(action) {
50
+ const nodeEditor = (await fixture(html `
51
+ <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>
52
+ `));
53
+ await nodeEditor.updateComplete;
54
+ expect(nodeEditor).to.exist;
55
+ return nodeEditor;
56
+ }
57
+ /**
58
+ * Takes a screenshot of the dialog container within a node editor
59
+ */
60
+ async assertDialogScreenshot(el, screenshotName) {
61
+ const dialog = el.shadowRoot
62
+ .querySelector('temba-dialog')
63
+ .shadowRoot.querySelector('.dialog-container');
64
+ await assertScreenshot(screenshotName, getClip(dialog));
65
+ }
66
+ /**
67
+ * Complete test for an action configuration
68
+ * 1. Renders the action in a flow node (with screenshot)
69
+ * 2. Opens the node editor (with screenshot)
70
+ * 3. Simulates save and validates round-trip conversion
71
+ */
72
+ async testAction(action, testName) {
73
+ it(`${testName}`, async () => {
74
+ // Step 1: Render action in flow node
75
+ const flowNode = await this.renderAction(action);
76
+ expect(flowNode.querySelector('.body')).to.exist;
77
+ await assertScreenshot(`actions/${this.actionName}/render/${testName}`, getClip(flowNode));
78
+ // Step 2: Open node editor
79
+ const nodeEditor = await this.openNodeEditor(action);
80
+ await this.assertDialogScreenshot(nodeEditor, `actions/${this.actionName}/editor/${testName}`);
81
+ // Step 3: Test round-trip conversion (simulates save workflow)
82
+ if (this.actionConfig.toFormData && this.actionConfig.fromFormData) {
83
+ const formData = this.actionConfig.toFormData(action);
84
+ const convertedAction = this.actionConfig.fromFormData(formData);
85
+ // Validate the round trip worked
86
+ expect(convertedAction.uuid).to.equal(action.uuid);
87
+ expect(convertedAction.type).to.equal(action.type);
88
+ // Validate the converted action
89
+ if (this.actionConfig.validate) {
90
+ const validation = this.actionConfig.validate(convertedAction);
91
+ expect(validation.valid).to.be.true;
92
+ }
93
+ }
94
+ });
95
+ }
96
+ /**
97
+ * Run basic property tests
98
+ */
99
+ testBasicProperties() {
100
+ it('has correct basic properties', () => {
101
+ expect(this.actionConfig.name).to.be.a('string');
102
+ expect(this.actionConfig.color).to.exist;
103
+ // toFormData and fromFormData are optional - only needed for complex data transformations
104
+ if (this.actionConfig.toFormData) {
105
+ expect(this.actionConfig.toFormData).to.be.a('function');
106
+ }
107
+ if (this.actionConfig.fromFormData) {
108
+ expect(this.actionConfig.fromFormData).to.be.a('function');
109
+ }
110
+ if (this.actionConfig.validate) {
111
+ expect(this.actionConfig.validate).to.be.a('function');
112
+ }
113
+ });
114
+ }
115
+ }
116
+ //# sourceMappingURL=ActionHelper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionHelper.js","sourceRoot":"","sources":["../../test/ActionHelper.ts"],"names":[],"mappings":"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","sourcesContent":["import { fixture, expect } from '@open-wc/testing';\nimport { html } from 'lit';\nimport { Action, Node } from '../src/store/flow-definition';\nimport { assertScreenshot, getClip } from './utils.test';\nimport { Editor } from '../src/flow/Editor';\nimport '../temba-modules';\n\n/**\n * Generic action test framework\n * Tests the complete action lifecycle: render → edit → save → validate\n */\nexport class ActionTest<T extends Action> {\n constructor(private actionConfig: any, private actionName: string) {}\n\n /**\n * Renders an action in the flow editor and returns the flow node\n */\n private async renderAction(action: T): Promise<HTMLElement> {\n const node: Node = {\n uuid: 'test-node',\n actions: [action],\n exits: []\n };\n\n const mockDefinition = {\n nodes: [node],\n _ui: {\n nodes: {\n [node.uuid]: {\n type: 'execute_actions',\n position: { left: 50, top: 50 }\n }\n }\n }\n };\n\n const editor = (await fixture(html`\n <temba-flow-editor>\n <div id=\"canvas\"></div>\n </temba-flow-editor>\n `)) as Editor;\n\n (editor as any).definition = mockDefinition;\n (editor as any).canvasSize = { width: 400, height: 300 };\n await editor.updateComplete;\n\n const flowNode = editor.querySelector('temba-flow-node') as HTMLElement;\n expect(flowNode).to.exist;\n\n return flowNode;\n }\n\n /**\n * Opens the node editor for an action and returns the editor element\n */\n private async openNodeEditor(action: T): Promise<HTMLElement> {\n const nodeEditor = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as HTMLElement;\n\n await (nodeEditor as any).updateComplete;\n expect(nodeEditor).to.exist;\n\n return nodeEditor;\n }\n\n /**\n * Takes a screenshot of the dialog container within a node editor\n */\n private async assertDialogScreenshot(\n el: HTMLElement,\n screenshotName: string\n ) {\n const dialog = el.shadowRoot\n .querySelector('temba-dialog')\n .shadowRoot.querySelector('.dialog-container') as HTMLElement;\n await assertScreenshot(screenshotName, getClip(dialog));\n }\n\n /**\n * Complete test for an action configuration\n * 1. Renders the action in a flow node (with screenshot)\n * 2. Opens the node editor (with screenshot)\n * 3. Simulates save and validates round-trip conversion\n */\n async testAction(action: T, testName: string) {\n it(`${testName}`, async () => {\n // Step 1: Render action in flow node\n const flowNode = await this.renderAction(action);\n expect(flowNode.querySelector('.body')).to.exist;\n await assertScreenshot(\n `actions/${this.actionName}/render/${testName}`,\n getClip(flowNode)\n );\n\n // Step 2: Open node editor\n const nodeEditor = await this.openNodeEditor(action);\n await this.assertDialogScreenshot(\n nodeEditor,\n `actions/${this.actionName}/editor/${testName}`\n );\n\n // Step 3: Test round-trip conversion (simulates save workflow)\n if (this.actionConfig.toFormData && this.actionConfig.fromFormData) {\n const formData = this.actionConfig.toFormData(action);\n const convertedAction = this.actionConfig.fromFormData(formData) as T;\n\n // Validate the round trip worked\n expect(convertedAction.uuid).to.equal(action.uuid);\n expect(convertedAction.type).to.equal(action.type);\n\n // Validate the converted action\n if (this.actionConfig.validate) {\n const validation = this.actionConfig.validate(convertedAction);\n expect(validation.valid).to.be.true;\n }\n }\n });\n }\n\n /**\n * Run basic property tests\n */\n testBasicProperties() {\n it('has correct basic properties', () => {\n expect(this.actionConfig.name).to.be.a('string');\n expect(this.actionConfig.color).to.exist;\n\n // toFormData and fromFormData are optional - only needed for complex data transformations\n if (this.actionConfig.toFormData) {\n expect(this.actionConfig.toFormData).to.be.a('function');\n }\n if (this.actionConfig.fromFormData) {\n expect(this.actionConfig.fromFormData).to.be.a('function');\n }\n\n if (this.actionConfig.validate) {\n expect(this.actionConfig.validate).to.be.a('function');\n }\n });\n }\n}\n"]}
@@ -0,0 +1,66 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { add_contact_groups } from '../../src/flow/actions/add_contact_groups';
3
+ import { ActionTest } from '../ActionHelper';
4
+ /**
5
+ * Test suite for the add_contact_groups action configuration.
6
+ */
7
+ describe('add_contact_groups action config', () => {
8
+ const helper = new ActionTest(add_contact_groups, 'add_contact_groups');
9
+ describe('basic properties', () => {
10
+ helper.testBasicProperties();
11
+ it('has correct name', () => {
12
+ expect(add_contact_groups.name).to.equal('Add to Group');
13
+ });
14
+ });
15
+ describe('action scenarios', () => {
16
+ helper.testAction({
17
+ uuid: 'test-action-1',
18
+ type: 'add_contact_groups',
19
+ groups: [{ uuid: 'group-1', name: 'VIP Customers' }]
20
+ }, 'single-group');
21
+ helper.testAction({
22
+ uuid: 'test-action-2',
23
+ type: 'add_contact_groups',
24
+ groups: [
25
+ { uuid: 'group-1', name: 'VIP Customers' },
26
+ { uuid: 'group-2', name: 'Newsletter Subscribers' },
27
+ { uuid: 'group-3', name: 'Beta Testers' }
28
+ ]
29
+ }, 'multiple-groups');
30
+ helper.testAction({
31
+ uuid: 'test-action-3',
32
+ type: 'add_contact_groups',
33
+ groups: [
34
+ {
35
+ uuid: 'group-1',
36
+ name: 'Very Long Group Name That Might Wrap to Multiple Lines'
37
+ },
38
+ {
39
+ uuid: 'group-2',
40
+ name: 'Another Extremely Long Group Name for Testing Layout'
41
+ }
42
+ ]
43
+ }, 'long-group-names');
44
+ helper.testAction({
45
+ uuid: 'test-action-4',
46
+ type: 'add_contact_groups',
47
+ groups: [
48
+ { uuid: 'group-1', name: 'Sales Team' },
49
+ { uuid: 'group-2', name: 'Marketing Team' },
50
+ { uuid: 'group-3', name: 'Support Team' },
51
+ { uuid: 'group-4', name: 'Development Team' },
52
+ { uuid: 'group-5', name: 'QA Team' }
53
+ ]
54
+ }, 'many-groups');
55
+ helper.testAction({
56
+ uuid: 'test-action-5',
57
+ type: 'add_contact_groups',
58
+ groups: [
59
+ { uuid: 'group-1', name: 'Customers - Premium Plan (Monthly)' },
60
+ { uuid: 'group-2', name: 'Customers - Enterprise Plan (Annual)' },
61
+ { uuid: 'group-3', name: 'Trial Users - 30 Day Free Trial' }
62
+ ]
63
+ }, 'descriptive-group-names');
64
+ });
65
+ });
66
+ //# sourceMappingURL=add_contact_groups.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add_contact_groups.test.js","sourceRoot":"","sources":["../../../test/actions/add_contact_groups.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAE/E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C;;GAEG;AACH,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,CAAC;IAExE,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAE7B,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;YAC1B,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC3D,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,oBAAoB;YAC1B,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;SACvC,EACf,cAAc,CACf,CAAC;QAEF,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE;gBACN,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE;gBAC1C,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,wBAAwB,EAAE;gBACnD,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE;aAC1C;SACY,EACf,iBAAiB,CAClB,CAAC;QAEF,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE;gBACN;oBACE,IAAI,EAAE,SAAS;oBACf,IAAI,EAAE,wDAAwD;iBAC/D;gBACD;oBACE,IAAI,EAAE,SAAS;oBACf,IAAI,EAAE,sDAAsD;iBAC7D;aACF;SACY,EACf,kBAAkB,CACnB,CAAC;QAEF,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE;gBACN,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE;gBACvC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE;gBAC3C,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE;gBACzC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,EAAE;gBAC7C,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;aACrC;SACY,EACf,aAAa,CACd,CAAC;QAEF,MAAM,CAAC,UAAU,CACf;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE;gBACN,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,oCAAoC,EAAE;gBAC/D,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,sCAAsC,EAAE;gBACjE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,iCAAiC,EAAE;aAC7D;SACY,EACf,yBAAyB,CAC1B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { add_contact_groups } from '../../src/flow/actions/add_contact_groups';\nimport { AddToGroup } from '../../src/store/flow-definition';\nimport { ActionTest } from '../ActionHelper';\n\n/**\n * Test suite for the add_contact_groups action configuration.\n */\ndescribe('add_contact_groups action config', () => {\n const helper = new ActionTest(add_contact_groups, 'add_contact_groups');\n\n describe('basic properties', () => {\n helper.testBasicProperties();\n\n it('has correct name', () => {\n expect(add_contact_groups.name).to.equal('Add to Group');\n });\n });\n\n describe('action scenarios', () => {\n helper.testAction(\n {\n uuid: 'test-action-1',\n type: 'add_contact_groups',\n groups: [{ uuid: 'group-1', name: 'VIP Customers' }]\n } as AddToGroup,\n 'single-group'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-2',\n type: 'add_contact_groups',\n groups: [\n { uuid: 'group-1', name: 'VIP Customers' },\n { uuid: 'group-2', name: 'Newsletter Subscribers' },\n { uuid: 'group-3', name: 'Beta Testers' }\n ]\n } as AddToGroup,\n 'multiple-groups'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-3',\n type: 'add_contact_groups',\n groups: [\n {\n uuid: 'group-1',\n name: 'Very Long Group Name That Might Wrap to Multiple Lines'\n },\n {\n uuid: 'group-2',\n name: 'Another Extremely Long Group Name for Testing Layout'\n }\n ]\n } as AddToGroup,\n 'long-group-names'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-4',\n type: 'add_contact_groups',\n groups: [\n { uuid: 'group-1', name: 'Sales Team' },\n { uuid: 'group-2', name: 'Marketing Team' },\n { uuid: 'group-3', name: 'Support Team' },\n { uuid: 'group-4', name: 'Development Team' },\n { uuid: 'group-5', name: 'QA Team' }\n ]\n } as AddToGroup,\n 'many-groups'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-5',\n type: 'add_contact_groups',\n groups: [\n { uuid: 'group-1', name: 'Customers - Premium Plan (Monthly)' },\n { uuid: 'group-2', name: 'Customers - Enterprise Plan (Annual)' },\n { uuid: 'group-3', name: 'Trial Users - 30 Day Free Trial' }\n ]\n } as AddToGroup,\n 'descriptive-group-names'\n );\n });\n});\n"]}
@@ -0,0 +1,226 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { remove_contact_groups } from '../../src/flow/actions/remove_contact_groups';
3
+ import { ActionTest } from '../ActionHelper';
4
+ /**
5
+ * Test suite for the remove_contact_groups action configuration.
6
+ */
7
+ describe('remove_contact_groups action config', () => {
8
+ const helper = new ActionTest(remove_contact_groups, 'remove_contact_groups');
9
+ describe('basic properties', () => {
10
+ helper.testBasicProperties();
11
+ it('has correct name', () => {
12
+ expect(remove_contact_groups.name).to.equal('Remove from Group');
13
+ });
14
+ });
15
+ describe('action scenarios', () => {
16
+ helper.testAction({
17
+ uuid: 'test-action-1',
18
+ type: 'remove_contact_groups',
19
+ groups: [
20
+ {
21
+ uuid: 'group-1',
22
+ name: 'VIP Customers',
23
+ status: 'ready',
24
+ system: false,
25
+ query: '',
26
+ count: 150
27
+ }
28
+ ],
29
+ all_groups: false
30
+ }, 'single-group');
31
+ helper.testAction({
32
+ uuid: 'test-action-2',
33
+ type: 'remove_contact_groups',
34
+ groups: [
35
+ {
36
+ uuid: 'group-1',
37
+ name: 'Newsletter Subscribers',
38
+ status: 'ready',
39
+ system: false,
40
+ query: '',
41
+ count: 1250
42
+ },
43
+ {
44
+ uuid: 'group-2',
45
+ name: 'Beta Testers',
46
+ status: 'ready',
47
+ system: false,
48
+ query: '',
49
+ count: 45
50
+ },
51
+ {
52
+ uuid: 'group-3',
53
+ name: 'Inactive Users',
54
+ status: 'ready',
55
+ system: false,
56
+ query: '',
57
+ count: 300
58
+ }
59
+ ],
60
+ all_groups: false
61
+ }, 'multiple-groups');
62
+ helper.testAction({
63
+ uuid: 'test-action-3',
64
+ type: 'remove_contact_groups',
65
+ groups: [],
66
+ all_groups: true
67
+ }, 'remove-from-all-groups');
68
+ helper.testAction({
69
+ uuid: 'test-action-4',
70
+ type: 'remove_contact_groups',
71
+ groups: [
72
+ {
73
+ uuid: 'group-1',
74
+ name: 'Customers - Premium Subscription (Annual Billing)',
75
+ status: 'ready',
76
+ system: false,
77
+ query: '',
78
+ count: 89
79
+ },
80
+ {
81
+ uuid: 'group-2',
82
+ name: 'Users - Trial Period Expired (30+ Days)',
83
+ status: 'ready',
84
+ system: false,
85
+ query: '',
86
+ count: 234
87
+ }
88
+ ],
89
+ all_groups: false
90
+ }, 'long-descriptive-group-names');
91
+ helper.testAction({
92
+ uuid: 'test-action-5',
93
+ type: 'remove_contact_groups',
94
+ groups: [
95
+ {
96
+ uuid: 'group-1',
97
+ name: 'Sales Team',
98
+ status: 'ready',
99
+ system: false,
100
+ query: '',
101
+ count: 12
102
+ },
103
+ {
104
+ uuid: 'group-2',
105
+ name: 'Marketing Team',
106
+ status: 'ready',
107
+ system: false,
108
+ query: '',
109
+ count: 8
110
+ },
111
+ {
112
+ uuid: 'group-3',
113
+ name: 'Support Team',
114
+ status: 'ready',
115
+ system: false,
116
+ query: '',
117
+ count: 15
118
+ },
119
+ {
120
+ uuid: 'group-4',
121
+ name: 'Development Team',
122
+ status: 'ready',
123
+ system: false,
124
+ query: '',
125
+ count: 25
126
+ },
127
+ {
128
+ uuid: 'group-5',
129
+ name: 'QA Team',
130
+ status: 'ready',
131
+ system: false,
132
+ query: '',
133
+ count: 6
134
+ },
135
+ {
136
+ uuid: 'group-6',
137
+ name: 'Management Team',
138
+ status: 'ready',
139
+ system: false,
140
+ query: '',
141
+ count: 4
142
+ }
143
+ ],
144
+ all_groups: false
145
+ }, 'many-groups');
146
+ helper.testAction({
147
+ uuid: 'test-action-6',
148
+ type: 'remove_contact_groups',
149
+ groups: [
150
+ {
151
+ uuid: 'group-1',
152
+ name: 'Unsubscribed Users',
153
+ status: 'ready',
154
+ system: false,
155
+ query: '',
156
+ count: 567
157
+ },
158
+ {
159
+ uuid: 'group-2',
160
+ name: 'Bounced Email Addresses',
161
+ status: 'ready',
162
+ system: false,
163
+ query: '',
164
+ count: 89
165
+ }
166
+ ],
167
+ all_groups: false
168
+ }, 'cleanup-groups');
169
+ });
170
+ describe('validation edge cases', () => {
171
+ it('fails validation when no groups selected and all_groups is false', () => {
172
+ const action = {
173
+ uuid: 'test-action',
174
+ type: 'remove_contact_groups',
175
+ groups: [],
176
+ all_groups: false
177
+ };
178
+ const result = remove_contact_groups.validate(action);
179
+ expect(result.valid).to.be.false;
180
+ expect(result.errors.groups).to.equal('At least one group must be selected or check "Remove from All Groups"');
181
+ });
182
+ it('fails validation when groups is undefined and all_groups is false', () => {
183
+ const action = {
184
+ uuid: 'test-action',
185
+ type: 'remove_contact_groups',
186
+ groups: undefined,
187
+ all_groups: false
188
+ };
189
+ const result = remove_contact_groups.validate(action);
190
+ expect(result.valid).to.be.false;
191
+ expect(result.errors.groups).to.equal('At least one group must be selected or check "Remove from All Groups"');
192
+ });
193
+ it('passes validation when all_groups is true even with empty groups', () => {
194
+ const action = {
195
+ uuid: 'test-action',
196
+ type: 'remove_contact_groups',
197
+ groups: [],
198
+ all_groups: true
199
+ };
200
+ const result = remove_contact_groups.validate(action);
201
+ expect(result.valid).to.be.true;
202
+ expect(Object.keys(result.errors)).to.have.length(0);
203
+ });
204
+ it('passes validation when groups are provided and all_groups is false', () => {
205
+ const action = {
206
+ uuid: 'test-action',
207
+ type: 'remove_contact_groups',
208
+ groups: [
209
+ {
210
+ uuid: 'group-1',
211
+ name: 'Test Group',
212
+ status: 'ready',
213
+ system: false,
214
+ query: '',
215
+ count: 10
216
+ }
217
+ ],
218
+ all_groups: false
219
+ };
220
+ const result = remove_contact_groups.validate(action);
221
+ expect(result.valid).to.be.true;
222
+ expect(Object.keys(result.errors)).to.have.length(0);
223
+ });
224
+ });
225
+ });
226
+ //# sourceMappingURL=remove_contact_groups.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove_contact_groups.test.js","sourceRoot":"","sources":["../../../test/actions/remove_contact_groups.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { remove_contact_groups } from '../../src/flow/actions/remove_contact_groups';\nimport { RemoveFromGroup } from '../../src/store/flow-definition';\nimport { ActionTest } from '../ActionHelper';\n\n/**\n * Test suite for the remove_contact_groups action configuration.\n */\ndescribe('remove_contact_groups action config', () => {\n const helper = new ActionTest(remove_contact_groups, 'remove_contact_groups');\n\n describe('basic properties', () => {\n helper.testBasicProperties();\n\n it('has correct name', () => {\n expect(remove_contact_groups.name).to.equal('Remove from Group');\n });\n });\n\n describe('action scenarios', () => {\n helper.testAction(\n {\n uuid: 'test-action-1',\n type: 'remove_contact_groups',\n groups: [\n {\n uuid: 'group-1',\n name: 'VIP Customers',\n status: 'ready',\n system: false,\n query: '',\n count: 150\n }\n ],\n all_groups: false\n } as RemoveFromGroup,\n 'single-group'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-2',\n type: 'remove_contact_groups',\n groups: [\n {\n uuid: 'group-1',\n name: 'Newsletter Subscribers',\n status: 'ready',\n system: false,\n query: '',\n count: 1250\n },\n {\n uuid: 'group-2',\n name: 'Beta Testers',\n status: 'ready',\n system: false,\n query: '',\n count: 45\n },\n {\n uuid: 'group-3',\n name: 'Inactive Users',\n status: 'ready',\n system: false,\n query: '',\n count: 300\n }\n ],\n all_groups: false\n } as RemoveFromGroup,\n 'multiple-groups'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-3',\n type: 'remove_contact_groups',\n groups: [],\n all_groups: true\n } as RemoveFromGroup,\n 'remove-from-all-groups'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-4',\n type: 'remove_contact_groups',\n groups: [\n {\n uuid: 'group-1',\n name: 'Customers - Premium Subscription (Annual Billing)',\n status: 'ready',\n system: false,\n query: '',\n count: 89\n },\n {\n uuid: 'group-2',\n name: 'Users - Trial Period Expired (30+ Days)',\n status: 'ready',\n system: false,\n query: '',\n count: 234\n }\n ],\n all_groups: false\n } as RemoveFromGroup,\n 'long-descriptive-group-names'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-5',\n type: 'remove_contact_groups',\n groups: [\n {\n uuid: 'group-1',\n name: 'Sales Team',\n status: 'ready',\n system: false,\n query: '',\n count: 12\n },\n {\n uuid: 'group-2',\n name: 'Marketing Team',\n status: 'ready',\n system: false,\n query: '',\n count: 8\n },\n {\n uuid: 'group-3',\n name: 'Support Team',\n status: 'ready',\n system: false,\n query: '',\n count: 15\n },\n {\n uuid: 'group-4',\n name: 'Development Team',\n status: 'ready',\n system: false,\n query: '',\n count: 25\n },\n {\n uuid: 'group-5',\n name: 'QA Team',\n status: 'ready',\n system: false,\n query: '',\n count: 6\n },\n {\n uuid: 'group-6',\n name: 'Management Team',\n status: 'ready',\n system: false,\n query: '',\n count: 4\n }\n ],\n all_groups: false\n } as RemoveFromGroup,\n 'many-groups'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-6',\n type: 'remove_contact_groups',\n groups: [\n {\n uuid: 'group-1',\n name: 'Unsubscribed Users',\n status: 'ready',\n system: false,\n query: '',\n count: 567\n },\n {\n uuid: 'group-2',\n name: 'Bounced Email Addresses',\n status: 'ready',\n system: false,\n query: '',\n count: 89\n }\n ],\n all_groups: false\n } as RemoveFromGroup,\n 'cleanup-groups'\n );\n });\n\n describe('validation edge cases', () => {\n it('fails validation when no groups selected and all_groups is false', () => {\n const action: RemoveFromGroup = {\n uuid: 'test-action',\n type: 'remove_contact_groups',\n groups: [],\n all_groups: false\n };\n\n const result = remove_contact_groups.validate(action);\n expect(result.valid).to.be.false;\n expect(result.errors.groups).to.equal(\n 'At least one group must be selected or check \"Remove from All Groups\"'\n );\n });\n\n it('fails validation when groups is undefined and all_groups is false', () => {\n const action: RemoveFromGroup = {\n uuid: 'test-action',\n type: 'remove_contact_groups',\n groups: undefined as any,\n all_groups: false\n };\n\n const result = remove_contact_groups.validate(action);\n expect(result.valid).to.be.false;\n expect(result.errors.groups).to.equal(\n 'At least one group must be selected or check \"Remove from All Groups\"'\n );\n });\n\n it('passes validation when all_groups is true even with empty groups', () => {\n const action: RemoveFromGroup = {\n uuid: 'test-action',\n type: 'remove_contact_groups',\n groups: [],\n all_groups: true\n };\n\n const result = remove_contact_groups.validate(action);\n expect(result.valid).to.be.true;\n expect(Object.keys(result.errors)).to.have.length(0);\n });\n\n it('passes validation when groups are provided and all_groups is false', () => {\n const action: RemoveFromGroup = {\n uuid: 'test-action',\n type: 'remove_contact_groups',\n groups: [\n {\n uuid: 'group-1',\n name: 'Test Group',\n status: 'ready',\n system: false,\n query: '',\n count: 10\n }\n ],\n all_groups: false\n };\n\n const result = remove_contact_groups.validate(action);\n expect(result.valid).to.be.true;\n expect(Object.keys(result.errors)).to.have.length(0);\n });\n });\n});\n"]}
@@ -0,0 +1,160 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { send_email } from '../../src/flow/actions/send_email';
3
+ import { ActionTest } from '../ActionHelper';
4
+ /**
5
+ * Test suite for the send_email action configuration.
6
+ */
7
+ describe('send_email action config', () => {
8
+ const helper = new ActionTest(send_email, 'send_email');
9
+ describe('basic properties', () => {
10
+ helper.testBasicProperties();
11
+ it('has correct name', () => {
12
+ expect(send_email.name).to.equal('Send Email');
13
+ });
14
+ });
15
+ describe('action scenarios', () => {
16
+ helper.testAction({
17
+ uuid: 'test-action-1',
18
+ type: 'send_email',
19
+ addresses: ['user@example.com'],
20
+ subject: 'Welcome!',
21
+ body: 'Thank you for signing up.'
22
+ }, 'simple-email');
23
+ helper.testAction({
24
+ uuid: 'test-action-2',
25
+ type: 'send_email',
26
+ addresses: [
27
+ 'user1@example.com',
28
+ 'user2@example.com',
29
+ 'user3@example.com'
30
+ ],
31
+ subject: 'Multiple Recipients',
32
+ body: 'This email is sent to multiple recipients.'
33
+ }, 'multiple-recipients');
34
+ helper.testAction({
35
+ uuid: 'test-action-3',
36
+ type: 'send_email',
37
+ addresses: ['admin@company.com'],
38
+ subject: 'Multi-line Email Content',
39
+ body: 'Dear User,\n\nThis is a multi-line email body.\n\nIt contains multiple paragraphs and line breaks.\n\nBest regards,\nThe Team'
40
+ }, 'multiline-body');
41
+ helper.testAction({
42
+ uuid: 'test-action-4',
43
+ type: 'send_email',
44
+ addresses: ['support@company.com'],
45
+ subject: 'Email with expressions',
46
+ body: 'Hello @contact.name,\n\nYour order @run.results.order_id has been processed.\n\nThank you!'
47
+ }, 'with-expressions');
48
+ helper.testAction({
49
+ uuid: 'test-action-5',
50
+ type: 'send_email',
51
+ addresses: ['notification@company.com'],
52
+ subject: 'Very Long Subject Line That Could Potentially Exceed Normal Length Limits To Test Subject Field Handling',
53
+ body: 'Short body content.'
54
+ }, 'long-subject');
55
+ helper.testAction({
56
+ uuid: 'test-action-6',
57
+ type: 'send_email',
58
+ addresses: [
59
+ 'customer.service@department.company.com',
60
+ 'technical.support@department.company.com',
61
+ 'billing.inquiries@department.company.com'
62
+ ],
63
+ subject: 'Customer Support Escalation',
64
+ body: 'Customer Name: @contact.name\nTicket ID: @run.results.ticket_id\n\nIssue Description:\n@run.results.issue_description\n\nPriority: High\nCategory: @run.results.category\n\nPlease respond within 2 hours.'
65
+ }, 'complex-business-email');
66
+ helper.testAction({
67
+ uuid: 'test-action-7',
68
+ type: 'send_email',
69
+ addresses: ['alerts@company.com'],
70
+ subject: '',
71
+ body: 'Email with empty subject'
72
+ }, 'empty-subject');
73
+ helper.testAction({
74
+ uuid: 'test-action-8',
75
+ type: 'send_email',
76
+ addresses: ['test@company.com'],
77
+ subject: 'Empty body test',
78
+ body: ''
79
+ }, 'empty-body');
80
+ });
81
+ describe('validation edge cases', () => {
82
+ it('fails validation for empty addresses array', () => {
83
+ const action = {
84
+ uuid: 'test-action',
85
+ type: 'send_email',
86
+ addresses: [],
87
+ subject: 'Test Subject',
88
+ body: 'Test Body'
89
+ };
90
+ const result = send_email.validate(action);
91
+ expect(result.valid).to.be.false;
92
+ expect(result.errors.addresses).to.equal('At least one recipient email address is required');
93
+ });
94
+ it('fails validation for null addresses', () => {
95
+ const action = {
96
+ uuid: 'test-action',
97
+ type: 'send_email',
98
+ addresses: null,
99
+ subject: 'Test Subject',
100
+ body: 'Test Body'
101
+ };
102
+ const result = send_email.validate(action);
103
+ expect(result.valid).to.be.false;
104
+ expect(result.errors.addresses).to.equal('At least one recipient email address is required');
105
+ });
106
+ it('fails validation for undefined addresses', () => {
107
+ const action = {
108
+ uuid: 'test-action',
109
+ type: 'send_email',
110
+ addresses: undefined,
111
+ subject: 'Test Subject',
112
+ body: 'Test Body'
113
+ };
114
+ const result = send_email.validate(action);
115
+ expect(result.valid).to.be.false;
116
+ expect(result.errors.addresses).to.equal('At least one recipient email address is required');
117
+ });
118
+ it('passes validation with valid email addresses', () => {
119
+ const action = {
120
+ uuid: 'test-action',
121
+ type: 'send_email',
122
+ addresses: ['valid@example.com'],
123
+ subject: 'Test Subject',
124
+ body: 'Test Body'
125
+ };
126
+ const result = send_email.validate(action);
127
+ expect(result.valid).to.be.true;
128
+ expect(Object.keys(result.errors)).to.have.length(0);
129
+ });
130
+ it('passes validation with multiple email addresses', () => {
131
+ const action = {
132
+ uuid: 'test-action',
133
+ type: 'send_email',
134
+ addresses: [
135
+ 'user1@example.com',
136
+ 'user2@example.com',
137
+ 'user3@example.com'
138
+ ],
139
+ subject: 'Test Subject',
140
+ body: 'Test Body'
141
+ };
142
+ const result = send_email.validate(action);
143
+ expect(result.valid).to.be.true;
144
+ expect(Object.keys(result.errors)).to.have.length(0);
145
+ });
146
+ it('passes validation with empty subject and body', () => {
147
+ const action = {
148
+ uuid: 'test-action',
149
+ type: 'send_email',
150
+ addresses: ['test@example.com'],
151
+ subject: '',
152
+ body: ''
153
+ };
154
+ const result = send_email.validate(action);
155
+ expect(result.valid).to.be.true;
156
+ expect(Object.keys(result.errors)).to.have.length(0);
157
+ });
158
+ });
159
+ });
160
+ //# sourceMappingURL=send_email.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send_email.test.js","sourceRoot":"","sources":["../../../test/actions/send_email.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { send_email } from '../../src/flow/actions/send_email';\nimport { SendEmail } from '../../src/store/flow-definition';\nimport { ActionTest } from '../ActionHelper';\n\n/**\n * Test suite for the send_email action configuration.\n */\ndescribe('send_email action config', () => {\n const helper = new ActionTest(send_email, 'send_email');\n\n describe('basic properties', () => {\n helper.testBasicProperties();\n\n it('has correct name', () => {\n expect(send_email.name).to.equal('Send Email');\n });\n });\n\n describe('action scenarios', () => {\n helper.testAction(\n {\n uuid: 'test-action-1',\n type: 'send_email',\n addresses: ['user@example.com'],\n subject: 'Welcome!',\n body: 'Thank you for signing up.'\n } as SendEmail,\n 'simple-email'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-2',\n type: 'send_email',\n addresses: [\n 'user1@example.com',\n 'user2@example.com',\n 'user3@example.com'\n ],\n subject: 'Multiple Recipients',\n body: 'This email is sent to multiple recipients.'\n } as SendEmail,\n 'multiple-recipients'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-3',\n type: 'send_email',\n addresses: ['admin@company.com'],\n subject: 'Multi-line Email Content',\n body: 'Dear User,\\n\\nThis is a multi-line email body.\\n\\nIt contains multiple paragraphs and line breaks.\\n\\nBest regards,\\nThe Team'\n } as SendEmail,\n 'multiline-body'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-4',\n type: 'send_email',\n addresses: ['support@company.com'],\n subject: 'Email with expressions',\n body: 'Hello @contact.name,\\n\\nYour order @run.results.order_id has been processed.\\n\\nThank you!'\n } as SendEmail,\n 'with-expressions'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-5',\n type: 'send_email',\n addresses: ['notification@company.com'],\n subject:\n 'Very Long Subject Line That Could Potentially Exceed Normal Length Limits To Test Subject Field Handling',\n body: 'Short body content.'\n } as SendEmail,\n 'long-subject'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-6',\n type: 'send_email',\n addresses: [\n 'customer.service@department.company.com',\n 'technical.support@department.company.com',\n 'billing.inquiries@department.company.com'\n ],\n subject: 'Customer Support Escalation',\n body: 'Customer Name: @contact.name\\nTicket ID: @run.results.ticket_id\\n\\nIssue Description:\\n@run.results.issue_description\\n\\nPriority: High\\nCategory: @run.results.category\\n\\nPlease respond within 2 hours.'\n } as SendEmail,\n 'complex-business-email'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-7',\n type: 'send_email',\n addresses: ['alerts@company.com'],\n subject: '',\n body: 'Email with empty subject'\n } as SendEmail,\n 'empty-subject'\n );\n\n helper.testAction(\n {\n uuid: 'test-action-8',\n type: 'send_email',\n addresses: ['test@company.com'],\n subject: 'Empty body test',\n body: ''\n } as SendEmail,\n 'empty-body'\n );\n });\n\n describe('validation edge cases', () => {\n it('fails validation for empty addresses array', () => {\n const action: SendEmail = {\n uuid: 'test-action',\n type: 'send_email',\n addresses: [],\n subject: 'Test Subject',\n body: 'Test Body'\n };\n\n const result = send_email.validate(action);\n expect(result.valid).to.be.false;\n expect(result.errors.addresses).to.equal(\n 'At least one recipient email address is required'\n );\n });\n\n it('fails validation for null addresses', () => {\n const action: SendEmail = {\n uuid: 'test-action',\n type: 'send_email',\n addresses: null as any,\n subject: 'Test Subject',\n body: 'Test Body'\n };\n\n const result = send_email.validate(action);\n expect(result.valid).to.be.false;\n expect(result.errors.addresses).to.equal(\n 'At least one recipient email address is required'\n );\n });\n\n it('fails validation for undefined addresses', () => {\n const action: SendEmail = {\n uuid: 'test-action',\n type: 'send_email',\n addresses: undefined as any,\n subject: 'Test Subject',\n body: 'Test Body'\n };\n\n const result = send_email.validate(action);\n expect(result.valid).to.be.false;\n expect(result.errors.addresses).to.equal(\n 'At least one recipient email address is required'\n );\n });\n\n it('passes validation with valid email addresses', () => {\n const action: SendEmail = {\n uuid: 'test-action',\n type: 'send_email',\n addresses: ['valid@example.com'],\n subject: 'Test Subject',\n body: 'Test Body'\n };\n\n const result = send_email.validate(action);\n expect(result.valid).to.be.true;\n expect(Object.keys(result.errors)).to.have.length(0);\n });\n\n it('passes validation with multiple email addresses', () => {\n const action: SendEmail = {\n uuid: 'test-action',\n type: 'send_email',\n addresses: [\n 'user1@example.com',\n 'user2@example.com',\n 'user3@example.com'\n ],\n subject: 'Test Subject',\n body: 'Test Body'\n };\n\n const result = send_email.validate(action);\n expect(result.valid).to.be.true;\n expect(Object.keys(result.errors)).to.have.length(0);\n });\n\n it('passes validation with empty subject and body', () => {\n const action: SendEmail = {\n uuid: 'test-action',\n type: 'send_email',\n addresses: ['test@example.com'],\n subject: '',\n body: ''\n };\n\n const result = send_email.validate(action);\n expect(result.valid).to.be.true;\n expect(Object.keys(result.errors)).to.have.length(0);\n });\n });\n});\n"]}