coveo.analytics 2.18.52 → 2.18.53

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.
@@ -0,0 +1,262 @@
1
+ import {CaseAssistClient} from './caseAssistClient';
2
+ import {
3
+ CaseAssistActions,
4
+ CaseAssistEvents,
5
+ CaseCancelledReasons,
6
+ DocumentSuggestion,
7
+ FieldSuggestion,
8
+ } from './caseAssistActions';
9
+
10
+ import {mockFetch} from '../../tests/fetchMock';
11
+ import {TicketProperties} from '../plugins/svc';
12
+
13
+ const {fetchMock, fetchMockBeforeEach} = mockFetch();
14
+
15
+ describe('CaseAssistClient', () => {
16
+ let client: CaseAssistClient;
17
+
18
+ beforeEach(() => {
19
+ fetchMockBeforeEach();
20
+
21
+ client = initClient();
22
+ fetchMock.mock(/.*/, {
23
+ visitorId: 'visitor-id',
24
+ });
25
+ });
26
+
27
+ afterEach(() => {
28
+ fetchMock.reset();
29
+ });
30
+
31
+ const initClient = () => {
32
+ return new CaseAssistClient({
33
+ enableAnalytics: true,
34
+ });
35
+ };
36
+
37
+ const noTicket: Record<string, unknown> = undefined;
38
+ const noActionData: Record<string, unknown> = undefined;
39
+ const emptyTicket: TicketProperties = {};
40
+
41
+ const fakeTicket: TicketProperties = {
42
+ id: 'the-ticket-id',
43
+ subject: 'the ticket subject',
44
+ description: 'the ticket description',
45
+ category: 'ticket-category',
46
+ productId: 'some-product-id',
47
+ custom: {
48
+ customA: 'custom-a-value',
49
+ customB: 'custom-b-value',
50
+ },
51
+ };
52
+
53
+ const fakeFieldSuggestion: FieldSuggestion = {
54
+ classification: {
55
+ value: 'some-field-value',
56
+ confidence: 0.5,
57
+ },
58
+ classificationId: 'field-suggestion-id',
59
+ fieldName: 'some-field',
60
+ responseId: 'field-suggestion-response-id',
61
+ };
62
+
63
+ const fakeDocumentSuggestion: DocumentSuggestion = {
64
+ responseId: 'document-suggestion-response-id',
65
+ suggestionId: 'document-suggestion-id',
66
+ suggestion: {
67
+ documentPosition: 0,
68
+ documentTitle: 'the document title',
69
+ documentUri: 'some-document-uri',
70
+ documentUriHash: 'documenturihash',
71
+ documentUrl: 'some-document-url',
72
+ },
73
+ };
74
+
75
+ const expectMatchPayload = (actionName: CaseAssistActions, actionData: Object, ticket: TicketProperties) => {
76
+ const [, {body}] = fetchMock.lastCall();
77
+ const content = JSON.parse(body.toString());
78
+
79
+ expectMatchActionPayload(content, actionName, actionData);
80
+ expectMatchTicketPayload(content, ticket);
81
+ };
82
+
83
+ const expectMatchActionPayload = (
84
+ content: Record<string, unknown>,
85
+ actionName: CaseAssistActions,
86
+ actionData: Object
87
+ ) => {
88
+ expectMatchProperty(content, 'ec', 'svc');
89
+ expectMatchProperty(content, 'svc_action', actionName);
90
+ expectMatchProperty(content, 'svc_action_data', actionData);
91
+
92
+ const expectedEvent =
93
+ actionName === CaseAssistActions.enterInterface ? CaseAssistEvents.flowStart : CaseAssistEvents.click;
94
+ expectMatchProperty(content, 'ea', expectedEvent);
95
+ };
96
+
97
+ const expectMatchTicketPayload = (content: Record<string, unknown>, ticket?: TicketProperties) => {
98
+ expectMatchProperty(content, 'svc_ticket_id', ticket?.id);
99
+ expectMatchProperty(content, 'svc_ticket_subject', ticket?.subject);
100
+ expectMatchProperty(content, 'svc_ticket_description', ticket?.description);
101
+ expectMatchProperty(content, 'svc_ticket_category', ticket?.category);
102
+ expectMatchProperty(content, 'svc_ticket_product_id', ticket?.productId);
103
+ expectMatchProperty(content, 'svc_ticket_custom', ticket?.custom);
104
+ };
105
+
106
+ const expectMatchProperty = (content: Record<string, unknown>, propName: string, propValue: unknown) => {
107
+ if (typeof propValue !== 'undefined') {
108
+ if (typeof propValue === 'object') {
109
+ expect(content).toHaveProperty(propName);
110
+ expect(content[propName]).toMatchObject(propValue);
111
+ } else {
112
+ expect(content).toHaveProperty(propName, propValue);
113
+ }
114
+ } else {
115
+ expect(content).not.toHaveProperty(propName);
116
+ }
117
+ };
118
+
119
+ it('should have #enableAnalytics default to #true', async () => {
120
+ client = new CaseAssistClient({});
121
+
122
+ await client.logEnterInterface({
123
+ ticket: emptyTicket,
124
+ });
125
+
126
+ expect(fetchMock.called()).toBe(true);
127
+ });
128
+
129
+ it('should not send events when #enableAnalytics option is #false', async () => {
130
+ client = new CaseAssistClient({
131
+ enableAnalytics: false,
132
+ });
133
+
134
+ await client.logEnterInterface({
135
+ ticket: emptyTicket,
136
+ });
137
+
138
+ expect(fetchMock.called()).toBe(false);
139
+ });
140
+
141
+ it('should not send events after #disable function is called', async () => {
142
+ client.disable();
143
+
144
+ await client.logEnterInterface({
145
+ ticket: emptyTicket,
146
+ });
147
+
148
+ expect(fetchMock.called()).toBe(false);
149
+ });
150
+
151
+ it('should send events after #enable function is called', async () => {
152
+ client = new CaseAssistClient({
153
+ enableAnalytics: false,
154
+ });
155
+
156
+ client.enable();
157
+
158
+ await client.logEnterInterface({
159
+ ticket: emptyTicket,
160
+ });
161
+
162
+ expect(fetchMock.called()).toBe(true);
163
+ });
164
+
165
+ it('should send proper payload for #logEnterInterface', async () => {
166
+ await client.logEnterInterface({
167
+ ticket: emptyTicket,
168
+ });
169
+
170
+ expectMatchPayload(CaseAssistActions.enterInterface, noActionData, noTicket);
171
+ });
172
+
173
+ it('should send proper payload for #logUpdateCaseField', async () => {
174
+ await client.logUpdateCaseField({
175
+ fieldName: 'subject',
176
+ ticket: fakeTicket,
177
+ });
178
+
179
+ expectMatchPayload(CaseAssistActions.fieldUpdate, {fieldName: 'subject'}, fakeTicket);
180
+ });
181
+
182
+ it('should send proper payload for #logSelectFieldSuggestion', async () => {
183
+ await client.logSelectFieldSuggestion({
184
+ suggestion: fakeFieldSuggestion,
185
+ ticket: fakeTicket,
186
+ });
187
+
188
+ expectMatchPayload(CaseAssistActions.fieldSuggestionClick, fakeFieldSuggestion, fakeTicket);
189
+ });
190
+
191
+ it('should send proper payload for #logSelectDocumentSuggestion', async () => {
192
+ await client.logSelectDocumentSuggestion({
193
+ suggestion: fakeDocumentSuggestion,
194
+ ticket: fakeTicket,
195
+ });
196
+
197
+ expectMatchPayload(CaseAssistActions.suggestionClick, fakeDocumentSuggestion, fakeTicket);
198
+ });
199
+
200
+ it('should send proper payload for #logRateDocumentSuggestion', async () => {
201
+ const rating = 0.8;
202
+
203
+ await client.logRateDocumentSuggestion({
204
+ rating,
205
+ suggestion: fakeDocumentSuggestion,
206
+ ticket: fakeTicket,
207
+ });
208
+
209
+ expectMatchPayload(
210
+ CaseAssistActions.suggestionRate,
211
+ {
212
+ ...fakeDocumentSuggestion,
213
+ rate: rating,
214
+ },
215
+ fakeTicket
216
+ );
217
+ });
218
+
219
+ it('should send proper payload for #logMoveToNextCaseStep', async () => {
220
+ await client.logMoveToNextCaseStep({
221
+ ticket: fakeTicket,
222
+ });
223
+
224
+ expectMatchPayload(CaseAssistActions.nextCaseStep, noActionData, fakeTicket);
225
+ });
226
+
227
+ it('should send proper payload for #logCaseCancelled', async () => {
228
+ await client.logCaseCancelled({
229
+ ticket: fakeTicket,
230
+ });
231
+
232
+ expectMatchPayload(
233
+ CaseAssistActions.caseCancelled,
234
+ {
235
+ reason: CaseCancelledReasons.quit,
236
+ },
237
+ fakeTicket
238
+ );
239
+ });
240
+
241
+ it('should send proper payload for #logCaseSolved', async () => {
242
+ await client.logCaseSolved({
243
+ ticket: fakeTicket,
244
+ });
245
+
246
+ expectMatchPayload(
247
+ CaseAssistActions.caseSolved,
248
+ {
249
+ reason: CaseCancelledReasons.solved,
250
+ },
251
+ fakeTicket
252
+ );
253
+ });
254
+
255
+ it('should send proper payload for #logCaseCreated', async () => {
256
+ await client.logCaseCreated({
257
+ ticket: fakeTicket,
258
+ });
259
+
260
+ expectMatchPayload(CaseAssistActions.caseCreated, noActionData, fakeTicket);
261
+ });
262
+ });
@@ -0,0 +1,117 @@
1
+ import CoveoAnalyticsClient, {AnalyticsClient, ClientOptions} from '../client/analytics';
2
+ import {NoopAnalytics} from '../client/noopAnalytics';
3
+ import {SVCPlugin} from '../plugins/svc';
4
+ import {
5
+ CaseAssistActions,
6
+ CaseAssistEvents,
7
+ CaseCancelledMetadata,
8
+ CaseCancelledReasons,
9
+ CaseCreatedMetadata,
10
+ CaseSolvedMetadata,
11
+ EnterInterfaceMetadata,
12
+ MoveToNextCaseStepMetadata,
13
+ RateDocumentSuggestionMetadata,
14
+ SelectDocumentSuggestionMetadata,
15
+ SelectFieldSuggestionMetadata,
16
+ UpdateCaseFieldMetadata,
17
+ } from './caseAssistActions';
18
+
19
+ export interface CaseAssistClientOptions extends ClientOptions {
20
+ enableAnalytics?: boolean;
21
+ }
22
+
23
+ export class CaseAssistClient {
24
+ public coveoAnalyticsClient: AnalyticsClient;
25
+ private svc: SVCPlugin;
26
+
27
+ constructor(private options: Partial<CaseAssistClientOptions>) {
28
+ const analyticsEnabled = options.enableAnalytics ?? true;
29
+
30
+ this.coveoAnalyticsClient = analyticsEnabled ? new CoveoAnalyticsClient(options) : new NoopAnalytics();
31
+ this.svc = new SVCPlugin({client: this.coveoAnalyticsClient});
32
+ }
33
+
34
+ public disable() {
35
+ if (this.coveoAnalyticsClient instanceof CoveoAnalyticsClient) {
36
+ this.coveoAnalyticsClient.clear();
37
+ }
38
+ this.coveoAnalyticsClient = new NoopAnalytics();
39
+ this.svc = new SVCPlugin({client: this.coveoAnalyticsClient});
40
+ }
41
+
42
+ public enable() {
43
+ this.coveoAnalyticsClient = new CoveoAnalyticsClient(this.options);
44
+ this.svc = new SVCPlugin({client: this.coveoAnalyticsClient});
45
+ }
46
+
47
+ public logEnterInterface(meta: EnterInterfaceMetadata) {
48
+ this.svc.setAction(CaseAssistActions.enterInterface);
49
+ this.svc.setTicket(meta.ticket);
50
+ return this.sendFlowStartEvent();
51
+ }
52
+
53
+ public logUpdateCaseField(meta: UpdateCaseFieldMetadata) {
54
+ this.svc.setAction(CaseAssistActions.fieldUpdate, {
55
+ fieldName: meta.fieldName,
56
+ });
57
+ this.svc.setTicket(meta.ticket);
58
+ return this.sendClickEvent();
59
+ }
60
+
61
+ public logSelectFieldSuggestion(meta: SelectFieldSuggestionMetadata) {
62
+ this.svc.setAction(CaseAssistActions.fieldSuggestionClick, meta.suggestion);
63
+ this.svc.setTicket(meta.ticket);
64
+ return this.sendClickEvent();
65
+ }
66
+
67
+ public logSelectDocumentSuggestion(meta: SelectDocumentSuggestionMetadata) {
68
+ this.svc.setAction(CaseAssistActions.suggestionClick, meta.suggestion);
69
+ this.svc.setTicket(meta.ticket);
70
+ return this.sendClickEvent();
71
+ }
72
+
73
+ public logRateDocumentSuggestion(meta: RateDocumentSuggestionMetadata) {
74
+ this.svc.setAction(CaseAssistActions.suggestionRate, {
75
+ rate: meta.rating,
76
+ ...meta.suggestion,
77
+ });
78
+ this.svc.setTicket(meta.ticket);
79
+ return this.sendClickEvent();
80
+ }
81
+
82
+ public logMoveToNextCaseStep(meta: MoveToNextCaseStepMetadata) {
83
+ this.svc.setAction(CaseAssistActions.nextCaseStep);
84
+ this.svc.setTicket(meta.ticket);
85
+ return this.sendClickEvent();
86
+ }
87
+
88
+ public logCaseCancelled(meta: CaseCancelledMetadata) {
89
+ this.svc.setAction(CaseAssistActions.caseCancelled, {
90
+ reason: CaseCancelledReasons.quit,
91
+ });
92
+ this.svc.setTicket(meta.ticket);
93
+ return this.sendClickEvent();
94
+ }
95
+
96
+ public logCaseSolved(meta: CaseSolvedMetadata) {
97
+ this.svc.setAction(CaseAssistActions.caseSolved, {
98
+ reason: CaseCancelledReasons.solved,
99
+ });
100
+ this.svc.setTicket(meta.ticket);
101
+ return this.sendClickEvent();
102
+ }
103
+
104
+ public logCaseCreated(meta: CaseCreatedMetadata) {
105
+ this.svc.setAction(CaseAssistActions.caseCreated);
106
+ this.svc.setTicket(meta.ticket);
107
+ return this.sendClickEvent();
108
+ }
109
+
110
+ private sendFlowStartEvent() {
111
+ return this.coveoAnalyticsClient.sendEvent('event', 'svc', CaseAssistEvents.flowStart);
112
+ }
113
+
114
+ private sendClickEvent() {
115
+ return this.coveoAnalyticsClient.sendEvent('event', 'svc', CaseAssistEvents.click);
116
+ }
117
+ }
@@ -1,4 +1,5 @@
1
1
  export {CoveoSearchPageClient, SearchPageClientProvider} from '../searchPage/searchPageClient';
2
+ export {CaseAssistClient} from '../caseAssist/caseAssistClient';
2
3
  export {CoveoAnalyticsClient, AnalyticsClientSendEventHook} from '../client/analytics';
3
4
  export {PreprocessAnalyticsRequest} from '../client/analyticsRequestClient';
4
5
  export * as history from '../history';
@@ -8,5 +8,6 @@ export {PreprocessAnalyticsRequest} from '../client/analyticsRequestClient';
8
8
  export {IRuntimeEnvironment} from '../client/runtimeEnvironment';
9
9
  export {CoveoUA, getCurrentClient, handleOneAnalyticsEvent} from './simpleanalytics';
10
10
  export {CoveoSearchPageClient, SearchPageClientProvider} from '../searchPage/searchPageClient';
11
+ export {CaseAssistClient} from '../caseAssist/caseAssistClient';
11
12
 
12
13
  export {analytics, donottrack, history, SimpleAnalytics, storage};