@coveo/quantic 3.40.0 → 3.42.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 (62) hide show
  1. package/docs/out/quantic-docs.json +9 -0
  2. package/force-app/main/default/labels/CustomLabels.labels-meta.xml +91 -0
  3. package/force-app/main/default/lwc/quanticGeneratedAnswer/__tests__/quanticGeneratedAnswer.test.js +271 -22
  4. package/force-app/main/default/lwc/quanticGeneratedAnswer/quanticGeneratedAnswer.js +92 -31
  5. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/conversation.css +7 -0
  6. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/conversation.html +63 -0
  7. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/generatedAnswer.css +0 -11
  8. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/generatedAnswer.html +2 -2
  9. package/force-app/main/default/lwc/quanticGeneratedAnswerBody/__tests__/quanticGeneratedAnswerBody.test.js +379 -0
  10. package/force-app/main/default/lwc/quanticGeneratedAnswerBody/quanticGeneratedAnswerBody.js +152 -0
  11. package/force-app/main/default/lwc/quanticGeneratedAnswerBody/quanticGeneratedAnswerBody.js-meta.xml +5 -0
  12. package/force-app/main/default/lwc/quanticGeneratedAnswerBody/templates/answer.css +3 -0
  13. package/force-app/main/default/lwc/quanticGeneratedAnswerBody/templates/answer.html +59 -0
  14. package/force-app/main/default/lwc/quanticGeneratedAnswerBody/templates/cannotAnswer.html +7 -0
  15. package/force-app/main/default/lwc/quanticGeneratedAnswerBody/templates/error.html +7 -0
  16. package/force-app/main/default/lwc/quanticGeneratedAnswerCopyToClipboard/__tests__/quanticGeneratedAnswerCopyToClipboard.test.js +5 -1
  17. package/force-app/main/default/lwc/quanticGeneratedAnswerCopyToClipboard/quanticGeneratedAnswerCopyToClipboard.js +9 -2
  18. package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/__tests__/quanticGeneratedAnswerFollowUpInput.test.js +109 -55
  19. package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.css +59 -11
  20. package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.html +14 -13
  21. package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.js +40 -2
  22. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/__tests__/quanticGeneratedAnswerStreamOfThought.test.js +348 -0
  23. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/quanticGeneratedAnswerStreamOfThought.css +17 -0
  24. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/quanticGeneratedAnswerStreamOfThought.js +163 -0
  25. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/quanticGeneratedAnswerStreamOfThought.js-meta.xml +5 -0
  26. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/templates/collapsedSummary.css +1 -0
  27. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/templates/collapsedSummary.html +32 -0
  28. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/templates/streamOfThought.css +1 -0
  29. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/templates/streamOfThought.html +65 -0
  30. package/force-app/main/default/lwc/quanticGeneratedAnswerThread/__tests__/quanticGeneratedAnswerThread.test.js +285 -0
  31. package/force-app/main/default/lwc/quanticGeneratedAnswerThread/quanticGeneratedAnswerThread.css +47 -0
  32. package/force-app/main/default/lwc/quanticGeneratedAnswerThread/quanticGeneratedAnswerThread.html +67 -0
  33. package/force-app/main/default/lwc/quanticGeneratedAnswerThread/quanticGeneratedAnswerThread.js +93 -0
  34. package/force-app/main/default/lwc/quanticGeneratedAnswerThread/quanticGeneratedAnswerThread.js-meta.xml +5 -0
  35. package/force-app/main/default/lwc/quanticSourceCitations/__tests__/quanticSourceCitations.test.js +22 -2
  36. package/force-app/main/default/lwc/quanticSourceCitations/quanticSourceCitations.js +7 -0
  37. package/force-app/main/default/lwc/quanticThreadItem/quanticThreadItem.css +0 -4
  38. package/force-app/main/default/lwc/quanticThreadItem/quanticThreadItem.html +1 -1
  39. package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +15 -15
  40. package/force-app/main/default/staticresources/coveoheadless/definitions/api/commerce/common/pagination.d.ts +1 -1
  41. package/force-app/main/default/staticresources/coveoheadless/definitions/commerce.index.d.ts +1 -1
  42. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/core/sub-controller/headless-sub-controller.d.ts +3 -1
  43. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/recent-queries-list/headless-recent-queries-list.d.ts +13 -2
  44. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/search/did-you-mean/headless-did-you-mean.d.ts +10 -1
  45. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/core/generated-answer/headless-core-generated-answer.d.ts +1 -0
  46. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/core/generated-answer/headless-core-interactive-citation.d.ts +3 -3
  47. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/generated-answer/headless-generated-answer.d.ts +2 -2
  48. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/generated-answer/interactive-citation-analytics-client.d.ts +3 -0
  49. package/force-app/main/default/staticresources/coveoheadless/definitions/features/analytics/analytics-utils.d.ts +3 -2
  50. package/force-app/main/default/staticresources/coveoheadless/definitions/features/follow-up-answers/follow-up-answers-actions.d.ts +32 -1
  51. package/force-app/main/default/staticresources/coveoheadless/definitions/features/generated-answer/generated-answer-actions.d.ts +26 -1
  52. package/force-app/main/default/staticresources/coveoheadless/definitions/features/generated-answer/generated-answer-analytics-actions.d.ts +2 -0
  53. package/force-app/main/default/staticresources/coveoheadless/definitions/features/generated-answer/generated-answer-state.d.ts +19 -0
  54. package/force-app/main/default/staticresources/coveoheadless/definitions/index.d.ts +1 -1
  55. package/force-app/main/default/staticresources/coveoheadless/definitions/ssr/commerce/controllers/did-you-mean/headless-did-you-mean.ssr.d.ts +4 -3
  56. package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-commerce-next.index.d.ts +1 -1
  57. package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-commerce.index.d.ts +1 -1
  58. package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-next/commerce/controllers/did-you-mean/headless-did-you-mean.ssr.d.ts +4 -3
  59. package/force-app/main/default/staticresources/coveoheadless/headless.js +18 -18
  60. package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +16 -16
  61. package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +14 -14
  62. package/package.json +2 -2
@@ -1031,6 +1031,15 @@
1031
1031
  "defaultValue": "{undefined}",
1032
1032
  "type": "string"
1033
1033
  },
1034
+ {
1035
+ "name": "agentId",
1036
+ "attribute": "agent-id",
1037
+ "access": "@api",
1038
+ "description": "<p>The unique identifier of the agent to use to generate the answer.</p>",
1039
+ "required": false,
1040
+ "defaultValue": "{undefined}",
1041
+ "type": "string"
1042
+ },
1034
1043
  {
1035
1044
  "name": "maxCollapsedHeight",
1036
1045
  "attribute": "max-collapsed-height",
@@ -455,6 +455,13 @@
455
455
  <protected>false</protected>
456
456
  <shortDescription>Collapse [component label]</shortDescription>
457
457
  </labels>
458
+ <labels>
459
+ <fullName>quantic_CollapseButton</fullName>
460
+ <value>Collapse</value>
461
+ <language>en_US</language>
462
+ <protected>false</protected>
463
+ <shortDescription>Collapse</shortDescription>
464
+ </labels>
458
465
  <labels>
459
466
  <fullName>quantic_Expand</fullName>
460
467
  <value>Expand {{0}}</value>
@@ -1288,6 +1295,13 @@
1288
1295
  <protected>false</protected>
1289
1296
  <shortDescription>Something went wrong and we couldn't generate an answer.</shortDescription>
1290
1297
  </labels>
1298
+ <labels>
1299
+ <fullName>quantic_GeneratedAnswerErrorTurnLimitReached</fullName>
1300
+ <value>Conversation turn limit reached. Please start a new conversation.</value>
1301
+ <language>en_US</language>
1302
+ <protected>false</protected>
1303
+ <shortDescription>Generated answer conversation turn limit reached error.</shortDescription>
1304
+ </labels>
1291
1305
  <labels>
1292
1306
  <fullName>quantic_ShowingResultsFor</fullName>
1293
1307
  <value>Showing results for {{0}}</value>
@@ -1638,4 +1652,81 @@
1638
1652
  <protected>false</protected>
1639
1653
  <shortDescription>Submit follow-up</shortDescription>
1640
1654
  </labels>
1655
+ <labels>
1656
+ <fullName>quantic_ShowPreviousQuestions_plural</fullName>
1657
+ <value>Show {{0}} previous questions</value>
1658
+ <language>en_US</language>
1659
+ <protected>false</protected>
1660
+ <shortDescription>Show previous questions (plural)</shortDescription>
1661
+ </labels>
1662
+ <labels>
1663
+ <fullName>quantic_AgentGenerationStepAnalyzingQuestion</fullName>
1664
+ <value>Analyzing your question…</value>
1665
+ <language>en_US</language>
1666
+ <protected>false</protected>
1667
+ <shortDescription>Analyzing your question</shortDescription>
1668
+ </labels>
1669
+ <labels>
1670
+ <fullName>quantic_AgentGenerationStepAnalyzingQuestionCompleted</fullName>
1671
+ <value>Question analyzed</value>
1672
+ <language>en_US</language>
1673
+ <protected>false</protected>
1674
+ <shortDescription>Question analyzed</shortDescription>
1675
+ </labels>
1676
+ <labels>
1677
+ <fullName>quantic_AgentGenerationStepSearch</fullName>
1678
+ <value>Searching knowledge base…</value>
1679
+ <language>en_US</language>
1680
+ <protected>false</protected>
1681
+ <shortDescription>Searching knowledge base…</shortDescription>
1682
+ </labels>
1683
+ <labels>
1684
+ <fullName>quantic_AgentGenerationStepSearchCompleted</fullName>
1685
+ <value>Knowledge base searched</value>
1686
+ <language>en_US</language>
1687
+ <protected>false</protected>
1688
+ <shortDescription>Knowledge base searched</shortDescription>
1689
+ </labels>
1690
+ <labels>
1691
+ <fullName>quantic_AgentGenerationStepAnalyzingResults</fullName>
1692
+ <value>Analyzing results…</value>
1693
+ <language>en_US</language>
1694
+ <protected>false</protected>
1695
+ <shortDescription>Analyzing results…</shortDescription>
1696
+ </labels>
1697
+ <labels>
1698
+ <fullName>quantic_AgentGenerationStepAnalyzingResultsCompleted</fullName>
1699
+ <value>Results analyzed</value>
1700
+ <language>en_US</language>
1701
+ <protected>false</protected>
1702
+ <shortDescription>Results analyzed</shortDescription>
1703
+ </labels>
1704
+ <labels>
1705
+ <fullName>quantic_AgentGenerationStepAnswering</fullName>
1706
+ <value>Generating answer…</value>
1707
+ <language>en_US</language>
1708
+ <protected>false</protected>
1709
+ <shortDescription>Generating answer…</shortDescription>
1710
+ </labels>
1711
+ <labels>
1712
+ <fullName>quantic_AgentGenerationStepAnsweringCompleted</fullName>
1713
+ <value>Answer generated</value>
1714
+ <language>en_US</language>
1715
+ <protected>false</protected>
1716
+ <shortDescription>Answer generated</shortDescription>
1717
+ </labels>
1718
+ <labels>
1719
+ <fullName>quantic_Like</fullName>
1720
+ <value>Like</value>
1721
+ <language>en_US</language>
1722
+ <protected>false</protected>
1723
+ <shortDescription>Like</shortDescription>
1724
+ </labels>
1725
+ <labels>
1726
+ <fullName>quantic_Dislike</fullName>
1727
+ <value>Dislike</value>
1728
+ <language>en_US</language>
1729
+ <protected>false</protected>
1730
+ <shortDescription>Dislike</shortDescription>
1731
+ </labels>
1641
1732
  </CustomLabels>
@@ -2,6 +2,7 @@
2
2
  // @ts-ignore
3
3
  import {createElement} from 'lwc';
4
4
  import QuanticGeneratedAnswer from 'c/quanticGeneratedAnswer';
5
+ import FeedbackModalQna from 'c/quanticFeedbackModalQna';
5
6
  import * as mockHeadlessLoader from 'c/quanticHeadlessLoader';
6
7
 
7
8
  let mockAnswerHeight = 250;
@@ -25,6 +26,12 @@ const exampleCitations = [
25
26
  const exampleEngineId = 'example engine id';
26
27
  const exampleAnswerId = 'example answer id';
27
28
  jest.mock('c/quanticHeadlessLoader');
29
+ jest.mock('c/quanticFeedbackModalQna', () => ({
30
+ __esModule: true,
31
+ default: {
32
+ open: jest.fn(() => Promise.resolve()),
33
+ },
34
+ }));
28
35
  jest.mock('c/quanticUtils', () => ({
29
36
  AriaLiveRegion: jest.fn(() => ({
30
37
  dispatchMessage: jest.fn(),
@@ -95,6 +102,9 @@ const selectors = {
95
102
  generatedAnswerNoAnswerMessage:
96
103
  '[data-testid="generated-answer__no-answer-message"]',
97
104
  generatedAnswerCitations: 'c-quantic-source-citations',
105
+ generatedAnswerThread: 'c-quantic-generated-answer-thread',
106
+ generatedAnswerFollowUpInput: 'c-quantic-generated-answer-follow-up-input',
107
+ generatedAnswerFeedback: 'c-quantic-feedback',
98
108
  loadingSpinner: 'lightning-spinner',
99
109
  };
100
110
 
@@ -103,7 +113,10 @@ const initialSearchStatusState = {
103
113
  };
104
114
  let searchStatusState = initialSearchStatusState;
105
115
 
106
- const initialGeneratedAnswerState = {isVisible: true, cannotAnswer: false};
116
+ const initialGeneratedAnswerState = {
117
+ isVisible: true,
118
+ cannotAnswer: false,
119
+ };
107
120
  let generatedAnswerState = initialGeneratedAnswerState;
108
121
 
109
122
  const functionsMocks = {
@@ -111,6 +124,10 @@ const functionsMocks = {
111
124
  state: generatedAnswerState,
112
125
  subscribe: functionsMocks.generatedAnswerStateSubscriber,
113
126
  retry: functionsMocks.retry,
127
+ askFollowUp: functionsMocks.askFollowUp,
128
+ like: functionsMocks.like,
129
+ dislike: functionsMocks.dislike,
130
+ closeFeedbackModal: functionsMocks.closeFeedbackModal,
114
131
  })),
115
132
  buildSearchStatus: jest.fn(() => ({
116
133
  state: searchStatusState,
@@ -127,6 +144,10 @@ const functionsMocks = {
127
144
  generatedAnswerStateUnsubscriber: jest.fn(),
128
145
  searchStatusStateUnsubscriber: jest.fn(),
129
146
  retry: jest.fn(),
147
+ askFollowUp: jest.fn(),
148
+ like: jest.fn(),
149
+ dislike: jest.fn(),
150
+ closeFeedbackModal: jest.fn(),
130
151
  };
131
152
 
132
153
  /**
@@ -145,8 +166,10 @@ function flushPromises() {
145
166
  return new Promise((resolve) => setTimeout(resolve, 0));
146
167
  }
147
168
 
169
+ const exampleQuery = 'example query';
148
170
  const exampleEngine = {
149
171
  id: 'dummy engine',
172
+ state: {query: {q: exampleQuery}},
150
173
  };
151
174
  let isInitialized = false;
152
175
  const defaultAnswerHeight = 250;
@@ -339,6 +362,36 @@ describe('c-quantic-generated-answer', () => {
339
362
  );
340
363
  });
341
364
  });
365
+
366
+ describe('when the agent id property is passed to the component', () => {
367
+ it('should initialize the controller with the correct agent id value', async () => {
368
+ const exampleAgentId = 'example agent id';
369
+ createTestComponent({
370
+ ...defaultOptions,
371
+ agentId: exampleAgentId,
372
+ });
373
+ await flushPromises();
374
+
375
+ expect(functionsMocks.buildGeneratedAnswer).toHaveBeenCalledTimes(1);
376
+ expect(functionsMocks.buildGeneratedAnswer).toHaveBeenCalledWith(
377
+ exampleEngine,
378
+ expect.objectContaining({agentId: exampleAgentId})
379
+ );
380
+ });
381
+ });
382
+
383
+ describe('when the agent id property is not passed to the component', () => {
384
+ it('should initialize the controller without the agent id value', async () => {
385
+ createTestComponent();
386
+ await flushPromises();
387
+
388
+ expect(functionsMocks.buildGeneratedAnswer).toHaveBeenCalledTimes(1);
389
+ expect(functionsMocks.buildGeneratedAnswer).toHaveBeenCalledWith(
390
+ exampleEngine,
391
+ expect.not.objectContaining({agentId: expect.any(String)})
392
+ );
393
+ });
394
+ });
342
395
  });
343
396
 
344
397
  describe('the rendering of the generated answer', () => {
@@ -792,25 +845,119 @@ describe('c-quantic-generated-answer', () => {
792
845
  });
793
846
 
794
847
  describe('when follow-ups are enabled', () => {
795
- // TODO SFINT-6786: Add test cases to cover the behavior of the component when follow-ups are enabled based on the actual implementation of the follow-up feature in the state.
796
- it.skip('should render the content section with the scrollable class and ignore the collapsible feature', async () => {
797
- mockAnswerHeight = defaultAnswerHeight + 100;
848
+ const exampleAgentId = 'example agent id';
849
+ const exampleFollowUpAnswers = [
850
+ {
851
+ answer: 'follow-up answer',
852
+ answerId: 'answer-id-2',
853
+ question: 'follow-up question',
854
+ },
855
+ ];
856
+ const followUpOptions = {...defaultOptions, agentId: exampleAgentId};
857
+
858
+ beforeEach(() => {
859
+ generatedAnswerState = {
860
+ ...initialGeneratedAnswerState,
861
+ answer: exampleAnswer,
862
+ answerId: 'answer-id-1',
863
+ followUpAnswers: {
864
+ isEnabled: true,
865
+ followUpAnswers: exampleFollowUpAnswers,
866
+ },
867
+ };
868
+ });
869
+
870
+ it('should render the conversation thread and follow-up input instead of the single answer body', async () => {
871
+ const element = createTestComponent(followUpOptions);
872
+ await flushPromises();
873
+
874
+ const thread = element.shadowRoot.querySelector(
875
+ selectors.generatedAnswerThread
876
+ );
877
+ const followUpInput = element.shadowRoot.querySelector(
878
+ selectors.generatedAnswerFollowUpInput
879
+ );
880
+ const singleAnswerBody = element.shadowRoot.querySelector(
881
+ selectors.generatedAnswerBody
882
+ );
883
+
884
+ expect(thread).not.toBeNull();
885
+ expect(followUpInput).not.toBeNull();
886
+ expect(singleAnswerBody).toBeNull();
887
+ });
888
+
889
+ it('should display the scrollable container', async () => {
890
+ const element = createTestComponent(followUpOptions);
891
+ await flushPromises();
892
+
893
+ const scrollableContainer = element.shadowRoot.querySelector(
894
+ '.generated-answer__content--scrollable'
895
+ );
896
+
897
+ expect(scrollableContainer).not.toBeNull();
898
+ });
899
+
900
+ it('should pass the engine, citation anchoring and the combined initial and follow-up answers to the thread', async () => {
798
901
  const element = createTestComponent({
799
- ...defaultOptions,
800
- collapsible: true,
902
+ ...followUpOptions,
903
+ disableCitationAnchoring: true,
801
904
  });
802
905
  await flushPromises();
803
906
 
804
- const generatedAnswerBody = element.shadowRoot.querySelector(
805
- selectors.generatedAnswerBody
907
+ const thread = element.shadowRoot.querySelector(
908
+ selectors.generatedAnswerThread
806
909
  );
807
910
 
808
- expect(generatedAnswerBody).not.toBeNull();
809
- expect(
810
- generatedAnswerBody.classList.contains(
811
- 'generated-answer__content--scrollable'
812
- )
813
- ).toBe(true);
911
+ expect(thread.engineId).toBe(exampleEngineId);
912
+ expect(thread.disableCitationAnchoring).toBe(true);
913
+ expect(thread.generatedAnswers).toEqual([
914
+ expect.objectContaining({
915
+ answer: exampleAnswer,
916
+ question: exampleQuery,
917
+ }),
918
+ ...exampleFollowUpAnswers,
919
+ ]);
920
+ });
921
+
922
+ it('should ask a follow-up question when the follow-up input emits a submit event', async () => {
923
+ const element = createTestComponent(followUpOptions);
924
+ await flushPromises();
925
+
926
+ const followUpInput = element.shadowRoot.querySelector(
927
+ selectors.generatedAnswerFollowUpInput
928
+ );
929
+ const exampleFollowUpQuestion = 'example follow-up question';
930
+ followUpInput.dispatchEvent(
931
+ new CustomEvent('quantic__submitfollowup', {
932
+ detail: {value: exampleFollowUpQuestion},
933
+ })
934
+ );
935
+
936
+ expect(functionsMocks.askFollowUp).toHaveBeenCalledTimes(1);
937
+ expect(functionsMocks.askFollowUp).toHaveBeenCalledWith(
938
+ exampleFollowUpQuestion
939
+ );
940
+ });
941
+
942
+ it('should disable the follow-up submit button while an answer is being generated', async () => {
943
+ generatedAnswerState = {...generatedAnswerState, isStreaming: true};
944
+ const element = createTestComponent(followUpOptions);
945
+ await flushPromises();
946
+
947
+ const followUpInput = element.shadowRoot.querySelector(
948
+ selectors.generatedAnswerFollowUpInput
949
+ );
950
+
951
+ expect(followUpInput.submitButtonDisabled).toBe(true);
952
+ });
953
+
954
+ it('should ignore the collapsible feature and not render the single answer collapse toggle', async () => {
955
+ mockAnswerHeight = defaultAnswerHeight + 100;
956
+ const element = createTestComponent({
957
+ ...followUpOptions,
958
+ collapsible: true,
959
+ });
960
+ await flushPromises();
814
961
 
815
962
  const generatedAnswerCollapseToggle =
816
963
  element.shadowRoot.querySelector(
@@ -822,20 +969,19 @@ describe('c-quantic-generated-answer', () => {
822
969
  });
823
970
 
824
971
  describe('when follow-ups are not enabled', () => {
825
- it('should not render the content section with the scrollable class', async () => {
972
+ it('should render the single answer body instead of the conversation thread', async () => {
826
973
  const element = createTestComponent();
827
974
  await flushPromises();
828
975
 
829
- const generatedAnswerBody = element.shadowRoot.querySelector(
976
+ const singleAnswerBody = element.shadowRoot.querySelector(
830
977
  selectors.generatedAnswerBody
831
978
  );
979
+ const thread = element.shadowRoot.querySelector(
980
+ selectors.generatedAnswerThread
981
+ );
832
982
 
833
- expect(generatedAnswerBody).not.toBeNull();
834
- expect(
835
- generatedAnswerBody.classList.contains(
836
- 'generated-answer__content--scrollable'
837
- )
838
- ).toBe(false);
983
+ expect(singleAnswerBody).not.toBeNull();
984
+ expect(thread).toBeNull();
839
985
  });
840
986
  });
841
987
  });
@@ -1238,4 +1384,107 @@ describe('c-quantic-generated-answer', () => {
1238
1384
  });
1239
1385
  });
1240
1386
  });
1387
+
1388
+ describe('the generated answer feedback', () => {
1389
+ const exampleAnswer = 'answer generated successfully';
1390
+ const exampleAgentId = 'example agent id';
1391
+
1392
+ describe.each([
1393
+ {
1394
+ action: 'like',
1395
+ feedbackEvent: 'quantic__like',
1396
+ threadEvent: 'quantic__generatedanswerlike',
1397
+ controllerMethod: 'like',
1398
+ },
1399
+ {
1400
+ action: 'dislike',
1401
+ feedbackEvent: 'quantic__dislike',
1402
+ threadEvent: 'quantic__generatedanswerdislike',
1403
+ controllerMethod: 'dislike',
1404
+ },
1405
+ ])(
1406
+ 'when the user clicks the $action button',
1407
+ ({feedbackEvent, threadEvent, controllerMethod}) => {
1408
+ describe('when follow-ups are not enabled', () => {
1409
+ beforeEach(() => {
1410
+ generatedAnswerState = {
1411
+ ...initialGeneratedAnswerState,
1412
+ isStreaming: false,
1413
+ answer: exampleAnswer,
1414
+ answerContentFormat: 'text/markdown',
1415
+ answerId: exampleAnswerId,
1416
+ citations: exampleCitations,
1417
+ };
1418
+ mockSuccessfulHeadlessInitialization();
1419
+ prepareHeadlessState();
1420
+ });
1421
+
1422
+ afterAll(() => {
1423
+ generatedAnswerState = initialGeneratedAnswerState;
1424
+ });
1425
+
1426
+ it('should call the controller and open the feedback modal', async () => {
1427
+ const element = createTestComponent();
1428
+ await flushPromises();
1429
+
1430
+ const feedback = element.shadowRoot.querySelector(
1431
+ selectors.generatedAnswerFeedback
1432
+ );
1433
+ feedback.dispatchEvent(
1434
+ new CustomEvent(feedbackEvent, {
1435
+ detail: {answerId: exampleAnswerId},
1436
+ })
1437
+ );
1438
+ await flushPromises();
1439
+
1440
+ expect(functionsMocks[controllerMethod]).toHaveBeenCalledTimes(1);
1441
+ expect(functionsMocks[controllerMethod]).toHaveBeenCalledWith(
1442
+ exampleAnswerId
1443
+ );
1444
+ expect(FeedbackModalQna.open).toHaveBeenCalledTimes(1);
1445
+ });
1446
+ });
1447
+
1448
+ describe('when follow-ups are enabled', () => {
1449
+ beforeEach(() => {
1450
+ generatedAnswerState = {
1451
+ ...initialGeneratedAnswerState,
1452
+ answer: exampleAnswer,
1453
+ followUpAnswers: {isEnabled: true, followUpAnswers: []},
1454
+ };
1455
+ mockSuccessfulHeadlessInitialization();
1456
+ prepareHeadlessState();
1457
+ });
1458
+
1459
+ afterAll(() => {
1460
+ generatedAnswerState = initialGeneratedAnswerState;
1461
+ });
1462
+
1463
+ it('should call the controller without opening the feedback modal', async () => {
1464
+ const element = createTestComponent({
1465
+ ...defaultOptions,
1466
+ agentId: exampleAgentId,
1467
+ });
1468
+ await flushPromises();
1469
+
1470
+ const thread = element.shadowRoot.querySelector(
1471
+ selectors.generatedAnswerThread
1472
+ );
1473
+ thread.dispatchEvent(
1474
+ new CustomEvent(threadEvent, {
1475
+ detail: {answerId: exampleAnswerId},
1476
+ })
1477
+ );
1478
+ await flushPromises();
1479
+
1480
+ expect(functionsMocks[controllerMethod]).toHaveBeenCalledTimes(1);
1481
+ expect(functionsMocks[controllerMethod]).toHaveBeenCalledWith(
1482
+ exampleAnswerId
1483
+ );
1484
+ expect(FeedbackModalQna.open).not.toHaveBeenCalled();
1485
+ });
1486
+ });
1487
+ }
1488
+ );
1489
+ });
1241
1490
  });