@coveo/quantic 3.12.1 → 3.13.1

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 (31) hide show
  1. package/force-app/main/default/lwc/quanticFeedback/__tests__/quanticFeedback.test.js +2 -2
  2. package/force-app/main/default/lwc/quanticFeedback/quanticFeedback.html +3 -3
  3. package/force-app/main/default/lwc/quanticFeedbackModal/error.html +1 -1
  4. package/force-app/main/default/lwc/quanticFeedbackModal/feedbackForm.html +2 -2
  5. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/data.ts +1 -1
  6. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/pageObject.ts +4 -0
  7. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/quanticGeneratedAnswer.e2e.ts +22 -1
  8. package/force-app/main/default/lwc/quanticGeneratedAnswer/quanticGeneratedAnswer.js +19 -7
  9. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/generatedAnswer.html +1 -0
  10. package/force-app/main/default/lwc/quanticGeneratedAnswerContent/quanticGeneratedAnswerContent.js +2 -0
  11. package/force-app/main/default/lwc/quanticSmartSnippet/__tests__/quanticSmartSnippet.test.js +357 -0
  12. package/force-app/main/default/lwc/quanticSmartSnippet/e2e/data.ts +30 -0
  13. package/force-app/main/default/lwc/quanticSmartSnippet/e2e/fixture.ts +87 -0
  14. package/force-app/main/default/lwc/quanticSmartSnippet/e2e/pageObject.ts +188 -0
  15. package/force-app/main/default/lwc/quanticSmartSnippet/e2e/quanticSmartSnippet.e2e.ts +118 -0
  16. package/force-app/main/default/lwc/quanticSmartSnippet/quanticSmartSnippet.html +1 -1
  17. package/force-app/main/default/lwc/quanticSmartSnippet/quanticSmartSnippet.js +2 -2
  18. package/force-app/main/default/lwc/quanticSmartSnippetSource/quanticSmartSnippetSource.html +2 -2
  19. package/force-app/main/default/lwc/quanticTabBar/e2e/quanticTabBar.e2e.ts +1 -1
  20. package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +7 -8
  21. package/force-app/main/default/staticresources/coveoheadless/definitions/api/service/insight/get-interface/get-interface-config-response.d.ts +1 -0
  22. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/product-list/headless-product-list.ssr.d.ts +2 -1
  23. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/search-box/headless-search-box.d.ts +1 -1
  24. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/search-box/headless-search-box.ssr.d.ts +1 -1
  25. package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/common/actions.d.ts +1 -0
  26. package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-commerce.index.d.ts +4 -3
  27. package/force-app/main/default/staticresources/coveoheadless/definitions/tsdoc-metadata.json +1 -1
  28. package/force-app/main/default/staticresources/coveoheadless/headless.js +9 -10
  29. package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +8 -9
  30. package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +7 -8
  31. package/package.json +5 -5
@@ -0,0 +1,188 @@
1
+ import type {Locator, Page, Request} from '@playwright/test';
2
+ import {
3
+ isUaClickEvent,
4
+ isUaCustomEvent,
5
+ } from '../../../../../../playwright/utils/requests';
6
+
7
+ export class SmartSnippetObject {
8
+ constructor(public page: Page) {
9
+ this.page = page;
10
+ }
11
+
12
+ get smartSnippet(): Locator {
13
+ return this.page.locator('c-quantic-smart-snippet');
14
+ }
15
+
16
+ get smartSnippetSourceTitle(): Locator {
17
+ return this.page.getByTestId('smart-snippet__source-title');
18
+ }
19
+
20
+ get smartSnippetSourceUri(): Locator {
21
+ return this.page.getByTestId('smart-snippet__source-uri');
22
+ }
23
+
24
+ get smartSnippetAnwerInlineLink(): Locator {
25
+ return this.page.locator('.smart-snippet-answer a');
26
+ }
27
+
28
+ get smartSnippetToggleButton(): Locator {
29
+ return this.page.getByTestId('smart-snippet__toggle-button');
30
+ }
31
+
32
+ get likeButton(): Locator {
33
+ return this.page.getByTestId('feedback__like-button');
34
+ }
35
+
36
+ get dislikeButton(): Locator {
37
+ return this.page.getByTestId('feedback__dislike-button');
38
+ }
39
+
40
+ get explainWhyButton(): Locator {
41
+ return this.page.getByTestId('feedback__explain-why-button');
42
+ }
43
+
44
+ get feedbackModalCancelButton(): Locator {
45
+ return this.page.getByTestId('feedback-modal-footer__cancel');
46
+ }
47
+
48
+ get firstFeedbackOptionLabel(): Locator {
49
+ const labelFirstOption = "Didn't answer my question at all";
50
+ return this.page.getByText(labelFirstOption);
51
+ }
52
+
53
+ get feedbackSubmitButton(): Locator {
54
+ return this.page.getByTestId('feedback-modal-footer__submit');
55
+ }
56
+
57
+ async clickToggleButton(): Promise<void> {
58
+ await this.smartSnippetToggleButton.click();
59
+ }
60
+
61
+ async clickOnSourceTitle(): Promise<void> {
62
+ await this.smartSnippetSourceTitle.click();
63
+ }
64
+
65
+ async clickOnSourceUri(): Promise<void> {
66
+ await this.smartSnippetSourceUri.click();
67
+ }
68
+
69
+ async clickOnFirstInlineLink(): Promise<void> {
70
+ await this.smartSnippetAnwerInlineLink.first().click();
71
+ }
72
+
73
+ async clickLikeButton(): Promise<void> {
74
+ await this.likeButton.click();
75
+ }
76
+
77
+ async clickDislikeButton(): Promise<void> {
78
+ await this.dislikeButton.click();
79
+ }
80
+
81
+ async clickExplainWhyButton(): Promise<void> {
82
+ await this.explainWhyButton.click();
83
+ }
84
+
85
+ async clickFeedbackModalCancelButton(): Promise<void> {
86
+ await this.feedbackModalCancelButton.click();
87
+ }
88
+
89
+ async selectFirstFeedbackOptionLabel(): Promise<void> {
90
+ await this.firstFeedbackOptionLabel.click({force: true});
91
+ }
92
+
93
+ async clickFeedbackSubmitButton(): Promise<void> {
94
+ await this.feedbackSubmitButton.click();
95
+ }
96
+
97
+ async waitForExpandSmartSnippetUaAnalytics(): Promise<Request> {
98
+ return this.waitForSmartSnippetCustomUaAnalytics('expandSmartSnippet');
99
+ }
100
+
101
+ async waitForCollapseSmartSnippetUaAnalytics(): Promise<Request> {
102
+ return this.waitForSmartSnippetCustomUaAnalytics('collapseSmartSnippet');
103
+ }
104
+
105
+ async waitForLikeSmartSnippetUaAnalytics(): Promise<Request> {
106
+ return this.waitForSmartSnippetCustomUaAnalytics('likeSmartSnippet');
107
+ }
108
+
109
+ async waitForDislikeSmartSnippetUaAnalytics(): Promise<Request> {
110
+ return this.waitForSmartSnippetCustomUaAnalytics('dislikeSmartSnippet');
111
+ }
112
+
113
+ async waitForOpenFeedbackModalUaAnalytics(): Promise<Request> {
114
+ return this.waitForSmartSnippetCustomUaAnalytics(
115
+ 'openSmartSnippetFeedbackModal'
116
+ );
117
+ }
118
+
119
+ async waitForCloseFeedbackModalUaAnalytics(): Promise<Request> {
120
+ return this.waitForSmartSnippetCustomUaAnalytics(
121
+ 'closeSmartSnippetFeedbackModal'
122
+ );
123
+ }
124
+
125
+ async waitForFeedbackSubmitUaAnalytics(
126
+ expectedReason: string
127
+ ): Promise<Request> {
128
+ return this.waitForSmartSnippetCustomUaAnalytics(
129
+ 'sendSmartSnippetReason',
130
+ expectedReason
131
+ );
132
+ }
133
+
134
+ async waitForSmartSnippetSourceClickUaAnalytics(): Promise<Request> {
135
+ return this.waitForSmartSnippetClickUaAnalytics('openSmartSnippetSource');
136
+ }
137
+
138
+ async waitForSmartSnippetInlineLinkClickUaAnalytics(): Promise<Request> {
139
+ return this.waitForSmartSnippetClickUaAnalytics(
140
+ 'openSmartSnippetInlineLink'
141
+ );
142
+ }
143
+
144
+ async waitForSmartSnippetClickUaAnalytics(
145
+ actionCause: string
146
+ ): Promise<Request> {
147
+ const uaRequest = this.page.waitForRequest((request) => {
148
+ if (isUaClickEvent(request)) {
149
+ const requestBody = request.postDataJSON?.();
150
+ const requestData = JSON.parse(requestBody.clickEvent);
151
+
152
+ const expectedFields: Record<string, any> = {
153
+ actionCause,
154
+ };
155
+
156
+ return requestData?.actionCause === expectedFields.actionCause;
157
+ }
158
+ return false;
159
+ });
160
+ return uaRequest;
161
+ }
162
+
163
+ async waitForSmartSnippetCustomUaAnalytics(
164
+ eventValue: any,
165
+ reason?: string
166
+ ): Promise<Request> {
167
+ const uaRequest = this.page.waitForRequest((request) => {
168
+ if (isUaCustomEvent(request)) {
169
+ const requestBody = request.postDataJSON?.();
170
+ const expectedFields: Record<string, any> = {
171
+ eventType: 'smartSnippet',
172
+ eventValue: eventValue,
173
+ };
174
+
175
+ const matchesExpectedFields = Object.keys(expectedFields).every(
176
+ (key) => requestBody?.[key] === expectedFields[key]
177
+ );
178
+
179
+ const customData = requestBody?.customData;
180
+ const matchesReason = customData?.reason === reason;
181
+
182
+ return matchesExpectedFields && matchesReason;
183
+ }
184
+ return false;
185
+ });
186
+ return uaRequest;
187
+ }
188
+ }
@@ -0,0 +1,118 @@
1
+ import {testSearch, testInsight} from './fixture';
2
+ import {useCaseTestCases} from '../../../../../../playwright/utils/useCase';
3
+
4
+ const maximumSnippetHeight = 250;
5
+
6
+ const fixtures = {
7
+ search: testSearch,
8
+ insight: testInsight,
9
+ };
10
+
11
+ useCaseTestCases.forEach((useCase) => {
12
+ let test = fixtures[useCase.value];
13
+
14
+ test.describe(`quantic smart snippet ${useCase.label}`, () => {
15
+ test.use({
16
+ options: {maximumSnippetHeight},
17
+ });
18
+
19
+ test.describe('when the smart snippet is expanded', () => {
20
+ test('should send the source title analytics event when the title is clicked', async ({
21
+ smartSnippet,
22
+ }) => {
23
+ const smartSnippetTitleClickPromise =
24
+ smartSnippet.waitForSmartSnippetSourceClickUaAnalytics();
25
+ await smartSnippet.clickOnSourceTitle();
26
+ await smartSnippetTitleClickPromise;
27
+ });
28
+
29
+ test('should send the source uri analytics event when the source uri is clicked', async ({
30
+ smartSnippet,
31
+ }) => {
32
+ const smartSnippetUriClickPromise =
33
+ smartSnippet.waitForSmartSnippetSourceClickUaAnalytics();
34
+ await smartSnippet.clickOnSourceUri();
35
+ await smartSnippetUriClickPromise;
36
+ });
37
+
38
+ test('should send the inline link analytics event when the inline link is clicked', async ({
39
+ smartSnippet,
40
+ }) => {
41
+ const smartSnippetUriClickPromise =
42
+ smartSnippet.waitForSmartSnippetInlineLinkClickUaAnalytics();
43
+ await smartSnippet.clickOnFirstInlineLink();
44
+ await smartSnippetUriClickPromise;
45
+ });
46
+
47
+ test('should send the expand and collapse analytics events', async ({
48
+ smartSnippet,
49
+ }) => {
50
+ const expandSmartSnippetAnalyticsPromise =
51
+ smartSnippet.waitForExpandSmartSnippetUaAnalytics();
52
+ await smartSnippet.clickToggleButton();
53
+ await expandSmartSnippetAnalyticsPromise;
54
+
55
+ const collapseSmartSnippetAnalyticsPromise =
56
+ smartSnippet.waitForCollapseSmartSnippetUaAnalytics();
57
+ await smartSnippet.clickToggleButton();
58
+ await collapseSmartSnippetAnalyticsPromise;
59
+ });
60
+ });
61
+
62
+ test.describe('feedback modal interactions', () => {
63
+ test.describe('when clicking on the feedback like button', () => {
64
+ test('should send the like analytics event', async ({smartSnippet}) => {
65
+ const likeAnalyticsPromise =
66
+ smartSnippet.waitForLikeSmartSnippetUaAnalytics();
67
+ await smartSnippet.clickLikeButton();
68
+ await likeAnalyticsPromise;
69
+ });
70
+ });
71
+
72
+ test.describe('when clicking on the feedback dislike button', () => {
73
+ test('should send the dislike analytics event', async ({
74
+ smartSnippet,
75
+ }) => {
76
+ const dislikeAnalyticsPromise =
77
+ smartSnippet.waitForDislikeSmartSnippetUaAnalytics();
78
+ await smartSnippet.clickDislikeButton();
79
+ await dislikeAnalyticsPromise;
80
+ });
81
+
82
+ test.describe('when opening the feedback modal and providing feedback', () => {
83
+ test('should send the smart snippet reason analytics events', async ({
84
+ smartSnippet,
85
+ }) => {
86
+ const expectedReason = 'does_not_answer';
87
+
88
+ const openFeedbackModalAnalyticsPromise =
89
+ smartSnippet.waitForOpenFeedbackModalUaAnalytics();
90
+ await smartSnippet.clickDislikeButton();
91
+ await smartSnippet.clickExplainWhyButton();
92
+ await openFeedbackModalAnalyticsPromise;
93
+
94
+ const submitFeedbackAnalyticsPromise =
95
+ smartSnippet.waitForFeedbackSubmitUaAnalytics(expectedReason);
96
+
97
+ await smartSnippet.selectFirstFeedbackOptionLabel();
98
+ await smartSnippet.clickFeedbackSubmitButton();
99
+ await submitFeedbackAnalyticsPromise;
100
+ });
101
+ });
102
+
103
+ test.describe('when closing the feedback modal', () => {
104
+ test('should send the correct close feedback modal analytics event', async ({
105
+ smartSnippet,
106
+ }) => {
107
+ await smartSnippet.clickDislikeButton();
108
+ await smartSnippet.clickExplainWhyButton();
109
+ const closeFeedbackModalAnalyticsPromise =
110
+ smartSnippet.waitForCloseFeedbackModalUaAnalytics();
111
+ await smartSnippet.clickFeedbackModalCancelButton();
112
+ await closeFeedbackModalAnalyticsPromise;
113
+ });
114
+ });
115
+ });
116
+ });
117
+ });
118
+ });
@@ -16,7 +16,7 @@
16
16
  </div>
17
17
  <template if:true={snippetHeightExceedsMaxHeight}>
18
18
  <div class="slds-align_absolute-center slds-var-m-bottom_small">
19
- <lightning-button data-cy="smart-snippet-answer-toggle" icon-name={toggleSmartSnippetAnswerIcon}
19
+ <lightning-button data-testid="smart-snippet__toggle-button" icon-name={toggleSmartSnippetAnswerIcon}
20
20
  variant="base" label={toggleSmartSnippetAnswerLabel} title={toggleSmartSnippetAnswerLabel}
21
21
  onclick={handleToggleSmartSnippetAnswer} icon-position="right"></lightning-button>
22
22
  </div>
@@ -152,8 +152,8 @@ export default class QuanticSmartSnippet extends LightningElement {
152
152
  */
153
153
  updateSmartSnippetCSSVariables() {
154
154
  const styles = this.smartSnippetAnswerElement?.style;
155
- styles.setProperty('--maxHeight', `${this.maximumSnippetHeight}px`);
156
- styles.setProperty(
155
+ styles?.setProperty('--maxHeight', `${this.maximumSnippetHeight}px`);
156
+ styles?.setProperty(
157
157
  '--fullHeight',
158
158
  `${this.smartSnippetAnswerElementHeight}px`
159
159
  );
@@ -2,13 +2,13 @@
2
2
  <div class="smart-snippet__source">
3
3
  <template lwc:if={sourceUri}>
4
4
  <div class="slds-truncate">
5
- <a href={hrefValue} aria-label={ariaLabelValue} onclick={handleClick} data-cy="smart-snippet__source-uri"
5
+ <a href={hrefValue} aria-label={ariaLabelValue} onclick={handleClick} data-testid="smart-snippet__source-uri"
6
6
  class="slds-text-title_bold smart-snippet__source-uri" title={sourceUri} target="_blank">{sourceUri}</a>
7
7
  </div>
8
8
  </template>
9
9
  <template lwc:if={sourceTitle}>
10
10
  <div class="slds-var-m-top_xx-small">
11
- <a href={hrefValue} aria-label={ariaLabelValue} onclick={handleClick} data-cy="smart-snippet__source-title"
11
+ <a href={hrefValue} aria-label={ariaLabelValue} onclick={handleClick} data-testid="smart-snippet__source-title"
12
12
  class="smart-snippet__source-title slds-text-heading_small" title={sourceTitle}
13
13
  target="_blank">{sourceTitle}</a>
14
14
  </div>
@@ -48,7 +48,7 @@ useCaseTestCases.forEach((useCase) => {
48
48
  }) => {
49
49
  const tabBarElement = tabBar.tabBar;
50
50
  await tabBarElement.scrollIntoViewIfNeeded();
51
- const tabs = tabBar.allVisibleTabs;
51
+ const tabs = tabBar.allTabs;
52
52
  expect(tabs).not.toBeNull();
53
53
  expect(await tabs.count()).toEqual(expectedNumberOfTabs);
54
54