@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,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import '@testing-library/jest-dom';
3
3
  import '@testing-library/jest-dom/extend-expect';
4
4
  import {
@@ -15,8 +15,7 @@ import {
15
15
  mockMessageChannel,
16
16
  } from '../test/test-utils';
17
17
  import {
18
- SearchEmbed, AppEmbed, LiveboardEmbed, useEmbedRef, SearchBarEmbed, PreRenderedLiveboardEmbed,
19
- SpotterAgentEmbed
18
+ SearchEmbed, AppEmbed, LiveboardEmbed, useEmbedRef, SearchBarEmbed, PreRenderedLiveboardEmbed, PreRenderedSearchEmbed, PreRenderedAppEmbed, useSpotterAgent, SpotterMessage, useInit
20
19
  } from './index';
21
20
  import * as allExports from './index';
22
21
  import {
@@ -236,199 +235,599 @@ describe('React Components', () => {
236
235
  });
237
236
  });
238
237
 
239
- describe('SpotterAgentEmbed', () => {
240
- it('Should work as a React component integrating BodylessConversation', async () => {
241
- const mockDiv = document.createElement('div');
238
+ describe('SpotterMessage', () => {
239
+ const mockMessage = {
240
+ sessionId: "session123",
241
+ genNo: 1,
242
+ acSessionId: "acSession123",
243
+ acGenNo: 2,
244
+ worksheetId: "worksheet123"
245
+ };
246
+
247
+ it('Should render the SpotterMessage component with required props', async () => {
248
+ const { container } = render(
249
+ <SpotterMessage message={mockMessage} />,
250
+ );
251
+
252
+ await waitFor(() => getIFrameEl(container));
253
+
254
+ expect(getIFrameEl(container)).not.toBe(null);
255
+ expect(getIFrameSrc(container)).toContain('sessionId=session123');
256
+ expect(getIFrameSrc(container)).toContain('genNo=1');
257
+ expect(getIFrameSrc(container)).toContain('acSessionId=acSession123');
258
+ expect(getIFrameSrc(container)).toContain('acGenNo=2');
259
+ });
260
+
261
+ it('Should render the SpotterMessage component with optional query', async () => {
262
+ const { container } = render(
263
+ <SpotterMessage
264
+ message={mockMessage}
265
+ query="show me sales"
266
+ />,
267
+ );
268
+
269
+ await waitFor(() => getIFrameEl(container));
270
+
271
+ expect(getIFrameEl(container)).not.toBe(null);
272
+ expect(getIFrameSrc(container)).toContain('sessionId=session123');
273
+ });
274
+
275
+ it('Should have the correct container element with className', async () => {
276
+ const { container } = render(
277
+ <SpotterMessage
278
+ message={mockMessage}
279
+ className="custom-class"
280
+ />,
281
+ );
282
+
283
+ await waitFor(() => getIFrameEl(container));
284
+
285
+ expect(
286
+ getIFrameEl(container).parentElement.classList.contains('custom-class'),
287
+ ).toBe(true);
288
+ });
289
+
290
+ // Note: insertAsSibling is not supported for SpotterMessage as it's not part of the allowed props
291
+ });
292
+
293
+ describe('Component Factory Coverage', () => {
294
+ it('Should test basic component creation', () => {
295
+ expect(() => {
296
+ render(<LiveboardEmbed liveboardId="test" />);
297
+ }).not.toThrow();
242
298
 
243
- let conversationService: any = null;
299
+ expect(() => {
300
+ render(<SearchEmbed dataSource="test" />);
301
+ }).not.toThrow();
244
302
 
245
- render(
246
- <SpotterAgentEmbed
247
- ref={(instance: any) => {
248
- conversationService = instance;
249
-
250
- if (instance) {
251
- instance.sendMessage = jest.fn().mockResolvedValue({
252
- container: mockDiv
253
- });
254
- }
255
- }}
256
- worksheetId="test-worksheet-id"
257
- />
303
+ expect(() => {
304
+ render(<AppEmbed showPrimaryNavbar={false} />);
305
+ }).not.toThrow();
306
+ });
307
+
308
+ it('Should test component factory existence', () => {
309
+ expect(PreRenderedLiveboardEmbed).toBeDefined();
310
+ expect(PreRenderedSearchEmbed).toBeDefined();
311
+ expect(PreRenderedAppEmbed).toBeDefined();
312
+ expect(typeof PreRenderedLiveboardEmbed).toBe('object');
313
+ expect(typeof PreRenderedSearchEmbed).toBe('object');
314
+ expect(typeof PreRenderedAppEmbed).toBe('object');
315
+ });
316
+ });
317
+
318
+ describe('Components with insertAsSibling', () => {
319
+ it('Should render LiveboardEmbed with insertAsSibling', async () => {
320
+ const { container } = render(
321
+ <LiveboardEmbed
322
+ liveboardId="test-liveboard"
323
+ insertAsSibling={true}
324
+ />,
258
325
  );
259
326
 
260
- expect(conversationService).not.toBeNull();
261
-
262
- if (conversationService) {
263
- expect(typeof conversationService.sendMessage).toBe('function');
327
+ await waitFor(() => getIFrameEl(container));
328
+ expect(container.querySelector('span')).not.toBe(null);
329
+ expect(container.querySelector('span')?.style.position).toBe('absolute');
330
+ });
331
+
332
+ it('Should render SearchEmbed with insertAsSibling', async () => {
333
+ const { container } = render(
334
+ <SearchEmbed
335
+ dataSource="test-datasource"
336
+ insertAsSibling={true}
337
+ />,
338
+ );
339
+
340
+ await waitFor(() => getIFrameEl(container));
341
+ expect(container.querySelector('span')).not.toBe(null);
342
+ expect(container.querySelector('span')?.style.position).toBe('absolute');
343
+ });
344
+
345
+ it('Should render AppEmbed with insertAsSibling', async () => {
346
+ const { container } = render(
347
+ <AppEmbed
348
+ showPrimaryNavbar={false}
349
+ insertAsSibling={true}
350
+ />,
351
+ );
352
+
353
+ await waitFor(() => getIFrameEl(container));
354
+ expect(container.querySelector('span')).not.toBe(null);
355
+ expect(container.querySelector('span')?.style.position).toBe('absolute');
356
+ });
357
+
358
+ it('Should render SearchBarEmbed with insertAsSibling', async () => {
359
+ const { container } = render(
360
+ <SearchBarEmbed
361
+ dataSource="test-datasource"
362
+ insertAsSibling={true}
363
+ />,
364
+ );
365
+
366
+ await waitFor(() => getIFrameEl(container));
367
+ expect(container.querySelector('span')).not.toBe(null);
368
+ expect(container.querySelector('span')?.style.position).toBe('absolute');
369
+ });
370
+
371
+ it('Should render components with both insertAsSibling and className', async () => {
372
+ const { container } = render(
373
+ <LiveboardEmbed
374
+ liveboardId="test-liveboard"
375
+ insertAsSibling={true}
376
+ className="custom-class"
377
+ />,
378
+ );
379
+
380
+ await waitFor(() => getIFrameEl(container));
381
+ expect(container.querySelector('span')).not.toBe(null);
382
+ expect(getIFrameEl(container).classList.contains('custom-class')).toBe(true);
383
+ });
384
+ });
385
+
386
+ describe('useSpotterAgent', () => {
387
+ it('Should return an object with sendMessage function', () => {
388
+ const TestComponent = () => {
389
+ const spotterAgent = useSpotterAgent({ worksheetId: 'test-worksheet' });
390
+ expect(typeof spotterAgent).toBe('object');
391
+ expect(typeof spotterAgent.sendMessage).toBe('function');
392
+ return <div>Test</div>;
393
+ };
394
+
395
+ render(<TestComponent />);
396
+ });
397
+
398
+ it('Should have proper sendMessage callback structure', () => {
399
+ const TestComponent = () => {
400
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
401
+
402
+ // Test that sendMessage is a function that accepts a string
403
+ expect(typeof sendMessage).toBe('function');
404
+ expect(sendMessage.length).toBe(1); // Should accept one parameter
264
405
 
265
- const response = await conversationService.sendMessage("What are my sales this month?");
266
- expect(response.container).toBe(mockDiv);
267
- }
406
+ return <div>Test</div>;
407
+ };
408
+
409
+ render(<TestComponent />);
268
410
  });
269
411
 
270
- it('Should work as a React component with ref support', () => {
271
- const mockSendMessage = jest.fn().mockResolvedValue({
272
- container: document.createElement('div'),
273
- viz: {}
274
- });
412
+ it('Should return error when service is not initialized', async () => {
413
+ let sendMessageResult: any;
275
414
 
276
415
  const TestComponent = () => {
277
- const conversationRef = React.useRef(null);
416
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
278
417
 
279
- const handleClick = () => {
280
- if (conversationRef.current) {
281
- conversationRef.current.sendMessage = mockSendMessage;
282
-
283
- conversationRef.current.sendMessage("Test message");
284
- }
285
- };
418
+ // Call sendMessage immediately before service has time to initialize
419
+ sendMessageResult = sendMessage('test query');
286
420
 
287
- return (
288
- <>
289
- <SpotterAgentEmbed
290
- ref={conversationRef}
291
- worksheetId="test-worksheet-id"
292
- />
293
- <button data-testid="test-button" onClick={handleClick}>
294
- Send Message
295
- </button>
296
- </>
297
- );
421
+ return <div>Test</div>;
298
422
  };
423
+
424
+ render(<TestComponent />);
299
425
 
300
- const { getByTestId } = render(<TestComponent />);
426
+ const result = await sendMessageResult;
427
+ expect(result).toEqual({
428
+ error: expect.any(Error)
429
+ });
430
+ expect(result.error.message).toBe('SpotterAgent not initialized');
431
+ });
432
+
433
+ it('Should call sendMessage and handle async behavior', async () => {
434
+ let sendMessageFunction: any;
301
435
 
302
- fireEvent.click(getByTestId('test-button'));
436
+ const TestComponent = () => {
437
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
438
+ sendMessageFunction = sendMessage;
439
+ return <div>Test</div>;
440
+ };
441
+
442
+ render(<TestComponent />);
303
443
 
304
- expect(mockSendMessage).toHaveBeenCalledWith("Test message");
444
+ // Test that sendMessage is a function
445
+ expect(typeof sendMessageFunction).toBe('function');
446
+
447
+ // Call sendMessage - should not throw
448
+ expect(() => {
449
+ sendMessageFunction('test query');
450
+ }).not.toThrow();
305
451
  });
306
452
 
307
- it('Should work with the useEmbedRef hook', () => {
308
- const mockSendMessage = jest.fn().mockResolvedValue({
309
- container: document.createElement('div'),
310
- viz: {}
311
- });
453
+ it('Should handle multiple calls to sendMessage', async () => {
454
+ let sendMessageFunction: any;
312
455
 
313
456
  const TestComponent = () => {
314
- const embedRef = useEmbedRef<typeof SpotterAgentEmbed>();
315
-
316
- const handleClick = () => {
317
- if (embedRef.current) {
318
- const service = embedRef.current as unknown as {
319
- sendMessage: typeof mockSendMessage
320
- };
321
-
322
- service.sendMessage = mockSendMessage;
323
-
324
- service.sendMessage("Test with useEmbedRef");
325
- }
326
- };
327
-
328
- return (
329
- <>
330
- <SpotterAgentEmbed
331
- ref={embedRef}
332
- worksheetId="test-worksheet-id"
333
- />
334
- <button data-testid="use-embed-ref-button" onClick={handleClick}>
335
- Send with useEmbedRef
336
- </button>
337
- </>
338
- );
457
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
458
+ sendMessageFunction = sendMessage;
459
+ return <div>Test</div>;
339
460
  };
461
+
462
+ render(<TestComponent />);
340
463
 
341
- const { getByTestId } = render(<TestComponent />);
464
+ // Multiple calls should not throw
465
+ expect(() => {
466
+ sendMessageFunction('query 1');
467
+ sendMessageFunction('query 2');
468
+ sendMessageFunction('query 3');
469
+ }).not.toThrow();
470
+ });
471
+
472
+ it('Should handle config object changes', () => {
473
+ const TestComponent = ({ config }: { config: any }) => {
474
+ const { sendMessage } = useSpotterAgent(config);
475
+ expect(sendMessage).toBeDefined();
476
+ return <div>Test</div>;
477
+ };
478
+
479
+ const config1 = { worksheetId: 'test1' };
480
+ const config2 = { worksheetId: 'test2' };
342
481
 
343
- fireEvent.click(getByTestId('use-embed-ref-button'));
482
+ const { rerender } = render(<TestComponent config={config1} />);
344
483
 
345
- expect(mockSendMessage).toHaveBeenCalledWith("Test with useEmbedRef");
484
+ // Should not throw when config changes
485
+ expect(() => {
486
+ rerender(<TestComponent config={config2} />);
487
+ }).not.toThrow();
346
488
  });
347
489
 
348
- it('Should work with the className prop', async () => {
349
- let capturedInstance: any = null;
350
-
490
+ it('Should handle unmounting without errors', () => {
351
491
  const TestComponent = () => {
352
- const embedRef = useEmbedRef<typeof SpotterAgentEmbed>();
353
-
354
- React.useEffect(() => {
355
- capturedInstance = embedRef.current;
356
-
357
- if (capturedInstance) {
358
- const mockConversationService = {
359
- sendMessage: jest.fn().mockResolvedValue({
360
- data: {
361
- sessionId: 'test-session',
362
- genNo: 1,
363
- stateKey: {
364
- transactionId: 'test-transaction',
365
- generationNumber: 1
366
- }
367
- }
368
- })
369
- };
370
- (capturedInstance as any).conversationService = mockConversationService;
371
- }
372
- }, []);
373
-
374
- return (
375
- <SpotterAgentEmbed
376
- ref={embedRef}
377
- worksheetId="test-worksheet-id"
378
- className="embedClass"
379
- />
380
- );
492
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
493
+ expect(sendMessage).toBeDefined();
494
+ return <div>Test</div>;
381
495
  };
496
+
497
+ const { unmount } = render(<TestComponent />);
382
498
 
383
- render(<TestComponent />);
499
+ // Should not throw when unmounting
500
+ expect(() => {
501
+ unmount();
502
+ }).not.toThrow();
503
+ });
504
+
505
+ it('Should create stable hook structure', () => {
506
+ let hookResult1: any, hookResult2: any;
384
507
 
385
- expect(capturedInstance).not.toBeNull();
508
+ const TestComponent = ({ counter }: { counter: number }) => {
509
+ const result = useSpotterAgent({ worksheetId: 'test-worksheet' });
510
+
511
+ if (counter === 1) {
512
+ hookResult1 = result;
513
+ } else {
514
+ hookResult2 = result;
515
+ }
516
+
517
+ return <div>Test</div>;
518
+ };
519
+
520
+ const { rerender } = render(<TestComponent counter={1} />);
521
+ rerender(<TestComponent counter={2} />);
386
522
 
387
- if (capturedInstance) {
388
- const result = await capturedInstance.sendMessage("test");
389
- expect(result.container.className).toBe("embedClass");
390
- }
523
+ // Both should have same structure
524
+ expect(hookResult1).toEqual({ sendMessage: expect.any(Function) });
525
+ expect(hookResult2).toEqual({ sendMessage: expect.any(Function) });
391
526
  });
527
+
528
+ it('Should handle different worksheet IDs', () => {
529
+ const TestComponent = ({ worksheetId }: { worksheetId: string }) => {
530
+ const { sendMessage } = useSpotterAgent({ worksheetId });
531
+ expect(sendMessage).toBeDefined();
532
+ return <div>Test</div>;
533
+ };
534
+
535
+ const { rerender } = render(<TestComponent worksheetId="worksheet1" />);
536
+
537
+ // Should handle different worksheet IDs
538
+ expect(() => {
539
+ rerender(<TestComponent worksheetId="worksheet2" />);
540
+ rerender(<TestComponent worksheetId="worksheet3" />);
541
+ }).not.toThrow();
542
+ });
543
+
544
+ it('Should handle empty query strings', () => {
545
+ let sendMessageFunction: any;
546
+
547
+ const TestComponent = () => {
548
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
549
+ sendMessageFunction = sendMessage;
550
+ return <div>Test</div>;
551
+ };
552
+
553
+ render(<TestComponent />);
554
+
555
+ // Should handle empty strings
556
+ expect(() => {
557
+ sendMessageFunction('');
558
+ sendMessageFunction(' ');
559
+ }).not.toThrow();
560
+ });
561
+
562
+ it('Should handle complex config objects', () => {
563
+ const complexConfig = {
564
+ worksheetId: 'test-worksheet',
565
+ hiddenActions: [Action.ReportError],
566
+ className: 'test-class',
567
+ searchOptions: {
568
+ searchQuery: 'test query'
569
+ }
570
+ };
571
+
572
+ const TestComponent = () => {
573
+ const { sendMessage } = useSpotterAgent(complexConfig);
574
+ expect(sendMessage).toBeDefined();
575
+ return <div>Test</div>;
576
+ };
577
+
578
+ // Should not throw with complex config
579
+ expect(() => {
580
+ render(<TestComponent />);
581
+ }).not.toThrow();
582
+ });
583
+
584
+ it('Should maintain function identity across re-renders with same config', () => {
585
+ let sendMessage1: any, sendMessage2: any;
586
+
587
+ const TestComponent = ({ forceRender }: { forceRender: number }) => {
588
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
589
+
590
+ if (forceRender === 1) {
591
+ sendMessage1 = sendMessage;
592
+ } else {
593
+ sendMessage2 = sendMessage;
594
+ }
595
+
596
+ return <div>Test</div>;
597
+ };
598
+
599
+ const { rerender } = render(<TestComponent forceRender={1} />);
600
+ rerender(<TestComponent forceRender={2} />);
601
+
602
+ // Functions should exist
603
+ expect(sendMessage1).toBeDefined();
604
+ expect(sendMessage2).toBeDefined();
605
+ expect(typeof sendMessage1).toBe('function');
606
+ expect(typeof sendMessage2).toBe('function');
607
+ });
608
+
609
+ it('Should handle sendMessage calls with null service ref', async () => {
610
+ let capturedSendMessage: any;
611
+
612
+ const TestComponent = () => {
613
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
614
+ capturedSendMessage = sendMessage;
615
+ return <div>Test</div>;
616
+ };
617
+
618
+ const { unmount } = render(<TestComponent />);
619
+
620
+ // Unmount to trigger cleanup
621
+ unmount();
622
+
623
+ // Now call sendMessage after unmount - should return error
624
+ const result = await capturedSendMessage('test query');
625
+ expect(result).toEqual({
626
+ error: expect.any(Error)
627
+ });
628
+ });
629
+
630
+ it('Should test service ref cleanup on config change', () => {
631
+ const TestComponent = ({ worksheetId }: { worksheetId: string }) => {
632
+ const { sendMessage } = useSpotterAgent({ worksheetId });
633
+ expect(sendMessage).toBeDefined();
634
+ return <div>Test</div>;
635
+ };
636
+
637
+ const { rerender } = render(<TestComponent worksheetId="worksheet1" />);
638
+
639
+ // This should trigger the cleanup and create new service
640
+ rerender(<TestComponent worksheetId="worksheet2" />);
641
+
642
+ // Should still work after rerender
643
+ expect(() => {
644
+ rerender(<TestComponent worksheetId="worksheet3" />);
645
+ }).not.toThrow();
646
+ });
647
+
648
+ it('Should test different config variations', () => {
649
+ const configs = [
650
+ { worksheetId: 'test1' },
651
+ { worksheetId: 'test2', hiddenActions: [Action.ReportError] },
652
+ { worksheetId: 'test3', className: 'test-class' },
653
+ { worksheetId: 'test4', searchOptions: { searchQuery: 'test' } }
654
+ ];
655
+
656
+ configs.forEach((config, index) => {
657
+ const TestComponent = () => {
658
+ const { sendMessage } = useSpotterAgent(config);
659
+ expect(sendMessage).toBeDefined();
660
+ return <div>Test {index}</div>;
661
+ };
662
+
663
+ expect(() => {
664
+ const { unmount } = render(<TestComponent />);
665
+ unmount();
666
+ }).not.toThrow();
667
+ });
668
+ });
669
+
670
+ it('Should handle rapid config changes', () => {
671
+ const TestComponent = ({ worksheetId }: { worksheetId: string }) => {
672
+ const { sendMessage } = useSpotterAgent({ worksheetId });
673
+ expect(sendMessage).toBeDefined();
674
+ return <div>Test</div>;
675
+ };
676
+
677
+ const { rerender } = render(<TestComponent worksheetId="worksheet1" />);
678
+
679
+ // Rapid config changes to test cleanup logic
680
+ for (let i = 2; i <= 10; i++) {
681
+ rerender(<TestComponent worksheetId={`worksheet${i}`} />);
682
+ }
683
+
684
+ // Should still work after many changes
685
+ expect(() => {
686
+ rerender(<TestComponent worksheetId="final-worksheet" />);
687
+ }).not.toThrow();
688
+ });
689
+
690
+ it('Should handle sendMessage with different query types', () => {
691
+ let sendMessageFunction: any;
692
+
693
+ const TestComponent = () => {
694
+ const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
695
+ sendMessageFunction = sendMessage;
696
+ return <div>Test</div>;
697
+ };
698
+
699
+ render(<TestComponent />);
700
+
701
+ // Test different query types
702
+ const queries = [
703
+ 'simple query',
704
+ 'query with numbers 123',
705
+ 'query with special chars !@#$%',
706
+ 'very long query that might test different code paths in the system when processing',
707
+ '',
708
+ ' whitespace ',
709
+ 'null',
710
+ 'undefined'
711
+ ];
712
+
713
+ queries.forEach(query => {
714
+ expect(() => {
715
+ sendMessageFunction(query);
716
+ }).not.toThrow();
717
+ });
718
+ });
719
+
720
+ it('Should handle service ref cleanup when it already exists', () => {
721
+ const TestComponent = ({ worksheetId }: { worksheetId: string }) => {
722
+ const { sendMessage } = useSpotterAgent({ worksheetId });
723
+ expect(sendMessage).toBeDefined();
724
+ return <div>Test</div>;
725
+ };
726
+
727
+ const { rerender } = render(<TestComponent worksheetId="worksheet1" />);
728
+
729
+ // This should trigger the "if (serviceRef.current)" branch in useEffect
730
+ rerender(<TestComponent worksheetId="worksheet1" />);
731
+ rerender(<TestComponent worksheetId="worksheet2" />);
732
+ rerender(<TestComponent worksheetId="worksheet3" />);
733
+
734
+ // Multiple rapid changes should exercise the cleanup logic
735
+ for (let i = 0; i < 5; i++) {
736
+ rerender(<TestComponent worksheetId={`worksheet${i}`} />);
737
+ }
738
+ });
739
+
740
+ it('Should test various config combinations to hit all branches', () => {
741
+ const testConfigs = [
742
+ { worksheetId: 'test1' },
743
+ { worksheetId: 'test2', className: 'custom-class' },
744
+ { worksheetId: 'test3', hiddenActions: [Action.ReportError] },
745
+ { worksheetId: 'test4', searchOptions: { searchQuery: 'test' } },
746
+ { worksheetId: 'test5', insertAsSibling: true },
747
+ { worksheetId: 'test6', insertAsSibling: false },
748
+ ];
749
+
750
+ testConfigs.forEach((config, index) => {
751
+ const TestComponent = () => {
752
+ const { sendMessage } = useSpotterAgent(config);
753
+ expect(sendMessage).toBeDefined();
754
+ return <div>Test {index}</div>;
755
+ };
756
+
757
+ const { unmount } = render(<TestComponent />);
758
+ unmount();
759
+ });
760
+ });
392
761
  });
393
762
 
394
- describe('PreRenderedLiveboardEmbed', () => {
395
- it('should preRender the liveboard ', async () => {
396
- const preRenderId = 'tsEmbed-pre-render-wrapper-test';
763
+ describe('Component Props and Functions', () => {
764
+ it('Should have PreRenderedLiveboardEmbed component', () => {
765
+ expect(PreRenderedLiveboardEmbed).toBeDefined();
766
+ expect(typeof PreRenderedLiveboardEmbed).toBe('object');
767
+ });
397
768
 
398
- const { container } = render(
399
- <PreRenderedLiveboardEmbed
400
- className="embedClass"
401
- preRenderId="test"
402
- liveboardId="libId"
403
- />,
404
- );
769
+ it('Should have useInit hook', () => {
770
+ expect(typeof useInit).toBe('function');
771
+ });
405
772
 
406
- await waitFor(() => getIFrameEl());
407
- const preRenderWrapper = document.body.querySelector(`#${preRenderId}`) as HTMLDivElement;
773
+ it('Should test basic component factory patterns', () => {
774
+ // Test that components can be created without errors
775
+ expect(() => {
776
+ const TestComponent = () => <div>Test</div>;
777
+ render(<TestComponent />);
778
+ }).not.toThrow();
779
+ });
780
+ });
408
781
 
409
- expect(preRenderWrapper).toBeInstanceOf(HTMLDivElement);
410
- expect((preRenderWrapper as HTMLDivElement).childElementCount).toBe(1);
782
+ describe('Hook Coverage', () => {
783
+ it('Should have useInit function available', () => {
784
+ expect(typeof useInit).toBe('function');
785
+ });
411
786
 
412
- const preRenderChildId = 'tsEmbed-pre-render-child-test';
413
- const preRenderChild = document.body.querySelector(`#${preRenderChildId}`);
414
- expect(preRenderWrapper.children[0]).toBe(preRenderChild);
787
+ it('Should test useInit hook basic functionality', () => {
788
+ const TestComponent = () => {
789
+ const authEE = useInit({
790
+ thoughtSpotHost: 'localhost',
791
+ authType: AuthType.None
792
+ });
793
+ expect(authEE).toBeDefined();
794
+ expect(authEE.current).toBeDefined();
795
+ return <div>Test</div>;
796
+ };
415
797
 
416
- (window as any).ResizeObserver = jest.fn().mockImplementation(() => ({
417
- disconnect: jest.fn(),
418
- observe: jest.fn(),
419
- unobserve: jest.fn(),
420
- }));
421
- const { container: libContainer } = render(
422
- <LiveboardEmbed
423
- className="embedClass"
424
- preRenderId="test"
425
- liveboardId="libId"
426
- />,
427
- );
798
+ render(<TestComponent />);
799
+ });
428
800
 
429
- expect(preRenderWrapper.style.opacity).toBe('');
430
- expect(preRenderWrapper.style.pointerEvents).toBe('');
431
- expect(preRenderWrapper.style.zIndex).toBe('');
801
+ it('Should handle useInit with different config changes', () => {
802
+ const TestComponent = ({ host }: { host: string }) => {
803
+ const authEE = useInit({
804
+ thoughtSpotHost: host,
805
+ authType: AuthType.None
806
+ });
807
+ expect(authEE).toBeDefined();
808
+ return <div>Test</div>;
809
+ };
810
+
811
+ const { rerender } = render(<TestComponent host="localhost" />);
812
+
813
+ // Change config to test useDeepCompareEffect
814
+ rerender(<TestComponent host="localhost2" />);
815
+ rerender(<TestComponent host="localhost3" />);
816
+ });
817
+
818
+ it('Should test useInit with complex config objects', () => {
819
+ const TestComponent = () => {
820
+ const authEE = useInit({
821
+ thoughtSpotHost: 'localhost',
822
+ authType: AuthType.None,
823
+ suppressNoCookieAccessAlert: true,
824
+ suppressErrorAlerts: true
825
+ });
826
+ expect(authEE).toBeDefined();
827
+ return <div>Test</div>;
828
+ };
829
+
830
+ render(<TestComponent />);
432
831
  });
433
832
  });
434
833
  });