@nyaruka/temba-components 0.136.1 → 0.138.0

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 (73) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/demo/components/webchat/example.html +2 -2
  3. package/dist/temba-components.js +692 -622
  4. package/dist/temba-components.js.map +1 -1
  5. package/out-tsc/src/display/Chat.js +123 -44
  6. package/out-tsc/src/display/Chat.js.map +1 -1
  7. package/out-tsc/src/display/FloatingTab.js +2 -2
  8. package/out-tsc/src/display/FloatingTab.js.map +1 -1
  9. package/out-tsc/src/events/eventRenderers.js +442 -0
  10. package/out-tsc/src/events/eventRenderers.js.map +1 -0
  11. package/out-tsc/src/flow/CanvasNode.js +45 -24
  12. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  13. package/out-tsc/src/flow/Editor.js +308 -18
  14. package/out-tsc/src/flow/Editor.js.map +1 -1
  15. package/out-tsc/src/flow/NodeEditor.js +0 -1
  16. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  17. package/out-tsc/src/flow/Plumber.js +110 -64
  18. package/out-tsc/src/flow/Plumber.js.map +1 -1
  19. package/out-tsc/src/list/ShortcutList.js +1 -1
  20. package/out-tsc/src/list/ShortcutList.js.map +1 -1
  21. package/out-tsc/src/live/ContactChat.js +12 -321
  22. package/out-tsc/src/live/ContactChat.js.map +1 -1
  23. package/out-tsc/src/simulator/Simulator.js +439 -575
  24. package/out-tsc/src/simulator/Simulator.js.map +1 -1
  25. package/out-tsc/src/store/AppState.js +12 -2
  26. package/out-tsc/src/store/AppState.js.map +1 -1
  27. package/out-tsc/test/temba-flow-editor-node.test.js +2 -1
  28. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  29. package/out-tsc/test/temba-flow-editor-revisions.test.js +106 -0
  30. package/out-tsc/test/temba-flow-editor-revisions.test.js.map +1 -0
  31. package/out-tsc/test/temba-flow-editor.test.js +14 -10
  32. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  33. package/out-tsc/test/temba-flow-plumber-connections.test.js +7 -1
  34. package/out-tsc/test/temba-flow-plumber-connections.test.js.map +1 -1
  35. package/out-tsc/test/temba-flow-plumber.test.js +6 -0
  36. package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
  37. package/out-tsc/test/temba-simulator.test.js +51 -32
  38. package/out-tsc/test/temba-simulator.test.js.map +1 -1
  39. package/package.json +1 -1
  40. package/screenshots/truth/contacts/chat-failure.png +0 -0
  41. package/screenshots/truth/contacts/chat-for-archived-contact.png +0 -0
  42. package/screenshots/truth/contacts/chat-for-blocked-contact.png +0 -0
  43. package/screenshots/truth/contacts/chat-for-stopped-contact.png +0 -0
  44. package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
  45. package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
  46. package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
  47. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  48. package/screenshots/truth/simulator/after-message-sent.png +0 -0
  49. package/screenshots/truth/simulator/after-reset.png +0 -0
  50. package/screenshots/truth/simulator/attachment-menu.png +0 -0
  51. package/screenshots/truth/simulator/context-expanded.png +0 -0
  52. package/screenshots/truth/simulator/context-explorer-open.png +0 -0
  53. package/screenshots/truth/simulator/event-info.png +0 -0
  54. package/screenshots/truth/simulator/image-attachment.png +0 -0
  55. package/screenshots/truth/simulator/open-initial.png +0 -0
  56. package/screenshots/truth/simulator/quick-replies.png +0 -0
  57. package/src/display/Chat.ts +123 -44
  58. package/src/display/FloatingTab.ts +2 -2
  59. package/src/events/eventRenderers.ts +527 -0
  60. package/src/flow/CanvasNode.ts +54 -29
  61. package/src/flow/Editor.ts +360 -19
  62. package/src/flow/NodeEditor.ts +0 -1
  63. package/src/flow/Plumber.ts +123 -69
  64. package/src/list/ShortcutList.ts +1 -1
  65. package/src/live/ContactChat.ts +17 -376
  66. package/src/simulator/Simulator.ts +498 -617
  67. package/src/store/AppState.ts +13 -2
  68. package/test/temba-flow-editor-node.test.ts +2 -1
  69. package/test/temba-flow-editor-revisions.test.ts +134 -0
  70. package/test/temba-flow-editor.test.ts +16 -10
  71. package/test/temba-flow-plumber-connections.test.ts +7 -1
  72. package/test/temba-flow-plumber.test.ts +6 -0
  73. package/test/temba-simulator.test.ts +64 -34
@@ -28,7 +28,7 @@ const openSimulator = async (simulator) => {
28
28
  // trigger the button clicked event on the tab
29
29
  tab.dispatchEvent(new CustomEvent('temba-button-clicked', { bubbles: true }));
30
30
  await simulator.updateComplete;
31
- // brief delay for async API mock processing
31
+ // brief delay for async API response processing
32
32
  await delay(50);
33
33
  };
34
34
  // helper to get clip for the simulator window (fixed positioning)
@@ -95,15 +95,30 @@ const getSimulatorClip = (simulator, includeContext = false) => {
95
95
  };
96
96
  }
97
97
  const frameBounds = phoneFrame.getBoundingClientRect();
98
- // add padding around the phone frame
98
+ // clip to just the phone frame area
99
99
  const padding = 10;
100
100
  return {
101
101
  x: frameBounds.x - padding,
102
102
  y: frameBounds.y - padding,
103
- width: frameBounds.width + padding * 2,
103
+ // only add padding to the left to avoid capturing option pane on right
104
+ width: frameBounds.width + padding,
104
105
  height: frameBounds.height + padding * 2
105
106
  };
106
107
  };
108
+ // helper to get message count from chat component
109
+ const getMessageCount = (simulator) => {
110
+ var _a;
111
+ const chat = simulator.shadowRoot.querySelector('temba-chat');
112
+ if (!chat) {
113
+ return 0;
114
+ }
115
+ // check how many messages are in the chat component
116
+ // the chat component stores messages in its internal state
117
+ return (((_a = chat.messageGroups) === null || _a === void 0 ? void 0 : _a.reduce((total, group) => {
118
+ var _a;
119
+ return total + (((_a = group.messages) === null || _a === void 0 ? void 0 : _a.length) || 0);
120
+ }, 0)) || 0);
121
+ };
107
122
  // mock responses for simulation endpoints
108
123
  const mockSimulatorStart = () => {
109
124
  const response = {
@@ -277,8 +292,6 @@ describe('temba-simulator', () => {
277
292
  it('opens simulator window and starts flow', async () => {
278
293
  mockSimulatorStart();
279
294
  const simulator = await createSimulator();
280
- // ensure consistent size for screenshot
281
- simulator.size = 'medium';
282
295
  await simulator.updateComplete;
283
296
  await openSimulator(simulator);
284
297
  const phoneWindow = simulator.shadowRoot.querySelector('temba-floating-window');
@@ -286,20 +299,18 @@ describe('temba-simulator', () => {
286
299
  // verify phone screen is visible
287
300
  const phoneScreen = simulator.shadowRoot.querySelector('.phone-screen');
288
301
  expect(phoneScreen).to.exist;
289
- // verify initial message is displayed
290
- const messages = simulator.shadowRoot.querySelectorAll('.message');
291
- expect(messages.length).to.be.greaterThan(0);
302
+ // verify initial message is displayed via chat component
303
+ const messageCount = getMessageCount(simulator);
304
+ expect(messageCount).to.be.greaterThan(0);
292
305
  await assertScreenshot('simulator/open-initial', getSimulatorClip(simulator));
293
306
  });
294
307
  it('sends a text message', async () => {
295
308
  mockSimulatorStart();
296
309
  const simulator = await createSimulator();
297
- simulator.size = 'medium';
298
310
  await simulator.updateComplete;
299
311
  await openSimulator(simulator);
300
312
  // count initial messages
301
- let messages = simulator.shadowRoot.querySelectorAll('.message');
302
- const initialCount = messages.length;
313
+ const initialCount = getMessageCount(simulator);
303
314
  // mock the resume response
304
315
  mockSimulatorResume('Thanks for your message!');
305
316
  // type a message
@@ -314,12 +325,17 @@ describe('temba-simulator', () => {
314
325
  bubbles: true
315
326
  });
316
327
  input.dispatchEvent(enterEvent);
328
+ // wait for the message to be sent and response to come back
329
+ await waitForCondition(() => getMessageCount(simulator) > initialCount, 40, 50);
317
330
  await simulator.updateComplete;
318
- // brief delay for async API mock processing
319
- await delay(100);
331
+ // wait for chat component to update
332
+ const chat = simulator.shadowRoot.querySelector('temba-chat');
333
+ if (chat) {
334
+ await chat.updateComplete;
335
+ }
320
336
  // verify we have more messages than before
321
- messages = simulator.shadowRoot.querySelectorAll('.message');
322
- expect(messages.length).to.be.greaterThan(initialCount);
337
+ const newCount = getMessageCount(simulator);
338
+ expect(newCount).to.be.greaterThan(initialCount);
323
339
  // ensure DOM is settled
324
340
  await simulator.updateComplete;
325
341
  await assertScreenshot('simulator/after-message-sent', getSimulatorClip(simulator));
@@ -327,7 +343,6 @@ describe('temba-simulator', () => {
327
343
  it('tests message flow and takes screenshot', async () => {
328
344
  mockSimulatorStart();
329
345
  const simulator = await createSimulator();
330
- simulator.size = 'medium';
331
346
  await simulator.updateComplete;
332
347
  await openSimulator(simulator);
333
348
  // clear previous mocks and set up new mock for a response
@@ -343,15 +358,15 @@ describe('temba-simulator', () => {
343
358
  });
344
359
  input.dispatchEvent(enterEvent);
345
360
  // wait for quick replies to appear
346
- await waitForCondition(() => simulator.shadowRoot.querySelectorAll('.quick-reply-btn').length > 0, 2000);
361
+ await waitForCondition(() => simulator.shadowRoot.querySelectorAll('.quick-reply-btn').length > 0, 5000);
347
362
  await simulator.updateComplete;
363
+ await delay(100); // extra delay for rendering
348
364
  // take screenshot with quick replies
349
365
  await assertScreenshot('simulator/quick-replies', getSimulatorClip(simulator));
350
366
  });
351
367
  it('opens attachment menu', async () => {
352
368
  mockSimulatorStart();
353
369
  const simulator = await createSimulator();
354
- simulator.size = 'medium';
355
370
  await simulator.updateComplete;
356
371
  await openSimulator(simulator);
357
372
  // click the attachment button
@@ -368,11 +383,12 @@ describe('temba-simulator', () => {
368
383
  it('sends an image attachment', async () => {
369
384
  mockSimulatorStart();
370
385
  const simulator = await createSimulator();
371
- simulator.size = 'medium';
372
386
  await simulator.updateComplete;
373
387
  // reset attachment indices for deterministic testing
374
388
  simulator.resetAttachmentIndices();
375
389
  await openSimulator(simulator);
390
+ // count initial messages
391
+ const initialCount = getMessageCount(simulator);
376
392
  // mock the response for image attachment
377
393
  mockSimulatorResume('Nice picture!');
378
394
  // open attachment menu and click image option
@@ -383,11 +399,17 @@ describe('temba-simulator', () => {
383
399
  const imageMenuItem = Array.from(simulator.shadowRoot.querySelectorAll('.attachment-menu-item')).find((el) => { var _a; return (_a = el.textContent) === null || _a === void 0 ? void 0 : _a.includes('Image'); });
384
400
  expect(imageMenuItem).to.exist;
385
401
  imageMenuItem.click();
386
- await delay(100);
402
+ // wait for the attachment to be sent and response to come back
403
+ await waitForCondition(() => getMessageCount(simulator) > initialCount, 40, 50);
387
404
  await simulator.updateComplete;
388
- // verify attachment wrapper is displayed (image attachments show in attachments not messages)
389
- const attachmentWrappers = simulator.shadowRoot.querySelectorAll('.attachment-wrapper');
390
- expect(attachmentWrappers.length).to.be.greaterThan(0);
405
+ // wait for chat component to update
406
+ const chat = simulator.shadowRoot.querySelector('temba-chat');
407
+ if (chat) {
408
+ await chat.updateComplete;
409
+ }
410
+ // verify message with attachment was added via chat component
411
+ const newCount = getMessageCount(simulator);
412
+ expect(newCount).to.be.greaterThan(initialCount);
391
413
  await assertScreenshot('simulator/image-attachment', getSimulatorClip(simulator));
392
414
  });
393
415
  it('opens context explorer', async () => {
@@ -486,7 +508,6 @@ describe('temba-simulator', () => {
486
508
  it('resets simulation', async () => {
487
509
  mockSimulatorStart();
488
510
  const simulator = await createSimulator();
489
- simulator.size = 'medium';
490
511
  await simulator.updateComplete;
491
512
  await openSimulator(simulator);
492
513
  // send a message first
@@ -502,8 +523,7 @@ describe('temba-simulator', () => {
502
523
  await delay(1000);
503
524
  await simulator.updateComplete;
504
525
  // verify we have multiple messages
505
- let messages = simulator.shadowRoot.querySelectorAll('.message');
506
- const messageCountBefore = messages.length;
526
+ const messageCountBefore = getMessageCount(simulator);
507
527
  expect(messageCountBefore).to.be.greaterThan(1);
508
528
  // mock the start response for reset
509
529
  mockSimulatorStart();
@@ -515,8 +535,8 @@ describe('temba-simulator', () => {
515
535
  await delay(100);
516
536
  await simulator.updateComplete;
517
537
  // verify messages are reset - should go back to just initial message
518
- messages = simulator.shadowRoot.querySelectorAll('.message');
519
- expect(messages.length).to.be.lessThan(messageCountBefore);
538
+ const messageCountAfter = getMessageCount(simulator);
539
+ expect(messageCountAfter).to.be.lessThan(messageCountBefore);
520
540
  await assertScreenshot('simulator/after-reset', getSimulatorClip(simulator));
521
541
  });
522
542
  it('displays event info messages', async () => {
@@ -590,12 +610,11 @@ describe('temba-simulator', () => {
590
610
  };
591
611
  mockPOST(/\/flow\/simulate\/.*\//, responseWithEvents);
592
612
  const simulator = await createSimulator();
593
- simulator.size = 'medium';
594
613
  await simulator.updateComplete;
595
614
  await openSimulator(simulator);
596
- // verify event info is displayed
597
- const eventInfo = simulator.shadowRoot.querySelectorAll('.event-info');
598
- expect(eventInfo.length).to.be.greaterThan(0);
615
+ // verify events are displayed via chat component (field change + message = 2 events)
616
+ const messageCount = getMessageCount(simulator);
617
+ expect(messageCount).to.be.greaterThan(0);
599
618
  await assertScreenshot('simulator/event-info', getSimulatorClip(simulator));
600
619
  });
601
620
  it('displays different simulator sizes', async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"temba-simulator.test.js","sourceRoot":"","sources":["../../test/temba-simulator.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { fixture, expect, assert } from '@open-wc/testing';\nimport { Simulator } from '../src/simulator/Simulator';\nimport {\n assertScreenshot,\n getClip,\n mockPOST,\n clearMockPosts,\n delay,\n waitForCondition,\n loadStore\n} from './utils.test';\n\nconst FLOW_UUID = 'test-flow-123';\n\nconst createSimulator = async (attrs: any = {}) => {\n // load store first since simulator depends on it\n await loadStore();\n\n const defaults = {\n flow: FLOW_UUID,\n animationTime: '0' // disable animations for deterministic tests\n };\n const mergedAttrs = { ...defaults, ...attrs };\n\n const attrString = Object.entries(mergedAttrs)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(' ');\n\n const simulator: Simulator = await fixture(\n `<temba-simulator ${attrString}></temba-simulator>`\n );\n\n // reset cookie-based properties for deterministic tests\n simulator.size = 'medium';\n (simulator as any).following = true;\n (simulator as any).contextExplorerOpen = false;\n await simulator.updateComplete;\n\n return simulator;\n};\n\n// helper to open the simulator\nconst openSimulator = async (simulator: Simulator) => {\n const tab = simulator.shadowRoot.querySelector('temba-floating-tab') as any;\n expect(tab).to.exist;\n\n // trigger the button clicked event on the tab\n tab.dispatchEvent(new CustomEvent('temba-button-clicked', { bubbles: true }));\n\n await simulator.updateComplete;\n // brief delay for async API mock processing\n await delay(50);\n};\n\n// helper to get clip for the simulator window (fixed positioning)\nconst getSimulatorClip = (\n simulator: Simulator,\n includeContext: boolean = false\n) => {\n const phoneWindow = simulator.shadowRoot.querySelector(\n 'temba-floating-window'\n ) as any;\n\n if (!phoneWindow) {\n // if window not open, use default clip\n return getClip(simulator);\n }\n\n const windowElement = phoneWindow.shadowRoot?.querySelector(\n '.window'\n ) as HTMLElement;\n if (!windowElement) {\n return getClip(simulator);\n }\n\n const windowBounds = windowElement.getBoundingClientRect();\n\n if (includeContext) {\n // get the context explorer and phone to clip just those areas\n const phoneSimulator = phoneWindow.querySelector(\n '.phone-simulator'\n ) as HTMLElement;\n if (!phoneSimulator) {\n return getClip(simulator);\n }\n\n const contextExplorer = phoneSimulator.querySelector(\n '.context-explorer'\n ) as HTMLElement;\n const phoneFrame = phoneSimulator.querySelector(\n '.phone-frame'\n ) as HTMLElement;\n\n if (!contextExplorer || !phoneFrame) {\n return {\n x: windowBounds.x,\n y: windowBounds.y,\n width: windowBounds.width,\n height: windowBounds.height\n };\n }\n\n const contextBounds = contextExplorer.getBoundingClientRect();\n const phoneBounds = phoneFrame.getBoundingClientRect();\n\n // clip from the left edge of context explorer to the right edge of phone frame only\n // do not include the option-pane which is to the right of the phone\n // keep padding within the phone bounds to avoid capturing the gap to the option pane\n const padding = 10;\n const leftX = contextBounds.x - padding;\n\n // don't extend past the phone frame right edge - the option pane is close by\n const rightX = phoneBounds.right;\n\n const topY = Math.min(contextBounds.y, phoneBounds.y) - padding;\n const bottomY =\n Math.max(contextBounds.bottom, phoneBounds.bottom) + padding;\n\n return {\n x: leftX,\n y: topY,\n width: rightX - leftX,\n height: bottomY - topY\n };\n }\n\n // the phone-simulator is in the light DOM of the phoneWindow (slotted content)\n const phoneSimulator = phoneWindow.querySelector(\n '.phone-simulator'\n ) as HTMLElement;\n if (!phoneSimulator) {\n return getClip(simulator);\n }\n\n // get the phone-frame from within the phone-simulator\n const phoneFrame = phoneSimulator.querySelector(\n '.phone-frame'\n ) as HTMLElement;\n if (!phoneFrame) {\n // fallback to window bounds if phone-frame not found\n return {\n x: windowBounds.x,\n y: windowBounds.y,\n width: windowBounds.width,\n height: windowBounds.height\n };\n }\n\n const frameBounds = phoneFrame.getBoundingClientRect();\n\n // add padding around the phone frame\n const padding = 10;\n return {\n x: frameBounds.x - padding,\n y: frameBounds.y - padding,\n width: frameBounds.width + padding * 2,\n height: frameBounds.height + padding * 2\n };\n};\n\n// mock responses for simulation endpoints\nconst mockSimulatorStart = () => {\n const response = {\n session: {\n status: 'waiting',\n trigger: {\n type: 'manual',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' }\n },\n runs: [\n {\n uuid: 'run-1',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' },\n status: 'waiting',\n path: [\n {\n uuid: 'step-1',\n node_uuid: 'node-1',\n arrived_on: new Date().toISOString(),\n exit_uuid: null\n }\n ]\n }\n ],\n environment: {\n date_format: 'YYYY-MM-DD',\n time_format: 'HH:mm',\n timezone: 'America/New_York',\n allowed_languages: ['eng'],\n default_country: 'US'\n }\n },\n events: [\n {\n type: 'msg_created',\n created_on: new Date().toISOString(),\n msg: {\n uuid: 'msg-1',\n text: 'Hello! How can I help you today?',\n urn: 'tel:+12065551212'\n }\n }\n ],\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n urns: ['tel:+12065551212'],\n fields: {},\n groups: [],\n language: 'eng',\n status: 'active',\n created_on: new Date().toISOString()\n },\n context: {\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n name: 'Test User',\n urns: {\n tel: ['+12065551212'],\n __default__: '+12065551212'\n },\n fields: {\n age: '25',\n city: 'Seattle'\n }\n },\n trigger: {\n type: 'manual',\n __default__: 'manual'\n }\n }\n };\n\n mockPOST(/\\/flow\\/simulate\\/.*\\//, response);\n};\n\nconst mockSimulatorResume = (responseText: string, quickReplies?: string[]) => {\n const msg: any = {\n uuid: 'msg-response',\n text: responseText,\n urn: 'tel:+12065551212'\n };\n\n if (quickReplies) {\n msg.quick_replies = quickReplies.map((text) => ({ text }));\n }\n\n const response = {\n session: {\n status: 'waiting',\n trigger: {\n type: 'manual',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' }\n },\n runs: [\n {\n uuid: 'run-1',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' },\n status: 'waiting',\n path: [\n {\n uuid: 'step-1',\n node_uuid: 'node-1',\n arrived_on: new Date().toISOString(),\n exit_uuid: 'exit-1'\n },\n {\n uuid: 'step-2',\n node_uuid: 'node-2',\n arrived_on: new Date().toISOString(),\n exit_uuid: null\n }\n ]\n }\n ],\n environment: {\n date_format: 'YYYY-MM-DD',\n time_format: 'HH:mm',\n timezone: 'America/New_York',\n allowed_languages: ['eng'],\n default_country: 'US'\n }\n },\n events: [\n {\n type: 'msg_created',\n created_on: new Date().toISOString(),\n msg\n }\n ],\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n urns: ['tel:+12065551212'],\n fields: {\n age: '25',\n city: 'Seattle'\n },\n groups: [],\n language: 'eng',\n status: 'active',\n created_on: new Date().toISOString()\n },\n context: {\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n name: 'Test User',\n urns: {\n tel: ['+12065551212'],\n __default__: '+12065551212'\n },\n fields: {\n age: '25',\n city: 'Seattle'\n }\n },\n results: {\n user_response: {\n value: responseText,\n __default__: responseText\n }\n }\n }\n };\n\n mockPOST(/\\/flow\\/simulate\\/.*\\//, response);\n};\n\ndescribe('temba-simulator', () => {\n beforeEach(() => {\n clearMockPosts();\n });\n\n it('can be created', async () => {\n const simulator: Simulator = await createSimulator();\n assert.instanceOf(simulator, Simulator);\n expect(simulator.flow).to.equal(FLOW_UUID);\n expect(simulator.endpoint).to.equal(`/flow/simulate/${FLOW_UUID}/`);\n });\n\n it('opens simulator window and starts flow', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n // ensure consistent size for screenshot\n simulator.size = 'medium';\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n const phoneWindow = simulator.shadowRoot.querySelector(\n 'temba-floating-window'\n ) as any;\n expect(phoneWindow).to.exist;\n\n // verify phone screen is visible\n const phoneScreen = simulator.shadowRoot.querySelector('.phone-screen');\n expect(phoneScreen).to.exist;\n\n // verify initial message is displayed\n const messages = simulator.shadowRoot.querySelectorAll('.message');\n expect(messages.length).to.be.greaterThan(0);\n\n await assertScreenshot(\n 'simulator/open-initial',\n getSimulatorClip(simulator)\n );\n });\n\n it('sends a text message', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n simulator.size = 'medium';\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n // count initial messages\n let messages = simulator.shadowRoot.querySelectorAll('.message');\n const initialCount = messages.length;\n\n // mock the resume response\n mockSimulatorResume('Thanks for your message!');\n\n // type a message\n const input = simulator.shadowRoot.querySelector(\n '.message-input input'\n ) as HTMLInputElement;\n expect(input).to.exist;\n\n input.value = 'Hello from test';\n input.dispatchEvent(new Event('input'));\n await simulator.updateComplete;\n\n // press enter to send\n const enterEvent = new KeyboardEvent('keyup', {\n key: 'Enter',\n bubbles: true\n });\n input.dispatchEvent(enterEvent);\n\n await simulator.updateComplete;\n // brief delay for async API mock processing\n await delay(100);\n\n // verify we have more messages than before\n messages = simulator.shadowRoot.querySelectorAll('.message');\n expect(messages.length).to.be.greaterThan(initialCount);\n\n // ensure DOM is settled\n await simulator.updateComplete;\n\n await assertScreenshot(\n 'simulator/after-message-sent',\n getSimulatorClip(simulator)\n );\n });\n\n it('tests message flow and takes screenshot', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n simulator.size = 'medium';\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n // clear previous mocks and set up new mock for a response\n clearMockPosts();\n mockSimulatorResume('Thank you for your message!', ['Yes', 'No', 'Maybe']);\n\n // send a message\n const input = simulator.shadowRoot.querySelector(\n '.message-input input'\n ) as HTMLInputElement;\n input.value = 'Test message';\n input.dispatchEvent(new Event('input'));\n\n const enterEvent = new KeyboardEvent('keyup', {\n key: 'Enter',\n bubbles: true\n });\n input.dispatchEvent(enterEvent);\n\n // wait for quick replies to appear\n await waitForCondition(\n () =>\n simulator.shadowRoot.querySelectorAll('.quick-reply-btn').length > 0,\n 2000\n );\n await simulator.updateComplete;\n\n // take screenshot with quick replies\n await assertScreenshot(\n 'simulator/quick-replies',\n getSimulatorClip(simulator)\n );\n });\n\n it('opens attachment menu', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n simulator.size = 'medium';\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n // click the attachment button\n const attachmentButton = simulator.shadowRoot.querySelector(\n '.attachment-button'\n ) as HTMLElement;\n expect(attachmentButton).to.exist;\n attachmentButton.click();\n\n await simulator.updateComplete;\n\n // verify attachment menu is displayed\n const attachmentMenu =\n simulator.shadowRoot.querySelector('.attachment-menu');\n expect(attachmentMenu).to.exist;\n expect(attachmentMenu.classList.contains('open')).to.be.true;\n\n await assertScreenshot(\n 'simulator/attachment-menu',\n getSimulatorClip(simulator)\n );\n });\n\n it('sends an image attachment', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n simulator.size = 'medium';\n await simulator.updateComplete;\n // reset attachment indices for deterministic testing\n simulator.resetAttachmentIndices();\n await openSimulator(simulator);\n\n // mock the response for image attachment\n mockSimulatorResume('Nice picture!');\n\n // open attachment menu and click image option\n const attachmentButton = simulator.shadowRoot.querySelector(\n '.attachment-button'\n ) as HTMLElement;\n attachmentButton.click();\n await simulator.updateComplete;\n await delay(200);\n\n const imageMenuItem = Array.from(\n simulator.shadowRoot.querySelectorAll('.attachment-menu-item')\n ).find((el) => el.textContent?.includes('Image')) as HTMLElement;\n expect(imageMenuItem).to.exist;\n imageMenuItem.click();\n\n await delay(100);\n await simulator.updateComplete;\n\n // verify attachment wrapper is displayed (image attachments show in attachments not messages)\n const attachmentWrappers = simulator.shadowRoot.querySelectorAll(\n '.attachment-wrapper'\n );\n expect(attachmentWrappers.length).to.be.greaterThan(0);\n\n await assertScreenshot(\n 'simulator/image-attachment',\n getSimulatorClip(simulator)\n );\n });\n\n it('opens context explorer', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await openSimulator(simulator);\n\n // find and click the context explorer button (has expressions icon)\n const optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n const contextButton = optionButtons.find((btn) =>\n btn.querySelector('temba-icon[name=\"expressions\"]')\n ) as HTMLElement;\n expect(contextButton).to.exist;\n contextButton.click();\n\n await simulator.updateComplete;\n await delay(100);\n\n // verify context explorer is displayed\n const contextExplorer =\n simulator.shadowRoot.querySelector('.context-explorer');\n expect(contextExplorer).to.exist;\n expect(contextExplorer.classList.contains('open')).to.be.true;\n\n // delay for context explorer to fully render\n await delay(300);\n await simulator.updateComplete;\n await document.fonts.ready;\n\n await assertScreenshot(\n 'simulator/context-explorer-open',\n getSimulatorClip(simulator, true)\n );\n });\n\n it('expands context tree items', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await openSimulator(simulator);\n\n // ensure context explorer starts closed\n if ((simulator as any).contextExplorerOpen) {\n // click to close it first\n const optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n const contextButton = optionButtons.find((btn) =>\n btn.querySelector('temba-icon[name=\"expressions\"]')\n ) as HTMLElement;\n contextButton.click();\n await simulator.updateComplete;\n await delay(100);\n }\n\n // now open context explorer\n const optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n const contextButton = optionButtons.find((btn) =>\n btn.querySelector('temba-icon[name=\"expressions\"]')\n ) as HTMLElement;\n expect(contextButton).to.exist;\n contextButton.click();\n\n await simulator.updateComplete;\n await delay(100);\n\n // verify context explorer is now open\n expect((simulator as any).contextExplorerOpen).to.be.true;\n const contextExplorer =\n simulator.shadowRoot.querySelector('.context-explorer');\n expect(contextExplorer).to.exist;\n expect(contextExplorer.classList.contains('open')).to.be.true;\n\n // find and click on an expandable item (should have context-item-expandable class)\n const expandableItems = simulator.shadowRoot.querySelectorAll(\n '.context-item-expandable'\n );\n expect(expandableItems.length).to.be.greaterThan(0);\n\n const firstExpandable = expandableItems[0] as HTMLElement;\n firstExpandable.click();\n\n // wait for children to be displayed with specific content\n await waitForCondition(() => {\n const children =\n simulator.shadowRoot.querySelectorAll('.context-children');\n if (children.length === 0) return false;\n // also check that the children have rendered content\n const items = simulator.shadowRoot.querySelectorAll('.context-item');\n return items.length > expandableItems.length;\n }, 2000);\n\n // verify children are displayed\n const contextChildren =\n simulator.shadowRoot.querySelectorAll('.context-children');\n expect(contextChildren.length).to.be.greaterThan(0);\n\n await simulator.updateComplete;\n // delay for DOM to fully render expanded content (context rendering is complex)\n await delay(300);\n await simulator.updateComplete;\n\n // ensure fonts are loaded and give extra time for rendering\n await document.fonts.ready;\n\n // wait for any pending animation frames\n await new Promise((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(resolve))\n );\n await delay(200);\n\n await assertScreenshot(\n 'simulator/context-expanded',\n getSimulatorClip(simulator, true)\n );\n });\n\n it('cycles simulator size', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await openSimulator(simulator);\n\n // initially should be medium (set in createSimulator)\n expect(simulator.size).to.equal('medium');\n\n // find and click the size button (shows current size as text)\n const optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n const sizeButton = optionButtons.find((btn) => {\n const text = btn.textContent?.trim();\n return text === 'S' || text === 'M' || text === 'L';\n }) as HTMLElement;\n expect(sizeButton).to.exist;\n sizeButton.click();\n\n await simulator.updateComplete;\n\n // should now be large (medium -> large)\n expect(simulator.size).to.equal('large');\n });\n\n it('resets simulation', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n simulator.size = 'medium';\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n // send a message first\n mockSimulatorResume('Response to test message');\n const input = simulator.shadowRoot.querySelector(\n '.message-input input'\n ) as HTMLInputElement;\n input.value = 'Test message';\n input.dispatchEvent(new Event('input'));\n\n const enterEvent = new KeyboardEvent('keyup', {\n key: 'Enter',\n bubbles: true\n });\n input.dispatchEvent(enterEvent);\n\n await delay(1000);\n await simulator.updateComplete;\n\n // verify we have multiple messages\n let messages = simulator.shadowRoot.querySelectorAll('.message');\n const messageCountBefore = messages.length;\n expect(messageCountBefore).to.be.greaterThan(1);\n\n // mock the start response for reset\n mockSimulatorStart();\n\n // click the reset button (has delete icon)\n const optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n const resetButton = optionButtons.find((btn) =>\n btn.querySelector('temba-icon[name=\"delete\"]')\n ) as HTMLElement;\n expect(resetButton).to.exist;\n resetButton.click();\n\n await delay(100);\n await simulator.updateComplete;\n\n // verify messages are reset - should go back to just initial message\n messages = simulator.shadowRoot.querySelectorAll('.message');\n expect(messages.length).to.be.lessThan(messageCountBefore);\n\n await assertScreenshot(\n 'simulator/after-reset',\n getSimulatorClip(simulator)\n );\n });\n\n it('displays event info messages', async () => {\n const responseWithEvents = {\n session: {\n status: 'waiting',\n trigger: {\n type: 'manual',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' }\n },\n runs: [\n {\n uuid: 'run-1',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' },\n status: 'waiting',\n path: [\n {\n uuid: 'step-1',\n node_uuid: 'node-1',\n arrived_on: new Date().toISOString(),\n exit_uuid: null\n }\n ]\n }\n ],\n environment: {\n date_format: 'YYYY-MM-DD',\n time_format: 'HH:mm',\n timezone: 'America/New_York',\n allowed_languages: ['eng'],\n default_country: 'US'\n }\n },\n events: [\n {\n type: 'contact_field_changed',\n created_on: new Date().toISOString(),\n field: { key: 'name', name: 'Name' },\n value: { text: 'John Doe' }\n },\n {\n type: 'msg_created',\n created_on: new Date().toISOString(),\n msg: {\n uuid: 'msg-1',\n text: 'Your name has been updated!',\n urn: 'tel:+12065551212'\n }\n }\n ],\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n urns: ['tel:+12065551212'],\n fields: {\n name: 'John Doe'\n },\n groups: [],\n language: 'eng',\n status: 'active',\n created_on: new Date().toISOString()\n },\n context: {\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n name: 'John Doe',\n fields: {\n name: 'John Doe'\n }\n }\n }\n };\n\n mockPOST(/\\/flow\\/simulate\\/.*\\//, responseWithEvents);\n\n const simulator: Simulator = await createSimulator();\n simulator.size = 'medium';\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n // verify event info is displayed\n const eventInfo = simulator.shadowRoot.querySelectorAll('.event-info');\n expect(eventInfo.length).to.be.greaterThan(0);\n\n await assertScreenshot('simulator/event-info', getSimulatorClip(simulator));\n });\n\n it('displays different simulator sizes', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await openSimulator(simulator);\n\n // get size button - find it by checking if textContent is a size indicator\n let optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n let sizeButton = optionButtons.find((btn) => {\n const text = btn.textContent?.trim();\n return text === 'S' || text === 'M' || text === 'L';\n }) as HTMLElement;\n expect(sizeButton).to.exist;\n\n // cycle to next size\n sizeButton.click();\n await simulator.updateComplete;\n await delay(200);\n\n // verify size changed\n expect(simulator.size).to.equal('large');\n\n // re-query the button after it updated\n optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n sizeButton = optionButtons.find((btn) => {\n const text = btn.textContent?.trim();\n return text === 'S' || text === 'M' || text === 'L';\n }) as HTMLElement;\n\n // cycle to next size\n sizeButton.click();\n await simulator.updateComplete;\n await delay(200);\n\n expect(simulator.size).to.equal('small');\n });\n\n it('verifies simulator endpoint configuration', async () => {\n const simulator: Simulator = await createSimulator();\n\n // verify endpoint is set correctly from flow prop\n expect(simulator.endpoint).to.equal(`/flow/simulate/${FLOW_UUID}/`);\n\n // change flow prop and verify endpoint updates\n simulator.flow = 'different-flow-456';\n await simulator.updateComplete;\n\n expect(simulator.endpoint).to.equal('/flow/simulate/different-flow-456/');\n });\n});\n"]}
1
+ {"version":3,"file":"temba-simulator.test.js","sourceRoot":"","sources":["../../test/temba-simulator.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { fixture, expect, assert } from '@open-wc/testing';\nimport { Simulator } from '../src/simulator/Simulator';\nimport {\n assertScreenshot,\n getClip,\n mockPOST,\n clearMockPosts,\n delay,\n waitForCondition,\n loadStore\n} from './utils.test';\n\nconst FLOW_UUID = 'test-flow-123';\n\nconst createSimulator = async (attrs: any = {}) => {\n // load store first since simulator depends on it\n await loadStore();\n\n const defaults = {\n flow: FLOW_UUID,\n animationTime: '0' // disable animations for deterministic tests\n };\n const mergedAttrs = { ...defaults, ...attrs };\n\n const attrString = Object.entries(mergedAttrs)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(' ');\n\n const simulator: Simulator = await fixture(\n `<temba-simulator ${attrString}></temba-simulator>`\n );\n\n // reset cookie-based properties for deterministic tests\n simulator.size = 'medium';\n (simulator as any).following = true;\n (simulator as any).contextExplorerOpen = false;\n await simulator.updateComplete;\n\n return simulator;\n};\n\n// helper to open the simulator\nconst openSimulator = async (simulator: Simulator) => {\n const tab = simulator.shadowRoot.querySelector('temba-floating-tab') as any;\n expect(tab).to.exist;\n\n // trigger the button clicked event on the tab\n tab.dispatchEvent(new CustomEvent('temba-button-clicked', { bubbles: true }));\n\n await simulator.updateComplete;\n // brief delay for async API response processing\n await delay(50);\n};\n\n// helper to get clip for the simulator window (fixed positioning)\nconst getSimulatorClip = (\n simulator: Simulator,\n includeContext: boolean = false\n) => {\n const phoneWindow = simulator.shadowRoot.querySelector(\n 'temba-floating-window'\n ) as any;\n\n if (!phoneWindow) {\n // if window not open, use default clip\n return getClip(simulator);\n }\n\n const windowElement = phoneWindow.shadowRoot?.querySelector(\n '.window'\n ) as HTMLElement;\n if (!windowElement) {\n return getClip(simulator);\n }\n\n const windowBounds = windowElement.getBoundingClientRect();\n\n if (includeContext) {\n // get the context explorer and phone to clip just those areas\n const phoneSimulator = phoneWindow.querySelector(\n '.phone-simulator'\n ) as HTMLElement;\n if (!phoneSimulator) {\n return getClip(simulator);\n }\n\n const contextExplorer = phoneSimulator.querySelector(\n '.context-explorer'\n ) as HTMLElement;\n const phoneFrame = phoneSimulator.querySelector(\n '.phone-frame'\n ) as HTMLElement;\n\n if (!contextExplorer || !phoneFrame) {\n return {\n x: windowBounds.x,\n y: windowBounds.y,\n width: windowBounds.width,\n height: windowBounds.height\n };\n }\n\n const contextBounds = contextExplorer.getBoundingClientRect();\n const phoneBounds = phoneFrame.getBoundingClientRect();\n\n // clip from the left edge of context explorer to the right edge of phone frame only\n // do not include the option-pane which is to the right of the phone\n // keep padding within the phone bounds to avoid capturing the gap to the option pane\n const padding = 10;\n const leftX = contextBounds.x - padding;\n\n // don't extend past the phone frame right edge - the option pane is close by\n const rightX = phoneBounds.right;\n\n const topY = Math.min(contextBounds.y, phoneBounds.y) - padding;\n const bottomY =\n Math.max(contextBounds.bottom, phoneBounds.bottom) + padding;\n\n return {\n x: leftX,\n y: topY,\n width: rightX - leftX,\n height: bottomY - topY\n };\n }\n\n // the phone-simulator is in the light DOM of the phoneWindow (slotted content)\n const phoneSimulator = phoneWindow.querySelector(\n '.phone-simulator'\n ) as HTMLElement;\n if (!phoneSimulator) {\n return getClip(simulator);\n }\n\n // get the phone-frame from within the phone-simulator\n const phoneFrame = phoneSimulator.querySelector(\n '.phone-frame'\n ) as HTMLElement;\n if (!phoneFrame) {\n // fallback to window bounds if phone-frame not found\n return {\n x: windowBounds.x,\n y: windowBounds.y,\n width: windowBounds.width,\n height: windowBounds.height\n };\n }\n\n const frameBounds = phoneFrame.getBoundingClientRect();\n\n // clip to just the phone frame area\n const padding = 10;\n\n return {\n x: frameBounds.x - padding,\n y: frameBounds.y - padding,\n // only add padding to the left to avoid capturing option pane on right\n width: frameBounds.width + padding,\n height: frameBounds.height + padding * 2\n };\n};\n\n// helper to get message count from chat component\nconst getMessageCount = (simulator: Simulator): number => {\n const chat = simulator.shadowRoot.querySelector('temba-chat') as any;\n if (!chat) {\n return 0;\n }\n // check how many messages are in the chat component\n // the chat component stores messages in its internal state\n return (\n chat.messageGroups?.reduce((total: number, group: any) => {\n return total + (group.messages?.length || 0);\n }, 0) || 0\n );\n};\n\n// mock responses for simulation endpoints\nconst mockSimulatorStart = () => {\n const response = {\n session: {\n status: 'waiting',\n trigger: {\n type: 'manual',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' }\n },\n runs: [\n {\n uuid: 'run-1',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' },\n status: 'waiting',\n path: [\n {\n uuid: 'step-1',\n node_uuid: 'node-1',\n arrived_on: new Date().toISOString(),\n exit_uuid: null\n }\n ]\n }\n ],\n environment: {\n date_format: 'YYYY-MM-DD',\n time_format: 'HH:mm',\n timezone: 'America/New_York',\n allowed_languages: ['eng'],\n default_country: 'US'\n }\n },\n events: [\n {\n type: 'msg_created',\n created_on: new Date().toISOString(),\n msg: {\n uuid: 'msg-1',\n text: 'Hello! How can I help you today?',\n urn: 'tel:+12065551212'\n }\n }\n ],\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n urns: ['tel:+12065551212'],\n fields: {},\n groups: [],\n language: 'eng',\n status: 'active',\n created_on: new Date().toISOString()\n },\n context: {\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n name: 'Test User',\n urns: {\n tel: ['+12065551212'],\n __default__: '+12065551212'\n },\n fields: {\n age: '25',\n city: 'Seattle'\n }\n },\n trigger: {\n type: 'manual',\n __default__: 'manual'\n }\n }\n };\n\n mockPOST(/\\/flow\\/simulate\\/.*\\//, response);\n};\n\nconst mockSimulatorResume = (responseText: string, quickReplies?: string[]) => {\n const msg: any = {\n uuid: 'msg-response',\n text: responseText,\n urn: 'tel:+12065551212'\n };\n\n if (quickReplies) {\n msg.quick_replies = quickReplies.map((text) => ({ text }));\n }\n\n const response = {\n session: {\n status: 'waiting',\n trigger: {\n type: 'manual',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' }\n },\n runs: [\n {\n uuid: 'run-1',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' },\n status: 'waiting',\n path: [\n {\n uuid: 'step-1',\n node_uuid: 'node-1',\n arrived_on: new Date().toISOString(),\n exit_uuid: 'exit-1'\n },\n {\n uuid: 'step-2',\n node_uuid: 'node-2',\n arrived_on: new Date().toISOString(),\n exit_uuid: null\n }\n ]\n }\n ],\n environment: {\n date_format: 'YYYY-MM-DD',\n time_format: 'HH:mm',\n timezone: 'America/New_York',\n allowed_languages: ['eng'],\n default_country: 'US'\n }\n },\n events: [\n {\n type: 'msg_created',\n created_on: new Date().toISOString(),\n msg\n }\n ],\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n urns: ['tel:+12065551212'],\n fields: {\n age: '25',\n city: 'Seattle'\n },\n groups: [],\n language: 'eng',\n status: 'active',\n created_on: new Date().toISOString()\n },\n context: {\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n name: 'Test User',\n urns: {\n tel: ['+12065551212'],\n __default__: '+12065551212'\n },\n fields: {\n age: '25',\n city: 'Seattle'\n }\n },\n results: {\n user_response: {\n value: responseText,\n __default__: responseText\n }\n }\n }\n };\n\n mockPOST(/\\/flow\\/simulate\\/.*\\//, response);\n};\n\ndescribe('temba-simulator', () => {\n beforeEach(() => {\n clearMockPosts();\n });\n\n it('can be created', async () => {\n const simulator: Simulator = await createSimulator();\n assert.instanceOf(simulator, Simulator);\n expect(simulator.flow).to.equal(FLOW_UUID);\n expect(simulator.endpoint).to.equal(`/flow/simulate/${FLOW_UUID}/`);\n });\n\n it('opens simulator window and starts flow', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n const phoneWindow = simulator.shadowRoot.querySelector(\n 'temba-floating-window'\n ) as any;\n expect(phoneWindow).to.exist;\n\n // verify phone screen is visible\n const phoneScreen = simulator.shadowRoot.querySelector('.phone-screen');\n expect(phoneScreen).to.exist;\n\n // verify initial message is displayed via chat component\n const messageCount = getMessageCount(simulator);\n expect(messageCount).to.be.greaterThan(0);\n\n await assertScreenshot(\n 'simulator/open-initial',\n getSimulatorClip(simulator)\n );\n });\n\n it('sends a text message', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n // count initial messages\n const initialCount = getMessageCount(simulator);\n\n // mock the resume response\n mockSimulatorResume('Thanks for your message!');\n\n // type a message\n const input = simulator.shadowRoot.querySelector(\n '.message-input input'\n ) as HTMLInputElement;\n expect(input).to.exist;\n\n input.value = 'Hello from test';\n input.dispatchEvent(new Event('input'));\n await simulator.updateComplete;\n\n // press enter to send\n const enterEvent = new KeyboardEvent('keyup', {\n key: 'Enter',\n bubbles: true\n });\n input.dispatchEvent(enterEvent);\n\n // wait for the message to be sent and response to come back\n await waitForCondition(\n () => getMessageCount(simulator) > initialCount,\n 40,\n 50\n );\n\n await simulator.updateComplete;\n // wait for chat component to update\n const chat = simulator.shadowRoot.querySelector('temba-chat') as any;\n if (chat) {\n await chat.updateComplete;\n }\n\n // verify we have more messages than before\n const newCount = getMessageCount(simulator);\n expect(newCount).to.be.greaterThan(initialCount);\n\n // ensure DOM is settled\n await simulator.updateComplete;\n\n await assertScreenshot(\n 'simulator/after-message-sent',\n getSimulatorClip(simulator)\n );\n });\n\n it('tests message flow and takes screenshot', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n // clear previous mocks and set up new mock for a response\n clearMockPosts();\n mockSimulatorResume('Thank you for your message!', ['Yes', 'No', 'Maybe']);\n\n // send a message\n const input = simulator.shadowRoot.querySelector(\n '.message-input input'\n ) as HTMLInputElement;\n input.value = 'Test message';\n input.dispatchEvent(new Event('input'));\n\n const enterEvent = new KeyboardEvent('keyup', {\n key: 'Enter',\n bubbles: true\n });\n input.dispatchEvent(enterEvent);\n\n // wait for quick replies to appear\n await waitForCondition(\n () =>\n simulator.shadowRoot.querySelectorAll('.quick-reply-btn').length > 0,\n 5000\n );\n await simulator.updateComplete;\n await delay(100); // extra delay for rendering\n\n // take screenshot with quick replies\n await assertScreenshot(\n 'simulator/quick-replies',\n getSimulatorClip(simulator)\n );\n });\n\n it('opens attachment menu', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n // click the attachment button\n const attachmentButton = simulator.shadowRoot.querySelector(\n '.attachment-button'\n ) as HTMLElement;\n expect(attachmentButton).to.exist;\n attachmentButton.click();\n\n await simulator.updateComplete;\n\n // verify attachment menu is displayed\n const attachmentMenu =\n simulator.shadowRoot.querySelector('.attachment-menu');\n expect(attachmentMenu).to.exist;\n expect(attachmentMenu.classList.contains('open')).to.be.true;\n\n await assertScreenshot(\n 'simulator/attachment-menu',\n getSimulatorClip(simulator)\n );\n });\n\n it('sends an image attachment', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await simulator.updateComplete;\n // reset attachment indices for deterministic testing\n simulator.resetAttachmentIndices();\n await openSimulator(simulator);\n\n // count initial messages\n const initialCount = getMessageCount(simulator);\n\n // mock the response for image attachment\n mockSimulatorResume('Nice picture!');\n\n // open attachment menu and click image option\n const attachmentButton = simulator.shadowRoot.querySelector(\n '.attachment-button'\n ) as HTMLElement;\n attachmentButton.click();\n await simulator.updateComplete;\n await delay(200);\n\n const imageMenuItem = Array.from(\n simulator.shadowRoot.querySelectorAll('.attachment-menu-item')\n ).find((el) => el.textContent?.includes('Image')) as HTMLElement;\n expect(imageMenuItem).to.exist;\n imageMenuItem.click();\n\n // wait for the attachment to be sent and response to come back\n await waitForCondition(\n () => getMessageCount(simulator) > initialCount,\n 40,\n 50\n );\n\n await simulator.updateComplete;\n // wait for chat component to update\n const chat = simulator.shadowRoot.querySelector('temba-chat') as any;\n if (chat) {\n await chat.updateComplete;\n }\n\n // verify message with attachment was added via chat component\n const newCount = getMessageCount(simulator);\n expect(newCount).to.be.greaterThan(initialCount);\n\n await assertScreenshot(\n 'simulator/image-attachment',\n getSimulatorClip(simulator)\n );\n });\n\n it('opens context explorer', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await openSimulator(simulator);\n\n // find and click the context explorer button (has expressions icon)\n const optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n const contextButton = optionButtons.find((btn) =>\n btn.querySelector('temba-icon[name=\"expressions\"]')\n ) as HTMLElement;\n expect(contextButton).to.exist;\n contextButton.click();\n\n await simulator.updateComplete;\n await delay(100);\n\n // verify context explorer is displayed\n const contextExplorer =\n simulator.shadowRoot.querySelector('.context-explorer');\n expect(contextExplorer).to.exist;\n expect(contextExplorer.classList.contains('open')).to.be.true;\n\n // delay for context explorer to fully render\n await delay(300);\n await simulator.updateComplete;\n await document.fonts.ready;\n\n await assertScreenshot(\n 'simulator/context-explorer-open',\n getSimulatorClip(simulator, true)\n );\n });\n\n it('expands context tree items', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await openSimulator(simulator);\n\n // ensure context explorer starts closed\n if ((simulator as any).contextExplorerOpen) {\n // click to close it first\n const optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n const contextButton = optionButtons.find((btn) =>\n btn.querySelector('temba-icon[name=\"expressions\"]')\n ) as HTMLElement;\n contextButton.click();\n await simulator.updateComplete;\n await delay(100);\n }\n\n // now open context explorer\n const optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n const contextButton = optionButtons.find((btn) =>\n btn.querySelector('temba-icon[name=\"expressions\"]')\n ) as HTMLElement;\n expect(contextButton).to.exist;\n contextButton.click();\n\n await simulator.updateComplete;\n await delay(100);\n\n // verify context explorer is now open\n expect((simulator as any).contextExplorerOpen).to.be.true;\n const contextExplorer =\n simulator.shadowRoot.querySelector('.context-explorer');\n expect(contextExplorer).to.exist;\n expect(contextExplorer.classList.contains('open')).to.be.true;\n\n // find and click on an expandable item (should have context-item-expandable class)\n const expandableItems = simulator.shadowRoot.querySelectorAll(\n '.context-item-expandable'\n );\n expect(expandableItems.length).to.be.greaterThan(0);\n\n const firstExpandable = expandableItems[0] as HTMLElement;\n firstExpandable.click();\n\n // wait for children to be displayed with specific content\n await waitForCondition(() => {\n const children =\n simulator.shadowRoot.querySelectorAll('.context-children');\n if (children.length === 0) return false;\n // also check that the children have rendered content\n const items = simulator.shadowRoot.querySelectorAll('.context-item');\n return items.length > expandableItems.length;\n }, 2000);\n\n // verify children are displayed\n const contextChildren =\n simulator.shadowRoot.querySelectorAll('.context-children');\n expect(contextChildren.length).to.be.greaterThan(0);\n\n await simulator.updateComplete;\n // delay for DOM to fully render expanded content (context rendering is complex)\n await delay(300);\n await simulator.updateComplete;\n\n // ensure fonts are loaded and give extra time for rendering\n await document.fonts.ready;\n\n // wait for any pending animation frames\n await new Promise((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(resolve))\n );\n await delay(200);\n\n await assertScreenshot(\n 'simulator/context-expanded',\n getSimulatorClip(simulator, true)\n );\n });\n\n it('cycles simulator size', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await openSimulator(simulator);\n\n // initially should be medium (set in createSimulator)\n expect(simulator.size).to.equal('medium');\n\n // find and click the size button (shows current size as text)\n const optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n const sizeButton = optionButtons.find((btn) => {\n const text = btn.textContent?.trim();\n return text === 'S' || text === 'M' || text === 'L';\n }) as HTMLElement;\n expect(sizeButton).to.exist;\n sizeButton.click();\n\n await simulator.updateComplete;\n\n // should now be large (medium -> large)\n expect(simulator.size).to.equal('large');\n });\n\n it('resets simulation', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n // send a message first\n mockSimulatorResume('Response to test message');\n const input = simulator.shadowRoot.querySelector(\n '.message-input input'\n ) as HTMLInputElement;\n input.value = 'Test message';\n input.dispatchEvent(new Event('input'));\n\n const enterEvent = new KeyboardEvent('keyup', {\n key: 'Enter',\n bubbles: true\n });\n input.dispatchEvent(enterEvent);\n\n await delay(1000);\n await simulator.updateComplete;\n\n // verify we have multiple messages\n const messageCountBefore = getMessageCount(simulator);\n expect(messageCountBefore).to.be.greaterThan(1);\n\n // mock the start response for reset\n mockSimulatorStart();\n\n // click the reset button (has delete icon)\n const optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n const resetButton = optionButtons.find((btn) =>\n btn.querySelector('temba-icon[name=\"delete\"]')\n ) as HTMLElement;\n expect(resetButton).to.exist;\n resetButton.click();\n\n await delay(100);\n await simulator.updateComplete;\n\n // verify messages are reset - should go back to just initial message\n const messageCountAfter = getMessageCount(simulator);\n expect(messageCountAfter).to.be.lessThan(messageCountBefore);\n\n await assertScreenshot(\n 'simulator/after-reset',\n getSimulatorClip(simulator)\n );\n });\n\n it('displays event info messages', async () => {\n const responseWithEvents = {\n session: {\n status: 'waiting',\n trigger: {\n type: 'manual',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' }\n },\n runs: [\n {\n uuid: 'run-1',\n flow: { uuid: FLOW_UUID, name: 'Test Flow' },\n status: 'waiting',\n path: [\n {\n uuid: 'step-1',\n node_uuid: 'node-1',\n arrived_on: new Date().toISOString(),\n exit_uuid: null\n }\n ]\n }\n ],\n environment: {\n date_format: 'YYYY-MM-DD',\n time_format: 'HH:mm',\n timezone: 'America/New_York',\n allowed_languages: ['eng'],\n default_country: 'US'\n }\n },\n events: [\n {\n type: 'contact_field_changed',\n created_on: new Date().toISOString(),\n field: { key: 'name', name: 'Name' },\n value: { text: 'John Doe' }\n },\n {\n type: 'msg_created',\n created_on: new Date().toISOString(),\n msg: {\n uuid: 'msg-1',\n text: 'Your name has been updated!',\n urn: 'tel:+12065551212'\n }\n }\n ],\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n urns: ['tel:+12065551212'],\n fields: {\n name: 'John Doe'\n },\n groups: [],\n language: 'eng',\n status: 'active',\n created_on: new Date().toISOString()\n },\n context: {\n contact: {\n uuid: 'fb3787ab-2eda-48a0-a2bc-e2ddadec1286',\n name: 'John Doe',\n fields: {\n name: 'John Doe'\n }\n }\n }\n };\n\n mockPOST(/\\/flow\\/simulate\\/.*\\//, responseWithEvents);\n\n const simulator: Simulator = await createSimulator();\n await simulator.updateComplete;\n await openSimulator(simulator);\n\n // verify events are displayed via chat component (field change + message = 2 events)\n const messageCount = getMessageCount(simulator);\n expect(messageCount).to.be.greaterThan(0);\n\n await assertScreenshot('simulator/event-info', getSimulatorClip(simulator));\n });\n\n it('displays different simulator sizes', async () => {\n mockSimulatorStart();\n\n const simulator: Simulator = await createSimulator();\n await openSimulator(simulator);\n\n // get size button - find it by checking if textContent is a size indicator\n let optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n let sizeButton = optionButtons.find((btn) => {\n const text = btn.textContent?.trim();\n return text === 'S' || text === 'M' || text === 'L';\n }) as HTMLElement;\n expect(sizeButton).to.exist;\n\n // cycle to next size\n sizeButton.click();\n await simulator.updateComplete;\n await delay(200);\n\n // verify size changed\n expect(simulator.size).to.equal('large');\n\n // re-query the button after it updated\n optionButtons = Array.from(\n simulator.shadowRoot.querySelectorAll('.option-btn')\n );\n sizeButton = optionButtons.find((btn) => {\n const text = btn.textContent?.trim();\n return text === 'S' || text === 'M' || text === 'L';\n }) as HTMLElement;\n\n // cycle to next size\n sizeButton.click();\n await simulator.updateComplete;\n await delay(200);\n\n expect(simulator.size).to.equal('small');\n });\n\n it('verifies simulator endpoint configuration', async () => {\n const simulator: Simulator = await createSimulator();\n\n // verify endpoint is set correctly from flow prop\n expect(simulator.endpoint).to.equal(`/flow/simulate/${FLOW_UUID}/`);\n\n // change flow prop and verify endpoint updates\n simulator.flow = 'different-flow-456';\n await simulator.updateComplete;\n\n expect(simulator.endpoint).to.equal('/flow/simulate/different-flow-456/');\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyaruka/temba-components",
3
- "version": "0.136.1",
3
+ "version": "0.138.0",
4
4
  "description": "Web components to support rapidpro and related projects",
5
5
  "author": "Nyaruka <code@nyaruka.coim>",
6
6
  "main": "dist/index.js",