@microsoft/omnichannel-chat-widget 1.5.1-main.76c6a71 → 1.5.1-main.e2be12d

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 (28) hide show
  1. package/lib/cjs/common/Constants.js +10 -8
  2. package/lib/cjs/common/telemetry/TelemetryConstants.js +0 -2
  3. package/lib/cjs/common/utils.js +2 -1
  4. package/lib/cjs/components/livechatwidget/common/chatDisconnectHelper.js +31 -16
  5. package/lib/cjs/components/livechatwidget/common/endChat.js +16 -4
  6. package/lib/cjs/components/livechatwidget/common/startChat.js +16 -84
  7. package/lib/cjs/components/livechatwidget/common/startChatErrorHandler.js +198 -0
  8. package/lib/cjs/components/livechatwidget/common/startChatErrorHandler.spec.js +282 -0
  9. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +2 -10
  10. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +0 -8
  11. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.spec.js +1 -1
  12. package/lib/esm/common/Constants.js +7 -6
  13. package/lib/esm/common/telemetry/TelemetryConstants.js +0 -2
  14. package/lib/esm/common/utils.js +3 -2
  15. package/lib/esm/components/livechatwidget/common/chatDisconnectHelper.js +31 -16
  16. package/lib/esm/components/livechatwidget/common/endChat.js +14 -3
  17. package/lib/esm/components/livechatwidget/common/startChat.js +17 -85
  18. package/lib/esm/components/livechatwidget/common/startChatErrorHandler.js +191 -0
  19. package/lib/esm/components/livechatwidget/common/startChatErrorHandler.spec.js +280 -0
  20. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +3 -11
  21. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +0 -8
  22. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.spec.js +1 -1
  23. package/lib/types/common/Constants.d.ts +8 -4
  24. package/lib/types/common/telemetry/TelemetryConstants.d.ts +0 -1
  25. package/lib/types/components/livechatwidget/common/endChat.d.ts +4 -3
  26. package/lib/types/components/livechatwidget/common/startChatErrorHandler.d.ts +5 -0
  27. package/lib/types/components/livechatwidget/common/startChatErrorHandler.spec.d.ts +1 -0
  28. package/package.json +2 -2
@@ -1,13 +1,11 @@
1
1
  import { BroadcastEvent, LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
2
- import { ChatSDKError, Constants, LiveWorkItemState } from "../../../common/Constants";
2
+ import { Constants, LiveWorkItemState, WidgetLoadCustomErrorString, WidgetLoadTelemetryMessage } from "../../../common/Constants";
3
3
  import { checkContactIdError, createTimer, getConversationDetailsCall, getStateFromCache, getWidgetCacheIdfromProps, isNullOrEmptyString, isUndefinedOrEmpty } from "../../../common/utils";
4
4
  import { getAuthClientFunction, handleAuthentication } from "./authHelper";
5
5
  import { ActivityStreamHandler } from "./ActivityStreamHandler";
6
6
  import { BroadcastService } from "@microsoft/omnichannel-chat-components";
7
7
  import { ConversationState } from "../../../contexts/common/ConversationState";
8
8
  import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
9
- import { NotificationHandler } from "../../webchatcontainerstateful/webchatcontroller/notification/NotificationHandler";
10
- import { NotificationScenarios } from "../../webchatcontainerstateful/webchatcontroller/enums/NotificationScenarios";
11
9
  import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
12
10
  import { TelemetryTimers } from "../../../common/telemetry/TelemetryManager";
13
11
  import { createAdapter } from "./createAdapter";
@@ -15,7 +13,8 @@ import { createOnNewAdapterActivityHandler } from "../../../plugins/newMessageEv
15
13
  import { handleChatReconnect, isPersistentEnabled, isReconnectEnabled } from "./reconnectChatHelper";
16
14
  import { setPostChatContextAndLoadSurvey } from "./setPostChatContextAndLoadSurvey";
17
15
  import { updateSessionDataForTelemetry } from "./updateSessionDataForTelemetry";
18
- import { uuidv4 } from "@microsoft/omnichannel-chat-sdk";
16
+ import { logWidgetLoadComplete, handleStartChatError } from "./startChatErrorHandler";
17
+ import { chatSDKStateCleanUp } from "./endChat";
19
18
 
20
19
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
20
  let optionalParams = {};
@@ -108,19 +107,21 @@ const setPreChatAndInitiateChat = async (chatSDK, dispatch, setAdapter, isProact
108
107
 
109
108
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
109
  const initStartChat = async (chatSDK, dispatch, setAdapter, state, props, params, persistedState) => {
111
- var _props$controlProps2;
112
110
  let isStartChatSuccessful = false;
113
111
  const chatConfig = props === null || props === void 0 ? void 0 : props.chatConfig;
114
112
  const getAuthToken = props === null || props === void 0 ? void 0 : props.getAuthToken;
115
- const hideErrorUIPane = props === null || props === void 0 ? void 0 : (_props$controlProps2 = props.controlProps) === null || _props$controlProps2 === void 0 ? void 0 : _props$controlProps2.hideErrorUIPane;
116
113
  if ((state === null || state === void 0 ? void 0 : state.appStates.conversationState) === ConversationState.Closed) {
117
114
  // Preventive reset to avoid starting chat with previous requestId which could potentially cause problems
118
- chatSDK.requestId = uuidv4();
119
- chatSDK.chatToken = {};
120
- chatSDK.reconnectId = null;
115
+ chatSDKStateCleanUp(chatSDK);
121
116
  }
122
117
  try {
123
- var _newAdapter$activity$, _TelemetryTimers$Widg2;
118
+ var _state$appStates, _newAdapter$activity$;
119
+ // Clear disconnect state on start chat
120
+ (state === null || state === void 0 ? void 0 : (_state$appStates = state.appStates) === null || _state$appStates === void 0 ? void 0 : _state$appStates.chatDisconnectEventReceived) && dispatch({
121
+ type: LiveChatWidgetActionType.SET_CHAT_DISCONNECT_EVENT_RECEIVED,
122
+ payload: false
123
+ });
124
+
124
125
  //Start widget load timer
125
126
  TelemetryTimers.WidgetLoadTimer = createTimer();
126
127
  TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
@@ -132,8 +133,7 @@ const initStartChat = async (chatSDK, dispatch, setAdapter, state, props, params
132
133
  // set auth token to chat sdk before start chat
133
134
  const authSuccess = await handleAuthentication(chatSDK, chatConfig, getAuthToken);
134
135
  if (!authSuccess) {
135
- // Replacing with error ui
136
- throw new Error("Authentication was not successful");
136
+ throw new Error(WidgetLoadCustomErrorString.AuthenticationFailedErrorString);
137
137
  }
138
138
  }
139
139
 
@@ -196,16 +196,11 @@ const initStartChat = async (chatSDK, dispatch, setAdapter, state, props, params
196
196
  });
197
197
  }
198
198
  if (persistedState) {
199
- var _TelemetryTimers$Widg;
200
199
  dispatch({
201
200
  type: LiveChatWidgetActionType.SET_WIDGET_STATE,
202
201
  payload: persistedState
203
202
  });
204
- TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
205
- Event: TelemetryEvent.WidgetLoadComplete,
206
- Description: "Widget load complete. Persisted state retrieved",
207
- ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg === void 0 ? void 0 : _TelemetryTimers$Widg.milliSecondsElapsed
208
- });
203
+ logWidgetLoadComplete(WidgetLoadTelemetryMessage.PersistedStateRetrievedMessage);
209
204
  await setPostChatContextAndLoadSurvey(chatSDK, dispatch, true);
210
205
  return;
211
206
  }
@@ -216,11 +211,7 @@ const initStartChat = async (chatSDK, dispatch, setAdapter, state, props, params
216
211
  type: LiveChatWidgetActionType.SET_LIVE_CHAT_CONTEXT,
217
212
  payload: liveChatContext
218
213
  });
219
- TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
220
- Event: TelemetryEvent.WidgetLoadComplete,
221
- Description: "Widget load complete",
222
- ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg2 = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg2 === void 0 ? void 0 : _TelemetryTimers$Widg2.milliSecondsElapsed
223
- });
214
+ logWidgetLoadComplete();
224
215
 
225
216
  // Set post chat context in state
226
217
  // Commenting this for now as post chat context is fetched during end chat
@@ -229,77 +220,18 @@ const initStartChat = async (chatSDK, dispatch, setAdapter, state, props, params
229
220
  // Updating chat session detail for telemetry
230
221
  await updateSessionDataForTelemetry(chatSDK, dispatch);
231
222
  } catch (ex) {
232
- var _TelemetryTimers$Widg4;
233
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
234
- if (ex.message === ChatSDKError.WidgetUseOutsideOperatingHour) {
235
- var _TelemetryTimers$Widg3;
236
- dispatch({
237
- type: LiveChatWidgetActionType.SET_OUTSIDE_OPERATING_HOURS,
238
- payload: true
239
- });
240
- dispatch({
241
- type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
242
- payload: ConversationState.OutOfOffice
243
- });
244
- TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
245
- Event: TelemetryEvent.WidgetLoadComplete,
246
- Description: "Widget load complete. Widget is OOOH.",
247
- ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg3 = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg3 === void 0 ? void 0 : _TelemetryTimers$Widg3.milliSecondsElapsed
248
- });
249
- return;
250
- }
251
- TelemetryHelper.logLoadingEvent(LogLevel.ERROR, {
252
- Event: TelemetryEvent.WidgetLoadFailed,
253
- ExceptionDetails: {
254
- Exception: `Widget load Failed: ${ex}`
255
- },
256
- ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg4 = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg4 === void 0 ? void 0 : _TelemetryTimers$Widg4.milliSecondsElapsed
257
- });
258
- NotificationHandler.notifyError(NotificationScenarios.Connection, "Start Chat Failed: " + ex);
259
- dispatch({
260
- type: LiveChatWidgetActionType.SET_START_CHAT_FAILING,
261
- payload: true
262
- });
263
- if (!hideErrorUIPane) {
264
- // Set app state to failing start chat if hideErrorUI is not turned on
265
- TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
266
- Event: TelemetryEvent.ErrorUIPaneLoaded,
267
- Description: "Error UI Pane Loaded"
268
- });
269
- }
270
- // Show the loading pane in other cases for failure, this will help for both hideStartChatButton case
271
- dispatch({
272
- type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
273
- payload: ConversationState.Loading
274
- });
275
-
276
- // If sessionInit was successful but LCW startchat failed due to some reason e.g adapter didn't load
277
- // we need to directly endChat to avoid leaving ghost chats in OC, not disturbing any other UI state
278
- if (isStartChatSuccessful === true) {
279
- await forceEndChat(chatSDK);
280
- }
223
+ handleStartChatError(dispatch, chatSDK, props, ex, isStartChatSuccessful);
281
224
  } finally {
282
225
  optionalParams = {};
283
226
  widgetInstanceId = "";
284
227
  }
285
228
  };
286
229
 
287
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
288
- const forceEndChat = async chatSDK => {
289
- TelemetryHelper.logLoadingEvent(LogLevel.ERROR, {
290
- Event: TelemetryEvent.WidgetLoadFailed,
291
- ExceptionDetails: {
292
- Exception: "SessionInit was successful, but widget load failed."
293
- }
294
- });
295
- chatSDK === null || chatSDK === void 0 ? void 0 : chatSDK.endChat();
296
- };
297
-
298
230
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
299
231
  const canConnectToExistingChat = async (props, chatSDK, state, dispatch, setAdapter) => {
300
- var _state$appStates, _persistedState$domai6, _persistedState$appSt;
232
+ var _state$appStates2, _persistedState$domai6, _persistedState$appSt;
301
233
  // By pass this function in case of popout chat
302
- if ((state === null || state === void 0 ? void 0 : (_state$appStates = state.appStates) === null || _state$appStates === void 0 ? void 0 : _state$appStates.hideStartChatButton) === true) {
234
+ if ((state === null || state === void 0 ? void 0 : (_state$appStates2 = state.appStates) === null || _state$appStates2 === void 0 ? void 0 : _state$appStates2.hideStartChatButton) === true) {
303
235
  return false;
304
236
  }
305
237
  const persistedState = getStateFromCache(getWidgetCacheIdfromProps(props));
@@ -0,0 +1,191 @@
1
+ import { ChatSDKErrorName, ChatSDKError } from "@microsoft/omnichannel-chat-sdk";
2
+ import { LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
3
+ import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
4
+ import { TelemetryTimers } from "../../../common/telemetry/TelemetryManager";
5
+ import { ConversationState } from "../../../contexts/common/ConversationState";
6
+ import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
7
+ import { callingStateCleanUp, endChatStateCleanUp, closeChatStateCleanUp, chatSDKStateCleanUp } from "./endChat";
8
+ import { DataStoreManager } from "../../../common/contextDataStore/DataStoreManager";
9
+ import { getWidgetCacheIdfromProps } from "../../../common/utils";
10
+ import { WidgetLoadCustomErrorString, WidgetLoadTelemetryMessage } from "../../../common/Constants";
11
+
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ export const handleStartChatError = (dispatch, chatSDK, props, ex, isStartChatSuccessful) => {
14
+ var _props$controlProps;
15
+ if (!ex) {
16
+ logWidgetLoadFailed();
17
+ return;
18
+ }
19
+
20
+ // Handle internal or misc errors
21
+ if (ex.message === WidgetLoadCustomErrorString.AuthenticationFailedErrorString || ex.message === WidgetLoadCustomErrorString.NetworkErrorString) {
22
+ logWidgetLoadCompleteWithError(ex);
23
+ }
24
+
25
+ // Handle ChatSDK errors
26
+ if (ex instanceof ChatSDKError) {
27
+ switch (ex.message) {
28
+ case ChatSDKErrorName.WidgetUseOutsideOperatingHour:
29
+ handleWidgetUseOutsideOperatingHour(dispatch);
30
+ return;
31
+ case ChatSDKErrorName.PersistentChatConversationRetrievalFailure:
32
+ handlePersistentChatConversationRetrievalFailure(ex);
33
+ break;
34
+ case ChatSDKErrorName.ConversationInitializationFailure:
35
+ handleConversationInitializationFailure(ex);
36
+ break;
37
+ case ChatSDKErrorName.ChatTokenRetrievalFailure:
38
+ handleChatTokenRetrievalFailure(ex);
39
+ break;
40
+ case ChatSDKErrorName.UninitializedChatSDK:
41
+ handleUninitializedChatSDK(ex);
42
+ break;
43
+ case ChatSDKErrorName.InvalidConversation:
44
+ case ChatSDKErrorName.ClosedConversation:
45
+ handleInvalidOrClosedConversation(dispatch, chatSDK, props, ex);
46
+ return;
47
+ default:
48
+ logWidgetLoadFailed(ex);
49
+ }
50
+ }
51
+
52
+ // Show the error UI pane
53
+ dispatch({
54
+ type: LiveChatWidgetActionType.SET_START_CHAT_FAILING,
55
+ payload: true
56
+ });
57
+ if (!(props !== null && props !== void 0 && (_props$controlProps = props.controlProps) !== null && _props$controlProps !== void 0 && _props$controlProps.hideErrorUIPane)) {
58
+ // Set app state to failing start chat if hideErrorUI is not turned on
59
+ TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
60
+ Event: TelemetryEvent.ErrorUIPaneLoaded,
61
+ Description: "Error UI Pane Loaded"
62
+ });
63
+ }
64
+ // Show the loading pane in other cases for failure, this will help for both hideStartChatButton case
65
+ dispatch({
66
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
67
+ payload: ConversationState.Loading
68
+ });
69
+
70
+ // If sessionInit was successful but LCW startchat failed due to some reason e.g adapter didn't load
71
+ // we need to directly endChat to avoid leaving ghost chats in OC, not disturbing any other UI state
72
+ if (isStartChatSuccessful === true) {
73
+ forceEndChat(chatSDK);
74
+ }
75
+ };
76
+ const logWidgetLoadFailed = ex => {
77
+ var _TelemetryTimers$Widg;
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ const exDetails = {
80
+ Exception: `Widget load complete with error: ${ex}`
81
+ };
82
+ if (ex !== null && ex !== void 0 && ex.httpResponseStatusCode) {
83
+ exDetails.HttpResponseStatusCode = ex.httpResponseStatusCode;
84
+ }
85
+ TelemetryHelper.logLoadingEvent(LogLevel.ERROR, {
86
+ Event: TelemetryEvent.WidgetLoadFailed,
87
+ ExceptionDetails: exDetails,
88
+ ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg === void 0 ? void 0 : _TelemetryTimers$Widg.milliSecondsElapsed
89
+ });
90
+ };
91
+ export const logWidgetLoadComplete = additionalMessage => {
92
+ var _TelemetryTimers$Widg2;
93
+ let descriptionString = "Widget load complete";
94
+ if (additionalMessage) {
95
+ descriptionString += `. ${additionalMessage}`;
96
+ }
97
+ TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
98
+ Event: TelemetryEvent.WidgetLoadComplete,
99
+ Description: descriptionString,
100
+ ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg2 = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg2 === void 0 ? void 0 : _TelemetryTimers$Widg2.milliSecondsElapsed
101
+ });
102
+ };
103
+ const logWidgetLoadCompleteWithError = ex => {
104
+ var _TelemetryTimers$Widg3;
105
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
106
+ const exDetails = {
107
+ Exception: `Widget load complete with error: ${ex}`
108
+ };
109
+ if (ex !== null && ex !== void 0 && ex.httpResponseStatusCode) {
110
+ exDetails.HttpResponseStatusCode = ex.httpResponseStatusCode;
111
+ }
112
+ TelemetryHelper.logLoadingEvent(LogLevel.WARN, {
113
+ Event: TelemetryEvent.WidgetLoadComplete,
114
+ Description: "Widget load complete with error",
115
+ ExceptionDetails: exDetails,
116
+ ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg3 = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg3 === void 0 ? void 0 : _TelemetryTimers$Widg3.milliSecondsElapsed
117
+ });
118
+ };
119
+
120
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
121
+ const forceEndChat = chatSDK => {
122
+ TelemetryHelper.logLoadingEvent(LogLevel.ERROR, {
123
+ Event: TelemetryEvent.WidgetLoadFailed,
124
+ ExceptionDetails: {
125
+ Exception: "SessionInit was successful, but widget load failed."
126
+ }
127
+ });
128
+ chatSDK === null || chatSDK === void 0 ? void 0 : chatSDK.endChat();
129
+ };
130
+ const handleWidgetUseOutsideOperatingHour = dispatch => {
131
+ dispatch({
132
+ type: LiveChatWidgetActionType.SET_OUTSIDE_OPERATING_HOURS,
133
+ payload: true
134
+ });
135
+ dispatch({
136
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
137
+ payload: ConversationState.OutOfOffice
138
+ });
139
+ logWidgetLoadComplete(WidgetLoadTelemetryMessage.OOOHMessage);
140
+ };
141
+
142
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
143
+ const handlePersistentChatConversationRetrievalFailure = ex => {
144
+ if (ex.httpResponseStatusCode === 400) {
145
+ logWidgetLoadFailed(ex);
146
+ } else {
147
+ logWidgetLoadCompleteWithError(ex);
148
+ }
149
+ };
150
+
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ const handleConversationInitializationFailure = ex => {
153
+ if (ex.httpResponseStatusCode === 400) {
154
+ logWidgetLoadFailed(ex);
155
+ } else {
156
+ logWidgetLoadCompleteWithError(ex);
157
+ }
158
+ };
159
+
160
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
161
+ const handleChatTokenRetrievalFailure = ex => {
162
+ if (ex.httpResponseStatusCode === 400) {
163
+ logWidgetLoadFailed(ex);
164
+ } else {
165
+ logWidgetLoadCompleteWithError(ex);
166
+ }
167
+ };
168
+
169
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
170
+ const handleUninitializedChatSDK = ex => {
171
+ logWidgetLoadCompleteWithError(ex);
172
+ };
173
+
174
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
175
+ const handleInvalidOrClosedConversation = (dispatch, chatSDK, props, ex) => {
176
+ var _DataStoreManager$cli;
177
+ logWidgetLoadCompleteWithError(ex);
178
+
179
+ // Reset all internal states
180
+ callingStateCleanUp(dispatch);
181
+ endChatStateCleanUp(dispatch);
182
+ closeChatStateCleanUp(dispatch);
183
+ chatSDKStateCleanUp(chatSDK);
184
+ (_DataStoreManager$cli = DataStoreManager.clientDataStore) === null || _DataStoreManager$cli === void 0 ? void 0 : _DataStoreManager$cli.removeData(getWidgetCacheIdfromProps(props));
185
+
186
+ // Starts new chat
187
+ dispatch({
188
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
189
+ payload: ConversationState.Closed
190
+ });
191
+ };
@@ -0,0 +1,280 @@
1
+ import { BroadcastService } from "@microsoft/omnichannel-chat-components";
2
+ import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
3
+ import { handleStartChatError } from "./startChatErrorHandler";
4
+ import { WidgetLoadCustomErrorString, WidgetLoadTelemetryMessage } from "../../../common/Constants";
5
+ import { ChatSDKError, ChatSDKErrorName } from "@microsoft/omnichannel-chat-sdk";
6
+ import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
7
+ import { ConversationState } from "../../../contexts/common/ConversationState";
8
+ describe("startChatErrorHandler unit test", () => {
9
+ it("handleStartChatError should log failed event and return if exception is undefined", () => {
10
+ const dispatch = jest.fn();
11
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
12
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
13
+ handleStartChatError(dispatch, {}, {}, undefined, false);
14
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(1);
15
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("ERROR", expect.objectContaining({
16
+ ExceptionDetails: expect.objectContaining({
17
+ Exception: "Widget load complete with error: undefined"
18
+ })
19
+ }));
20
+ expect(dispatch).not.toHaveBeenCalled();
21
+ });
22
+ it("handleStartChatError should log failed with error event for AuthenticationFailedErrorString", () => {
23
+ const dispatch = jest.fn();
24
+ const mockEx = new Error(WidgetLoadCustomErrorString.AuthenticationFailedErrorString);
25
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
26
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
27
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
28
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(2);
29
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("WARN", expect.objectContaining({
30
+ ExceptionDetails: expect.objectContaining({
31
+ Exception: `Widget load complete with error: Error: ${WidgetLoadCustomErrorString.AuthenticationFailedErrorString}`
32
+ })
33
+ }));
34
+ expect(dispatch).toHaveBeenCalledTimes(2);
35
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
36
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
37
+ }));
38
+ });
39
+ it("handleStartChatError should log failed with error event for NetworkErrorString", () => {
40
+ const dispatch = jest.fn();
41
+ const mockEx = new Error(WidgetLoadCustomErrorString.NetworkErrorString);
42
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
43
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
44
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
45
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(2);
46
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("WARN", expect.objectContaining({
47
+ ExceptionDetails: expect.objectContaining({
48
+ Exception: `Widget load complete with error: Error: ${WidgetLoadCustomErrorString.NetworkErrorString}`
49
+ })
50
+ }));
51
+ expect(dispatch).toHaveBeenCalledTimes(2);
52
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
53
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
54
+ }));
55
+ });
56
+ it("handleStartChatError should log complete event for WidgetUseOutsideOperatingHour", () => {
57
+ const dispatch = jest.fn();
58
+ const mockEx = new ChatSDKError(ChatSDKErrorName.WidgetUseOutsideOperatingHour);
59
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
60
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
61
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
62
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(1);
63
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("INFO", expect.objectContaining({
64
+ Description: `Widget load complete. ${WidgetLoadTelemetryMessage.OOOHMessage}`
65
+ }));
66
+ expect(dispatch).toHaveBeenCalledTimes(2);
67
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
68
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
69
+ }));
70
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
71
+ type: LiveChatWidgetActionType.SET_OUTSIDE_OPERATING_HOURS
72
+ }));
73
+ });
74
+ it("handleStartChatError should log failed with error event for PersistentChatConversationRetrievalFailure for non-400 status", () => {
75
+ const dispatch = jest.fn();
76
+ const mockEx = new ChatSDKError(ChatSDKErrorName.PersistentChatConversationRetrievalFailure, 429);
77
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
78
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
79
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
80
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(2);
81
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("WARN", expect.objectContaining({
82
+ Description: "Widget load complete with error",
83
+ ExceptionDetails: expect.objectContaining({
84
+ Exception: `Widget load complete with error: ${ChatSDKErrorName.PersistentChatConversationRetrievalFailure}`,
85
+ HttpResponseStatusCode: 429
86
+ })
87
+ }));
88
+ expect(dispatch).toHaveBeenCalledTimes(2);
89
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
90
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
91
+ }));
92
+ });
93
+ it("handleStartChatError should log failed event for PersistentChatConversationRetrievalFailure for 400 status", () => {
94
+ const dispatch = jest.fn();
95
+ const mockEx = new ChatSDKError(ChatSDKErrorName.PersistentChatConversationRetrievalFailure, 400);
96
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
97
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
98
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
99
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(2);
100
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("ERROR", expect.objectContaining({
101
+ ExceptionDetails: expect.objectContaining({
102
+ Exception: `Widget load complete with error: ${ChatSDKErrorName.PersistentChatConversationRetrievalFailure}`,
103
+ HttpResponseStatusCode: 400
104
+ })
105
+ }));
106
+ expect(dispatch).toHaveBeenCalledTimes(2);
107
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
108
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
109
+ }));
110
+ });
111
+ it("handleStartChatError should log failed with error event for ConversationInitializationFailure for non-400 status", () => {
112
+ const dispatch = jest.fn();
113
+ const mockEx = new ChatSDKError(ChatSDKErrorName.ConversationInitializationFailure, 429);
114
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
115
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
116
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
117
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(2);
118
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("WARN", expect.objectContaining({
119
+ Description: "Widget load complete with error",
120
+ ExceptionDetails: expect.objectContaining({
121
+ Exception: `Widget load complete with error: ${ChatSDKErrorName.ConversationInitializationFailure}`,
122
+ HttpResponseStatusCode: 429
123
+ })
124
+ }));
125
+ expect(dispatch).toHaveBeenCalledTimes(2);
126
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
127
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
128
+ }));
129
+ });
130
+ it("handleStartChatError should log failed event for ConversationInitializationFailure for 400 status", () => {
131
+ const dispatch = jest.fn();
132
+ const mockEx = new ChatSDKError(ChatSDKErrorName.ConversationInitializationFailure, 400);
133
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
134
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
135
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
136
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(2);
137
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("ERROR", expect.objectContaining({
138
+ ExceptionDetails: expect.objectContaining({
139
+ Exception: `Widget load complete with error: ${ChatSDKErrorName.ConversationInitializationFailure}`,
140
+ HttpResponseStatusCode: 400
141
+ })
142
+ }));
143
+ expect(dispatch).toHaveBeenCalledTimes(2);
144
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
145
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
146
+ }));
147
+ });
148
+ it("handleStartChatError should log failed with error event for ChatTokenRetrievalFailure for non-400 status", () => {
149
+ const dispatch = jest.fn();
150
+ const mockEx = new ChatSDKError(ChatSDKErrorName.ChatTokenRetrievalFailure, 429);
151
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
152
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
153
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
154
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(2);
155
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("WARN", expect.objectContaining({
156
+ Description: "Widget load complete with error",
157
+ ExceptionDetails: expect.objectContaining({
158
+ Exception: `Widget load complete with error: ${ChatSDKErrorName.ChatTokenRetrievalFailure}`,
159
+ HttpResponseStatusCode: 429
160
+ })
161
+ }));
162
+ expect(dispatch).toHaveBeenCalledTimes(2);
163
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
164
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
165
+ }));
166
+ });
167
+ it("handleStartChatError should log failed event for ChatTokenRetrievalFailure for 400 status", () => {
168
+ const dispatch = jest.fn();
169
+ const mockEx = new ChatSDKError(ChatSDKErrorName.ChatTokenRetrievalFailure, 400);
170
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
171
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
172
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
173
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(2);
174
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("ERROR", expect.objectContaining({
175
+ ExceptionDetails: expect.objectContaining({
176
+ Exception: `Widget load complete with error: ${ChatSDKErrorName.ChatTokenRetrievalFailure}`,
177
+ HttpResponseStatusCode: 400
178
+ })
179
+ }));
180
+ expect(dispatch).toHaveBeenCalledTimes(2);
181
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
182
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
183
+ }));
184
+ });
185
+ it("handleStartChatError should log failed with error event for UninitializedChatSDK", () => {
186
+ const dispatch = jest.fn();
187
+ const mockEx = new ChatSDKError(ChatSDKErrorName.UninitializedChatSDK);
188
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
189
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
190
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
191
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(2);
192
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("WARN", expect.objectContaining({
193
+ Description: "Widget load complete with error",
194
+ ExceptionDetails: expect.objectContaining({
195
+ Exception: `Widget load complete with error: ${ChatSDKErrorName.UninitializedChatSDK}`
196
+ })
197
+ }));
198
+ expect(dispatch).toHaveBeenCalledTimes(2);
199
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
200
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
201
+ }));
202
+ });
203
+ it("handleStartChatError should log failed with error event for InvalidConversation", () => {
204
+ const dispatch = jest.fn();
205
+ const mockEx = new ChatSDKError(ChatSDKErrorName.InvalidConversation);
206
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
207
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
208
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
209
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(1);
210
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("WARN", expect.objectContaining({
211
+ Description: "Widget load complete with error",
212
+ ExceptionDetails: expect.objectContaining({
213
+ Exception: `Widget load complete with error: ${ChatSDKErrorName.InvalidConversation}`
214
+ })
215
+ }));
216
+ expect(dispatch).toHaveBeenCalledTimes(16);
217
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
218
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
219
+ payload: ConversationState.Closed
220
+ }));
221
+ });
222
+ it("handleStartChatError should log failed with error event for ClosedConversation", () => {
223
+ const dispatch = jest.fn();
224
+ const mockEx = new ChatSDKError(ChatSDKErrorName.ClosedConversation);
225
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
226
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
227
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
228
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(1);
229
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("WARN", expect.objectContaining({
230
+ Description: "Widget load complete with error",
231
+ ExceptionDetails: expect.objectContaining({
232
+ Exception: `Widget load complete with error: ${ChatSDKErrorName.ClosedConversation}`
233
+ })
234
+ }));
235
+ expect(dispatch).toHaveBeenCalledTimes(16);
236
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
237
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
238
+ payload: ConversationState.Closed
239
+ }));
240
+ });
241
+ it("handleStartChatError should log failed event for any other errors", () => {
242
+ const dispatch = jest.fn();
243
+ const mockEx = new ChatSDKError(ChatSDKErrorName.ScriptLoadFailure, 405);
244
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
245
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
246
+ handleStartChatError(dispatch, {}, {}, mockEx, false);
247
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(2);
248
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("ERROR", expect.objectContaining({
249
+ ExceptionDetails: expect.objectContaining({
250
+ Exception: `Widget load complete with error: ${ChatSDKErrorName.ScriptLoadFailure}`,
251
+ HttpResponseStatusCode: 405
252
+ })
253
+ }));
254
+ expect(dispatch).toHaveBeenCalledTimes(2);
255
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
256
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
257
+ }));
258
+ });
259
+ it("handleStartChatError should force end chat if isStartChatSuccessful is true", () => {
260
+ const dispatch = jest.fn();
261
+ const mockEx = new ChatSDKError(ChatSDKErrorName.ScriptLoadFailure, 405);
262
+ const mockSDK = {
263
+ endChat: jest.fn()
264
+ };
265
+ spyOn(BroadcastService, "postMessage").and.callFake(() => false);
266
+ spyOn(TelemetryHelper, "logLoadingEvent").and.callFake(() => false);
267
+ handleStartChatError(dispatch, mockSDK, {}, mockEx, true);
268
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledTimes(3);
269
+ expect(TelemetryHelper.logLoadingEvent).toHaveBeenCalledWith("ERROR", expect.objectContaining({
270
+ ExceptionDetails: expect.objectContaining({
271
+ Exception: "SessionInit was successful, but widget load failed."
272
+ })
273
+ }));
274
+ expect(dispatch).toHaveBeenCalledTimes(2);
275
+ expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
276
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE
277
+ }));
278
+ expect(mockSDK.endChat).toHaveBeenCalled();
279
+ });
280
+ });