@coveo/quantic 3.9.1-pre.bbf2cab18e → 3.10.0-pre.c5a50cf39c
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.
- package/force-app/main/default/lwc/jsconfig.json +3 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/__tests__/quanticGeneratedAnswer.test.js +115 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/quanticGeneratedAnswer.js +10 -6
- package/force-app/main/default/lwc/quanticGeneratedAnswerContent/__tests__/quanticGeneratedAnswerContent.test.js +26 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswerContent/quanticGeneratedAnswerContent.js +25 -13
- package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +4 -4
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/facets/facet-api/request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/facets/facet-set/interfaces/request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/utils/facet-utils.d.ts +5 -0
- package/force-app/main/default/staticresources/coveoheadless/headless.js +6 -6
- package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +5 -5
- package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +4 -4
- package/package.json +2 -2
package/force-app/main/default/lwc/quanticGeneratedAnswer/__tests__/quanticGeneratedAnswer.test.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* eslint-disable no-import-assign */
|
|
2
|
+
// @ts-ignore
|
|
3
|
+
import {createElement} from 'lwc';
|
|
4
|
+
import QuanticGeneratedAnswer from 'c/quanticGeneratedAnswer';
|
|
5
|
+
import * as mockHeadlessLoader from 'c/quanticHeadlessLoader';
|
|
6
|
+
|
|
7
|
+
jest.mock('c/quanticHeadlessLoader');
|
|
8
|
+
|
|
9
|
+
function createTestComponent(options = {}) {
|
|
10
|
+
prepareHeadlessState();
|
|
11
|
+
|
|
12
|
+
const element = createElement('c-quantic-generated-answer', {
|
|
13
|
+
is: QuanticGeneratedAnswer,
|
|
14
|
+
});
|
|
15
|
+
for (const [key, value] of Object.entries(options)) {
|
|
16
|
+
element[key] = value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
document.body.appendChild(element);
|
|
20
|
+
return element;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const functionsMocks = {
|
|
24
|
+
buildGeneratedAnswer: jest.fn(() => ({
|
|
25
|
+
subscribe: jest.fn((callback) => callback()),
|
|
26
|
+
state: {},
|
|
27
|
+
})),
|
|
28
|
+
buildSearchStatus: jest.fn(() => ({
|
|
29
|
+
subscribe: jest.fn((callback) => callback()),
|
|
30
|
+
state: {},
|
|
31
|
+
})),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function prepareHeadlessState() {
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
mockHeadlessLoader.getHeadlessBundle = () => {
|
|
37
|
+
return {
|
|
38
|
+
buildGeneratedAnswer: functionsMocks.buildGeneratedAnswer,
|
|
39
|
+
buildSearchStatus: functionsMocks.buildSearchStatus,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Helper function to wait until the microtask queue is empty.
|
|
45
|
+
function flushPromises() {
|
|
46
|
+
// eslint-disable-next-line @lwc/lwc/no-async-operation
|
|
47
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const exampleEngine = {
|
|
51
|
+
id: 'dummy engine',
|
|
52
|
+
};
|
|
53
|
+
let isInitialized = false;
|
|
54
|
+
|
|
55
|
+
function mockSuccessfulHeadlessInitialization() {
|
|
56
|
+
// @ts-ignore
|
|
57
|
+
mockHeadlessLoader.initializeWithHeadless = (element, _, initialize) => {
|
|
58
|
+
if (element instanceof QuanticGeneratedAnswer && !isInitialized) {
|
|
59
|
+
isInitialized = true;
|
|
60
|
+
initialize(exampleEngine);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function cleanup() {
|
|
66
|
+
// The jsdom instance is shared across test cases in a single file so reset the DOM
|
|
67
|
+
while (document.body.firstChild) {
|
|
68
|
+
document.body.removeChild(document.body.firstChild);
|
|
69
|
+
}
|
|
70
|
+
jest.clearAllMocks();
|
|
71
|
+
isInitialized = false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe('c-quantic-generated-answer', () => {
|
|
75
|
+
beforeAll(() => {
|
|
76
|
+
mockSuccessfulHeadlessInitialization();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
afterEach(() => {
|
|
80
|
+
cleanup();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('controller initialization', () => {
|
|
84
|
+
describe('when the answer configuration id property is passed to the component', () => {
|
|
85
|
+
it('should initialize the controller with the correct answer configuration id value', async () => {
|
|
86
|
+
const exampleAnswerConfigValue = 'exampleAnswerConfig';
|
|
87
|
+
createTestComponent({answerConfigurationId: exampleAnswerConfigValue});
|
|
88
|
+
await flushPromises();
|
|
89
|
+
|
|
90
|
+
expect(functionsMocks.buildGeneratedAnswer).toHaveBeenCalledTimes(1);
|
|
91
|
+
expect(functionsMocks.buildGeneratedAnswer).toHaveBeenCalledWith(
|
|
92
|
+
exampleEngine,
|
|
93
|
+
expect.objectContaining({
|
|
94
|
+
answerConfigurationId: exampleAnswerConfigValue,
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('when the answer configuration id property is not passed to the component', () => {
|
|
101
|
+
it('should initialize the controller without the answer configuration id value', async () => {
|
|
102
|
+
createTestComponent();
|
|
103
|
+
await flushPromises();
|
|
104
|
+
|
|
105
|
+
expect(functionsMocks.buildGeneratedAnswer).toHaveBeenCalledTimes(1);
|
|
106
|
+
expect(functionsMocks.buildGeneratedAnswer).toHaveBeenCalledWith(
|
|
107
|
+
exampleEngine,
|
|
108
|
+
expect.not.objectContaining({
|
|
109
|
+
answerConfigurationId: expect.any(String),
|
|
110
|
+
})
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -80,6 +80,12 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
80
80
|
* @default {false}
|
|
81
81
|
*/
|
|
82
82
|
@api withToggle = false;
|
|
83
|
+
/**
|
|
84
|
+
* @internal
|
|
85
|
+
* The unique identifier of the answer configuration to use to generate the answer.
|
|
86
|
+
* @type {string}
|
|
87
|
+
*/
|
|
88
|
+
@api answerConfigurationId;
|
|
83
89
|
|
|
84
90
|
labels = {
|
|
85
91
|
generatedAnswerForYou,
|
|
@@ -187,6 +193,9 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
187
193
|
contentFormat: ['text/markdown', 'text/plain'],
|
|
188
194
|
},
|
|
189
195
|
},
|
|
196
|
+
...(this.answerConfigurationId && {
|
|
197
|
+
answerConfigurationId: this.answerConfigurationId,
|
|
198
|
+
}),
|
|
190
199
|
fieldsToIncludeInCitations: this.citationFields,
|
|
191
200
|
});
|
|
192
201
|
}
|
|
@@ -421,12 +430,7 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
421
430
|
|
|
422
431
|
get shouldDisplayGeneratedAnswer() {
|
|
423
432
|
const hasCitations = !!this.citations?.length;
|
|
424
|
-
return
|
|
425
|
-
!!this.answer ||
|
|
426
|
-
this.isStreaming ||
|
|
427
|
-
hasCitations ||
|
|
428
|
-
this.hasRetryableError
|
|
429
|
-
);
|
|
433
|
+
return !!this.answer || hasCitations || this.hasRetryableError;
|
|
430
434
|
}
|
|
431
435
|
|
|
432
436
|
get generatedAnswerClass() {
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
// @ts-ignore
|
|
2
2
|
import QuanticGeneratedAnswerContent from 'c/quanticGeneratedAnswerContent';
|
|
3
3
|
import {createElement} from 'lwc';
|
|
4
|
+
import {loadMarkdownDependencies} from 'c/quanticUtils';
|
|
5
|
+
|
|
6
|
+
jest.mock('c/quanticUtils', () => ({
|
|
7
|
+
loadMarkdownDependencies: jest.fn(
|
|
8
|
+
() =>
|
|
9
|
+
new Promise((resolve) => {
|
|
10
|
+
resolve();
|
|
11
|
+
})
|
|
12
|
+
),
|
|
13
|
+
transformMarkdownToHtml: jest.fn((value) => value),
|
|
14
|
+
}));
|
|
4
15
|
|
|
5
16
|
const mockMarkedUse = jest.fn();
|
|
6
17
|
const mockMarkedParse = jest.fn((text) => text);
|
|
@@ -122,4 +133,19 @@ describe('c-quantic-generated-answer-content', () => {
|
|
|
122
133
|
);
|
|
123
134
|
});
|
|
124
135
|
});
|
|
136
|
+
|
|
137
|
+
describe('the reactivity of the answerContentFormat property', () => {
|
|
138
|
+
it('should load the markdown dependencies when the answerContentFormat property is changed to text/markdown', async () => {
|
|
139
|
+
const element = createTestComponent({
|
|
140
|
+
...defaultOptions,
|
|
141
|
+
answerContentFormat: 'text/plain',
|
|
142
|
+
});
|
|
143
|
+
await flushPromises();
|
|
144
|
+
expect(loadMarkdownDependencies).not.toHaveBeenCalled();
|
|
145
|
+
|
|
146
|
+
element.answerContentFormat = 'text/markdown';
|
|
147
|
+
await flushPromises();
|
|
148
|
+
expect(loadMarkdownDependencies).toHaveBeenCalled();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
125
151
|
});
|
package/force-app/main/default/lwc/quanticGeneratedAnswerContent/quanticGeneratedAnswerContent.js
CHANGED
|
@@ -28,7 +28,19 @@ export default class QuanticGeneratedAnswerContent extends LightningElement {
|
|
|
28
28
|
* @type {'text/plain' | 'text/markdown'}
|
|
29
29
|
* @default {'text/plain'}
|
|
30
30
|
*/
|
|
31
|
-
@api
|
|
31
|
+
@api
|
|
32
|
+
get answerContentFormat() {
|
|
33
|
+
return this._answerContentFormat;
|
|
34
|
+
}
|
|
35
|
+
set answerContentFormat(value) {
|
|
36
|
+
this._answerContentFormat = value;
|
|
37
|
+
if (
|
|
38
|
+
this._answerContentFormat === 'text/markdown' &&
|
|
39
|
+
!this._markdownDependenciesLoaded
|
|
40
|
+
) {
|
|
41
|
+
this.loadMarkdownDependencies();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
32
44
|
/**
|
|
33
45
|
* The answer content to display.
|
|
34
46
|
* @api
|
|
@@ -42,30 +54,29 @@ export default class QuanticGeneratedAnswerContent extends LightningElement {
|
|
|
42
54
|
this._answer = value;
|
|
43
55
|
if (
|
|
44
56
|
this.answerContentFormat === 'text/markdown' &&
|
|
45
|
-
this.
|
|
46
|
-
this._domPurifyLoaded
|
|
57
|
+
this._markdownDependenciesLoaded
|
|
47
58
|
) {
|
|
48
59
|
this.updateHtmlContent();
|
|
49
60
|
}
|
|
50
61
|
}
|
|
51
62
|
|
|
52
63
|
_answer;
|
|
53
|
-
|
|
54
|
-
|
|
64
|
+
/** @type {'text/plain' | 'text/markdown'} */
|
|
65
|
+
_answerContentFormat = 'text/plain';
|
|
66
|
+
_markdownDependenciesLoaded = false;
|
|
55
67
|
|
|
56
|
-
|
|
57
|
-
if (
|
|
68
|
+
loadMarkdownDependencies() {
|
|
69
|
+
if (
|
|
70
|
+
!this._markdownDependenciesLoaded &&
|
|
71
|
+
this.answerContentFormat === 'text/markdown'
|
|
72
|
+
) {
|
|
58
73
|
loadMarkdownDependencies(this)
|
|
59
74
|
.then(() => {
|
|
60
|
-
|
|
61
|
-
this._markedLoaded = true;
|
|
62
|
-
// @ts-ignore
|
|
63
|
-
this._domPurifyLoaded = true;
|
|
75
|
+
this._markdownDependenciesLoaded = true;
|
|
64
76
|
this.updateHtmlContent();
|
|
65
77
|
})
|
|
66
78
|
.catch((error) => {
|
|
67
79
|
console.error('Error loading the Marked library.', error);
|
|
68
|
-
this._markedLoadingError = true;
|
|
69
80
|
});
|
|
70
81
|
}
|
|
71
82
|
}
|
|
@@ -74,11 +85,12 @@ export default class QuanticGeneratedAnswerContent extends LightningElement {
|
|
|
74
85
|
const answerContainer = this.template.querySelector(
|
|
75
86
|
'.generated-answer-content__answer'
|
|
76
87
|
);
|
|
77
|
-
if (this.
|
|
88
|
+
if (this._markdownDependenciesLoaded) {
|
|
78
89
|
// Transform the markdown answer to HTML and update the innerHTML of the container
|
|
79
90
|
const newHTMLContent =
|
|
80
91
|
// @ts-ignore
|
|
81
92
|
(window.marked &&
|
|
93
|
+
this.answer &&
|
|
82
94
|
// @ts-ignore
|
|
83
95
|
transformMarkdownToHtml(this.answer, window.marked)) ||
|
|
84
96
|
'';
|