@servicetitan/titan-chatbot-ui-cypress 3.0.1 → 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 (178) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/mocks/chatbot-api-client.mock.d.ts +0 -1
  7. package/dist/mocks/chatbot-api-client.mock.d.ts.map +1 -1
  8. package/dist/mocks/chatbot-api-client.mock.js +0 -6
  9. package/dist/mocks/chatbot-api-client.mock.js.map +1 -1
  10. package/dist/tests/index.d.ts +30 -0
  11. package/dist/tests/index.d.ts.map +1 -0
  12. package/dist/tests/index.js +30 -0
  13. package/dist/tests/index.js.map +1 -0
  14. package/dist/tests/titan-chat-ui/chat-error.shared-tests.d.ts +3 -0
  15. package/dist/tests/titan-chat-ui/chat-error.shared-tests.d.ts.map +1 -0
  16. package/dist/tests/titan-chat-ui/chat-error.shared-tests.js +138 -0
  17. package/dist/tests/titan-chat-ui/chat-error.shared-tests.js.map +1 -0
  18. package/dist/tests/titan-chat-ui/chat-input-file.shared-tests.d.ts +7 -0
  19. package/dist/tests/titan-chat-ui/chat-input-file.shared-tests.d.ts.map +1 -0
  20. package/dist/tests/titan-chat-ui/chat-input-file.shared-tests.js +123 -0
  21. package/dist/tests/titan-chat-ui/chat-input-file.shared-tests.js.map +1 -0
  22. package/dist/tests/titan-chat-ui/chat-input.shared-tests.d.ts +3 -0
  23. package/dist/tests/titan-chat-ui/chat-input.shared-tests.d.ts.map +1 -0
  24. package/dist/tests/titan-chat-ui/chat-input.shared-tests.js +71 -0
  25. package/dist/tests/titan-chat-ui/chat-input.shared-tests.js.map +1 -0
  26. package/dist/tests/titan-chat-ui/chat-log.shared-tests.d.ts +9 -0
  27. package/dist/tests/titan-chat-ui/chat-log.shared-tests.d.ts.map +1 -0
  28. package/dist/tests/titan-chat-ui/chat-log.shared-tests.js +73 -0
  29. package/dist/tests/titan-chat-ui/chat-log.shared-tests.js.map +1 -0
  30. package/dist/tests/titan-chat-ui/chat-messages.shared-tests.d.ts +8 -0
  31. package/dist/tests/titan-chat-ui/chat-messages.shared-tests.d.ts.map +1 -0
  32. package/dist/tests/titan-chat-ui/chat-messages.shared-tests.js +118 -0
  33. package/dist/tests/titan-chat-ui/chat-messages.shared-tests.js.map +1 -0
  34. package/dist/tests/titan-chat-ui/chat-notifications.shared-tests.d.ts +3 -0
  35. package/dist/tests/titan-chat-ui/chat-notifications.shared-tests.d.ts.map +1 -0
  36. package/dist/tests/titan-chat-ui/chat-notifications.shared-tests.js +110 -0
  37. package/dist/tests/titan-chat-ui/chat-notifications.shared-tests.js.map +1 -0
  38. package/dist/tests/titan-chat-ui/chat-timer.shared-tests.d.ts +3 -0
  39. package/dist/tests/titan-chat-ui/chat-timer.shared-tests.d.ts.map +1 -0
  40. package/dist/tests/titan-chat-ui/chat-timer.shared-tests.js +76 -0
  41. package/dist/tests/titan-chat-ui/chat-timer.shared-tests.js.map +1 -0
  42. package/dist/tests/titan-chat-ui/chat.shared-tests.d.ts +9 -0
  43. package/dist/tests/titan-chat-ui/chat.shared-tests.d.ts.map +1 -0
  44. package/dist/tests/titan-chat-ui/chat.shared-tests.js +111 -0
  45. package/dist/tests/titan-chat-ui/chat.shared-tests.js.map +1 -0
  46. package/dist/tests/titan-chat-ui/message-agent.shared-tests.d.ts +27 -0
  47. package/dist/tests/titan-chat-ui/message-agent.shared-tests.d.ts.map +1 -0
  48. package/dist/tests/titan-chat-ui/message-agent.shared-tests.js +67 -0
  49. package/dist/tests/titan-chat-ui/message-agent.shared-tests.js.map +1 -0
  50. package/dist/tests/titan-chat-ui/message-content-file.shared-tests.d.ts +10 -0
  51. package/dist/tests/titan-chat-ui/message-content-file.shared-tests.d.ts.map +1 -0
  52. package/dist/tests/titan-chat-ui/message-content-file.shared-tests.js +88 -0
  53. package/dist/tests/titan-chat-ui/message-content-file.shared-tests.js.map +1 -0
  54. package/dist/tests/titan-chat-ui/message-system.shared-tests.d.ts +16 -0
  55. package/dist/tests/titan-chat-ui/message-system.shared-tests.d.ts.map +1 -0
  56. package/dist/tests/titan-chat-ui/message-system.shared-tests.js +65 -0
  57. package/dist/tests/titan-chat-ui/message-system.shared-tests.js.map +1 -0
  58. package/dist/tests/titan-chat-ui/message-timeout.shared-tests.d.ts +8 -0
  59. package/dist/tests/titan-chat-ui/message-timeout.shared-tests.d.ts.map +1 -0
  60. package/dist/tests/titan-chat-ui/message-timeout.shared-tests.js +63 -0
  61. package/dist/tests/titan-chat-ui/message-timeout.shared-tests.js.map +1 -0
  62. package/dist/tests/titan-chat-ui/message-typing.shared-tests.d.ts +12 -0
  63. package/dist/tests/titan-chat-ui/message-typing.shared-tests.d.ts.map +1 -0
  64. package/dist/tests/titan-chat-ui/message-typing.shared-tests.js +46 -0
  65. package/dist/tests/titan-chat-ui/message-typing.shared-tests.js.map +1 -0
  66. package/dist/tests/titan-chat-ui/message-user.shared-tests.d.ts +10 -0
  67. package/dist/tests/titan-chat-ui/message-user.shared-tests.d.ts.map +1 -0
  68. package/dist/tests/titan-chat-ui/message-user.shared-tests.js +64 -0
  69. package/dist/tests/titan-chat-ui/message-user.shared-tests.js.map +1 -0
  70. package/dist/tests/titan-chatbot-ui/chatbot-filters.shared-tests.d.ts +7 -0
  71. package/dist/tests/titan-chatbot-ui/chatbot-filters.shared-tests.d.ts.map +1 -0
  72. package/dist/tests/titan-chatbot-ui/chatbot-filters.shared-tests.js +118 -0
  73. package/dist/tests/titan-chatbot-ui/chatbot-filters.shared-tests.js.map +1 -0
  74. package/dist/tests/titan-chatbot-ui/chatbot-help-center.shared-tests.d.ts +9 -0
  75. package/dist/tests/titan-chatbot-ui/chatbot-help-center.shared-tests.d.ts.map +1 -0
  76. package/dist/tests/titan-chatbot-ui/chatbot-help-center.shared-tests.js +162 -0
  77. package/dist/tests/titan-chatbot-ui/chatbot-help-center.shared-tests.js.map +1 -0
  78. package/dist/tests/titan-chatbot-ui/chatbot-links.shared-tests.d.ts +15 -0
  79. package/dist/tests/titan-chatbot-ui/chatbot-links.shared-tests.d.ts.map +1 -0
  80. package/dist/tests/titan-chatbot-ui/chatbot-links.shared-tests.js +123 -0
  81. package/dist/tests/titan-chatbot-ui/chatbot-links.shared-tests.js.map +1 -0
  82. package/dist/tests/titan-chatbot-ui/chatbot-live.shared-tests.d.ts +9 -0
  83. package/dist/tests/titan-chatbot-ui/chatbot-live.shared-tests.d.ts.map +1 -0
  84. package/dist/tests/titan-chatbot-ui/chatbot-live.shared-tests.js +84 -0
  85. package/dist/tests/titan-chatbot-ui/chatbot-live.shared-tests.js.map +1 -0
  86. package/dist/tests/titan-chatbot-ui/chatbot-message-answer-readonly.shared-tests.d.ts +8 -0
  87. package/dist/tests/titan-chatbot-ui/chatbot-message-answer-readonly.shared-tests.d.ts.map +1 -0
  88. package/dist/tests/titan-chatbot-ui/chatbot-message-answer-readonly.shared-tests.js +142 -0
  89. package/dist/tests/titan-chatbot-ui/chatbot-message-answer-readonly.shared-tests.js.map +1 -0
  90. package/dist/tests/titan-chatbot-ui/chatbot-message-answer.shared-tests.d.ts +8 -0
  91. package/dist/tests/titan-chatbot-ui/chatbot-message-answer.shared-tests.d.ts.map +1 -0
  92. package/dist/tests/titan-chatbot-ui/chatbot-message-answer.shared-tests.js +105 -0
  93. package/dist/tests/titan-chatbot-ui/chatbot-message-answer.shared-tests.js.map +1 -0
  94. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-form-guardrail.shared-tests.d.ts +3 -0
  95. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-form-guardrail.shared-tests.d.ts.map +1 -0
  96. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-form-guardrail.shared-tests.js +86 -0
  97. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-form-guardrail.shared-tests.js.map +1 -0
  98. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-form.shared-tests.d.ts +3 -0
  99. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-form.shared-tests.d.ts.map +1 -0
  100. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-form.shared-tests.js +143 -0
  101. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-form.shared-tests.js.map +1 -0
  102. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-popover.shared-tests.d.ts +8 -0
  103. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-popover.shared-tests.d.ts.map +1 -0
  104. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-popover.shared-tests.js +200 -0
  105. package/dist/tests/titan-chatbot-ui/chatbot-message-feedback-popover.shared-tests.js.map +1 -0
  106. package/dist/tests/titan-chatbot-ui/chatbot-message-typing.shared-tests.d.ts +11 -0
  107. package/dist/tests/titan-chatbot-ui/chatbot-message-typing.shared-tests.d.ts.map +1 -0
  108. package/dist/tests/titan-chatbot-ui/chatbot-message-typing.shared-tests.js +81 -0
  109. package/dist/tests/titan-chatbot-ui/chatbot-message-typing.shared-tests.js.map +1 -0
  110. package/dist/tests/titan-chatbot-ui/chatbot-restart-dialog.shared-tests.d.ts +8 -0
  111. package/dist/tests/titan-chatbot-ui/chatbot-restart-dialog.shared-tests.d.ts.map +1 -0
  112. package/dist/tests/titan-chatbot-ui/chatbot-restart-dialog.shared-tests.js +60 -0
  113. package/dist/tests/titan-chatbot-ui/chatbot-restart-dialog.shared-tests.js.map +1 -0
  114. package/dist/tests/titan-chatbot-ui/chatbot-restart-link.shared-tests.d.ts +8 -0
  115. package/dist/tests/titan-chatbot-ui/chatbot-restart-link.shared-tests.d.ts.map +1 -0
  116. package/dist/tests/titan-chatbot-ui/chatbot-restart-link.shared-tests.js +77 -0
  117. package/dist/tests/titan-chatbot-ui/chatbot-restart-link.shared-tests.js.map +1 -0
  118. package/dist/tests/titan-chatbot-ui/chatbot-session-feedback-modal.shared-tests.d.ts +7 -0
  119. package/dist/tests/titan-chatbot-ui/chatbot-session-feedback-modal.shared-tests.d.ts.map +1 -0
  120. package/dist/tests/titan-chatbot-ui/chatbot-session-feedback-modal.shared-tests.js +130 -0
  121. package/dist/tests/titan-chatbot-ui/chatbot-session-feedback-modal.shared-tests.js.map +1 -0
  122. package/dist/tests/titan-chatbot-ui/chatbot-titan-chatbot.shared-tests.d.ts +9 -0
  123. package/dist/tests/titan-chatbot-ui/chatbot-titan-chatbot.shared-tests.d.ts.map +1 -0
  124. package/dist/tests/titan-chatbot-ui/chatbot-titan-chatbot.shared-tests.js +157 -0
  125. package/dist/tests/titan-chatbot-ui/chatbot-titan-chatbot.shared-tests.js.map +1 -0
  126. package/dist/tests/titan-chatbot-ui/chatbot.shared-tests.d.ts +9 -0
  127. package/dist/tests/titan-chatbot-ui/chatbot.shared-tests.d.ts.map +1 -0
  128. package/dist/tests/titan-chatbot-ui/chatbot.shared-tests.js +114 -0
  129. package/dist/tests/titan-chatbot-ui/chatbot.shared-tests.js.map +1 -0
  130. package/dist/utils/chat-ui-selectors.d.ts +12 -0
  131. package/dist/utils/chat-ui-selectors.d.ts.map +1 -1
  132. package/dist/utils/chat-ui-selectors.js +24 -0
  133. package/dist/utils/chat-ui-selectors.js.map +1 -1
  134. package/dist/utils/test-utils-chatbot.d.ts +5 -0
  135. package/dist/utils/test-utils-chatbot.d.ts.map +1 -0
  136. package/dist/utils/test-utils-chatbot.js +55 -0
  137. package/dist/utils/test-utils-chatbot.js.map +1 -0
  138. package/dist/utils/test-utils.d.ts +6 -0
  139. package/dist/utils/test-utils.d.ts.map +1 -0
  140. package/dist/utils/test-utils.js +38 -0
  141. package/dist/utils/test-utils.js.map +1 -0
  142. package/package.json +10 -5
  143. package/src/index.ts +3 -0
  144. package/src/mocks/chatbot-api-client.mock.ts +0 -1
  145. package/src/tests/index.ts +30 -0
  146. package/src/tests/titan-chat-ui/chat-error.shared-tests.tsx +185 -0
  147. package/src/tests/titan-chat-ui/chat-input-file.shared-tests.tsx +182 -0
  148. package/src/tests/titan-chat-ui/chat-input.shared-tests.tsx +99 -0
  149. package/src/tests/titan-chat-ui/chat-log.shared-tests.tsx +117 -0
  150. package/src/tests/titan-chat-ui/chat-messages.shared-tests.tsx +156 -0
  151. package/src/tests/titan-chat-ui/chat-notifications.shared-tests.tsx +153 -0
  152. package/src/tests/titan-chat-ui/chat-timer.shared-tests.tsx +106 -0
  153. package/src/tests/titan-chat-ui/chat.shared-tests.tsx +158 -0
  154. package/src/tests/titan-chat-ui/message-agent.shared-tests.tsx +170 -0
  155. package/src/tests/titan-chat-ui/message-content-file.shared-tests.tsx +139 -0
  156. package/src/tests/titan-chat-ui/message-system.shared-tests.tsx +147 -0
  157. package/src/tests/titan-chat-ui/message-timeout.shared-tests.tsx +117 -0
  158. package/src/tests/titan-chat-ui/message-typing.shared-tests.tsx +92 -0
  159. package/src/tests/titan-chat-ui/message-user.shared-tests.tsx +138 -0
  160. package/src/tests/titan-chatbot-ui/chatbot-filters.shared-tests.tsx +164 -0
  161. package/src/tests/titan-chatbot-ui/chatbot-help-center.shared-tests.tsx +225 -0
  162. package/src/tests/titan-chatbot-ui/chatbot-links.shared-tests.tsx +189 -0
  163. package/src/tests/titan-chatbot-ui/chatbot-live.shared-tests.tsx +127 -0
  164. package/src/tests/titan-chatbot-ui/chatbot-message-answer-readonly.shared-tests.tsx +187 -0
  165. package/src/tests/titan-chatbot-ui/chatbot-message-answer.shared-tests.tsx +144 -0
  166. package/src/tests/titan-chatbot-ui/chatbot-message-feedback-form-guardrail.shared-tests.tsx +127 -0
  167. package/src/tests/titan-chatbot-ui/chatbot-message-feedback-form.shared-tests.tsx +198 -0
  168. package/src/tests/titan-chatbot-ui/chatbot-message-feedback-popover.shared-tests.tsx +285 -0
  169. package/src/tests/titan-chatbot-ui/chatbot-message-typing.shared-tests.tsx +112 -0
  170. package/src/tests/titan-chatbot-ui/chatbot-restart-dialog.shared-tests.tsx +91 -0
  171. package/src/tests/titan-chatbot-ui/chatbot-restart-link.shared-tests.tsx +116 -0
  172. package/src/tests/titan-chatbot-ui/chatbot-session-feedback-modal.shared-tests.tsx +182 -0
  173. package/src/tests/titan-chatbot-ui/chatbot-titan-chatbot.shared-tests.tsx +221 -0
  174. package/src/tests/titan-chatbot-ui/chatbot.shared-tests.tsx +158 -0
  175. package/src/utils/chat-ui-selectors.ts +24 -0
  176. package/src/utils/test-utils-chatbot.tsx +73 -0
  177. package/src/utils/test-utils.tsx +52 -0
  178. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,285 @@
1
+ import { Container } from '@servicetitan/react-ioc';
2
+ import {
3
+ CHATBOT_API_CLIENT,
4
+ CHATBOT_UI_STORE_TOKEN,
5
+ ChatbotUiStore,
6
+ IChatbotUiStore,
7
+ MessageFeedbackGuardrailStore,
8
+ MessageFeedbackStore,
9
+ Models,
10
+ ModelsMocks,
11
+ } from '@servicetitan/titan-chatbot-api';
12
+ import { FC, ReactElement } from 'react';
13
+ import { CypressMocks, testInitContainerChatbot, testRenderWrapperChatbot } from '../..';
14
+ import { ChatUiSelectors } from '../../utils/chat-ui-selectors';
15
+
16
+ interface IChatbotMessageFeedbackPopoverProps {
17
+ botMessage: Models.IBotMessage;
18
+ }
19
+
20
+ export function runChatbotMessageFeedbackPopoverSharedTests(
21
+ ChatbotMessageFeedbackPopoverComponent: FC<IChatbotMessageFeedbackPopoverProps>,
22
+ wrapperComponent?: (component: ReactElement) => ReactElement
23
+ ) {
24
+ let container: Container;
25
+ let api: CypressMocks.ChatbotApiClientMock;
26
+ let chatbotUiStore: IChatbotUiStore;
27
+
28
+ const mockApiClient = () => {
29
+ api.postSession = cy.stub().resolves(ModelsMocks.mockSession());
30
+ api.getOptions = cy.stub().resolves(ModelsMocks.mockFrontendModel());
31
+ api.postFeedback = cy.stub().resolves(ModelsMocks.mockFeedback());
32
+ };
33
+
34
+ beforeEach(() => {
35
+ container = testInitContainerChatbot();
36
+ container.bind(MessageFeedbackStore).toSelf().inSingletonScope();
37
+ container.bind(MessageFeedbackGuardrailStore).toSelf().inSingletonScope();
38
+ api = container.get<CypressMocks.ChatbotApiClientMock>(CHATBOT_API_CLIENT);
39
+ chatbotUiStore = container.get<ChatbotUiStore>(CHATBOT_UI_STORE_TOKEN);
40
+ mockApiClient();
41
+ cy.viewport(550, 800);
42
+ });
43
+
44
+ const render = (botMessage: Models.IBotMessage, onAfterInit?: () => void) => {
45
+ return cy.wrap(null).then(() => {
46
+ const component = <ChatbotMessageFeedbackPopoverComponent botMessage={botMessage} />;
47
+ const wrappedComponent = wrapperComponent ? wrapperComponent(component) : component;
48
+
49
+ return testRenderWrapperChatbot(container, wrappedComponent, onAfterInit);
50
+ });
51
+ };
52
+
53
+ it('should render feedback component for regular bot message', () => {
54
+ const botMessage = ModelsMocks.mockBotMessage({
55
+ answer: 'Test answer',
56
+ isGuardrailed: false,
57
+ });
58
+
59
+ render(botMessage, () => {
60
+ chatbotUiStore.customizations.feedback = { title: 'Rate this answer' };
61
+ });
62
+
63
+ // Should show feedback title
64
+ cy.contains('Rate this answer').should('be.visible');
65
+
66
+ // Should show thumbs up button
67
+ ChatUiSelectors.chatbotMessageFeedbackThumbsUp.should('be.visible');
68
+
69
+ // Should show thumbs down button
70
+ ChatUiSelectors.chatbotMessageFeedbackThumbsDown.should('be.visible');
71
+ });
72
+
73
+ it('should render feedback component for guardrail bot message', () => {
74
+ const botMessage = ModelsMocks.mockBotMessage({
75
+ answer: 'Guardrail response',
76
+ isGuardrailed: true,
77
+ });
78
+
79
+ render(botMessage, () => {
80
+ chatbotUiStore.customizations.feedback = { title: 'Rate this answer' };
81
+ });
82
+
83
+ // Should not show feedback title for guardrail
84
+ cy.contains('Rate this answer').should('not.exist');
85
+
86
+ // Should not show thumbs up button for guardrail
87
+ ChatUiSelectors.chatbotMessageFeedbackThumbsUp.should('not.exist');
88
+
89
+ // Should show "Provide correct answer" link
90
+ ChatUiSelectors.chatbotMessageFeedbackProvideAnswer.should('be.visible');
91
+ ChatUiSelectors.chatbotMessageFeedbackProvideAnswer.should(
92
+ 'contain.text',
93
+ 'Provide correct answer'
94
+ );
95
+ });
96
+
97
+ it('should handle thumbs up feedback submission', () => {
98
+ const botMessage = ModelsMocks.mockBotMessage({
99
+ answer: 'Test answer',
100
+ isGuardrailed: false,
101
+ });
102
+
103
+ render(botMessage, () => {
104
+ chatbotUiStore.sendMessageFeedback = cy.stub().resolves();
105
+ });
106
+
107
+ ChatUiSelectors.chatbotMessageFeedbackThumbsUp.click();
108
+
109
+ // Should show success state
110
+ ChatUiSelectors.chatbotMessageFeedbackSuccess.should('be.visible');
111
+ ChatUiSelectors.chatbotMessageFeedbackSuccess.should(
112
+ 'contain.text',
113
+ 'Thanks for your feedback.'
114
+ );
115
+ });
116
+
117
+ it('should handle failed feedback submission', () => {
118
+ const botMessage = ModelsMocks.mockBotMessage({
119
+ answer: 'Test answer',
120
+ isGuardrailed: false,
121
+ });
122
+
123
+ render(botMessage, () => {
124
+ chatbotUiStore.sendMessageFeedback = cy.stub().rejects(new Error('API Error'));
125
+ });
126
+
127
+ ChatUiSelectors.chatbotMessageFeedbackThumbsUp.click();
128
+
129
+ // Should show failure state
130
+ ChatUiSelectors.chatbotMessageFeedbackFailed.should('be.visible');
131
+ ChatUiSelectors.chatbotMessageFeedbackFailed.should(
132
+ 'contain.text',
133
+ 'Failed to send feedback.'
134
+ );
135
+ });
136
+
137
+ it('should open feedback form popover when thumbs down is clicked', () => {
138
+ const botMessage = ModelsMocks.mockBotMessage({
139
+ answer: 'Test answer',
140
+ isGuardrailed: false,
141
+ });
142
+
143
+ render(botMessage);
144
+
145
+ ChatUiSelectors.chatbotMessageFeedbackThumbsDown.click();
146
+
147
+ // Should show popover with form
148
+ cy.contains("Why didn't this answer solve your problem?").should('be.visible');
149
+ ChatUiSelectors.chatbotMessageFeedback.should('be.visible');
150
+ ChatUiSelectors.chatbotMessageFeedbackSubmit.should('be.visible').and('be.disabled');
151
+ });
152
+
153
+ it('should open guardrail feedback form when provide answer link is clicked', () => {
154
+ const botMessage = ModelsMocks.mockBotMessage({
155
+ answer: 'Guardrail response',
156
+ isGuardrailed: true,
157
+ });
158
+
159
+ render(botMessage);
160
+
161
+ ChatUiSelectors.chatbotMessageFeedbackProvideAnswer.click();
162
+
163
+ // Should show popover with guardrail form
164
+ cy.contains('Provide correct answer').should('be.visible');
165
+ ChatUiSelectors.chatbotMessageFeedback.should('be.visible');
166
+ ChatUiSelectors.chatbotMessageFeedbackSubmit.should('be.visible').and('be.disabled');
167
+ });
168
+
169
+ it('should enable submit button when regular feedback form is valid', () => {
170
+ const botMessage = ModelsMocks.mockBotMessage({
171
+ answer: 'Test answer',
172
+ isGuardrailed: false,
173
+ });
174
+
175
+ render(botMessage);
176
+
177
+ ChatUiSelectors.chatbotMessageFeedbackThumbsDown.click();
178
+
179
+ // Select a feedback option
180
+ ChatUiSelectors.chatbotMessageFeedbackUnrelated
181
+ .parent()
182
+ .find('input')
183
+ .click({ force: true });
184
+
185
+ // Submit button should be enabled
186
+ ChatUiSelectors.chatbotMessageFeedbackSubmit.should('be.enabled');
187
+ });
188
+
189
+ it('should enable submit button when guardrail feedback form is valid', () => {
190
+ const botMessage = ModelsMocks.mockBotMessage({
191
+ answer: 'Guardrail response',
192
+ isGuardrailed: true,
193
+ });
194
+
195
+ render(botMessage);
196
+
197
+ ChatUiSelectors.chatbotMessageFeedbackProvideAnswer.click();
198
+
199
+ // Fill in the URL field
200
+ ChatUiSelectors.chatbotMessageFeedbackLinkUrl.type('https://example.com');
201
+
202
+ // Submit button should be enabled
203
+ ChatUiSelectors.chatbotMessageFeedbackSubmit.should('be.enabled');
204
+ });
205
+
206
+ it('should submit feedback form successfully', () => {
207
+ const botMessage = ModelsMocks.mockBotMessage({
208
+ answer: 'Test answer',
209
+ isGuardrailed: false,
210
+ });
211
+
212
+ render(botMessage, () => {
213
+ chatbotUiStore.sendMessageFeedback = cy.stub().resolves();
214
+ });
215
+
216
+ ChatUiSelectors.chatbotMessageFeedbackThumbsDown.click();
217
+ ChatUiSelectors.chatbotMessageFeedbackUnrelated
218
+ .parent()
219
+ .find('input')
220
+ .click({ force: true });
221
+ ChatUiSelectors.chatbotMessageFeedbackSubmit.click();
222
+
223
+ // Should show success state
224
+ ChatUiSelectors.chatbotMessageFeedbackSuccess.should('be.visible');
225
+ });
226
+
227
+ it('should submit guardrail feedback form successfully', () => {
228
+ const botMessage = ModelsMocks.mockBotMessage({
229
+ answer: 'Guardrail response',
230
+ isGuardrailed: true,
231
+ });
232
+
233
+ render(botMessage, () => {
234
+ chatbotUiStore.sendMessageFeedback = cy.stub().resolves();
235
+ });
236
+
237
+ ChatUiSelectors.chatbotMessageFeedbackProvideAnswer.click();
238
+ ChatUiSelectors.chatbotMessageFeedbackLinkUrl.type('https://example.com');
239
+ ChatUiSelectors.chatbotMessageFeedbackSubmit.click();
240
+
241
+ // Should show success state
242
+ ChatUiSelectors.chatbotMessageFeedbackSuccess.should('be.visible');
243
+ });
244
+
245
+ it('should handle form validation errors', () => {
246
+ const botMessage = ModelsMocks.mockBotMessage({
247
+ answer: 'Test answer',
248
+ isGuardrailed: false,
249
+ });
250
+
251
+ render(botMessage);
252
+
253
+ ChatUiSelectors.chatbotMessageFeedbackThumbsDown.click();
254
+
255
+ // Try to submit without selecting feedback
256
+ ChatUiSelectors.chatbotMessageFeedbackSubmit.should('be.disabled');
257
+
258
+ // Select "Other" which requires comment
259
+ ChatUiSelectors.chatbotMessageFeedbackOther.parent().find('input').click({ force: true });
260
+
261
+ // Add comment
262
+ ChatUiSelectors.chatbotMessageFeedbackOtherComment.type('Custom feedback');
263
+
264
+ // Submit button should be enabled
265
+ ChatUiSelectors.chatbotMessageFeedbackSubmit.should('be.enabled');
266
+ });
267
+
268
+ it('should not show title when feedback customization title is not set', () => {
269
+ const botMessage = ModelsMocks.mockBotMessage({
270
+ answer: 'Test answer',
271
+ isGuardrailed: false,
272
+ });
273
+
274
+ render(botMessage, () => {
275
+ chatbotUiStore.customizations.feedback = {};
276
+ });
277
+
278
+ // Should not show any title
279
+ cy.contains('Rate this answer').should('not.exist');
280
+
281
+ // But should still show feedback buttons
282
+ ChatUiSelectors.chatbotMessageFeedbackThumbsUp.should('be.visible');
283
+ ChatUiSelectors.chatbotMessageFeedbackThumbsDown.should('be.visible');
284
+ });
285
+ }
@@ -0,0 +1,112 @@
1
+ import { Container } from '@servicetitan/react-ioc';
2
+ import { ChatParticipantIcon } from '@servicetitan/titan-chat-ui-common';
3
+ import { CHATBOT_API_CLIENT, ModelsMocks } from '@servicetitan/titan-chatbot-api';
4
+ import { FC, ReactElement } from 'react';
5
+ import { CypressMocks, testInitContainerChatbot, testRenderWrapperChatbot } from '../..';
6
+ import { ChatUiSelectors } from '../../utils/chat-ui-selectors';
7
+
8
+ interface IChatbotMessageTypingProps {
9
+ avatar: {
10
+ name: string;
11
+ icon: ChatParticipantIcon;
12
+ };
13
+ }
14
+
15
+ export function runChatbotMessageTypingSharedTests(
16
+ ChatbotMessageTypingComponent: FC<IChatbotMessageTypingProps>,
17
+ wrapperComponent?: (component: ReactElement) => ReactElement
18
+ ) {
19
+ let container: Container;
20
+ let api: CypressMocks.ChatbotApiClientMock;
21
+
22
+ const mockApiClient = () => {
23
+ api.postSession = cy.stub().resolves(ModelsMocks.mockSession());
24
+ api.getOptions = cy.stub().resolves(ModelsMocks.mockFrontendModel());
25
+ };
26
+
27
+ beforeEach(() => {
28
+ container = testInitContainerChatbot();
29
+ api = container.get<CypressMocks.ChatbotApiClientMock>(CHATBOT_API_CLIENT);
30
+ mockApiClient();
31
+ cy.viewport(550, 800);
32
+ });
33
+
34
+ const render = (icon: ChatParticipantIcon, onAfterInit?: () => void) => {
35
+ const avatar = {
36
+ name: 'Test User Name',
37
+ icon,
38
+ };
39
+ return cy.wrap(null).then(() => {
40
+ const component = <ChatbotMessageTypingComponent avatar={avatar} />;
41
+ const wrappedComponent = wrapperComponent ? wrapperComponent(component) : component;
42
+
43
+ return testRenderWrapperChatbot(container, wrappedComponent, onAfterInit);
44
+ });
45
+ };
46
+
47
+ it('should render message with bot icon', () => {
48
+ render(ChatParticipantIcon.Bot);
49
+ ChatUiSelectors.chatMessageTyping.should('be.visible');
50
+ ChatUiSelectors.chatMessageTypingDots.should('be.visible');
51
+ ChatUiSelectors.chatAvatarBot
52
+ .should('be.visible')
53
+ .invoke('css', 'background-image')
54
+ .then(bgColor => {
55
+ const isSvg = Boolean(
56
+ (bgColor as any as string).indexOf('data:image/svg+xml;base64') >= 0
57
+ );
58
+ expect(isSvg).to.eq(true);
59
+ });
60
+ });
61
+
62
+ it('should render message with empty icon', () => {
63
+ render(ChatParticipantIcon.Empty);
64
+ ChatUiSelectors.chatMessageTyping.should('be.visible');
65
+ ChatUiSelectors.chatMessageTypingDots.should('be.visible');
66
+ ChatUiSelectors.chatAvatarBot.should('not.exist');
67
+ ChatUiSelectors.chatAvatarInitials.should('not.exist');
68
+ });
69
+
70
+ it('should render message with initials', () => {
71
+ render(ChatParticipantIcon.Initials);
72
+ ChatUiSelectors.chatMessageTyping.should('be.visible');
73
+ ChatUiSelectors.chatMessageTypingDots.should('be.visible');
74
+ ChatUiSelectors.chatAvatarInitials.should('be.visible').should('contain.text', 'TU');
75
+ });
76
+
77
+ it('should show different messages during the time', () => {
78
+ cy.clock(Date.parse('2023-10-01T00:00:00Z'));
79
+ render(ChatParticipantIcon.Bot);
80
+
81
+ // Verify the rendered component shows typing dots initially
82
+ ChatUiSelectors.chatMessageTyping.should('be.visible');
83
+ ChatUiSelectors.chatMessageTypingDots.should('be.visible');
84
+
85
+ // After 10 seconds, should show first message
86
+ cy.tick(10000);
87
+ ChatUiSelectors.chatMessageContentAgent
88
+ .should('be.visible')
89
+ .should('contain.text', 'Looking for the final details');
90
+ ChatUiSelectors.chatMessageTypingDots.should('not.exist');
91
+
92
+ // After 20 seconds total, should show second message
93
+ cy.tick(10000);
94
+ ChatUiSelectors.chatMessageContentAgent
95
+ .should('be.visible')
96
+ .should(
97
+ 'contain.text',
98
+ 'This is taking longer than usual, please give me a little bit more time'
99
+ );
100
+ ChatUiSelectors.chatMessageTypingDots.should('not.exist');
101
+
102
+ // After additional time, should still show the same message
103
+ cy.tick(60000);
104
+ ChatUiSelectors.chatMessageContentAgent
105
+ .should('be.visible')
106
+ .should(
107
+ 'contain.text',
108
+ 'This is taking longer than usual, please give me a little bit more time'
109
+ );
110
+ ChatUiSelectors.chatMessageTypingDots.should('not.exist');
111
+ });
112
+ }
@@ -0,0 +1,91 @@
1
+ import { Container } from '@servicetitan/react-ioc';
2
+ import {
3
+ CHATBOT_API_CLIENT,
4
+ CHATBOT_UI_STORE_TOKEN,
5
+ ChatbotUiStore,
6
+ IChatbotUiStore,
7
+ ModelsMocks,
8
+ } from '@servicetitan/titan-chatbot-api';
9
+ import { FC, ReactElement } from 'react';
10
+ import { CypressMocks } from '../..';
11
+ import { ChatUiSelectors } from '../../utils/chat-ui-selectors';
12
+ import { testInitContainerChatbot, testRenderWrapperChatbot } from '../../utils/test-utils-chatbot';
13
+
14
+ interface IChatbotRestartDialogProps {
15
+ confirmText: string;
16
+ onClose: () => void;
17
+ }
18
+
19
+ export function runChatbotRestartDialogSharedTests(
20
+ ChatbotRestartDialogComponent: FC<IChatbotRestartDialogProps>,
21
+ wrapperComponent?: (component: ReactElement) => ReactElement
22
+ ) {
23
+ let container: Container;
24
+ let api: CypressMocks.ChatbotApiClientMock;
25
+ let chatbotUiStore: IChatbotUiStore;
26
+
27
+ const mockApiClient = () => {
28
+ api.postSession = cy.stub().resolves(ModelsMocks.mockSession());
29
+ api.getOptions = cy.stub().resolves(ModelsMocks.mockFrontendModel());
30
+ };
31
+
32
+ beforeEach(() => {
33
+ container = testInitContainerChatbot();
34
+ api = container.get<CypressMocks.ChatbotApiClientMock>(CHATBOT_API_CLIENT);
35
+ chatbotUiStore = container.get<ChatbotUiStore>(CHATBOT_UI_STORE_TOKEN);
36
+ mockApiClient();
37
+ cy.viewport(550, 800);
38
+ });
39
+
40
+ const render = (confirmText: string, onClose: () => void, onAfterInit?: () => void) => {
41
+ return cy.wrap(null).then(() => {
42
+ const component = (
43
+ <ChatbotRestartDialogComponent confirmText={confirmText} onClose={onClose} />
44
+ );
45
+ const wrappedComponent = wrapperComponent ? wrapperComponent(component) : component;
46
+
47
+ return testRenderWrapperChatbot(container, wrappedComponent, onAfterInit);
48
+ });
49
+ };
50
+
51
+ it('should render restart dialog with confirm text', () => {
52
+ const onClose = cy.stub();
53
+ const confirmText = 'Are you sure you want to restart the conversation?';
54
+
55
+ render(confirmText, onClose);
56
+
57
+ ChatUiSelectors.chatbotRestartDialog.should('be.visible').within(() => {
58
+ cy.contains(confirmText).should('be.visible');
59
+ cy.get('button').contains('Restart').should('be.visible');
60
+ cy.get('button').contains('Cancel').should('be.visible');
61
+ });
62
+ });
63
+
64
+ it('should call onClose when Cancel button is clicked', () => {
65
+ const onClose = cy.stub().as('onClose');
66
+ const confirmText = 'Test confirmation text';
67
+
68
+ render(confirmText, onClose);
69
+
70
+ ChatUiSelectors.chatbotRestartDialog.should('be.visible').within(() => {
71
+ cy.get('button').contains('Cancel').closest('button').click();
72
+ });
73
+ cy.get('@onClose').should('have.been.called');
74
+ });
75
+
76
+ it('should call restart and onClose when Restart button is clicked', () => {
77
+ const onClose = cy.stub().as('onClose');
78
+ const confirmText = 'Test confirmation text';
79
+
80
+ render(confirmText, onClose).then(() => {
81
+ cy.spy(chatbotUiStore, 'restart').as('restart');
82
+ });
83
+
84
+ ChatUiSelectors.chatbotRestartDialog.should('be.visible').within(() => {
85
+ cy.get('button').contains('Restart').closest('button').click();
86
+ });
87
+ cy.wait(1);
88
+ cy.get('@onClose').should('have.been.called');
89
+ cy.get('@restart').should('have.been.called');
90
+ });
91
+ }
@@ -0,0 +1,116 @@
1
+ import { Container } from '@servicetitan/react-ioc';
2
+ import {
3
+ CHATBOT_API_CLIENT,
4
+ CHATBOT_UI_STORE_TOKEN,
5
+ ChatbotUiStore,
6
+ IChatbotUiStore,
7
+ ModelsMocks,
8
+ } from '@servicetitan/titan-chatbot-api';
9
+ import { FC, ReactElement } from 'react';
10
+ import { CypressMocks, testInitContainerChatbot, testRenderWrapperChatbot } from '../..';
11
+ import { ChatUiSelectors } from '../../utils/chat-ui-selectors';
12
+
13
+ interface ILinkProps {
14
+ className?: string;
15
+ onClick?: (...args: any[]) => any;
16
+ }
17
+
18
+ export function runChatbotRestartLinkSharedTests(
19
+ ChatbotRestartLinkComponent: FC<ILinkProps>,
20
+ wrapperComponent?: (component: ReactElement) => ReactElement
21
+ ) {
22
+ let container: Container;
23
+ let api: CypressMocks.ChatbotApiClientMock;
24
+ let chatbotUiStore: IChatbotUiStore;
25
+
26
+ const mockApiClient = () => {
27
+ api.postSession = cy.stub().resolves(ModelsMocks.mockSession());
28
+ api.getOptions = cy.stub().resolves(ModelsMocks.mockFrontendModel());
29
+ };
30
+
31
+ beforeEach(() => {
32
+ container = testInitContainerChatbot();
33
+ api = container.get<CypressMocks.ChatbotApiClientMock>(CHATBOT_API_CLIENT);
34
+ chatbotUiStore = container.get<ChatbotUiStore>(CHATBOT_UI_STORE_TOKEN);
35
+ mockApiClient();
36
+ cy.viewport(550, 800);
37
+ });
38
+
39
+ const render = (props: ILinkProps = {}, onAfterInit?: () => void) => {
40
+ return cy.wrap(null).then(() => {
41
+ const component = (
42
+ <ChatbotRestartLinkComponent {...props}>
43
+ Restart Session
44
+ </ChatbotRestartLinkComponent>
45
+ );
46
+ const wrappedComponent = wrapperComponent ? wrapperComponent(component) : component;
47
+
48
+ return testRenderWrapperChatbot(container, wrappedComponent, onAfterInit);
49
+ });
50
+ };
51
+
52
+ it('should render restart link with children', () => {
53
+ render();
54
+
55
+ ChatUiSelectors.chatbotRestartLink.should('be.visible');
56
+ cy.contains('Restart Session').should('be.visible');
57
+ });
58
+
59
+ it('should open restart dialog when link is clicked', () => {
60
+ render();
61
+
62
+ ChatUiSelectors.chatbotRestartLink.click();
63
+
64
+ ChatUiSelectors.chatbotRestartDialog.should('be.visible').within(() => {
65
+ cy.contains('Restart Titan session?').should('be.visible');
66
+ cy.get('button').contains('Restart').should('be.visible');
67
+ cy.get('button').contains('Cancel').should('be.visible');
68
+ });
69
+ });
70
+
71
+ it('should call onClick prop when link is clicked', () => {
72
+ const onClick = cy.stub().as('onClick');
73
+
74
+ render({ onClick });
75
+
76
+ ChatUiSelectors.chatbotRestartLink.click();
77
+
78
+ cy.get('@onClick').should('have.been.called');
79
+ ChatUiSelectors.chatbotRestartDialog.should('be.visible');
80
+ });
81
+
82
+ it('should close dialog when Cancel button is clicked', () => {
83
+ render();
84
+
85
+ ChatUiSelectors.chatbotRestartLink.click();
86
+ ChatUiSelectors.chatbotRestartDialog.should('be.visible').within(() => {
87
+ cy.get('button').contains('Cancel').closest('button').click();
88
+ });
89
+ ChatUiSelectors.chatbotRestartDialog.should('not.exist');
90
+ });
91
+
92
+ it('should close dialog and restart session when Restart button is clicked', () => {
93
+ render().then(() => {
94
+ cy.spy(chatbotUiStore, 'restart').as('restart');
95
+ });
96
+
97
+ ChatUiSelectors.chatbotRestartLink.click();
98
+ ChatUiSelectors.chatbotRestartDialog.should('be.visible').within(() => {
99
+ cy.get('button').contains('Restart').closest('button').click();
100
+ });
101
+ cy.wait(1).then(() => {
102
+ cy.get('@restart').should('have.been.called');
103
+ });
104
+ ChatUiSelectors.chatbotRestartDialog.should('not.exist');
105
+ });
106
+
107
+ it('should pass through additional Link props', () => {
108
+ const customClassName = 'custom-restart-link';
109
+
110
+ render({ className: customClassName });
111
+
112
+ ChatUiSelectors.chatbotRestartLink
113
+ .should('be.visible')
114
+ .should('have.class', customClassName);
115
+ });
116
+ }