@thoughtspot/visual-embed-sdk 1.39.1 → 1.39.2-alpha.2

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 (190) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/config.spec.js +9 -0
  3. package/cjs/src/config.spec.js.map +1 -1
  4. package/cjs/src/embed/app.d.ts +75 -15
  5. package/cjs/src/embed/app.d.ts.map +1 -1
  6. package/cjs/src/embed/app.js +69 -9
  7. package/cjs/src/embed/app.js.map +1 -1
  8. package/cjs/src/embed/app.spec.js +374 -12
  9. package/cjs/src/embed/app.spec.js.map +1 -1
  10. package/cjs/src/embed/bodyless-conversation.d.ts +19 -7
  11. package/cjs/src/embed/bodyless-conversation.d.ts.map +1 -1
  12. package/cjs/src/embed/bodyless-conversation.js +24 -4
  13. package/cjs/src/embed/bodyless-conversation.js.map +1 -1
  14. package/cjs/src/embed/bodyless-conversation.spec.js +8 -190
  15. package/cjs/src/embed/bodyless-conversation.spec.js.map +1 -1
  16. package/cjs/src/embed/conversation.spec.js +28 -0
  17. package/cjs/src/embed/conversation.spec.js.map +1 -1
  18. package/cjs/src/embed/embedConfig.d.ts +9 -7
  19. package/cjs/src/embed/embedConfig.d.ts.map +1 -1
  20. package/cjs/src/embed/embedConfig.js +9 -7
  21. package/cjs/src/embed/embedConfig.js.map +1 -1
  22. package/cjs/src/embed/liveboard.d.ts +56 -17
  23. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  24. package/cjs/src/embed/liveboard.js +48 -4
  25. package/cjs/src/embed/liveboard.js.map +1 -1
  26. package/cjs/src/embed/liveboard.spec.js +215 -11
  27. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  28. package/cjs/src/embed/ts-embed.d.ts +5 -0
  29. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  30. package/cjs/src/embed/ts-embed.js +16 -1
  31. package/cjs/src/embed/ts-embed.js.map +1 -1
  32. package/cjs/src/embed/ts-embed.spec.js +164 -0
  33. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  34. package/cjs/src/errors.d.ts +1 -0
  35. package/cjs/src/errors.d.ts.map +1 -1
  36. package/cjs/src/errors.js +1 -0
  37. package/cjs/src/errors.js.map +1 -1
  38. package/cjs/src/index.d.ts +2 -2
  39. package/cjs/src/index.d.ts.map +1 -1
  40. package/cjs/src/index.js +2 -1
  41. package/cjs/src/index.js.map +1 -1
  42. package/cjs/src/react/all-types-export.d.ts +1 -1
  43. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  44. package/cjs/src/react/all-types-export.js +3 -2
  45. package/cjs/src/react/all-types-export.js.map +1 -1
  46. package/cjs/src/react/index.d.ts +71 -20
  47. package/cjs/src/react/index.d.ts.map +1 -1
  48. package/cjs/src/react/index.js +79 -42
  49. package/cjs/src/react/index.js.map +1 -1
  50. package/cjs/src/react/index.spec.js +436 -100
  51. package/cjs/src/react/index.spec.js.map +1 -1
  52. package/cjs/src/types.d.ts +80 -6
  53. package/cjs/src/types.d.ts.map +1 -1
  54. package/cjs/src/types.js +45 -1
  55. package/cjs/src/types.js.map +1 -1
  56. package/cjs/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
  57. package/cjs/src/utils/graphql/nlsService/conversation-service.js +2 -0
  58. package/cjs/src/utils/graphql/nlsService/conversation-service.js.map +1 -1
  59. package/cjs/src/utils/processTrigger.js +2 -1
  60. package/cjs/src/utils/processTrigger.js.map +1 -1
  61. package/cjs/src/utils.d.ts +6 -0
  62. package/cjs/src/utils.d.ts.map +1 -1
  63. package/cjs/src/utils.js +23 -3
  64. package/cjs/src/utils.js.map +1 -1
  65. package/cjs/src/utils.spec.js +237 -1
  66. package/cjs/src/utils.spec.js.map +1 -1
  67. package/dist/{index-JaFaxrvQ.js → index-CmEQfuE3.js} +1 -1
  68. package/dist/index-DeFzsyFF.js +7371 -0
  69. package/dist/index-Dpf0rd6w.js +7371 -0
  70. package/dist/index-UuEbsISo.js +7447 -0
  71. package/dist/index-e3Uw3YFO.js +7371 -0
  72. package/dist/src/embed/app.d.ts +75 -15
  73. package/dist/src/embed/app.d.ts.map +1 -1
  74. package/dist/src/embed/bodyless-conversation.d.ts +19 -7
  75. package/dist/src/embed/bodyless-conversation.d.ts.map +1 -1
  76. package/dist/src/embed/embedConfig.d.ts +9 -7
  77. package/dist/src/embed/embedConfig.d.ts.map +1 -1
  78. package/dist/src/embed/liveboard.d.ts +56 -17
  79. package/dist/src/embed/liveboard.d.ts.map +1 -1
  80. package/dist/src/embed/ts-embed.d.ts +5 -0
  81. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  82. package/dist/src/errors.d.ts +1 -0
  83. package/dist/src/errors.d.ts.map +1 -1
  84. package/dist/src/index.d.ts +2 -2
  85. package/dist/src/index.d.ts.map +1 -1
  86. package/dist/src/react/all-types-export.d.ts +1 -1
  87. package/dist/src/react/all-types-export.d.ts.map +1 -1
  88. package/dist/src/react/index.d.ts +71 -20
  89. package/dist/src/react/index.d.ts.map +1 -1
  90. package/dist/src/types.d.ts +80 -6
  91. package/dist/src/types.d.ts.map +1 -1
  92. package/dist/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
  93. package/dist/src/utils.d.ts +6 -0
  94. package/dist/src/utils.d.ts.map +1 -1
  95. package/dist/tsembed-react.es.js +320 -78
  96. package/dist/tsembed-react.js +320 -76
  97. package/dist/tsembed.es.js +238 -31
  98. package/dist/tsembed.js +236 -29
  99. package/dist/visual-embed-sdk-react-full.d.ts +288 -72
  100. package/dist/visual-embed-sdk-react.d.ts +288 -72
  101. package/dist/visual-embed-sdk.d.ts +218 -53
  102. package/lib/package.json +1 -1
  103. package/lib/src/config.spec.js +9 -0
  104. package/lib/src/config.spec.js.map +1 -1
  105. package/lib/src/embed/app.d.ts +75 -15
  106. package/lib/src/embed/app.d.ts.map +1 -1
  107. package/lib/src/embed/app.js +69 -9
  108. package/lib/src/embed/app.js.map +1 -1
  109. package/lib/src/embed/app.spec.js +376 -14
  110. package/lib/src/embed/app.spec.js.map +1 -1
  111. package/lib/src/embed/bodyless-conversation.d.ts +19 -7
  112. package/lib/src/embed/bodyless-conversation.d.ts.map +1 -1
  113. package/lib/src/embed/bodyless-conversation.js +23 -4
  114. package/lib/src/embed/bodyless-conversation.js.map +1 -1
  115. package/lib/src/embed/bodyless-conversation.spec.js +9 -191
  116. package/lib/src/embed/bodyless-conversation.spec.js.map +1 -1
  117. package/lib/src/embed/conversation.spec.js +30 -2
  118. package/lib/src/embed/conversation.spec.js.map +1 -1
  119. package/lib/src/embed/embedConfig.d.ts +9 -7
  120. package/lib/src/embed/embedConfig.d.ts.map +1 -1
  121. package/lib/src/embed/embedConfig.js +9 -7
  122. package/lib/src/embed/embedConfig.js.map +1 -1
  123. package/lib/src/embed/liveboard.d.ts +56 -17
  124. package/lib/src/embed/liveboard.d.ts.map +1 -1
  125. package/lib/src/embed/liveboard.js +49 -5
  126. package/lib/src/embed/liveboard.js.map +1 -1
  127. package/lib/src/embed/liveboard.spec.js +215 -11
  128. package/lib/src/embed/liveboard.spec.js.map +1 -1
  129. package/lib/src/embed/ts-embed.d.ts +5 -0
  130. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  131. package/lib/src/embed/ts-embed.js +16 -1
  132. package/lib/src/embed/ts-embed.js.map +1 -1
  133. package/lib/src/embed/ts-embed.spec.js +164 -0
  134. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  135. package/lib/src/errors.d.ts +1 -0
  136. package/lib/src/errors.d.ts.map +1 -1
  137. package/lib/src/errors.js +1 -0
  138. package/lib/src/errors.js.map +1 -1
  139. package/lib/src/index.d.ts +2 -2
  140. package/lib/src/index.d.ts.map +1 -1
  141. package/lib/src/index.js +2 -2
  142. package/lib/src/index.js.map +1 -1
  143. package/lib/src/react/all-types-export.d.ts +1 -1
  144. package/lib/src/react/all-types-export.d.ts.map +1 -1
  145. package/lib/src/react/all-types-export.js +1 -1
  146. package/lib/src/react/all-types-export.js.map +1 -1
  147. package/lib/src/react/index.d.ts +71 -20
  148. package/lib/src/react/index.d.ts.map +1 -1
  149. package/lib/src/react/index.js +79 -43
  150. package/lib/src/react/index.js.map +1 -1
  151. package/lib/src/react/index.spec.js +439 -103
  152. package/lib/src/react/index.spec.js.map +1 -1
  153. package/lib/src/types.d.ts +80 -6
  154. package/lib/src/types.d.ts.map +1 -1
  155. package/lib/src/types.js +45 -1
  156. package/lib/src/types.js.map +1 -1
  157. package/lib/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
  158. package/lib/src/utils/graphql/nlsService/conversation-service.js +2 -0
  159. package/lib/src/utils/graphql/nlsService/conversation-service.js.map +1 -1
  160. package/lib/src/utils/processTrigger.js +2 -1
  161. package/lib/src/utils/processTrigger.js.map +1 -1
  162. package/lib/src/utils.d.ts +6 -0
  163. package/lib/src/utils.d.ts.map +1 -1
  164. package/lib/src/utils.js +21 -2
  165. package/lib/src/utils.js.map +1 -1
  166. package/lib/src/utils.spec.js +238 -2
  167. package/lib/src/utils.spec.js.map +1 -1
  168. package/lib/src/visual-embed-sdk.d.ts +219 -54
  169. package/package.json +1 -1
  170. package/src/config.spec.ts +11 -0
  171. package/src/embed/app.spec.ts +479 -26
  172. package/src/embed/app.ts +133 -27
  173. package/src/embed/bodyless-conversation.spec.ts +9 -203
  174. package/src/embed/bodyless-conversation.ts +24 -10
  175. package/src/embed/conversation.spec.ts +40 -2
  176. package/src/embed/embedConfig.ts +10 -8
  177. package/src/embed/liveboard.spec.ts +256 -5
  178. package/src/embed/liveboard.ts +99 -27
  179. package/src/embed/ts-embed.spec.ts +225 -0
  180. package/src/embed/ts-embed.ts +19 -0
  181. package/src/errors.ts +1 -0
  182. package/src/index.ts +2 -0
  183. package/src/react/all-types-export.ts +2 -1
  184. package/src/react/index.spec.tsx +556 -157
  185. package/src/react/index.tsx +117 -51
  186. package/src/types.ts +117 -43
  187. package/src/utils/graphql/nlsService/conversation-service.ts +2 -0
  188. package/src/utils/processTrigger.ts +1 -1
  189. package/src/utils.spec.ts +279 -2
  190. package/src/utils.ts +28 -2
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
2
  import '@testing-library/jest-dom';
3
3
  import '@testing-library/jest-dom/extend-expect';
4
- import { fireEvent, render, waitFor, } from '@testing-library/react';
4
+ import { render, waitFor, } from '@testing-library/react';
5
5
  import { Action, EmbedEvent, HostEvent, RuntimeFilterOp, } from '../types';
6
6
  import { executeAfterWait, getIFrameEl, getIFrameSrc, postMessageToParent, mockMessageChannel, } from '../test/test-utils';
7
- import { SearchEmbed, LiveboardEmbed, useEmbedRef, SearchBarEmbed, PreRenderedLiveboardEmbed, SpotterAgentEmbed } from './index';
7
+ import { SearchEmbed, AppEmbed, LiveboardEmbed, useEmbedRef, SearchBarEmbed, PreRenderedLiveboardEmbed, PreRenderedSearchEmbed, PreRenderedAppEmbed, useSpotterAgent, SpotterMessage, useInit } from './index';
8
8
  import * as allExports from './index';
9
9
  import { AuthType, init, } from '../index';
10
10
  import { version } from '../../package.json';
@@ -126,120 +126,456 @@ describe('React Components', () => {
126
126
  expect(getIFrameSrc(container)).toBe(`http://${thoughtSpotHost}/?embedApp=true&hostAppUrl=local-host&viewPortHeight=768&viewPortWidth=1024&sdkVersion=${version}&authType=None&blockNonEmbedFullAppAccess=true&hideAction=[%22${Action.ReportError}%22]&preAuthCache=true&overrideConsoleLogs=true&clientLogLevel=ERROR&dataSources=[%22test%22]&searchTokenString=%5Brevenue%5D&executeSearch=true&useLastSelectedSources=false&isSearchEmbed=true#/embed/search-bar-embed`);
127
127
  });
128
128
  });
129
- describe('SpotterAgentEmbed', () => {
130
- it('Should work as a React component integrating BodylessConversation', async () => {
131
- const mockDiv = document.createElement('div');
132
- let conversationService = null;
133
- render(React.createElement(SpotterAgentEmbed, { ref: (instance) => {
134
- conversationService = instance;
135
- if (instance) {
136
- instance.sendMessage = jest.fn().mockResolvedValue({
137
- container: mockDiv
138
- });
139
- }
140
- }, worksheetId: "test-worksheet-id" }));
141
- expect(conversationService).not.toBeNull();
142
- if (conversationService) {
143
- expect(typeof conversationService.sendMessage).toBe('function');
144
- const response = await conversationService.sendMessage("What are my sales this month?");
145
- expect(response.container).toBe(mockDiv);
146
- }
129
+ describe('SpotterMessage', () => {
130
+ const mockMessage = {
131
+ sessionId: "session123",
132
+ genNo: 1,
133
+ acSessionId: "acSession123",
134
+ acGenNo: 2,
135
+ worksheetId: "worksheet123"
136
+ };
137
+ it('Should render the SpotterMessage component with required props', async () => {
138
+ const { container } = render(React.createElement(SpotterMessage, { message: mockMessage }));
139
+ await waitFor(() => getIFrameEl(container));
140
+ expect(getIFrameEl(container)).not.toBe(null);
141
+ expect(getIFrameSrc(container)).toContain('sessionId=session123');
142
+ expect(getIFrameSrc(container)).toContain('genNo=1');
143
+ expect(getIFrameSrc(container)).toContain('acSessionId=acSession123');
144
+ expect(getIFrameSrc(container)).toContain('acGenNo=2');
147
145
  });
148
- it('Should work as a React component with ref support', () => {
149
- const mockSendMessage = jest.fn().mockResolvedValue({
150
- container: document.createElement('div'),
151
- viz: {}
152
- });
146
+ it('Should render the SpotterMessage component with optional query', async () => {
147
+ const { container } = render(React.createElement(SpotterMessage, { message: mockMessage, query: "show me sales" }));
148
+ await waitFor(() => getIFrameEl(container));
149
+ expect(getIFrameEl(container)).not.toBe(null);
150
+ expect(getIFrameSrc(container)).toContain('sessionId=session123');
151
+ });
152
+ it('Should have the correct container element with className', async () => {
153
+ const { container } = render(React.createElement(SpotterMessage, { message: mockMessage, className: "custom-class" }));
154
+ await waitFor(() => getIFrameEl(container));
155
+ expect(getIFrameEl(container).parentElement.classList.contains('custom-class')).toBe(true);
156
+ });
157
+ // Note: insertAsSibling is not supported for SpotterMessage as it's not part of the allowed props
158
+ });
159
+ describe('Component Factory Coverage', () => {
160
+ it('Should test basic component creation', () => {
161
+ expect(() => {
162
+ render(React.createElement(LiveboardEmbed, { liveboardId: "test" }));
163
+ }).not.toThrow();
164
+ expect(() => {
165
+ render(React.createElement(SearchEmbed, { dataSource: "test" }));
166
+ }).not.toThrow();
167
+ expect(() => {
168
+ render(React.createElement(AppEmbed, { showPrimaryNavbar: false }));
169
+ }).not.toThrow();
170
+ });
171
+ it('Should test component factory existence', () => {
172
+ expect(PreRenderedLiveboardEmbed).toBeDefined();
173
+ expect(PreRenderedSearchEmbed).toBeDefined();
174
+ expect(PreRenderedAppEmbed).toBeDefined();
175
+ expect(typeof PreRenderedLiveboardEmbed).toBe('object');
176
+ expect(typeof PreRenderedSearchEmbed).toBe('object');
177
+ expect(typeof PreRenderedAppEmbed).toBe('object');
178
+ });
179
+ });
180
+ describe('Components with insertAsSibling', () => {
181
+ it('Should render LiveboardEmbed with insertAsSibling', async () => {
182
+ var _a;
183
+ const { container } = render(React.createElement(LiveboardEmbed, { liveboardId: "test-liveboard", insertAsSibling: true }));
184
+ await waitFor(() => getIFrameEl(container));
185
+ expect(container.querySelector('span')).not.toBe(null);
186
+ expect((_a = container.querySelector('span')) === null || _a === void 0 ? void 0 : _a.style.position).toBe('absolute');
187
+ });
188
+ it('Should render SearchEmbed with insertAsSibling', async () => {
189
+ var _a;
190
+ const { container } = render(React.createElement(SearchEmbed, { dataSource: "test-datasource", insertAsSibling: true }));
191
+ await waitFor(() => getIFrameEl(container));
192
+ expect(container.querySelector('span')).not.toBe(null);
193
+ expect((_a = container.querySelector('span')) === null || _a === void 0 ? void 0 : _a.style.position).toBe('absolute');
194
+ });
195
+ it('Should render AppEmbed with insertAsSibling', async () => {
196
+ var _a;
197
+ const { container } = render(React.createElement(AppEmbed, { showPrimaryNavbar: false, insertAsSibling: true }));
198
+ await waitFor(() => getIFrameEl(container));
199
+ expect(container.querySelector('span')).not.toBe(null);
200
+ expect((_a = container.querySelector('span')) === null || _a === void 0 ? void 0 : _a.style.position).toBe('absolute');
201
+ });
202
+ it('Should render SearchBarEmbed with insertAsSibling', async () => {
203
+ var _a;
204
+ const { container } = render(React.createElement(SearchBarEmbed, { dataSource: "test-datasource", insertAsSibling: true }));
205
+ await waitFor(() => getIFrameEl(container));
206
+ expect(container.querySelector('span')).not.toBe(null);
207
+ expect((_a = container.querySelector('span')) === null || _a === void 0 ? void 0 : _a.style.position).toBe('absolute');
208
+ });
209
+ it('Should render components with both insertAsSibling and className', async () => {
210
+ const { container } = render(React.createElement(LiveboardEmbed, { liveboardId: "test-liveboard", insertAsSibling: true, className: "custom-class" }));
211
+ await waitFor(() => getIFrameEl(container));
212
+ expect(container.querySelector('span')).not.toBe(null);
213
+ expect(getIFrameEl(container).classList.contains('custom-class')).toBe(true);
214
+ });
215
+ });
216
+ describe('useSpotterAgent', () => {
217
+ it('Should return an object with sendMessage function', () => {
153
218
  const TestComponent = () => {
154
- const conversationRef = React.useRef(null);
155
- const handleClick = () => {
156
- if (conversationRef.current) {
157
- conversationRef.current.sendMessage = mockSendMessage;
158
- conversationRef.current.sendMessage("Test message");
159
- }
160
- };
161
- return (React.createElement(React.Fragment, null,
162
- React.createElement(SpotterAgentEmbed, { ref: conversationRef, worksheetId: "test-worksheet-id" }),
163
- React.createElement("button", { "data-testid": "test-button", onClick: handleClick }, "Send Message")));
164
- };
165
- const { getByTestId } = render(React.createElement(TestComponent, null));
166
- fireEvent.click(getByTestId('test-button'));
167
- expect(mockSendMessage).toHaveBeenCalledWith("Test message");
168
- });
169
- it('Should work with the useEmbedRef hook', () => {
170
- const mockSendMessage = jest.fn().mockResolvedValue({
171
- container: document.createElement('div'),
172
- viz: {}
219
+ const spotterAgent = useSpotterAgent({ worksheetId: 'test-worksheet' });
220
+ expect(typeof spotterAgent).toBe('object');
221
+ expect(typeof spotterAgent.sendMessage).toBe('function');
222
+ return React.createElement("div", null, "Test");
223
+ };
224
+ render(React.createElement(TestComponent, null));
225
+ });
226
+ it('Should have proper sendMessage callback structure', () => {
227
+ const TestComponent = () => {
228
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
229
+ // Test that sendMessage is a function that accepts a string
230
+ expect(typeof sendMessage).toBe('function');
231
+ expect(sendMessage.length).toBe(1); // Should accept one parameter
232
+ return React.createElement("div", null, "Test");
233
+ };
234
+ render(React.createElement(TestComponent, null));
235
+ });
236
+ it('Should return error when service is not initialized', async () => {
237
+ let sendMessageResult;
238
+ const TestComponent = () => {
239
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
240
+ // Call sendMessage immediately before service has time to initialize
241
+ sendMessageResult = sendMessage('test query');
242
+ return React.createElement("div", null, "Test");
243
+ };
244
+ render(React.createElement(TestComponent, null));
245
+ const result = await sendMessageResult;
246
+ expect(result).toEqual({
247
+ error: expect.any(Error)
173
248
  });
249
+ expect(result.error.message).toBe('SpotterAgent not initialized');
250
+ });
251
+ it('Should call sendMessage and handle async behavior', async () => {
252
+ let sendMessageFunction;
174
253
  const TestComponent = () => {
175
- const embedRef = useEmbedRef();
176
- const handleClick = () => {
177
- if (embedRef.current) {
178
- const service = embedRef.current;
179
- service.sendMessage = mockSendMessage;
180
- service.sendMessage("Test with useEmbedRef");
181
- }
254
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
255
+ sendMessageFunction = sendMessage;
256
+ return React.createElement("div", null, "Test");
257
+ };
258
+ render(React.createElement(TestComponent, null));
259
+ // Test that sendMessage is a function
260
+ expect(typeof sendMessageFunction).toBe('function');
261
+ // Call sendMessage - should not throw
262
+ expect(() => {
263
+ sendMessageFunction('test query');
264
+ }).not.toThrow();
265
+ });
266
+ it('Should handle multiple calls to sendMessage', async () => {
267
+ let sendMessageFunction;
268
+ const TestComponent = () => {
269
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
270
+ sendMessageFunction = sendMessage;
271
+ return React.createElement("div", null, "Test");
272
+ };
273
+ render(React.createElement(TestComponent, null));
274
+ // Multiple calls should not throw
275
+ expect(() => {
276
+ sendMessageFunction('query 1');
277
+ sendMessageFunction('query 2');
278
+ sendMessageFunction('query 3');
279
+ }).not.toThrow();
280
+ });
281
+ it('Should handle config object changes', () => {
282
+ const TestComponent = ({ config }) => {
283
+ const { sendMessage } = useSpotterAgent(config);
284
+ expect(sendMessage).toBeDefined();
285
+ return React.createElement("div", null, "Test");
286
+ };
287
+ const config1 = { worksheetId: 'test1' };
288
+ const config2 = { worksheetId: 'test2' };
289
+ const { rerender } = render(React.createElement(TestComponent, { config: config1 }));
290
+ // Should not throw when config changes
291
+ expect(() => {
292
+ rerender(React.createElement(TestComponent, { config: config2 }));
293
+ }).not.toThrow();
294
+ });
295
+ it('Should handle unmounting without errors', () => {
296
+ const TestComponent = () => {
297
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
298
+ expect(sendMessage).toBeDefined();
299
+ return React.createElement("div", null, "Test");
300
+ };
301
+ const { unmount } = render(React.createElement(TestComponent, null));
302
+ // Should not throw when unmounting
303
+ expect(() => {
304
+ unmount();
305
+ }).not.toThrow();
306
+ });
307
+ it('Should create stable hook structure', () => {
308
+ let hookResult1, hookResult2;
309
+ const TestComponent = ({ counter }) => {
310
+ const result = useSpotterAgent({ worksheetId: 'test-worksheet' });
311
+ if (counter === 1) {
312
+ hookResult1 = result;
313
+ }
314
+ else {
315
+ hookResult2 = result;
316
+ }
317
+ return React.createElement("div", null, "Test");
318
+ };
319
+ const { rerender } = render(React.createElement(TestComponent, { counter: 1 }));
320
+ rerender(React.createElement(TestComponent, { counter: 2 }));
321
+ // Both should have same structure
322
+ expect(hookResult1).toEqual({ sendMessage: expect.any(Function) });
323
+ expect(hookResult2).toEqual({ sendMessage: expect.any(Function) });
324
+ });
325
+ it('Should handle different worksheet IDs', () => {
326
+ const TestComponent = ({ worksheetId }) => {
327
+ const { sendMessage } = useSpotterAgent({ worksheetId });
328
+ expect(sendMessage).toBeDefined();
329
+ return React.createElement("div", null, "Test");
330
+ };
331
+ const { rerender } = render(React.createElement(TestComponent, { worksheetId: "worksheet1" }));
332
+ // Should handle different worksheet IDs
333
+ expect(() => {
334
+ rerender(React.createElement(TestComponent, { worksheetId: "worksheet2" }));
335
+ rerender(React.createElement(TestComponent, { worksheetId: "worksheet3" }));
336
+ }).not.toThrow();
337
+ });
338
+ it('Should handle empty query strings', () => {
339
+ let sendMessageFunction;
340
+ const TestComponent = () => {
341
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
342
+ sendMessageFunction = sendMessage;
343
+ return React.createElement("div", null, "Test");
344
+ };
345
+ render(React.createElement(TestComponent, null));
346
+ // Should handle empty strings
347
+ expect(() => {
348
+ sendMessageFunction('');
349
+ sendMessageFunction(' ');
350
+ }).not.toThrow();
351
+ });
352
+ it('Should handle complex config objects', () => {
353
+ const complexConfig = {
354
+ worksheetId: 'test-worksheet',
355
+ hiddenActions: [Action.ReportError],
356
+ className: 'test-class',
357
+ searchOptions: {
358
+ searchQuery: 'test query'
359
+ }
360
+ };
361
+ const TestComponent = () => {
362
+ const { sendMessage } = useSpotterAgent(complexConfig);
363
+ expect(sendMessage).toBeDefined();
364
+ return React.createElement("div", null, "Test");
365
+ };
366
+ // Should not throw with complex config
367
+ expect(() => {
368
+ render(React.createElement(TestComponent, null));
369
+ }).not.toThrow();
370
+ });
371
+ it('Should maintain function identity across re-renders with same config', () => {
372
+ let sendMessage1, sendMessage2;
373
+ const TestComponent = ({ forceRender }) => {
374
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
375
+ if (forceRender === 1) {
376
+ sendMessage1 = sendMessage;
377
+ }
378
+ else {
379
+ sendMessage2 = sendMessage;
380
+ }
381
+ return React.createElement("div", null, "Test");
382
+ };
383
+ const { rerender } = render(React.createElement(TestComponent, { forceRender: 1 }));
384
+ rerender(React.createElement(TestComponent, { forceRender: 2 }));
385
+ // Functions should exist
386
+ expect(sendMessage1).toBeDefined();
387
+ expect(sendMessage2).toBeDefined();
388
+ expect(typeof sendMessage1).toBe('function');
389
+ expect(typeof sendMessage2).toBe('function');
390
+ });
391
+ it('Should handle sendMessage calls with null service ref', async () => {
392
+ let capturedSendMessage;
393
+ const TestComponent = () => {
394
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
395
+ capturedSendMessage = sendMessage;
396
+ return React.createElement("div", null, "Test");
397
+ };
398
+ const { unmount } = render(React.createElement(TestComponent, null));
399
+ // Unmount to trigger cleanup
400
+ unmount();
401
+ // Now call sendMessage after unmount - should return error
402
+ const result = await capturedSendMessage('test query');
403
+ expect(result).toEqual({
404
+ error: expect.any(Error)
405
+ });
406
+ });
407
+ it('Should test service ref cleanup on config change', () => {
408
+ const TestComponent = ({ worksheetId }) => {
409
+ const { sendMessage } = useSpotterAgent({ worksheetId });
410
+ expect(sendMessage).toBeDefined();
411
+ return React.createElement("div", null, "Test");
412
+ };
413
+ const { rerender } = render(React.createElement(TestComponent, { worksheetId: "worksheet1" }));
414
+ // This should trigger the cleanup and create new service
415
+ rerender(React.createElement(TestComponent, { worksheetId: "worksheet2" }));
416
+ // Should still work after rerender
417
+ expect(() => {
418
+ rerender(React.createElement(TestComponent, { worksheetId: "worksheet3" }));
419
+ }).not.toThrow();
420
+ });
421
+ it('Should test different config variations', () => {
422
+ const configs = [
423
+ { worksheetId: 'test1' },
424
+ { worksheetId: 'test2', hiddenActions: [Action.ReportError] },
425
+ { worksheetId: 'test3', className: 'test-class' },
426
+ { worksheetId: 'test4', searchOptions: { searchQuery: 'test' } }
427
+ ];
428
+ configs.forEach((config, index) => {
429
+ const TestComponent = () => {
430
+ const { sendMessage } = useSpotterAgent(config);
431
+ expect(sendMessage).toBeDefined();
432
+ return React.createElement("div", null,
433
+ "Test ",
434
+ index);
182
435
  };
183
- return (React.createElement(React.Fragment, null,
184
- React.createElement(SpotterAgentEmbed, { ref: embedRef, worksheetId: "test-worksheet-id" }),
185
- React.createElement("button", { "data-testid": "use-embed-ref-button", onClick: handleClick }, "Send with useEmbedRef")));
436
+ expect(() => {
437
+ const { unmount } = render(React.createElement(TestComponent, null));
438
+ unmount();
439
+ }).not.toThrow();
440
+ });
441
+ });
442
+ it('Should handle rapid config changes', () => {
443
+ const TestComponent = ({ worksheetId }) => {
444
+ const { sendMessage } = useSpotterAgent({ worksheetId });
445
+ expect(sendMessage).toBeDefined();
446
+ return React.createElement("div", null, "Test");
186
447
  };
187
- const { getByTestId } = render(React.createElement(TestComponent, null));
188
- fireEvent.click(getByTestId('use-embed-ref-button'));
189
- expect(mockSendMessage).toHaveBeenCalledWith("Test with useEmbedRef");
448
+ const { rerender } = render(React.createElement(TestComponent, { worksheetId: "worksheet1" }));
449
+ // Rapid config changes to test cleanup logic
450
+ for (let i = 2; i <= 10; i++) {
451
+ rerender(React.createElement(TestComponent, { worksheetId: `worksheet${i}` }));
452
+ }
453
+ // Should still work after many changes
454
+ expect(() => {
455
+ rerender(React.createElement(TestComponent, { worksheetId: "final-worksheet" }));
456
+ }).not.toThrow();
190
457
  });
191
- it('Should work with the className prop', async () => {
192
- let capturedInstance = null;
458
+ it('Should handle sendMessage with different query types', () => {
459
+ let sendMessageFunction;
193
460
  const TestComponent = () => {
194
- const embedRef = useEmbedRef();
195
- React.useEffect(() => {
196
- capturedInstance = embedRef.current;
197
- if (capturedInstance) {
198
- const mockConversationService = {
199
- sendMessage: jest.fn().mockResolvedValue({
200
- data: {
201
- sessionId: 'test-session',
202
- genNo: 1,
203
- stateKey: {
204
- transactionId: 'test-transaction',
205
- generationNumber: 1
206
- }
207
- }
208
- })
209
- };
210
- capturedInstance.conversationService = mockConversationService;
211
- }
212
- }, []);
213
- return (React.createElement(SpotterAgentEmbed, { ref: embedRef, worksheetId: "test-worksheet-id", className: "embedClass" }));
461
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
462
+ sendMessageFunction = sendMessage;
463
+ return React.createElement("div", null, "Test");
214
464
  };
215
465
  render(React.createElement(TestComponent, null));
216
- expect(capturedInstance).not.toBeNull();
217
- if (capturedInstance) {
218
- const result = await capturedInstance.sendMessage("test");
219
- expect(result.container.className).toBe("embedClass");
466
+ // Test different query types
467
+ const queries = [
468
+ 'simple query',
469
+ 'query with numbers 123',
470
+ 'query with special chars !@#$%',
471
+ 'very long query that might test different code paths in the system when processing',
472
+ '',
473
+ ' whitespace ',
474
+ 'null',
475
+ 'undefined'
476
+ ];
477
+ queries.forEach(query => {
478
+ expect(() => {
479
+ sendMessageFunction(query);
480
+ }).not.toThrow();
481
+ });
482
+ });
483
+ it('Should handle service ref cleanup when it already exists', () => {
484
+ const TestComponent = ({ worksheetId }) => {
485
+ const { sendMessage } = useSpotterAgent({ worksheetId });
486
+ expect(sendMessage).toBeDefined();
487
+ return React.createElement("div", null, "Test");
488
+ };
489
+ const { rerender } = render(React.createElement(TestComponent, { worksheetId: "worksheet1" }));
490
+ // This should trigger the "if (serviceRef.current)" branch in useEffect
491
+ rerender(React.createElement(TestComponent, { worksheetId: "worksheet1" }));
492
+ rerender(React.createElement(TestComponent, { worksheetId: "worksheet2" }));
493
+ rerender(React.createElement(TestComponent, { worksheetId: "worksheet3" }));
494
+ // Multiple rapid changes should exercise the cleanup logic
495
+ for (let i = 0; i < 5; i++) {
496
+ rerender(React.createElement(TestComponent, { worksheetId: `worksheet${i}` }));
220
497
  }
221
498
  });
499
+ it('Should test various config combinations to hit all branches', () => {
500
+ const testConfigs = [
501
+ { worksheetId: 'test1' },
502
+ { worksheetId: 'test2', className: 'custom-class' },
503
+ { worksheetId: 'test3', hiddenActions: [Action.ReportError] },
504
+ { worksheetId: 'test4', searchOptions: { searchQuery: 'test' } },
505
+ { worksheetId: 'test5', insertAsSibling: true },
506
+ { worksheetId: 'test6', insertAsSibling: false },
507
+ ];
508
+ testConfigs.forEach((config, index) => {
509
+ const TestComponent = () => {
510
+ const { sendMessage } = useSpotterAgent(config);
511
+ expect(sendMessage).toBeDefined();
512
+ return React.createElement("div", null,
513
+ "Test ",
514
+ index);
515
+ };
516
+ const { unmount } = render(React.createElement(TestComponent, null));
517
+ unmount();
518
+ });
519
+ });
520
+ });
521
+ describe('Component Props and Functions', () => {
522
+ it('Should have PreRenderedLiveboardEmbed component', () => {
523
+ expect(PreRenderedLiveboardEmbed).toBeDefined();
524
+ expect(typeof PreRenderedLiveboardEmbed).toBe('object');
525
+ });
526
+ it('Should have useInit hook', () => {
527
+ expect(typeof useInit).toBe('function');
528
+ });
529
+ it('Should test basic component factory patterns', () => {
530
+ // Test that components can be created without errors
531
+ expect(() => {
532
+ const TestComponent = () => React.createElement("div", null, "Test");
533
+ render(React.createElement(TestComponent, null));
534
+ }).not.toThrow();
535
+ });
222
536
  });
223
- describe('PreRenderedLiveboardEmbed', () => {
224
- it('should preRender the liveboard ', async () => {
225
- const preRenderId = 'tsEmbed-pre-render-wrapper-test';
226
- const { container } = render(React.createElement(PreRenderedLiveboardEmbed, { className: "embedClass", preRenderId: "test", liveboardId: "libId" }));
227
- await waitFor(() => getIFrameEl());
228
- const preRenderWrapper = document.body.querySelector(`#${preRenderId}`);
229
- expect(preRenderWrapper).toBeInstanceOf(HTMLDivElement);
230
- expect(preRenderWrapper.childElementCount).toBe(1);
231
- const preRenderChildId = 'tsEmbed-pre-render-child-test';
232
- const preRenderChild = document.body.querySelector(`#${preRenderChildId}`);
233
- expect(preRenderWrapper.children[0]).toBe(preRenderChild);
234
- window.ResizeObserver = jest.fn().mockImplementation(() => ({
235
- disconnect: jest.fn(),
236
- observe: jest.fn(),
237
- unobserve: jest.fn(),
238
- }));
239
- const { container: libContainer } = render(React.createElement(LiveboardEmbed, { className: "embedClass", preRenderId: "test", liveboardId: "libId" }));
240
- expect(preRenderWrapper.style.opacity).toBe('');
241
- expect(preRenderWrapper.style.pointerEvents).toBe('');
242
- expect(preRenderWrapper.style.zIndex).toBe('');
537
+ describe('Hook Coverage', () => {
538
+ it('Should have useInit function available', () => {
539
+ expect(typeof useInit).toBe('function');
540
+ });
541
+ it('Should test useInit hook basic functionality', () => {
542
+ const TestComponent = () => {
543
+ const authEE = useInit({
544
+ thoughtSpotHost: 'localhost',
545
+ authType: AuthType.None
546
+ });
547
+ expect(authEE).toBeDefined();
548
+ expect(authEE.current).toBeDefined();
549
+ return React.createElement("div", null, "Test");
550
+ };
551
+ render(React.createElement(TestComponent, null));
552
+ });
553
+ it('Should handle useInit with different config changes', () => {
554
+ const TestComponent = ({ host }) => {
555
+ const authEE = useInit({
556
+ thoughtSpotHost: host,
557
+ authType: AuthType.None
558
+ });
559
+ expect(authEE).toBeDefined();
560
+ return React.createElement("div", null, "Test");
561
+ };
562
+ const { rerender } = render(React.createElement(TestComponent, { host: "localhost" }));
563
+ // Change config to test useDeepCompareEffect
564
+ rerender(React.createElement(TestComponent, { host: "localhost2" }));
565
+ rerender(React.createElement(TestComponent, { host: "localhost3" }));
566
+ });
567
+ it('Should test useInit with complex config objects', () => {
568
+ const TestComponent = () => {
569
+ const authEE = useInit({
570
+ thoughtSpotHost: 'localhost',
571
+ authType: AuthType.None,
572
+ suppressNoCookieAccessAlert: true,
573
+ suppressErrorAlerts: true
574
+ });
575
+ expect(authEE).toBeDefined();
576
+ return React.createElement("div", null, "Test");
577
+ };
578
+ render(React.createElement(TestComponent, null));
243
579
  });
244
580
  });
245
581
  });