@servicetitan/titan-chatbot-ui 3.1.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/components/chatbot/__tests-cy__/chatbot-help-center.test.js +2 -156
  3. package/dist/components/chatbot/__tests-cy__/chatbot-help-center.test.js.map +1 -1
  4. package/dist/components/chatbot/__tests-cy__/chatbot-live.test.js +2 -94
  5. package/dist/components/chatbot/__tests-cy__/chatbot-live.test.js.map +1 -1
  6. package/dist/components/chatbot/__tests-cy__/chatbot-titan-chatbot.test.js +2 -151
  7. package/dist/components/chatbot/__tests-cy__/chatbot-titan-chatbot.test.js.map +1 -1
  8. package/dist/components/chatbot/__tests-cy__/chatbot.test.js +2 -151
  9. package/dist/components/chatbot/__tests-cy__/chatbot.test.js.map +1 -1
  10. package/dist/components/chatbot/chatbot.d.ts +2 -1
  11. package/dist/components/chatbot/chatbot.d.ts.map +1 -1
  12. package/dist/components/chatbot/chatbot.js +2 -2
  13. package/dist/components/chatbot/chatbot.js.map +1 -1
  14. package/dist/components/chatbot/dialog/__tests-cy__/chatbot-restart-dialog.test.d.ts +2 -0
  15. package/dist/components/chatbot/dialog/__tests-cy__/chatbot-restart-dialog.test.d.ts.map +1 -0
  16. package/dist/components/chatbot/dialog/__tests-cy__/chatbot-restart-dialog.test.js +6 -0
  17. package/dist/components/chatbot/dialog/__tests-cy__/chatbot-restart-dialog.test.js.map +1 -0
  18. package/dist/components/chatbot/dialog/__tests-cy__/chatbot-restart-link.test.d.ts +2 -0
  19. package/dist/components/chatbot/dialog/__tests-cy__/chatbot-restart-link.test.d.ts.map +1 -0
  20. package/dist/components/chatbot/dialog/__tests-cy__/chatbot-restart-link.test.js +6 -0
  21. package/dist/components/chatbot/dialog/__tests-cy__/chatbot-restart-link.test.js.map +1 -0
  22. package/dist/components/chatbot/dialog/chatbot-restart-dialog.d.ts.map +1 -1
  23. package/dist/components/chatbot/dialog/chatbot-restart-dialog.js +1 -1
  24. package/dist/components/chatbot/dialog/chatbot-restart-dialog.js.map +1 -1
  25. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form-guardrail.test.js +2 -61
  26. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form-guardrail.test.js.map +1 -1
  27. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form.test.js +2 -96
  28. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form.test.js.map +1 -1
  29. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-popover.test.d.ts +2 -0
  30. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-popover.test.d.ts.map +1 -0
  31. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-popover.test.js +6 -0
  32. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-popover.test.js.map +1 -0
  33. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-session-feedback-modal.test.d.ts +2 -0
  34. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-session-feedback-modal.test.d.ts.map +1 -0
  35. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-session-feedback-modal.test.js +6 -0
  36. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-session-feedback-modal.test.js.map +1 -0
  37. package/dist/components/chatbot/feedback/chatbot-message-feedback-form-guardrail.d.ts.map +1 -1
  38. package/dist/components/chatbot/feedback/chatbot-message-feedback-form-guardrail.js +1 -1
  39. package/dist/components/chatbot/feedback/chatbot-message-feedback-form-guardrail.js.map +1 -1
  40. package/dist/components/chatbot/feedback/chatbot-message-feedback-form.d.ts.map +1 -1
  41. package/dist/components/chatbot/feedback/chatbot-message-feedback-form.js +1 -1
  42. package/dist/components/chatbot/feedback/chatbot-message-feedback-form.js.map +1 -1
  43. package/dist/components/chatbot/feedback/chatbot-message-feedback-popover.d.ts.map +1 -1
  44. package/dist/components/chatbot/feedback/chatbot-message-feedback-popover.js +2 -4
  45. package/dist/components/chatbot/feedback/chatbot-message-feedback-popover.js.map +1 -1
  46. package/dist/components/chatbot/feedback/chatbot-session-feedback-modal.d.ts.map +1 -1
  47. package/dist/components/chatbot/feedback/chatbot-session-feedback-modal.js +1 -2
  48. package/dist/components/chatbot/feedback/chatbot-session-feedback-modal.js.map +1 -1
  49. package/dist/components/chatbot/filters/__tests-cy__/chatbot-filters.test.d.ts +2 -0
  50. package/dist/components/chatbot/filters/__tests-cy__/chatbot-filters.test.d.ts.map +1 -0
  51. package/dist/components/chatbot/filters/__tests-cy__/chatbot-filters.test.js +6 -0
  52. package/dist/components/chatbot/filters/__tests-cy__/chatbot-filters.test.js.map +1 -0
  53. package/dist/components/chatbot/filters/chatbot-filter.js +1 -1
  54. package/dist/components/chatbot/filters/chatbot-filter.js.map +1 -1
  55. package/dist/components/chatbot/messages/__tests-cy__/chatbot-links.test.d.ts +2 -0
  56. package/dist/components/chatbot/messages/__tests-cy__/chatbot-links.test.d.ts.map +1 -0
  57. package/dist/components/chatbot/messages/__tests-cy__/chatbot-links.test.js +6 -0
  58. package/dist/components/chatbot/messages/__tests-cy__/chatbot-links.test.js.map +1 -0
  59. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-answer-readonly.test.d.ts +2 -0
  60. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-answer-readonly.test.d.ts.map +1 -0
  61. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-answer-readonly.test.js +6 -0
  62. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-answer-readonly.test.js.map +1 -0
  63. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-answer.test.js +3 -82
  64. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-answer.test.js.map +1 -1
  65. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-typing.test.js +3 -54
  66. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-typing.test.js.map +1 -1
  67. package/package.json +6 -6
  68. package/src/components/chatbot/__tests-cy__/chatbot-help-center.test.tsx +2 -206
  69. package/src/components/chatbot/__tests-cy__/chatbot-live.test.tsx +2 -128
  70. package/src/components/chatbot/__tests-cy__/chatbot-titan-chatbot.test.tsx +2 -202
  71. package/src/components/chatbot/__tests-cy__/chatbot.test.tsx +2 -197
  72. package/src/components/chatbot/chatbot.tsx +66 -63
  73. package/src/components/chatbot/dialog/__tests-cy__/chatbot-restart-dialog.test.tsx +6 -0
  74. package/src/components/chatbot/dialog/__tests-cy__/chatbot-restart-link.test.tsx +6 -0
  75. package/src/components/chatbot/dialog/chatbot-restart-dialog.tsx +1 -0
  76. package/src/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form-guardrail.test.tsx +2 -85
  77. package/src/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form.test.tsx +2 -127
  78. package/src/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-popover.test.tsx +6 -0
  79. package/src/components/chatbot/feedback/__tests-cy__/chatbot-session-feedback-modal.test.tsx +6 -0
  80. package/src/components/chatbot/feedback/chatbot-message-feedback-form-guardrail.tsx +1 -1
  81. package/src/components/chatbot/feedback/chatbot-message-feedback-form.tsx +1 -1
  82. package/src/components/chatbot/feedback/chatbot-message-feedback-popover.tsx +8 -5
  83. package/src/components/chatbot/feedback/chatbot-session-feedback-modal.tsx +1 -2
  84. package/src/components/chatbot/filters/__tests-cy__/chatbot-filters.test.tsx +6 -0
  85. package/src/components/chatbot/filters/chatbot-filter.tsx +1 -1
  86. package/src/components/chatbot/messages/__tests-cy__/chatbot-links.test.tsx +6 -0
  87. package/src/components/chatbot/messages/__tests-cy__/chatbot-message-answer-readonly.test.tsx +6 -0
  88. package/src/components/chatbot/messages/__tests-cy__/chatbot-message-answer.test.tsx +3 -107
  89. package/src/components/chatbot/messages/__tests-cy__/chatbot-message-typing.test.tsx +3 -68
  90. package/tsconfig.tsbuildinfo +1 -1
  91. package/dist/stores/__tests__/message-feedback-guardrail.store.test.d.ts +0 -2
  92. package/dist/stores/__tests__/message-feedback-guardrail.store.test.d.ts.map +0 -1
  93. package/dist/stores/__tests__/message-feedback-guardrail.store.test.js +0 -49
  94. package/dist/stores/__tests__/message-feedback-guardrail.store.test.js.map +0 -1
  95. package/dist/stores/__tests__/message-feedback.store.test.d.ts +0 -2
  96. package/dist/stores/__tests__/message-feedback.store.test.d.ts.map +0 -1
  97. package/dist/stores/__tests__/message-feedback.store.test.js +0 -114
  98. package/dist/stores/__tests__/message-feedback.store.test.js.map +0 -1
  99. package/dist/stores/__tests__/session-feedback.store.test.d.ts +0 -2
  100. package/dist/stores/__tests__/session-feedback.store.test.d.ts.map +0 -1
  101. package/dist/stores/__tests__/session-feedback.store.test.js +0 -39
  102. package/dist/stores/__tests__/session-feedback.store.test.js.map +0 -1
  103. package/dist/stores/message-feedback-base.store.d.ts +0 -8
  104. package/dist/stores/message-feedback-base.store.d.ts.map +0 -1
  105. package/dist/stores/message-feedback-base.store.js +0 -2
  106. package/dist/stores/message-feedback-base.store.js.map +0 -1
  107. package/dist/stores/message-feedback-guardrail.store.d.ts +0 -16
  108. package/dist/stores/message-feedback-guardrail.store.d.ts.map +0 -1
  109. package/dist/stores/message-feedback-guardrail.store.js +0 -85
  110. package/dist/stores/message-feedback-guardrail.store.js.map +0 -1
  111. package/dist/stores/message-feedback.store.d.ts +0 -23
  112. package/dist/stores/message-feedback.store.d.ts.map +0 -1
  113. package/dist/stores/message-feedback.store.js +0 -145
  114. package/dist/stores/message-feedback.store.js.map +0 -1
  115. package/dist/stores/session-feedback.store.d.ts +0 -15
  116. package/dist/stores/session-feedback.store.d.ts.map +0 -1
  117. package/dist/stores/session-feedback.store.js +0 -75
  118. package/dist/stores/session-feedback.store.js.map +0 -1
  119. package/dist/utils/__tests__/axios-utils.test.d.ts +0 -2
  120. package/dist/utils/__tests__/axios-utils.test.d.ts.map +0 -1
  121. package/dist/utils/__tests__/axios-utils.test.js +0 -33
  122. package/dist/utils/__tests__/axios-utils.test.js.map +0 -1
  123. package/dist/utils/axios-utils.d.ts +0 -5
  124. package/dist/utils/axios-utils.d.ts.map +0 -1
  125. package/dist/utils/axios-utils.js +0 -23
  126. package/dist/utils/axios-utils.js.map +0 -1
  127. package/dist/utils/test-utils.d.ts +0 -5
  128. package/dist/utils/test-utils.d.ts.map +0 -1
  129. package/dist/utils/test-utils.js +0 -17
  130. package/dist/utils/test-utils.js.map +0 -1
  131. package/src/stores/__tests__/message-feedback-guardrail.store.test.ts +0 -61
  132. package/src/stores/__tests__/message-feedback.store.test.ts +0 -121
  133. package/src/stores/__tests__/session-feedback.store.test.ts +0 -47
  134. package/src/stores/message-feedback-base.store.ts +0 -8
  135. package/src/stores/message-feedback-guardrail.store.ts +0 -60
  136. package/src/stores/message-feedback.store.ts +0 -113
  137. package/src/stores/session-feedback.store.ts +0 -44
  138. package/src/utils/__tests__/axios-utils.test.ts +0 -40
  139. package/src/utils/axios-utils.ts +0 -25
  140. package/src/utils/test-utils.ts +0 -22
@@ -1,206 +1,6 @@
1
- import { ILog, Log } from '@servicetitan/log-service';
2
- import { Container, Provider } from '@servicetitan/react-ioc';
3
- import {
4
- ApiClientTitanChat,
5
- CHATBOT_API_CLIENT,
6
- CHATBOT_CLIENT_SETTINGS,
7
- CHATBOT_UI_BACKEND_STORE_TOKEN,
8
- CHATBOT_UI_STORE_TOKEN,
9
- ChatbotCustomizations,
10
- ChatbotUiBackendStore,
11
- ChatbotUiStore,
12
- IChatbotApiClient,
13
- IChatbotUiBackendStore,
14
- IChatbotUiStore,
15
- Models,
16
- } from '@servicetitan/titan-chatbot-api';
17
- import { ChatUiSelectors, CypressMocks } from '@servicetitan/titan-chatbot-ui-cypress';
18
- import { mount } from 'cypress/react';
19
- import { FC, useMemo } from 'react';
1
+ import { runChatbotTitanChatbotSharedTests } from '@servicetitan/titan-chatbot-ui-cypress';
20
2
  import { Chatbot } from '../chatbot';
21
3
 
22
- const ChatbotBaseUrl = 'https://chatbot-api.example.com/base/url';
23
- const ChatbotVersion = 2;
24
-
25
- const initContainer = () => {
26
- const rootContainer = new Container();
27
- const container = new Container();
28
- container.parent = rootContainer;
29
- container.bind<ILog>(Log).to(CypressMocks.LogMock).inSingletonScope();
30
- container.bind<IChatbotUiStore>(CHATBOT_UI_STORE_TOKEN).to(ChatbotUiStore).inSingletonScope();
31
- container
32
- .bind<IChatbotUiBackendStore>(CHATBOT_UI_BACKEND_STORE_TOKEN)
33
- .to(ChatbotUiBackendStore)
34
- .inSingletonScope();
35
-
36
- // Set up Helpcenter API client
37
- const clientSettings: ApiClientTitanChat.IChatbotClientSettingsTitanChat = {
38
- clientId: 'TitanChatClientId',
39
- version: String(ChatbotVersion),
40
- constructorParametersFactory: () => ({
41
- baseUrl: ChatbotBaseUrl,
42
- }),
43
- };
44
- container
45
- .bind<ApiClientTitanChat.IChatbotClientSettingsTitanChat>(CHATBOT_CLIENT_SETTINGS)
46
- .toConstantValue(clientSettings);
47
- container
48
- .bind<IChatbotApiClient>(CHATBOT_API_CLIENT)
49
- .to(ApiClientTitanChat.ChatbotApiClient)
50
- .inSingletonScope();
51
- return container;
52
- };
53
-
54
4
  describe('[Chatbot with Titan Chat API]', () => {
55
- let container: Container;
56
- let log: CypressMocks.LogMock;
57
- let apiClientSettings: ApiClientTitanChat.IChatbotClientSettingsTitanChat;
58
- let apiClient: ApiClientTitanChat.ChatbotApiClient;
59
- let uiStore: ChatbotUiStore;
60
- let uiBackendStore: ChatbotUiBackendStore;
61
-
62
- beforeEach(() => {
63
- container = initContainer();
64
- log = container.get<CypressMocks.LogMock>(Log);
65
- apiClientSettings =
66
- container.get<ApiClientTitanChat.IChatbotClientSettingsTitanChat>(
67
- CHATBOT_CLIENT_SETTINGS
68
- );
69
- apiClient = container.get<ApiClientTitanChat.ChatbotApiClient>(CHATBOT_API_CLIENT);
70
- uiStore = container.get<ChatbotUiStore>(CHATBOT_UI_STORE_TOKEN);
71
- uiBackendStore = container.get<ChatbotUiBackendStore>(CHATBOT_UI_BACKEND_STORE_TOKEN);
72
- cy.viewport(550, 800);
73
- cy.clock(Date.parse('2023-10-01T00:00:00Z'));
74
- });
75
-
76
- const render = () => {
77
- const ChatbotWrapper: FC = () => {
78
- const customizationContext = useMemo<ChatbotCustomizations>(
79
- () => ({
80
- filters: { enabled: true },
81
- feedback: { title: 'TITLE' },
82
- }),
83
- []
84
- );
85
- return (
86
- <Provider
87
- singletons={[
88
- {
89
- provide: Log,
90
- useValue: log,
91
- },
92
- {
93
- provide: CHATBOT_CLIENT_SETTINGS,
94
- useValue: apiClientSettings,
95
- },
96
- { provide: CHATBOT_API_CLIENT, useValue: apiClient },
97
- {
98
- provide: CHATBOT_UI_STORE_TOKEN,
99
- useValue: uiStore,
100
- },
101
- {
102
- provide: CHATBOT_UI_BACKEND_STORE_TOKEN,
103
- useValue: uiBackendStore,
104
- },
105
- ]}
106
- >
107
- <Chatbot
108
- className="h-100vh max-h-100vh of-x-hidden"
109
- customizations={customizationContext}
110
- />
111
- </Provider>
112
- );
113
- };
114
- cy.spy(uiStore, 'run').as('runSpy');
115
- mount(<ChatbotWrapper />);
116
- return cy.wrap(
117
- new Promise(resolve => {
118
- cy.get('@runSpy')
119
- .should('have.been.calledOnce')
120
- .then((invocation: any) => {
121
- const initPromise = invocation.firstCall.returnValue as ReturnType<
122
- IChatbotUiStore['run']
123
- >;
124
- initPromise.then(resolve);
125
- });
126
- })
127
- );
128
- };
129
-
130
- it('should render the chatbot with titan chat API', () => {
131
- const optionsMock = CypressMocks.TC.Options;
132
- const sessionMock = CypressMocks.TC.SessionPost;
133
- const messageMock = CypressMocks.TC.MessagePost;
134
- const messageFeedbackMock = CypressMocks.TC.MessageFeedbackPost;
135
- const sessionFeedbackMock = CypressMocks.TC.SessionFeedbackPost;
136
-
137
- const aliasOptions = optionsMock.intercept(ChatbotBaseUrl, ChatbotVersion);
138
- const aliasSession = sessionMock.intercept(ChatbotBaseUrl, ChatbotVersion);
139
- const aliasMessage = messageMock.intercept(ChatbotBaseUrl, ChatbotVersion);
140
- const { aliasPostMessage, aliasPostSession } = CypressMocks.TC.interceptFeedback(
141
- ChatbotBaseUrl,
142
- ChatbotVersion
143
- );
144
-
145
- render().then(() => {
146
- cy.log('Getting options...');
147
- cy.wait(aliasOptions).then(interception => {
148
- expect(interception.request.url).to.equal(
149
- `${ChatbotBaseUrl}/api/v${ChatbotVersion}/options`
150
- );
151
- expect(interception.response?.statusCode).to.equal(200);
152
- expect(interception.response?.body).to.deep.equal(optionsMock.response);
153
- });
154
-
155
- // Ask the bot a question
156
- cy.log('Sending message...');
157
- ChatUiSelectors.chatInput.type(`${messageMock.request.question}{enter}`, { delay: 0 });
158
-
159
- // Check that the session was created
160
- cy.wait(aliasSession).then(interception => {
161
- expect(interception.request.url).to.equal(
162
- `${ChatbotBaseUrl}/api/v${ChatbotVersion}/session`
163
- );
164
- expect(interception.request.body).to.deep.equal(sessionMock.request);
165
- expect(interception.response?.body).to.deep.equal(sessionMock.response);
166
- expect(interception.response?.statusCode).to.equal(200);
167
- });
168
-
169
- // Check the user message is sent
170
- cy.wait(aliasMessage).then(interception => {
171
- expect(interception.request.url).to.equal(
172
- `${ChatbotBaseUrl}/api/v${ChatbotVersion}/message`
173
- );
174
- expect(interception.request.body).to.deep.equal(messageMock.request);
175
- expect(interception.response?.body).to.deep.equal(messageMock.response);
176
- expect(interception.response?.statusCode).to.equal(200);
177
- });
178
-
179
- cy.tick(100);
180
- // Send session feedback (manually)
181
- cy.log('Sending session feedback...');
182
- uiStore.sendSessionFeedback(new Models.Feedback(sessionFeedbackMock.request));
183
- cy.wait(aliasPostSession).then(interception => {
184
- expect(interception.request.url).to.equal(
185
- `${ChatbotBaseUrl}/api/v${ChatbotVersion}/feedback`
186
- );
187
- expect(interception.request.body).to.deep.equal(sessionFeedbackMock.request);
188
- expect(interception.response?.body).to.deep.equal(sessionFeedbackMock.response);
189
- expect(interception.response?.statusCode).to.equal(200);
190
- });
191
-
192
- cy.tick(100);
193
- // Check the feedback is sent with thumbs up
194
- cy.log('Sending message feedback...');
195
- ChatUiSelectors.chatbotMessageFeedbackThumbsUp.scrollIntoView().click({ force: true });
196
- cy.wait(aliasPostMessage).then(interception => {
197
- expect(interception.request.url).to.equal(
198
- `${ChatbotBaseUrl}/api/v${ChatbotVersion}/feedback`
199
- );
200
- expect(interception.request.body).to.deep.equal(messageFeedbackMock.request);
201
- expect(interception.response?.body).to.deep.equal(messageFeedbackMock.response);
202
- expect(interception.response?.statusCode).to.equal(200);
203
- });
204
- });
205
- });
5
+ runChatbotTitanChatbotSharedTests(Chatbot);
206
6
  });
@@ -1,201 +1,6 @@
1
- import { ILog, Log } from '@servicetitan/log-service';
2
- import { Container, Provider } from '@servicetitan/react-ioc';
3
- import {
4
- CHATBOT_API_CLIENT,
5
- CHATBOT_UI_BACKEND_STORE_TOKEN,
6
- CHATBOT_UI_STORE_TOKEN,
7
- ChatbotCustomizations,
8
- ChatbotUiBackendStore,
9
- ChatbotUiStore,
10
- IChatbotApiClient,
11
- IChatbotUiBackendStore,
12
- IChatbotUiStore,
13
- Models,
14
- ModelsMocks,
15
- } from '@servicetitan/titan-chatbot-api';
16
- import { ChatUiSelectors, CypressMocks } from '@servicetitan/titan-chatbot-ui-cypress';
17
- import { mount } from 'cypress/react';
18
- import { FC, useMemo } from 'react';
1
+ import { runChatbotSharedTests } from '@servicetitan/titan-chatbot-ui-cypress';
19
2
  import { Chatbot } from '../chatbot';
20
3
 
21
- const initContainer = () => {
22
- const rootContainer = new Container();
23
- const container = new Container();
24
- container.parent = rootContainer;
25
- container.bind<ILog>(Log).to(CypressMocks.LogMock).inSingletonScope();
26
- container
27
- .bind<IChatbotApiClient>(CHATBOT_API_CLIENT)
28
- .to(CypressMocks.ChatbotApiClientMock)
29
- .inSingletonScope();
30
- container.bind<IChatbotUiStore>(CHATBOT_UI_STORE_TOKEN).to(ChatbotUiStore).inSingletonScope();
31
- container
32
- .bind<IChatbotUiBackendStore>(CHATBOT_UI_BACKEND_STORE_TOKEN)
33
- .to(ChatbotUiBackendStore)
34
- .inSingletonScope();
35
- return container;
36
- };
37
-
38
4
  describe('[Chatbot]', () => {
39
- let container: Container;
40
- let log: CypressMocks.LogMock;
41
- let api: CypressMocks.ChatbotApiClientMock;
42
- let uiStore: ChatbotUiStore;
43
- let uiBackendStore: ChatbotUiBackendStore;
44
-
45
- const mockApi = () => {
46
- api.postSession = cy.stub().resolves(ModelsMocks.mockSession());
47
- api.getOptions = cy.stub().resolves(ModelsMocks.mockFrontendModel());
48
- };
49
-
50
- beforeEach(() => {
51
- container = initContainer();
52
- log = container.get<CypressMocks.LogMock>(Log);
53
- api = container.get<CypressMocks.ChatbotApiClientMock>(CHATBOT_API_CLIENT);
54
- uiStore = container.get<ChatbotUiStore>(CHATBOT_UI_STORE_TOKEN);
55
- uiBackendStore = container.get<ChatbotUiBackendStore>(CHATBOT_UI_BACKEND_STORE_TOKEN);
56
- mockApi();
57
- cy.viewport(550, 800);
58
- cy.clock(Date.parse('2023-10-01T00:00:00Z'));
59
- });
60
-
61
- const mockAskBotApi = (answer: string) => {
62
- api.postMessage = cy.stub().as('askBot').resolves(
63
- ModelsMocks.mockBotMessage({
64
- answer,
65
- })
66
- );
67
- api.postFeedback = cy
68
- .stub()
69
- .as('askBotFeedback')
70
- .resolves(
71
- ModelsMocks.mockFeedback({
72
- rating: Models.FeedbackRatings.ThumbsUp,
73
- })
74
- );
75
- };
76
-
77
- const render = () => {
78
- const ChatbotWrapper: FC = () => {
79
- const customizationContext = useMemo<ChatbotCustomizations>(
80
- () => ({
81
- filters: { enabled: true },
82
- feedback: { title: 'TITLE' },
83
- }),
84
- []
85
- );
86
- return (
87
- <Provider
88
- singletons={[
89
- {
90
- provide: Log,
91
- useValue: log,
92
- },
93
- { provide: CHATBOT_API_CLIENT, useValue: api },
94
- {
95
- provide: CHATBOT_UI_STORE_TOKEN,
96
- useValue: uiStore,
97
- },
98
- {
99
- provide: CHATBOT_UI_BACKEND_STORE_TOKEN,
100
- useValue: uiBackendStore,
101
- },
102
- ]}
103
- >
104
- <Chatbot
105
- className="h-100vh max-h-100vh of-x-hidden"
106
- customizations={customizationContext}
107
- />
108
- </Provider>
109
- );
110
- };
111
- cy.spy(uiStore, 'run').as('runSpy');
112
- mount(<ChatbotWrapper />);
113
- return cy.wrap(
114
- new Promise(resolve => {
115
- cy.get('@runSpy')
116
- .should('have.been.calledOnce')
117
- .then((invocation: any) => {
118
- const initPromise = invocation.firstCall.returnValue as ReturnType<
119
- IChatbotUiStore['run']
120
- >;
121
- initPromise.then(resolve);
122
- });
123
- })
124
- );
125
- };
126
-
127
- const ask = (message: string) => {
128
- ChatUiSelectors.chatInput.type(`${message}{enter}`, { delay: 0 });
129
- };
130
-
131
- const triggerFile = () => {
132
- uiStore.setFilePickerEnabled(true);
133
- ChatUiSelectors.chatUploadFile.should('be.visible');
134
- };
135
-
136
- it('should render the chatbot component', () => {
137
- mockAskBotApi("Hello! I'm chatbot mocked response!!!");
138
- render().then(() => {
139
- // Pick filter
140
- ChatUiSelectors.chatbotFilterButton.first().click();
141
- ChatUiSelectors.chatbotFilterOptions.find('label').first().click();
142
- ChatUiSelectors.chatbotFilterButton.first().click();
143
-
144
- // Ask chatbot
145
- ask('Hello, Chatbot!');
146
- ask('Hello, Chatbot! Hello, Chatbot! Hello, Chatbot! Hello, Chatbot! Hello, Chatbot!');
147
- triggerFile();
148
-
149
- // Check if the message is sent
150
- cy.get('@askBot').should('have.been.calledTwice');
151
-
152
- cy.tick(1000);
153
- });
154
- });
155
-
156
- describe('with error handling', () => {
157
- const mockAskBotApiError = (errorMessage: string) => {
158
- api.postMessage = cy.stub().as('askBot').rejects(new Error(errorMessage));
159
- };
160
-
161
- it('should react on send message API error', () => {
162
- mockAskBotApiError('Chatbot API error');
163
- render()
164
- .then(() => {
165
- ask('Hello, Chatbot!');
166
-
167
- ChatUiSelectors.chatMessageFooter
168
- .last()
169
- .should('be.visible')
170
- .should('contain.text', 'Message not delivered. Retry');
171
- const errorBanner = ChatUiSelectors.chatError.should('be.visible');
172
- errorBanner
173
- .find('.Banner__text > .Banner__title')
174
- .should('contain.text', 'Chat Error');
175
- ChatUiSelectors.chatErrorText
176
- .should('be.visible')
177
- .should('contain.text', 'Failed to send message');
178
- })
179
- .then(() => {
180
- mockAskBotApi("Hello! I'm chatbot mocked response!!!");
181
- ChatUiSelectors.chatMessageErrorRetry.click();
182
- ChatUiSelectors.chatMessageContentAgent
183
- .should('be.visible')
184
- .should('contain.text', "Hello! I'm chatbot mocked response!!!");
185
- });
186
- });
187
- });
188
-
189
- describe('with message feedback', () => {
190
- it('should render the chatbot component', () => {
191
- mockAskBotApi("Hello! I'm chatbot mocked response!!!");
192
- render();
193
- ask('Hello!');
194
- ChatUiSelectors.chatbotMessageFeedbackThumbsUp.should('be.visible').click();
195
- cy.get('@askBotFeedback').should('have.been.calledOnce');
196
- ChatUiSelectors.chatbotMessageFeedbackSuccess
197
- .should('be.visible')
198
- .should('contain.text', 'Thanks for your feedback.');
199
- });
200
- });
5
+ runChatbotSharedTests(Chatbot);
201
6
  });
@@ -13,7 +13,7 @@ import {
13
13
  ChatbotCustomizations,
14
14
  } from '@servicetitan/titan-chatbot-api';
15
15
  import { observer } from 'mobx-react';
16
- import { FC, useEffect, useMemo } from 'react';
16
+ import { CSSProperties, FC, useEffect, useMemo } from 'react';
17
17
  import { ChatbotToChatProviderAdapter } from './chatbot-to-chat-provider-adapter';
18
18
  import { ChatFilters } from './filters/chatbot-filters';
19
19
  import { ChatbotMessageAnswer } from './messages/chatbot-message-answer';
@@ -24,72 +24,75 @@ import { ChatbotMessageTemplateAgent } from './templates/chatbot-message-templat
24
24
 
25
25
  export interface IChatbotProps {
26
26
  className?: string;
27
+ style?: CSSProperties;
27
28
  customizations?: ChatbotCustomizations;
28
29
  onReady?: () => void;
29
30
  }
30
31
 
31
- export const Chatbot: FC<IChatbotProps> = observer(({ className, customizations, onReady }) => {
32
- const [chatUiStore, chatUiBackendStore] = useDependencies(
33
- CHATBOT_UI_STORE_TOKEN,
34
- CHATBOT_UI_BACKEND_STORE_TOKEN
35
- );
36
- useEffect(() => {
37
- const init = async () => {
38
- chatUiBackendStore.subscribe();
39
- await chatUiStore.run({
40
- agentName: 'Titan',
41
- agentIcon: ChatParticipantIcon.Bot,
42
- });
43
- onReady?.();
44
- };
45
- init().then(() => {});
46
- return () => chatUiBackendStore.unsubscribe();
47
- // eslint-disable-next-line react-hooks/exhaustive-deps
48
- }, []);
32
+ export const Chatbot: FC<IChatbotProps> = observer(
33
+ ({ className, customizations, onReady, style }) => {
34
+ const [chatUiStore, chatUiBackendStore] = useDependencies(
35
+ CHATBOT_UI_STORE_TOKEN,
36
+ CHATBOT_UI_BACKEND_STORE_TOKEN
37
+ );
38
+ useEffect(() => {
39
+ const init = async () => {
40
+ chatUiBackendStore.subscribe();
41
+ await chatUiStore.run({
42
+ agentName: 'Titan',
43
+ agentIcon: ChatParticipantIcon.Bot,
44
+ });
45
+ onReady?.();
46
+ };
47
+ init().then(() => {});
48
+ return () => chatUiBackendStore.unsubscribe();
49
+ // eslint-disable-next-line react-hooks/exhaustive-deps
50
+ }, []);
49
51
 
50
- const customizationsMerged = useMemo<ChatbotCustomizations>(() => {
51
- const templateCustomizations = customizations?.messageTemplates ?? {};
52
- templateCustomizations.agent = templateCustomizations.agent ?? {
53
- predicate: message => message.participant.isAgent,
54
- component: ChatbotMessageTemplateAgent,
55
- };
56
- return {
57
- error: customizations?.error,
58
- filters: customizations?.filters,
59
- feedback: customizations?.feedback,
60
- footerComponent: customizations?.footerComponent,
61
- messageTyping: customizations?.messageTyping ?? {
62
- component: ChatbotMessageTyping,
63
- },
64
- messages: [
65
- ...(customizations?.messages ?? []),
66
- {
67
- predicate: (message: ChatMessageModelWelcome) => message.type === 'welcome',
68
- component: ChatbotMessageWelcome,
52
+ const customizationsMerged = useMemo<ChatbotCustomizations>(() => {
53
+ const templateCustomizations = customizations?.messageTemplates ?? {};
54
+ templateCustomizations.agent = templateCustomizations.agent ?? {
55
+ predicate: message => message.participant.isAgent,
56
+ component: ChatbotMessageTemplateAgent,
57
+ };
58
+ return {
59
+ error: customizations?.error,
60
+ filters: customizations?.filters,
61
+ feedback: customizations?.feedback,
62
+ footerComponent: customizations?.footerComponent,
63
+ messageTyping: customizations?.messageTyping ?? {
64
+ component: ChatbotMessageTyping,
69
65
  },
70
- {
71
- predicate: (message: ChatMessageModelText) =>
72
- message.participant.isAgent && message.type === 'message',
73
- component: ChatbotMessageAnswer,
74
- },
75
- {
76
- predicate: (message: ChatMessageModelTimeout) => message.type === 'timeout',
77
- component: ChatbotMessageTimeout,
78
- isSystem: true,
79
- },
80
- ],
81
- messageTemplates: templateCustomizations,
82
- };
83
- }, [customizations]);
66
+ messages: [
67
+ ...(customizations?.messages ?? []),
68
+ {
69
+ predicate: (message: ChatMessageModelWelcome) => message.type === 'welcome',
70
+ component: ChatbotMessageWelcome,
71
+ },
72
+ {
73
+ predicate: (message: ChatMessageModelText) =>
74
+ message.participant.isAgent && message.type === 'message',
75
+ component: ChatbotMessageAnswer,
76
+ },
77
+ {
78
+ predicate: (message: ChatMessageModelTimeout) => message.type === 'timeout',
79
+ component: ChatbotMessageTimeout,
80
+ isSystem: true,
81
+ },
82
+ ],
83
+ messageTemplates: templateCustomizations,
84
+ };
85
+ }, [customizations]);
84
86
 
85
- return (
86
- <ChatbotToChatProviderAdapter>
87
- <Stack direction="column" className={className}>
88
- <Chat className="flex-grow-1 h-0" customizations={customizationsMerged} />
89
- {Boolean(customizationsMerged.filters?.enabled) && (
90
- <ChatFilters className="p-x-3 p-b-2" />
91
- )}
92
- </Stack>
93
- </ChatbotToChatProviderAdapter>
94
- );
95
- });
87
+ return (
88
+ <ChatbotToChatProviderAdapter>
89
+ <Stack direction="column" className={className} style={style}>
90
+ <Chat className="flex-grow-1 h-0" customizations={customizationsMerged} />
91
+ {Boolean(customizationsMerged.filters?.enabled) && (
92
+ <ChatFilters className="p-x-3 p-b-2" />
93
+ )}
94
+ </Stack>
95
+ </ChatbotToChatProviderAdapter>
96
+ );
97
+ }
98
+ );
@@ -0,0 +1,6 @@
1
+ import { runChatbotRestartDialogSharedTests } from '@servicetitan/titan-chatbot-ui-cypress';
2
+ import { ChatbotRestartDialog } from '../chatbot-restart-dialog';
3
+
4
+ describe('[ChatbotRestartDialog]', () => {
5
+ runChatbotRestartDialogSharedTests(ChatbotRestartDialog);
6
+ });
@@ -0,0 +1,6 @@
1
+ import { runChatbotRestartLinkSharedTests } from '@servicetitan/titan-chatbot-ui-cypress';
2
+ import { ChatbotRestartLink } from '../chatbot-restart-link';
3
+
4
+ describe('[ChatbotRestartLink]', () => {
5
+ runChatbotRestartLinkSharedTests(ChatbotRestartLink);
6
+ });
@@ -29,6 +29,7 @@ export const ChatbotRestartDialog: FC<{ confirmText: string; onClose: () => void
29
29
  primaryActionName="Restart"
30
30
  onSecondaryActionClick={handleCloseDialog}
31
31
  secondaryActionName="Cancel"
32
+ data-cy="titan-chatbot-restart-dialog"
32
33
  >
33
34
  {confirmText}
34
35
  </Dialog>
@@ -1,89 +1,6 @@
1
- import { Button } from '@servicetitan/design-system';
2
- import { Container, Provider, useDependencies } from '@servicetitan/react-ioc';
3
- import { ChatUiSelectors } from '@servicetitan/titan-chatbot-ui-cypress';
4
- import { mount } from 'cypress/react';
5
- import { FC } from 'react';
6
- import { MessageFeedbackGuardrailStore } from '../../../../stores/message-feedback-guardrail.store';
1
+ import { runChatbotMessageFeedbackFormGuardrailSharedTests } from '@servicetitan/titan-chatbot-ui-cypress';
7
2
  import { ChatbotMessageFeedbackFormGuardrail } from '../chatbot-message-feedback-form-guardrail';
8
3
 
9
- const initContainer = () => {
10
- const rootContainer = new Container();
11
- const container = new Container();
12
- container.parent = rootContainer;
13
- container.bind(MessageFeedbackGuardrailStore).toSelf().inSingletonScope();
14
- return container;
15
- };
16
-
17
4
  describe('[ChatbotMessageFeedbackFormGuardrail]', () => {
18
- let container: Container;
19
- let messageFeedbackGuardrailStore: MessageFeedbackGuardrailStore;
20
-
21
- beforeEach(() => {
22
- container = initContainer();
23
- messageFeedbackGuardrailStore = container.get(MessageFeedbackGuardrailStore);
24
- cy.viewport(780, 800);
25
- });
26
-
27
- const getFormError = () => cy.get('[data-anvil-component="Banner"]');
28
- const checkValid = () => {
29
- cy.getCy('validator').click();
30
- getFormError().should('not.exist');
31
- };
32
- const checkInvalid = () => {
33
- cy.getCy('validator').click();
34
- getFormError().should('be.visible');
35
- };
36
-
37
- const render = () => {
38
- const Wrapper: FC = () => {
39
- const Validator: FC = () => {
40
- const [formStore] = useDependencies(MessageFeedbackGuardrailStore);
41
- return (
42
- <Button
43
- className="m-t-3"
44
- data-cy="validator"
45
- onClick={() => formStore.formState.validate()}
46
- >
47
- Validate
48
- </Button>
49
- );
50
- };
51
- return (
52
- <Provider
53
- singletons={[
54
- {
55
- provide: MessageFeedbackGuardrailStore,
56
- useValue: messageFeedbackGuardrailStore,
57
- },
58
- ]}
59
- >
60
- <ChatbotMessageFeedbackFormGuardrail />
61
- <Validator />
62
- </Provider>
63
- );
64
- };
65
- mount(<Wrapper />);
66
- };
67
-
68
- it('should render form', () => {
69
- render();
70
-
71
- ChatUiSelectors.chatbotMessageFeedback.should('be.visible');
72
- ChatUiSelectors.chatbotMessageFeedbackLinkUrl.should('be.visible');
73
- ChatUiSelectors.chatbotMessageFeedbackOtherComment.should('be.visible');
74
- checkInvalid();
75
- getFormError().find('.Banner__title').should('contain.text', 'Form Incomplete');
76
- getFormError()
77
- .find('ul > li')
78
- .should('contain.text', 'At least one input must be provided.');
79
-
80
- ChatUiSelectors.chatbotMessageFeedbackLinkUrl.type('https://example.com');
81
- checkValid();
82
-
83
- ChatUiSelectors.chatbotMessageFeedbackLinkUrl.clear();
84
- checkInvalid();
85
-
86
- ChatUiSelectors.chatbotMessageFeedbackOtherComment.type('Test comment');
87
- checkValid();
88
- });
5
+ runChatbotMessageFeedbackFormGuardrailSharedTests(ChatbotMessageFeedbackFormGuardrail);
89
6
  });