@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
@@ -105,6 +105,7 @@ export interface AppState {
105
105
  languageNames: { [code: string]: Language };
106
106
  workspace: Workspace;
107
107
  isTranslating: boolean;
108
+ viewingRevision: boolean;
108
109
 
109
110
  dirtyDate: Date | null;
110
111
 
@@ -174,6 +175,7 @@ export const zustand = createStore<AppState>()(
174
175
  flowDefinition: null,
175
176
  flowInfo: null,
176
177
  isTranslating: false,
178
+ viewingRevision: false,
177
179
  dirtyDate: null,
178
180
  activity: null,
179
181
  simulatorActivity: null,
@@ -187,6 +189,7 @@ export const zustand = createStore<AppState>()(
187
189
  },
188
190
 
189
191
  fetchRevision: async (endpoint: string, id: string = null) => {
192
+ const viewingRevision = !!id && id !== 'latest';
190
193
  if (!id) {
191
194
  id = 'latest';
192
195
  }
@@ -198,7 +201,11 @@ export const zustand = createStore<AppState>()(
198
201
  throw new Error('Network response was not ok');
199
202
  }
200
203
  const data = (await response.json()) as FlowContents;
201
- set({ flowInfo: data.info, flowDefinition: data.definition });
204
+ set({
205
+ flowInfo: data.info,
206
+ flowDefinition: data.definition,
207
+ viewingRevision
208
+ });
202
209
  },
203
210
 
204
211
  fetchWorkspace: async (endpoint) => {
@@ -282,7 +289,11 @@ export const zustand = createStore<AppState>()(
282
289
  setFlowContents: (flow: FlowContents) => {
283
290
  set((state: AppState) => {
284
291
  const flowLang = flow.definition.language;
285
- state.flowDefinition = flow.definition;
292
+ // Clone to ensure mutable for sorting
293
+ state.flowDefinition = {
294
+ ...flow.definition,
295
+ nodes: [...(flow.definition.nodes || [])]
296
+ };
286
297
  state.flowInfo = flow.info;
287
298
  // Reset to the flow's default language when loading a new flow
288
299
  state.languageCode = flowLang;
@@ -835,7 +835,8 @@ describe('EditorNode', () => {
835
835
  makeTarget: stub(),
836
836
  makeSource: stub(),
837
837
  connectIds: stub(),
838
- removeExitConnection: stub()
838
+ removeExitConnection: stub(),
839
+ forgetNode: stub()
839
840
  };
840
841
  editorNode['plumber'] = mockPlumber;
841
842
  });
@@ -0,0 +1,134 @@
1
+ import { html, fixture, expect } from '@open-wc/testing';
2
+ import { Editor } from '../src/flow/Editor';
3
+ import { stub, restore, SinonStub } from 'sinon';
4
+
5
+ customElements.define('temba-flow-editor-revisions', Editor);
6
+
7
+ describe('Editor Revisions', () => {
8
+ let element: Editor;
9
+ let fetchStub: SinonStub;
10
+
11
+ beforeEach(async () => {
12
+ restore();
13
+ fetchStub = stub(window, 'fetch');
14
+ // Initialize without 'flow' attribute to prevent firstUpdated from calling getStore().getState()
15
+ element = await fixture(
16
+ html`<temba-flow-editor-revisions></temba-flow-editor-revisions>`
17
+ );
18
+ element.flow = 'test-flow';
19
+ });
20
+
21
+ afterEach(() => {
22
+ restore();
23
+ });
24
+
25
+ it('should exclude the most recent revision from the list', async () => {
26
+ const mockRevisions = [
27
+ {
28
+ id: 3,
29
+ created_on: '2023-01-03',
30
+ user: { id: 1, first_name: 'A', last_name: 'B', username: 'ab' }
31
+ },
32
+ {
33
+ id: 2,
34
+ created_on: '2023-01-02',
35
+ user: { id: 1, first_name: 'A', last_name: 'B', username: 'ab' }
36
+ },
37
+ {
38
+ id: 1,
39
+ created_on: '2023-01-01',
40
+ user: { id: 1, first_name: 'A', last_name: 'B', username: 'ab' }
41
+ }
42
+ ];
43
+
44
+ // Mock the fetch response for the revisions list
45
+ const mockResponse = new Response(
46
+ JSON.stringify({ results: mockRevisions }),
47
+ {
48
+ status: 200,
49
+ headers: { 'Content-Type': 'application/json' }
50
+ }
51
+ );
52
+
53
+ fetchStub.resolves(mockResponse);
54
+
55
+ // Call fetchRevisions (private)
56
+ // Note: fetchRevisions calls fetchResults -> fetchResultsPage -> fetch
57
+ await (element as any).fetchRevisions();
58
+
59
+ const revisions = (element as any).revisions;
60
+ expect(revisions.length).to.equal(2);
61
+ expect(revisions[0].id).to.equal(2);
62
+ expect(revisions[1].id).to.equal(1);
63
+ });
64
+
65
+ it('should handle empty revisions list', async () => {
66
+ const mockResponse = new Response(JSON.stringify({ results: [] }), {
67
+ status: 200
68
+ });
69
+ fetchStub.resolves(mockResponse);
70
+
71
+ await (element as any).fetchRevisions();
72
+ const revisions = (element as any).revisions;
73
+ expect(revisions.length).to.equal(0);
74
+ });
75
+
76
+ it('should handle single revision in list', async () => {
77
+ const mockRevisions = [
78
+ {
79
+ id: 1,
80
+ created_on: '2023-01-01',
81
+ user: { id: 1, first_name: 'A', last_name: 'B', username: 'ab' }
82
+ }
83
+ ];
84
+ const mockResponse = new Response(
85
+ JSON.stringify({ results: mockRevisions }),
86
+ { status: 200 }
87
+ );
88
+ fetchStub.resolves(mockResponse);
89
+
90
+ await (element as any).fetchRevisions();
91
+ const revisions = (element as any).revisions;
92
+ expect(revisions.length).to.equal(0);
93
+ });
94
+
95
+ it('should have purple color for revisions tab and blue for selected item', async () => {
96
+ // Force revisions window to show
97
+ (element as any).revisionsWindowHidden = false;
98
+ (element as any).localizationWindowHidden = true;
99
+
100
+ // Mock revisions so we can see list items
101
+ const mockRevisions = [
102
+ {
103
+ id: 2,
104
+ created_on: '2023-01-02',
105
+ user: { id: 1, first_name: 'A', last_name: 'B', username: 'ab' }
106
+ },
107
+ {
108
+ id: 1,
109
+ created_on: '2023-01-01',
110
+ user: { id: 1, first_name: 'A', last_name: 'B', username: 'ab' }
111
+ }
112
+ ];
113
+ (element as any).revisions = mockRevisions;
114
+ (element as any).viewingRevision = mockRevisions[0]; // Select the first one
115
+
116
+ await element.requestUpdate();
117
+
118
+ // Check tab color
119
+ const tab = element.querySelector('#revisions-tab');
120
+ expect(tab).to.exist;
121
+ expect(tab.getAttribute('color')).to.equal('rgb(142, 94, 167)');
122
+
123
+ // Check selected item styles
124
+ const selectedItem = element.querySelector(
125
+ '.revision-item.selected'
126
+ ) as HTMLElement;
127
+ expect(selectedItem).to.exist;
128
+
129
+ // We need to check inline styles because they are set in the template
130
+ const style = selectedItem.getAttribute('style');
131
+ expect(style).to.contain('#f0f6ff'); // Blue background
132
+ expect(style).to.contain('#a4cafe'); // Blue border
133
+ });
134
+ });
@@ -653,11 +653,14 @@ describe('Editor', () => {
653
653
 
654
654
  await editor.updateComplete;
655
655
 
656
- // node-2 should be first (top: 100 < top: 200)
657
- const flowNodes = editor.querySelectorAll('temba-flow-node');
658
- expect(flowNodes[0].getAttribute('uuid')).to.equal('node-2');
659
- expect(flowNodes[0].classList.contains('flow-start')).to.be.true;
660
- expect(flowNodes[1].classList.contains('flow-start')).to.be.false;
656
+ // node-2 is at top (top: 100 < top: 200) so it should be flow-start
657
+ const node1 = editor.querySelector('temba-flow-node[uuid="node-1"]');
658
+ const node2 = editor.querySelector('temba-flow-node[uuid="node-2"]');
659
+
660
+ expect(node2).to.exist;
661
+ expect(node1).to.exist;
662
+ expect(node2.classList.contains('flow-start')).to.be.true;
663
+ expect(node1.classList.contains('flow-start')).to.be.false;
661
664
 
662
665
  // move node-1 to the top
663
666
  zustand.getState().updateCanvasPositions({
@@ -720,7 +723,7 @@ describe('Editor', () => {
720
723
 
721
724
  await editor.updateComplete;
722
725
 
723
- let flowNodes = editor.querySelectorAll('temba-flow-node');
726
+ const flowNodes = editor.querySelectorAll('temba-flow-node');
724
727
  expect(flowNodes[0].classList.contains('flow-start')).to.be.true;
725
728
 
726
729
  // add a new node at the top
@@ -741,10 +744,13 @@ describe('Editor', () => {
741
744
  await editor.updateComplete;
742
745
 
743
746
  // new node should now be the flow-start
744
- flowNodes = editor.querySelectorAll('temba-flow-node');
745
- expect(flowNodes[0].getAttribute('uuid')).to.equal('node-2');
746
- expect(flowNodes[0].classList.contains('flow-start')).to.be.true;
747
- expect(flowNodes[1].classList.contains('flow-start')).to.be.false;
747
+ const node1 = editor.querySelector('temba-flow-node[uuid="node-1"]');
748
+ const node2 = editor.querySelector('temba-flow-node[uuid="node-2"]');
749
+
750
+ expect(node2).to.exist;
751
+ expect(node1).to.exist;
752
+ expect(node2.classList.contains('flow-start')).to.be.true;
753
+ expect(node1.classList.contains('flow-start')).to.be.false;
748
754
  });
749
755
 
750
756
  it('should handle flow-start when first node is removed', async () => {
@@ -29,7 +29,14 @@ describe('Plumber - Connection Management', () => {
29
29
  removeClass: stub(),
30
30
  batch: stub().callsFake((fn: any) => fn()),
31
31
  addEndpoint: stub().returns({}),
32
+ revalidate: stub(),
32
33
  connect: stub(),
34
+ getEndpoints: stub().returns([
35
+ { elementId: 'test-from', addClass: stub() }
36
+ ]),
37
+ select: stub().returns({
38
+ deleteAll: stub()
39
+ }),
33
40
  selectEndpoints: stub().returns({
34
41
  deleteAll: stub()
35
42
  }),
@@ -131,7 +138,6 @@ describe('Plumber - Connection Management', () => {
131
138
  expect(result).to.be.true;
132
139
  expect((plumber as any).jsPlumb.deleteConnection).to.have.been
133
140
  .calledTwice;
134
- expect((plumber as any).jsPlumb.removeAllEndpoints).to.have.been.called;
135
141
  });
136
142
 
137
143
  it('returns false when no connections exist', () => {
@@ -31,6 +31,12 @@ describe('Plumber', () => {
31
31
  batch: stub().callsFake((fn) => fn()),
32
32
  addEndpoint: stub().returns({}),
33
33
  connect: stub(),
34
+ getEndpoints: stub().returns([
35
+ { elementId: 'test-from', addClass: stub() }
36
+ ]),
37
+ select: stub().returns({
38
+ deleteAll: stub()
39
+ }),
34
40
  selectEndpoints: stub().returns({
35
41
  deleteAll: stub()
36
42
  }),
@@ -48,7 +48,7 @@ const openSimulator = async (simulator: Simulator) => {
48
48
  tab.dispatchEvent(new CustomEvent('temba-button-clicked', { bubbles: true }));
49
49
 
50
50
  await simulator.updateComplete;
51
- // brief delay for async API mock processing
51
+ // brief delay for async API response processing
52
52
  await delay(50);
53
53
  };
54
54
 
@@ -148,16 +148,33 @@ const getSimulatorClip = (
148
148
 
149
149
  const frameBounds = phoneFrame.getBoundingClientRect();
150
150
 
151
- // add padding around the phone frame
151
+ // clip to just the phone frame area
152
152
  const padding = 10;
153
+
153
154
  return {
154
155
  x: frameBounds.x - padding,
155
156
  y: frameBounds.y - padding,
156
- width: frameBounds.width + padding * 2,
157
+ // only add padding to the left to avoid capturing option pane on right
158
+ width: frameBounds.width + padding,
157
159
  height: frameBounds.height + padding * 2
158
160
  };
159
161
  };
160
162
 
163
+ // helper to get message count from chat component
164
+ const getMessageCount = (simulator: Simulator): number => {
165
+ const chat = simulator.shadowRoot.querySelector('temba-chat') as any;
166
+ if (!chat) {
167
+ return 0;
168
+ }
169
+ // check how many messages are in the chat component
170
+ // the chat component stores messages in its internal state
171
+ return (
172
+ chat.messageGroups?.reduce((total: number, group: any) => {
173
+ return total + (group.messages?.length || 0);
174
+ }, 0) || 0
175
+ );
176
+ };
177
+
161
178
  // mock responses for simulation endpoints
162
179
  const mockSimulatorStart = () => {
163
180
  const response = {
@@ -340,8 +357,6 @@ describe('temba-simulator', () => {
340
357
  mockSimulatorStart();
341
358
 
342
359
  const simulator: Simulator = await createSimulator();
343
- // ensure consistent size for screenshot
344
- simulator.size = 'medium';
345
360
  await simulator.updateComplete;
346
361
  await openSimulator(simulator);
347
362
 
@@ -354,9 +369,9 @@ describe('temba-simulator', () => {
354
369
  const phoneScreen = simulator.shadowRoot.querySelector('.phone-screen');
355
370
  expect(phoneScreen).to.exist;
356
371
 
357
- // verify initial message is displayed
358
- const messages = simulator.shadowRoot.querySelectorAll('.message');
359
- expect(messages.length).to.be.greaterThan(0);
372
+ // verify initial message is displayed via chat component
373
+ const messageCount = getMessageCount(simulator);
374
+ expect(messageCount).to.be.greaterThan(0);
360
375
 
361
376
  await assertScreenshot(
362
377
  'simulator/open-initial',
@@ -368,13 +383,11 @@ describe('temba-simulator', () => {
368
383
  mockSimulatorStart();
369
384
 
370
385
  const simulator: Simulator = await createSimulator();
371
- simulator.size = 'medium';
372
386
  await simulator.updateComplete;
373
387
  await openSimulator(simulator);
374
388
 
375
389
  // count initial messages
376
- let messages = simulator.shadowRoot.querySelectorAll('.message');
377
- const initialCount = messages.length;
390
+ const initialCount = getMessageCount(simulator);
378
391
 
379
392
  // mock the resume response
380
393
  mockSimulatorResume('Thanks for your message!');
@@ -396,13 +409,23 @@ describe('temba-simulator', () => {
396
409
  });
397
410
  input.dispatchEvent(enterEvent);
398
411
 
412
+ // wait for the message to be sent and response to come back
413
+ await waitForCondition(
414
+ () => getMessageCount(simulator) > initialCount,
415
+ 40,
416
+ 50
417
+ );
418
+
399
419
  await simulator.updateComplete;
400
- // brief delay for async API mock processing
401
- await delay(100);
420
+ // wait for chat component to update
421
+ const chat = simulator.shadowRoot.querySelector('temba-chat') as any;
422
+ if (chat) {
423
+ await chat.updateComplete;
424
+ }
402
425
 
403
426
  // verify we have more messages than before
404
- messages = simulator.shadowRoot.querySelectorAll('.message');
405
- expect(messages.length).to.be.greaterThan(initialCount);
427
+ const newCount = getMessageCount(simulator);
428
+ expect(newCount).to.be.greaterThan(initialCount);
406
429
 
407
430
  // ensure DOM is settled
408
431
  await simulator.updateComplete;
@@ -417,7 +440,6 @@ describe('temba-simulator', () => {
417
440
  mockSimulatorStart();
418
441
 
419
442
  const simulator: Simulator = await createSimulator();
420
- simulator.size = 'medium';
421
443
  await simulator.updateComplete;
422
444
  await openSimulator(simulator);
423
445
 
@@ -442,9 +464,10 @@ describe('temba-simulator', () => {
442
464
  await waitForCondition(
443
465
  () =>
444
466
  simulator.shadowRoot.querySelectorAll('.quick-reply-btn').length > 0,
445
- 2000
467
+ 5000
446
468
  );
447
469
  await simulator.updateComplete;
470
+ await delay(100); // extra delay for rendering
448
471
 
449
472
  // take screenshot with quick replies
450
473
  await assertScreenshot(
@@ -457,7 +480,6 @@ describe('temba-simulator', () => {
457
480
  mockSimulatorStart();
458
481
 
459
482
  const simulator: Simulator = await createSimulator();
460
- simulator.size = 'medium';
461
483
  await simulator.updateComplete;
462
484
  await openSimulator(simulator);
463
485
 
@@ -486,12 +508,14 @@ describe('temba-simulator', () => {
486
508
  mockSimulatorStart();
487
509
 
488
510
  const simulator: Simulator = await createSimulator();
489
- simulator.size = 'medium';
490
511
  await simulator.updateComplete;
491
512
  // reset attachment indices for deterministic testing
492
513
  simulator.resetAttachmentIndices();
493
514
  await openSimulator(simulator);
494
515
 
516
+ // count initial messages
517
+ const initialCount = getMessageCount(simulator);
518
+
495
519
  // mock the response for image attachment
496
520
  mockSimulatorResume('Nice picture!');
497
521
 
@@ -509,14 +533,23 @@ describe('temba-simulator', () => {
509
533
  expect(imageMenuItem).to.exist;
510
534
  imageMenuItem.click();
511
535
 
512
- await delay(100);
536
+ // wait for the attachment to be sent and response to come back
537
+ await waitForCondition(
538
+ () => getMessageCount(simulator) > initialCount,
539
+ 40,
540
+ 50
541
+ );
542
+
513
543
  await simulator.updateComplete;
544
+ // wait for chat component to update
545
+ const chat = simulator.shadowRoot.querySelector('temba-chat') as any;
546
+ if (chat) {
547
+ await chat.updateComplete;
548
+ }
514
549
 
515
- // verify attachment wrapper is displayed (image attachments show in attachments not messages)
516
- const attachmentWrappers = simulator.shadowRoot.querySelectorAll(
517
- '.attachment-wrapper'
518
- );
519
- expect(attachmentWrappers.length).to.be.greaterThan(0);
550
+ // verify message with attachment was added via chat component
551
+ const newCount = getMessageCount(simulator);
552
+ expect(newCount).to.be.greaterThan(initialCount);
520
553
 
521
554
  await assertScreenshot(
522
555
  'simulator/image-attachment',
@@ -674,7 +707,6 @@ describe('temba-simulator', () => {
674
707
  mockSimulatorStart();
675
708
 
676
709
  const simulator: Simulator = await createSimulator();
677
- simulator.size = 'medium';
678
710
  await simulator.updateComplete;
679
711
  await openSimulator(simulator);
680
712
 
@@ -696,8 +728,7 @@ describe('temba-simulator', () => {
696
728
  await simulator.updateComplete;
697
729
 
698
730
  // verify we have multiple messages
699
- let messages = simulator.shadowRoot.querySelectorAll('.message');
700
- const messageCountBefore = messages.length;
731
+ const messageCountBefore = getMessageCount(simulator);
701
732
  expect(messageCountBefore).to.be.greaterThan(1);
702
733
 
703
734
  // mock the start response for reset
@@ -717,8 +748,8 @@ describe('temba-simulator', () => {
717
748
  await simulator.updateComplete;
718
749
 
719
750
  // verify messages are reset - should go back to just initial message
720
- messages = simulator.shadowRoot.querySelectorAll('.message');
721
- expect(messages.length).to.be.lessThan(messageCountBefore);
751
+ const messageCountAfter = getMessageCount(simulator);
752
+ expect(messageCountAfter).to.be.lessThan(messageCountBefore);
722
753
 
723
754
  await assertScreenshot(
724
755
  'simulator/after-reset',
@@ -799,13 +830,12 @@ describe('temba-simulator', () => {
799
830
  mockPOST(/\/flow\/simulate\/.*\//, responseWithEvents);
800
831
 
801
832
  const simulator: Simulator = await createSimulator();
802
- simulator.size = 'medium';
803
833
  await simulator.updateComplete;
804
834
  await openSimulator(simulator);
805
835
 
806
- // verify event info is displayed
807
- const eventInfo = simulator.shadowRoot.querySelectorAll('.event-info');
808
- expect(eventInfo.length).to.be.greaterThan(0);
836
+ // verify events are displayed via chat component (field change + message = 2 events)
837
+ const messageCount = getMessageCount(simulator);
838
+ expect(messageCount).to.be.greaterThan(0);
809
839
 
810
840
  await assertScreenshot('simulator/event-info', getSimulatorClip(simulator));
811
841
  });