@microsoft/omnichannel-chat-widget 1.5.1-main.c3533cf → 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 (26) 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/endChat.js +16 -4
  5. package/lib/cjs/components/livechatwidget/common/startChat.js +8 -82
  6. package/lib/cjs/components/livechatwidget/common/startChatErrorHandler.js +198 -0
  7. package/lib/cjs/components/livechatwidget/common/startChatErrorHandler.spec.js +282 -0
  8. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +1 -7
  9. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +0 -8
  10. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.spec.js +1 -1
  11. package/lib/esm/common/Constants.js +7 -6
  12. package/lib/esm/common/telemetry/TelemetryConstants.js +0 -2
  13. package/lib/esm/common/utils.js +3 -2
  14. package/lib/esm/components/livechatwidget/common/endChat.js +14 -3
  15. package/lib/esm/components/livechatwidget/common/startChat.js +9 -83
  16. package/lib/esm/components/livechatwidget/common/startChatErrorHandler.js +191 -0
  17. package/lib/esm/components/livechatwidget/common/startChatErrorHandler.spec.js +280 -0
  18. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +2 -8
  19. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +0 -8
  20. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.spec.js +1 -1
  21. package/lib/types/common/Constants.d.ts +8 -4
  22. package/lib/types/common/telemetry/TelemetryConstants.d.ts +0 -1
  23. package/lib/types/components/livechatwidget/common/endChat.d.ts +4 -3
  24. package/lib/types/components/livechatwidget/common/startChatErrorHandler.d.ts +5 -0
  25. package/lib/types/components/livechatwidget/common/startChatErrorHandler.spec.d.ts +1 -0
  26. package/package.json +2 -2
@@ -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
+ });
@@ -8,7 +8,7 @@ import React, { useEffect, useRef, useState } from "react";
8
8
  import { checkIfConversationStillValid, initStartChat, prepareStartChat, setPreChatAndInitiateChat } from "../common/startChat";
9
9
  import { createTimer, getBroadcastChannelName, getConversationDetailsCall, getLocaleDirection, getStateFromCache, getWidgetCacheIdfromProps, getWidgetEndChatEventName, isNullOrEmptyString, isNullOrUndefined, isUndefinedOrEmpty } from "../../../common/utils";
10
10
  import { defaultClientDataStoreProvider, isCookieAllowed } from "../../../common/storage/default/defaultClientDataStoreProvider";
11
- import { endChat, endChatStateCleanUp, prepareEndChat } from "../common/endChat";
11
+ import { chatSDKStateCleanUp, endChat, endChatStateCleanUp, prepareEndChat } from "../common/endChat";
12
12
  import { handleChatReconnect, isPersistentEnabled, isReconnectEnabled } from "../common/reconnectChatHelper";
13
13
  import { shouldShowCallingContainer, shouldShowChatButton, shouldShowConfirmationPane, shouldShowEmailTranscriptPane, shouldShowHeader, shouldShowLoadingPane, shouldShowOutOfOfficeHoursPane, shouldShowPostChatLoadingPane, shouldShowPostChatSurveyPane, shouldShowPreChatSurveyPane, shouldShowProactiveChatPane, shouldShowReconnectChatPane, shouldShowWebChatContainer } from "../../../controller/componentController";
14
14
  import { ActivityStreamHandler } from "../common/ActivityStreamHandler";
@@ -51,7 +51,6 @@ import useChatAdapterStore from "../../../hooks/useChatAdapterStore";
51
51
  import useChatContextStore from "../../../hooks/useChatContextStore";
52
52
  import useChatSDKStore from "../../../hooks/useChatSDKStore";
53
53
  import { defaultAdaptiveCardStyles } from "../../webchatcontainerstateful/common/defaultStyles/defaultAdaptiveCardStyles";
54
- import { uuidv4 } from "@microsoft/omnichannel-chat-sdk";
55
54
  export const LiveChatWidgetStateful = props => {
56
55
  var _props$webChatContain, _props$styleProps, _props$controlProps, _props$controlProps3, _state$appStates7, _props$webChatContain5, _state$appStates14, _props$webChatContain6, _props$controlProps12, _props$draggableChatW, _props$draggableChatW2, _props$draggableChatW3, _props$draggableChatW4, _props$draggableChatW5, _props$webChatContain7, _props$webChatContain8, _props$webChatContain9, _props$webChatContain10, _livechatProps$webCha, _livechatProps$styleP, _livechatProps$contro, _livechatProps$contro2, _livechatProps$compon, _livechatProps$contro3, _livechatProps$compon2, _livechatProps$contro4, _livechatProps$compon3, _livechatProps$contro5, _livechatProps$compon4, _livechatProps$contro6, _livechatProps$compon5, _livechatProps$contro7, _livechatProps$compon6, _livechatProps$contro8, _livechatProps$compon7, _livechatProps$contro9, _livechatProps$contro10, _livechatProps$compon8, _livechatProps$contro11, _livechatProps$compon9, _livechatProps$contro12, _livechatProps$compon10, _livechatProps$compon11, _livechatProps$compon12;
57
56
  const [state, dispatch] = useChatContextStore();
@@ -425,12 +424,7 @@ export const LiveChatWidgetStateful = props => {
425
424
  if ((msg === null || msg === void 0 ? void 0 : (_msg$payload9 = msg.payload) === null || _msg$payload9 === void 0 ? void 0 : _msg$payload9.runtimeId) !== TelemetryManager.InternalTelemetryData.lcwRuntimeId) {
426
425
  endChat(props, chatSDK, state, dispatch, setAdapter, setWebChatStyles, adapter, true, false, false);
427
426
  endChatStateCleanUp(dispatch);
428
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
429
- chatSDK.requestId = uuidv4();
430
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
431
- chatSDK.chatToken = {};
432
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
433
- chatSDK.reconnectId = null;
427
+ chatSDKStateCleanUp(chatSDK);
434
428
  return;
435
429
  }
436
430
  });
@@ -11,7 +11,6 @@ import { LogLevel, TelemetryEvent } from "../../../../../common/telemetry/Teleme
11
11
  import { Constants } from "../../../../../common/Constants";
12
12
  import { DirectLineActivityType } from "../../enums/DirectLineActivityType";
13
13
  import { DirectLineSenderRole } from "../../enums/DirectLineSenderRole";
14
- import { MessageTypes } from "../../enums/MessageType";
15
14
  import React from "react";
16
15
  import { TelemetryHelper } from "../../../../../common/telemetry/TelemetryHelper";
17
16
  import { defaultSystemMessageStyles } from "./defaultStyles/defaultSystemMessageStyles";
@@ -74,13 +73,6 @@ export const createActivityMiddleware = (systemMessageStyleProps, userMessageSty
74
73
  if (card.activity) {
75
74
  var _card$activity$from;
76
75
  if (((_card$activity$from = card.activity.from) === null || _card$activity$from === void 0 ? void 0 : _card$activity$from.role) === DirectLineSenderRole.Channel) {
77
- var _card$activity$channe3;
78
- if (((_card$activity$channe3 = card.activity.channelData) === null || _card$activity$channe3 === void 0 ? void 0 : _card$activity$channe3.type) === MessageTypes.Thread) {
79
- TelemetryHelper.logActionEvent(LogLevel.INFO, {
80
- Event: TelemetryEvent.IC3ThreadUpdateEventReceived,
81
- Description: "IC3 ThreadUpdateEvent Received"
82
- });
83
- }
84
76
  return () => false;
85
77
  }
86
78
  if (isTagIncluded(card, Constants.hiddenTag)) {
@@ -20,7 +20,7 @@ describe("activityMiddleware test", () => {
20
20
  };
21
21
  const results = createActivityMiddleware()()(next)(args);
22
22
  expect(results()).toEqual(false);
23
- expect(TelemetryHelper.logActionEvent).toHaveBeenCalledTimes(1);
23
+ expect(TelemetryHelper.logActionEvent).toHaveBeenCalledTimes(0);
24
24
  });
25
25
  it("createActivityMiddleware() with Hidden tag should return nothing", () => {
26
26
  spyOn(TelemetryHelper, "logActionEvent").and.callFake(() => false);
@@ -160,10 +160,6 @@ export declare class LocaleConstants {
160
160
  export declare enum ElementType {
161
161
  CallingContainerSDK = "CallingContainerSDK"
162
162
  }
163
- export declare enum ChatSDKError {
164
- WidgetUseOutsideOperatingHour = "WidgetUseOutsideOperatingHour",
165
- AuthContactIdNotFoundFailure = "AuthContactIdNotFoundFailure"
166
- }
167
163
  export declare enum EnvironmentVersion {
168
164
  prod = "prod",
169
165
  dogfood = "df",
@@ -241,3 +237,11 @@ export declare class AriaTelemetryConstants {
241
237
  static readonly EU: string;
242
238
  static readonly lcwEUDomainNames: Array<string>;
243
239
  }
240
+ export declare class WidgetLoadTelemetryMessage {
241
+ static readonly OOOHMessage = "Widget is OOOH";
242
+ static readonly PersistedStateRetrievedMessage = "Persisted state retrieved";
243
+ }
244
+ export declare class WidgetLoadCustomErrorString {
245
+ static readonly AuthenticationFailedErrorString = "Authentication was not successful";
246
+ static readonly NetworkErrorString = "Network Error";
247
+ }
@@ -120,7 +120,6 @@ export declare enum TelemetryEvent {
120
120
  ErrorUIPaneLoaded = "ErrorUIPaneLoaded",
121
121
  DownloadTranscriptFailed = "DownloadTranscriptFailed",
122
122
  StartChatFailed = "StartChatFailed",
123
- IC3ThreadUpdateEventReceived = "IC3ThreadUpdateEventReceived",
124
123
  ConfirmationCancelButtonClicked = "ConfirmationCancelButtonClicked",
125
124
  ConfirmationConfirmButtonClicked = "ConfirmationConfirmButtonClicked",
126
125
  LoadingPaneLoaded = "LoadingPaneLoaded",
@@ -4,8 +4,9 @@ import { ILiveChatWidgetContext } from "../../../contexts/common/ILiveChatWidget
4
4
  import { ILiveChatWidgetProps } from "../interfaces/ILiveChatWidgetProps";
5
5
  declare const prepareEndChat: (props: ILiveChatWidgetProps, chatSDK: any, state: ILiveChatWidgetContext, dispatch: Dispatch<ILiveChatWidgetAction>, setAdapter: any, setWebChatStyles: any, adapter: any) => Promise<void>;
6
6
  declare const endChat: (props: ILiveChatWidgetProps, chatSDK: any, state: ILiveChatWidgetContext, dispatch: Dispatch<ILiveChatWidgetAction>, setAdapter: any, setWebChatStyles: any, adapter: any, skipEndChatSDK?: boolean | undefined, skipCloseChat?: boolean | undefined, postMessageToOtherTab?: boolean | undefined) => Promise<void>;
7
- export declare const callingStateCleanUp: (dispatch: Dispatch<ILiveChatWidgetAction>) => Promise<void>;
8
- export declare const endChatStateCleanUp: (dispatch: Dispatch<ILiveChatWidgetAction>) => Promise<void>;
9
- export declare const closeChatStateCleanUp: (dispatch: Dispatch<ILiveChatWidgetAction>) => Promise<void>;
7
+ export declare const callingStateCleanUp: (dispatch: Dispatch<ILiveChatWidgetAction>) => void;
8
+ export declare const endChatStateCleanUp: (dispatch: Dispatch<ILiveChatWidgetAction>) => void;
9
+ export declare const closeChatStateCleanUp: (dispatch: Dispatch<ILiveChatWidgetAction>) => void;
10
+ export declare const chatSDKStateCleanUp: (chatSDK: any) => void;
10
11
  export declare const endVoiceVideoCallIfOngoing: (chatSDK: any, dispatch: Dispatch<ILiveChatWidgetAction>) => Promise<void>;
11
12
  export { prepareEndChat, endChat };
@@ -0,0 +1,5 @@
1
+ import { Dispatch } from "react";
2
+ import { ILiveChatWidgetAction } from "../../../contexts/common/ILiveChatWidgetAction";
3
+ import { ILiveChatWidgetProps } from "../interfaces/ILiveChatWidgetProps";
4
+ export declare const handleStartChatError: (dispatch: Dispatch<ILiveChatWidgetAction>, chatSDK: any, props: ILiveChatWidgetProps | undefined, ex: any, isStartChatSuccessful: boolean) => void;
5
+ export declare const logWidgetLoadComplete: (additionalMessage?: string | undefined) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/omnichannel-chat-widget",
3
- "version": "1.5.1-main.c3533cf",
3
+ "version": "1.5.1-main.e2be12d",
4
4
  "description": "Microsoft Omnichannel Chat Widget",
5
5
  "main": "lib/cjs/index.js",
6
6
  "types": "lib/types/index.d.ts",
@@ -75,7 +75,7 @@
75
75
  },
76
76
  "dependencies": {
77
77
  "@microsoft/omnichannel-chat-components": "^1.0.9",
78
- "@microsoft/omnichannel-chat-sdk": "1.5.7",
78
+ "@microsoft/omnichannel-chat-sdk": "1.6.1",
79
79
  "abort-controller-es5": "^2.0.1",
80
80
  "dompurify": "^2.3.4",
81
81
  "markdown-it": "^12.3.2",