@thoughtspot/visual-embed-sdk 1.47.3 → 1.49.0

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 (244) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/auth.d.ts.map +1 -1
  3. package/cjs/src/auth.js +11 -1
  4. package/cjs/src/auth.js.map +1 -1
  5. package/cjs/src/auth.spec.js +38 -0
  6. package/cjs/src/auth.spec.js.map +1 -1
  7. package/cjs/src/authToken.d.ts +2 -0
  8. package/cjs/src/authToken.d.ts.map +1 -1
  9. package/cjs/src/authToken.js +7 -5
  10. package/cjs/src/authToken.js.map +1 -1
  11. package/cjs/src/css-variables.d.ts +140 -0
  12. package/cjs/src/css-variables.d.ts.map +1 -1
  13. package/cjs/src/embed/app.d.ts +63 -2
  14. package/cjs/src/embed/app.d.ts.map +1 -1
  15. package/cjs/src/embed/app.js +57 -6
  16. package/cjs/src/embed/app.js.map +1 -1
  17. package/cjs/src/embed/app.spec.js +200 -1
  18. package/cjs/src/embed/app.spec.js.map +1 -1
  19. package/cjs/src/embed/auto-frame-renderer.js +7 -2
  20. package/cjs/src/embed/auto-frame-renderer.js.map +1 -1
  21. package/cjs/src/embed/auto-frame-renderer.spec.js +385 -6
  22. package/cjs/src/embed/auto-frame-renderer.spec.js.map +1 -1
  23. package/cjs/src/embed/base.d.ts +1 -0
  24. package/cjs/src/embed/base.d.ts.map +1 -1
  25. package/cjs/src/embed/base.js +13 -1
  26. package/cjs/src/embed/base.js.map +1 -1
  27. package/cjs/src/embed/base.spec.js +21 -0
  28. package/cjs/src/embed/base.spec.js.map +1 -1
  29. package/cjs/src/embed/bodyless-conversation.spec.js +86 -0
  30. package/cjs/src/embed/bodyless-conversation.spec.js.map +1 -1
  31. package/cjs/src/embed/conversation.d.ts +16 -1
  32. package/cjs/src/embed/conversation.d.ts.map +1 -1
  33. package/cjs/src/embed/conversation.js +5 -1
  34. package/cjs/src/embed/conversation.js.map +1 -1
  35. package/cjs/src/embed/conversation.spec.js +26 -0
  36. package/cjs/src/embed/conversation.spec.js.map +1 -1
  37. package/cjs/src/embed/liveboard.d.ts +48 -2
  38. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  39. package/cjs/src/embed/liveboard.js +48 -7
  40. package/cjs/src/embed/liveboard.js.map +1 -1
  41. package/cjs/src/embed/liveboard.spec.js +139 -1
  42. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  43. package/cjs/src/embed/spotter-viz-utils.d.ts +85 -0
  44. package/cjs/src/embed/spotter-viz-utils.d.ts.map +1 -0
  45. package/cjs/src/embed/spotter-viz-utils.js +17 -0
  46. package/cjs/src/embed/spotter-viz-utils.js.map +1 -0
  47. package/cjs/src/embed/spotter-viz-utils.spec.d.ts +2 -0
  48. package/cjs/src/embed/spotter-viz-utils.spec.d.ts.map +1 -0
  49. package/cjs/src/embed/spotter-viz-utils.spec.js +31 -0
  50. package/cjs/src/embed/spotter-viz-utils.spec.js.map +1 -0
  51. package/cjs/src/embed/ts-embed.d.ts +58 -38
  52. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  53. package/cjs/src/embed/ts-embed.js +247 -151
  54. package/cjs/src/embed/ts-embed.js.map +1 -1
  55. package/cjs/src/embed/ts-embed.spec.js +397 -122
  56. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  57. package/cjs/src/index.d.ts +2 -1
  58. package/cjs/src/index.d.ts.map +1 -1
  59. package/cjs/src/index.js.map +1 -1
  60. package/cjs/src/react/index.d.ts.map +1 -1
  61. package/cjs/src/react/index.js +3 -0
  62. package/cjs/src/react/index.js.map +1 -1
  63. package/cjs/src/tokenizedFetch.d.ts.map +1 -1
  64. package/cjs/src/tokenizedFetch.js +12 -9
  65. package/cjs/src/tokenizedFetch.js.map +1 -1
  66. package/cjs/src/tokenizedFetch.spec.d.ts +2 -0
  67. package/cjs/src/tokenizedFetch.spec.d.ts.map +1 -0
  68. package/cjs/src/tokenizedFetch.spec.js +68 -0
  69. package/cjs/src/tokenizedFetch.spec.js.map +1 -0
  70. package/cjs/src/types.d.ts +309 -40
  71. package/cjs/src/types.d.ts.map +1 -1
  72. package/cjs/src/types.js +251 -23
  73. package/cjs/src/types.js.map +1 -1
  74. package/cjs/src/utils/authService/tokenizedAuthService.spec.js +6 -7
  75. package/cjs/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
  76. package/cjs/src/utils/logger.js +2 -1
  77. package/cjs/src/utils/logger.js.map +1 -1
  78. package/cjs/src/utils/logger.spec.d.ts +1 -0
  79. package/cjs/src/utils/logger.spec.d.ts.map +1 -1
  80. package/cjs/src/utils/logger.spec.js +10 -9
  81. package/cjs/src/utils/logger.spec.js.map +1 -1
  82. package/cjs/src/utils.d.ts +4 -1
  83. package/cjs/src/utils.d.ts.map +1 -1
  84. package/cjs/src/utils.js +107 -10
  85. package/cjs/src/utils.js.map +1 -1
  86. package/cjs/src/utils.spec.js +163 -4
  87. package/cjs/src/utils.spec.js.map +1 -1
  88. package/dist/{index-DZq20cR6.js → index-_UGCSSDR.js} +1 -1
  89. package/dist/src/auth.d.ts.map +1 -1
  90. package/dist/src/authToken.d.ts +2 -0
  91. package/dist/src/authToken.d.ts.map +1 -1
  92. package/dist/src/css-variables.d.ts +140 -0
  93. package/dist/src/css-variables.d.ts.map +1 -1
  94. package/dist/src/embed/app.d.ts +63 -2
  95. package/dist/src/embed/app.d.ts.map +1 -1
  96. package/dist/src/embed/base.d.ts +1 -0
  97. package/dist/src/embed/base.d.ts.map +1 -1
  98. package/dist/src/embed/conversation.d.ts +16 -1
  99. package/dist/src/embed/conversation.d.ts.map +1 -1
  100. package/dist/src/embed/liveboard.d.ts +48 -2
  101. package/dist/src/embed/liveboard.d.ts.map +1 -1
  102. package/dist/src/embed/spotter-viz-utils.d.ts +85 -0
  103. package/dist/src/embed/spotter-viz-utils.d.ts.map +1 -0
  104. package/dist/src/embed/spotter-viz-utils.spec.d.ts +2 -0
  105. package/dist/src/embed/spotter-viz-utils.spec.d.ts.map +1 -0
  106. package/dist/src/embed/ts-embed.d.ts +58 -38
  107. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  108. package/dist/src/index.d.ts +2 -1
  109. package/dist/src/index.d.ts.map +1 -1
  110. package/dist/src/react/index.d.ts.map +1 -1
  111. package/dist/src/tokenizedFetch.d.ts.map +1 -1
  112. package/dist/src/tokenizedFetch.spec.d.ts +2 -0
  113. package/dist/src/tokenizedFetch.spec.d.ts.map +1 -0
  114. package/dist/src/types.d.ts +309 -40
  115. package/dist/src/types.d.ts.map +1 -1
  116. package/dist/src/utils/logger.spec.d.ts +1 -0
  117. package/dist/src/utils/logger.spec.d.ts.map +1 -1
  118. package/dist/src/utils.d.ts +4 -1
  119. package/dist/src/utils.d.ts.map +1 -1
  120. package/dist/tsembed-react.es.js +3418 -2899
  121. package/dist/tsembed-react.js +3420 -2901
  122. package/dist/tsembed.es.js +3426 -2905
  123. package/dist/tsembed.js +3419 -2898
  124. package/dist/visual-embed-sdk-react-full.d.ts +687 -78
  125. package/dist/visual-embed-sdk-react.d.ts +687 -78
  126. package/dist/visual-embed-sdk.d.ts +702 -80
  127. package/lib/package.json +1 -1
  128. package/lib/src/auth.d.ts.map +1 -1
  129. package/lib/src/auth.js +12 -2
  130. package/lib/src/auth.js.map +1 -1
  131. package/lib/src/auth.spec.js +38 -0
  132. package/lib/src/auth.spec.js.map +1 -1
  133. package/lib/src/authToken.d.ts +2 -0
  134. package/lib/src/authToken.d.ts.map +1 -1
  135. package/lib/src/authToken.js +2 -2
  136. package/lib/src/authToken.js.map +1 -1
  137. package/lib/src/css-variables.d.ts +140 -0
  138. package/lib/src/css-variables.d.ts.map +1 -1
  139. package/lib/src/embed/app.d.ts +63 -2
  140. package/lib/src/embed/app.d.ts.map +1 -1
  141. package/lib/src/embed/app.js +58 -7
  142. package/lib/src/embed/app.js.map +1 -1
  143. package/lib/src/embed/app.spec.js +201 -2
  144. package/lib/src/embed/app.spec.js.map +1 -1
  145. package/lib/src/embed/auto-frame-renderer.js +7 -2
  146. package/lib/src/embed/auto-frame-renderer.js.map +1 -1
  147. package/lib/src/embed/auto-frame-renderer.spec.js +387 -8
  148. package/lib/src/embed/auto-frame-renderer.spec.js.map +1 -1
  149. package/lib/src/embed/base.d.ts +1 -0
  150. package/lib/src/embed/base.d.ts.map +1 -1
  151. package/lib/src/embed/base.js +11 -0
  152. package/lib/src/embed/base.js.map +1 -1
  153. package/lib/src/embed/base.spec.js +22 -1
  154. package/lib/src/embed/base.spec.js.map +1 -1
  155. package/lib/src/embed/bodyless-conversation.spec.js +86 -0
  156. package/lib/src/embed/bodyless-conversation.spec.js.map +1 -1
  157. package/lib/src/embed/conversation.d.ts +16 -1
  158. package/lib/src/embed/conversation.d.ts.map +1 -1
  159. package/lib/src/embed/conversation.js +5 -1
  160. package/lib/src/embed/conversation.js.map +1 -1
  161. package/lib/src/embed/conversation.spec.js +27 -1
  162. package/lib/src/embed/conversation.spec.js.map +1 -1
  163. package/lib/src/embed/liveboard.d.ts +48 -2
  164. package/lib/src/embed/liveboard.d.ts.map +1 -1
  165. package/lib/src/embed/liveboard.js +49 -8
  166. package/lib/src/embed/liveboard.js.map +1 -1
  167. package/lib/src/embed/liveboard.spec.js +139 -1
  168. package/lib/src/embed/liveboard.spec.js.map +1 -1
  169. package/lib/src/embed/spotter-viz-utils.d.ts +85 -0
  170. package/lib/src/embed/spotter-viz-utils.d.ts.map +1 -0
  171. package/lib/src/embed/spotter-viz-utils.js +13 -0
  172. package/lib/src/embed/spotter-viz-utils.js.map +1 -0
  173. package/lib/src/embed/spotter-viz-utils.spec.d.ts +2 -0
  174. package/lib/src/embed/spotter-viz-utils.spec.d.ts.map +1 -0
  175. package/lib/src/embed/spotter-viz-utils.spec.js +29 -0
  176. package/lib/src/embed/spotter-viz-utils.spec.js.map +1 -0
  177. package/lib/src/embed/ts-embed.d.ts +58 -38
  178. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  179. package/lib/src/embed/ts-embed.js +250 -154
  180. package/lib/src/embed/ts-embed.js.map +1 -1
  181. package/lib/src/embed/ts-embed.spec.js +397 -122
  182. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  183. package/lib/src/index.d.ts +2 -1
  184. package/lib/src/index.d.ts.map +1 -1
  185. package/lib/src/index.js.map +1 -1
  186. package/lib/src/react/index.d.ts.map +1 -1
  187. package/lib/src/react/index.js +3 -0
  188. package/lib/src/react/index.js.map +1 -1
  189. package/lib/src/tokenizedFetch.d.ts.map +1 -1
  190. package/lib/src/tokenizedFetch.js +13 -10
  191. package/lib/src/tokenizedFetch.js.map +1 -1
  192. package/lib/src/tokenizedFetch.spec.d.ts +2 -0
  193. package/lib/src/tokenizedFetch.spec.d.ts.map +1 -0
  194. package/lib/src/tokenizedFetch.spec.js +65 -0
  195. package/lib/src/tokenizedFetch.spec.js.map +1 -0
  196. package/lib/src/types.d.ts +309 -40
  197. package/lib/src/types.d.ts.map +1 -1
  198. package/lib/src/types.js +251 -23
  199. package/lib/src/types.js.map +1 -1
  200. package/lib/src/utils/authService/tokenizedAuthService.spec.js +6 -7
  201. package/lib/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
  202. package/lib/src/utils/logger.js +2 -1
  203. package/lib/src/utils/logger.js.map +1 -1
  204. package/lib/src/utils/logger.spec.d.ts +1 -0
  205. package/lib/src/utils/logger.spec.d.ts.map +1 -1
  206. package/lib/src/utils/logger.spec.js +10 -9
  207. package/lib/src/utils/logger.spec.js.map +1 -1
  208. package/lib/src/utils.d.ts +4 -1
  209. package/lib/src/utils.d.ts.map +1 -1
  210. package/lib/src/utils.js +103 -9
  211. package/lib/src/utils.js.map +1 -1
  212. package/lib/src/utils.spec.js +164 -5
  213. package/lib/src/utils.spec.js.map +1 -1
  214. package/lib/src/visual-embed-sdk.d.ts +702 -80
  215. package/package.json +1 -1
  216. package/src/auth.spec.ts +55 -1
  217. package/src/auth.ts +11 -2
  218. package/src/authToken.ts +2 -2
  219. package/src/css-variables.ts +175 -1
  220. package/src/embed/app.spec.ts +260 -3
  221. package/src/embed/app.ts +127 -7
  222. package/src/embed/auto-frame-renderer.spec.ts +457 -58
  223. package/src/embed/auto-frame-renderer.ts +7 -2
  224. package/src/embed/base.spec.ts +25 -1
  225. package/src/embed/base.ts +19 -5
  226. package/src/embed/bodyless-conversation.spec.ts +93 -0
  227. package/src/embed/conversation.spec.ts +34 -0
  228. package/src/embed/conversation.ts +22 -1
  229. package/src/embed/liveboard.spec.ts +163 -1
  230. package/src/embed/liveboard.ts +106 -10
  231. package/src/embed/spotter-viz-utils.spec.ts +30 -0
  232. package/src/embed/spotter-viz-utils.ts +94 -0
  233. package/src/embed/ts-embed.spec.ts +564 -231
  234. package/src/embed/ts-embed.ts +384 -258
  235. package/src/index.ts +3 -0
  236. package/src/react/index.tsx +3 -0
  237. package/src/tokenizedFetch.spec.ts +81 -0
  238. package/src/tokenizedFetch.ts +14 -11
  239. package/src/types.ts +326 -36
  240. package/src/utils/authService/tokenizedAuthService.spec.ts +6 -6
  241. package/src/utils/logger.spec.ts +11 -9
  242. package/src/utils/logger.ts +2 -2
  243. package/src/utils.spec.ts +200 -4
  244. package/src/utils.ts +128 -9
@@ -97,6 +97,7 @@ beforeAll(() => {
97
97
  const customisations = {
98
98
  style: {
99
99
  customCSS: {},
100
+ customCSSUrl: undefined as string | undefined,
100
101
  },
101
102
  content: {},
102
103
  };
@@ -132,6 +133,9 @@ const getMockAppInitPayload = (data: any) => {
132
133
  customVariablesForThirdPartyTools,
133
134
  interceptTimeout: undefined,
134
135
  interceptUrls: [],
136
+ shouldBypassPayloadValidation: undefined,
137
+ useHostEventsV2: undefined,
138
+ embedExpiryInAuthToken: true,
135
139
  };
136
140
  return {
137
141
  type: EmbedEvent.APP_INIT,
@@ -140,7 +144,7 @@ const getMockAppInitPayload = (data: any) => {
140
144
  ...data,
141
145
  },
142
146
  };
143
- }
147
+ };
144
148
 
145
149
  describe('Unit test case for ts embed', () => {
146
150
  const mockMixPanelEvent = jest.spyOn(mixpanelInstance, 'uploadMixpanelEvent');
@@ -219,9 +223,7 @@ describe('Unit test case for ts embed', () => {
219
223
  liveboardEmbed.render();
220
224
  mockProcessTrigger.mockResolvedValue({ session: 'test' });
221
225
  await executeAfterWait(async () => {
222
- await liveboardEmbed.trigger(
223
- HostEvent.Save,
224
- );
226
+ await liveboardEmbed.trigger(HostEvent.Save);
225
227
  expect(mockProcessTrigger).toHaveBeenCalledWith(
226
228
  getIFrameEl(),
227
229
  HostEvent.Save,
@@ -240,10 +242,7 @@ describe('Unit test case for ts embed', () => {
240
242
  liveboardEmbed.render();
241
243
  mockProcessTrigger.mockResolvedValue({ session: 'test' });
242
244
  await executeAfterWait(async () => {
243
- await liveboardEmbed.trigger(
244
- HostEvent.Save,
245
- false,
246
- );
245
+ await liveboardEmbed.trigger(HostEvent.Save, false);
247
246
  expect(mockProcessTrigger).toHaveBeenCalledWith(
248
247
  getIFrameEl(),
249
248
  HostEvent.Save,
@@ -394,9 +393,11 @@ describe('Unit test case for ts embed', () => {
394
393
  postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
395
394
  });
396
395
  await executeAfterWait(() => {
397
- expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
398
- customisations: customisationsView,
399
- }));
396
+ expect(mockPort.postMessage).toHaveBeenCalledWith(
397
+ getMockAppInitPayload({
398
+ customisations: customisationsView,
399
+ }),
400
+ );
400
401
  });
401
402
  });
402
403
 
@@ -423,9 +424,11 @@ describe('Unit test case for ts embed', () => {
423
424
  postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
424
425
  });
425
426
  await executeAfterWait(() => {
426
- expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
427
- hiddenHomepageModules: [HomepageModule.MyLibrary, HomepageModule.Learning],
428
- }));
427
+ expect(mockPort.postMessage).toHaveBeenCalledWith(
428
+ getMockAppInitPayload({
429
+ hiddenHomepageModules: [HomepageModule.MyLibrary, HomepageModule.Learning],
430
+ }),
431
+ );
429
432
  });
430
433
  });
431
434
 
@@ -476,10 +479,14 @@ describe('Unit test case for ts embed', () => {
476
479
  postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
477
480
  });
478
481
  await executeAfterWait(() => {
479
- expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
480
- reorderedHomepageModules:
481
- [HomepageModule.MyLibrary, HomepageModule.Watchlist],
482
- }));
482
+ expect(mockPort.postMessage).toHaveBeenCalledWith(
483
+ getMockAppInitPayload({
484
+ reorderedHomepageModules: [
485
+ HomepageModule.MyLibrary,
486
+ HomepageModule.Watchlist,
487
+ ],
488
+ }),
489
+ );
483
490
  });
484
491
  });
485
492
 
@@ -509,9 +516,11 @@ describe('Unit test case for ts embed', () => {
509
516
  postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
510
517
  });
511
518
  await executeAfterWait(() => {
512
- expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
513
- runtimeParameterParams: 'param1=color&paramVal1=blue',
514
- }));
519
+ expect(mockPort.postMessage).toHaveBeenCalledWith(
520
+ getMockAppInitPayload({
521
+ runtimeParameterParams: 'param1=color&paramVal1=blue',
522
+ }),
523
+ );
515
524
  });
516
525
  });
517
526
 
@@ -542,9 +551,11 @@ describe('Unit test case for ts embed', () => {
542
551
  postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
543
552
  });
544
553
  await executeAfterWait(() => {
545
- expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
546
- runtimeFilterParams: 'col1=color&op1=EQ&val1=blue',
547
- }));
554
+ expect(mockPort.postMessage).toHaveBeenCalledWith(
555
+ getMockAppInitPayload({
556
+ runtimeFilterParams: 'col1=color&op1=EQ&val1=blue',
557
+ }),
558
+ );
548
559
  });
549
560
  });
550
561
 
@@ -634,13 +645,48 @@ describe('Unit test case for ts embed', () => {
634
645
  });
635
646
 
636
647
  await executeAfterWait(() => {
637
- expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
638
- hiddenHomeLeftNavItems:
639
- [HomeLeftNavItem.Home, HomeLeftNavItem.MonitorSubscription],
640
- }));
648
+ expect(mockPort.postMessage).toHaveBeenCalledWith(
649
+ getMockAppInitPayload({
650
+ hiddenHomeLeftNavItems: [
651
+ HomeLeftNavItem.Home,
652
+ HomeLeftNavItem.MonitorSubscription,
653
+ ],
654
+ }),
655
+ );
641
656
  });
642
657
  });
643
658
 
659
+ test.each([
660
+ ['not set', undefined, true],
661
+ ['false', false, false],
662
+ ['true', true, true],
663
+ ] as [string, boolean | undefined, boolean][])(
664
+ 'embedExpiryInAuthToken is %s when refreshAuthTokenOnNearExpiry is %s',
665
+ async (_label, refreshAuthTokenOnNearExpiry, expectedEmbedExpiry) => {
666
+ const mockEmbedEventPayload = {
667
+ type: EmbedEvent.APP_INIT,
668
+ data: {},
669
+ };
670
+ const searchEmbed = new AppEmbed(getRootEl(), {
671
+ ...defaultViewConfig,
672
+ refreshAuthTokenOnNearExpiry,
673
+ });
674
+ searchEmbed.render();
675
+ const mockPort: any = {
676
+ postMessage: jest.fn(),
677
+ };
678
+ await executeAfterWait(() => {
679
+ const iframe = getIFrameEl();
680
+ postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
681
+ });
682
+ await executeAfterWait(() => {
683
+ expect(mockPort.postMessage).toHaveBeenCalledWith(
684
+ getMockAppInitPayload({ embedExpiryInAuthToken: expectedEmbedExpiry }),
685
+ );
686
+ });
687
+ },
688
+ );
689
+
644
690
  test('when Embed event status have start status', (done) => {
645
691
  const mockEmbedEventPayload = {
646
692
  type: EmbedEvent.Save,
@@ -789,10 +835,12 @@ describe('Unit test case for ts embed', () => {
789
835
  postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
790
836
  });
791
837
  await executeAfterWait(() => {
792
- expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
793
- authToken: 'test_auth_token1',
794
- customVariablesForThirdPartyTools: {},
795
- }));
838
+ expect(mockPort.postMessage).toHaveBeenCalledWith(
839
+ getMockAppInitPayload({
840
+ authToken: 'test_auth_token1',
841
+ customVariablesForThirdPartyTools: {},
842
+ }),
843
+ );
796
844
  });
797
845
 
798
846
  jest.spyOn(authService, 'verifyTokenService').mockClear();
@@ -843,37 +891,40 @@ describe('Unit test case for ts embed', () => {
843
891
 
844
892
  await executeAfterWait(() => {
845
893
  const iframe = getIFrameEl();
846
- expect(iframe.src).toContain('overrideStringIDsUrl=https://sample-string-ids-url.com');
894
+ expect(iframe.src).toContain(
895
+ 'overrideStringIDsUrl=https://sample-string-ids-url.com',
896
+ );
847
897
  postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
848
898
  });
849
899
 
850
900
  await executeAfterWait(() => {
851
- expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
852
- customisations: {
853
- content: {
854
- strings: {
855
- Liveboard: 'Dashboard',
901
+ expect(mockPort.postMessage).toHaveBeenCalledWith(
902
+ getMockAppInitPayload({
903
+ customisations: {
904
+ content: {
905
+ strings: {
906
+ Liveboard: 'Dashboard',
907
+ },
908
+ stringIDsUrl: 'https://sample-string-ids-url.com',
909
+ stringIDs: {
910
+ 'liveboard.header.title': 'Dashboard name',
911
+ },
856
912
  },
857
- stringIDsUrl: 'https://sample-string-ids-url.com',
858
- stringIDs: {
859
- 'liveboard.header.title': 'Dashboard name',
913
+ style: {
914
+ customCSS: {},
915
+ customCSSUrl: undefined,
860
916
  },
861
917
  },
862
- style: {
863
- customCSS: {},
864
- customCSSUrl: undefined,
865
- },
866
- },
867
- authToken: 'test_auth_token1',
868
- customVariablesForThirdPartyTools: {},
869
- }));
870
- const customisationContent = mockPort.postMessage.mock.calls[0][0].data.customisations.content;
871
- expect(customisationContent.stringIDsUrl)
872
- .toBe('https://sample-string-ids-url.com');
873
- expect(customisationContent.stringIDs)
874
- .toEqual({
875
- 'liveboard.header.title': 'Dashboard name',
876
- });
918
+ authToken: 'test_auth_token1',
919
+ customVariablesForThirdPartyTools: {},
920
+ }),
921
+ );
922
+ const customisationContent =
923
+ mockPort.postMessage.mock.calls[0][0].data.customisations.content;
924
+ expect(customisationContent.stringIDsUrl).toBe('https://sample-string-ids-url.com');
925
+ expect(customisationContent.stringIDs).toEqual({
926
+ 'liveboard.header.title': 'Dashboard name',
927
+ });
877
928
  });
878
929
  });
879
930
 
@@ -882,7 +933,10 @@ describe('Unit test case for ts embed', () => {
882
933
  type: EmbedEvent.APP_INIT,
883
934
  data: {},
884
935
  };
885
- const searchEmbed = new SearchEmbed(getRootEl(), { ...defaultViewConfig, exposeTranslationIDs: true });
936
+ const searchEmbed = new SearchEmbed(getRootEl(), {
937
+ ...defaultViewConfig,
938
+ exposeTranslationIDs: true,
939
+ });
886
940
  searchEmbed.render();
887
941
  const mockPort: any = {
888
942
  postMessage: jest.fn(),
@@ -928,16 +982,16 @@ describe('Unit test case for ts embed', () => {
928
982
  name: 'Valid Action',
929
983
  target: CustomActionTarget.LIVEBOARD,
930
984
  position: CustomActionsPosition.PRIMARY,
931
- metadataIds: { liveboardIds: ['lb123'] }
985
+ metadataIds: { liveboardIds: ['lb123'] },
932
986
  },
933
987
  {
934
988
  id: 'action2',
935
989
  name: 'Another Valid Action',
936
990
  target: CustomActionTarget.VIZ,
937
991
  position: CustomActionsPosition.MENU,
938
- metadataIds: { vizIds: ['viz456'] }
939
- }
940
- ]
992
+ metadataIds: { vizIds: ['viz456'] },
993
+ },
994
+ ],
941
995
  });
942
996
 
943
997
  searchEmbed.render();
@@ -951,33 +1005,35 @@ describe('Unit test case for ts embed', () => {
951
1005
  });
952
1006
 
953
1007
  await executeAfterWait(() => {
954
- expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
955
- customisations: {
956
- content: {},
957
- style: {
958
- customCSS: {},
959
- customCSSUrl: undefined,
960
- },
961
- },
962
- authToken: 'test_auth_token1',
963
- customActions: [
964
- {
965
- id: 'action2',
966
- name: 'Another Valid Action',
967
- target: CustomActionTarget.VIZ,
968
- position: CustomActionsPosition.MENU,
969
- metadataIds: { vizIds: ['viz456'] }
1008
+ expect(mockPort.postMessage).toHaveBeenCalledWith(
1009
+ getMockAppInitPayload({
1010
+ customisations: {
1011
+ content: {},
1012
+ style: {
1013
+ customCSS: {},
1014
+ customCSSUrl: undefined,
1015
+ },
970
1016
  },
971
- {
972
- id: 'action1',
973
- name: 'Valid Action',
974
- target: CustomActionTarget.LIVEBOARD,
975
- position: CustomActionsPosition.PRIMARY,
976
- metadataIds: { liveboardIds: ['lb123'] }
977
- }
978
- ], // Actions should be sorted by name
979
- customVariablesForThirdPartyTools: {},
980
- }));
1017
+ authToken: 'test_auth_token1',
1018
+ customActions: [
1019
+ {
1020
+ id: 'action2',
1021
+ name: 'Another Valid Action',
1022
+ target: CustomActionTarget.VIZ,
1023
+ position: CustomActionsPosition.MENU,
1024
+ metadataIds: { vizIds: ['viz456'] },
1025
+ },
1026
+ {
1027
+ id: 'action1',
1028
+ name: 'Valid Action',
1029
+ target: CustomActionTarget.LIVEBOARD,
1030
+ position: CustomActionsPosition.PRIMARY,
1031
+ metadataIds: { liveboardIds: ['lb123'] },
1032
+ },
1033
+ ], // Actions should be sorted by name
1034
+ customVariablesForThirdPartyTools: {},
1035
+ }),
1036
+ );
981
1037
 
982
1038
  // Verify that CustomActionsValidationResult structure is
983
1039
  // correct
@@ -989,15 +1045,15 @@ describe('Unit test case for ts embed', () => {
989
1045
  id: 'action1',
990
1046
  name: 'Valid Action',
991
1047
  target: CustomActionTarget.LIVEBOARD,
992
- position: CustomActionsPosition.PRIMARY
1048
+ position: CustomActionsPosition.PRIMARY,
993
1049
  }),
994
1050
  expect.objectContaining({
995
1051
  id: 'action2',
996
1052
  name: 'Another Valid Action',
997
1053
  target: CustomActionTarget.VIZ,
998
- position: CustomActionsPosition.MENU
999
- })
1000
- ])
1054
+ position: CustomActionsPosition.MENU,
1055
+ }),
1056
+ ]),
1001
1057
  );
1002
1058
 
1003
1059
  // Verify actions are sorted by name (alphabetically)
@@ -1059,7 +1115,10 @@ describe('Unit test case for ts embed', () => {
1059
1115
  type: EmbedEvent.APP_INIT,
1060
1116
  data: {},
1061
1117
  };
1062
- const searchEmbed = new SearchEmbed(getRootEl(), { ...defaultViewConfig, preRenderId: 'test' });
1118
+ const searchEmbed = new SearchEmbed(getRootEl(), {
1119
+ ...defaultViewConfig,
1120
+ preRenderId: 'test',
1121
+ });
1063
1122
  searchEmbed.preRender();
1064
1123
  const mockPort: any = {
1065
1124
  postMessage: jest.fn(),
@@ -1118,7 +1177,10 @@ describe('Unit test case for ts embed', () => {
1118
1177
  type: EmbedEvent.AuthExpire,
1119
1178
  data: {},
1120
1179
  };
1121
- const searchEmbed = new SearchEmbed(getRootEl(), { ...defaultViewConfig, preRenderId: 'test' });
1180
+ const searchEmbed = new SearchEmbed(getRootEl(), {
1181
+ ...defaultViewConfig,
1182
+ preRenderId: 'test',
1183
+ });
1122
1184
  jest.spyOn(baseInstance, 'notifyAuthFailure');
1123
1185
  searchEmbed.preRender();
1124
1186
  const loggerSpy = jest.spyOn(logger, 'error').mockImplementation(() => {});
@@ -1283,23 +1345,27 @@ describe('Unit test case for ts embed', () => {
1283
1345
  let mockGetPreauthInfo = null;
1284
1346
 
1285
1347
  if (overrideOrgId) {
1286
- mockGetPreauthInfo = jest.spyOn(sessionInfoService, 'getPreauthInfo').mockImplementation(jest.fn());
1348
+ mockGetPreauthInfo = jest
1349
+ .spyOn(sessionInfoService, 'getPreauthInfo')
1350
+ .mockImplementation(jest.fn());
1287
1351
  }
1288
1352
 
1289
- const mockPreauthInfoFetch = jest.spyOn(authService, 'fetchPreauthInfoService').mockResolvedValueOnce({
1290
- ok: true,
1291
- headers: new Headers({ 'content-type': 'application/json' }), // Mock headers correctly
1292
- json: async () => ({
1293
- info: {
1294
- configInfo: {
1295
- mixpanelConfig: {
1296
- devSdkKey: 'devSdkKey',
1353
+ const mockPreauthInfoFetch = jest
1354
+ .spyOn(authService, 'fetchPreauthInfoService')
1355
+ .mockResolvedValueOnce({
1356
+ ok: true,
1357
+ headers: new Headers({ 'content-type': 'application/json' }), // Mock headers correctly
1358
+ json: async () => ({
1359
+ info: {
1360
+ configInfo: {
1361
+ mixpanelConfig: {
1362
+ devSdkKey: 'devSdkKey',
1363
+ },
1297
1364
  },
1365
+ userGUID: 'userGUID',
1298
1366
  },
1299
- userGUID: 'userGUID',
1300
- },
1301
- }), // Mock JSON response
1302
- });
1367
+ }), // Mock JSON response
1368
+ });
1303
1369
  const iFrame: any = document.createElement('div');
1304
1370
  jest.spyOn(baseInstance, 'getAuthPromise').mockResolvedValueOnce(isLoggedIn);
1305
1371
  const tsEmbed = new SearchEmbed(getRootEl(), {
@@ -1325,10 +1391,7 @@ describe('Unit test case for ts embed', () => {
1325
1391
  };
1326
1392
 
1327
1393
  test('should call InfoSuccess Event on preauth call success', async () => {
1328
- const {
1329
- mockPreauthInfoFetch,
1330
- iFrame,
1331
- } = await setup(true);
1394
+ const { mockPreauthInfoFetch, iFrame } = await setup(true);
1332
1395
  expect(mockPreauthInfoFetch).toHaveBeenCalledTimes(1);
1333
1396
 
1334
1397
  await executeAfterWait(() => {
@@ -1343,9 +1406,7 @@ describe('Unit test case for ts embed', () => {
1343
1406
  });
1344
1407
 
1345
1408
  test('should not call InfoSuccess Event if overrideOrgId is true', async () => {
1346
- const {
1347
- mockGetPreauthInfo,
1348
- } = await setup(true, 123);
1409
+ const { mockGetPreauthInfo } = await setup(true, 123);
1349
1410
  expect(mockGetPreauthInfo).toHaveBeenCalledTimes(0);
1350
1411
  });
1351
1412
  });
@@ -1370,7 +1431,7 @@ describe('Unit test case for ts embed', () => {
1370
1431
  embedType: 'AppEmbed' | 'SearchEmbed',
1371
1432
  showPrimaryNavbar?: boolean,
1372
1433
  overrideOrgId?: number,
1373
- disablePreauthCache?: boolean
1434
+ disablePreauthCache?: boolean,
1374
1435
  ) => {
1375
1436
  jest.spyOn(window, 'addEventListener').mockImplementationOnce(
1376
1437
  (event, handler, options) => {
@@ -1387,18 +1448,23 @@ describe('Unit test case for ts embed', () => {
1387
1448
  let mockGetPreauthInfo = null;
1388
1449
 
1389
1450
  // Determine if preauth cache should be enabled
1390
- const isAppEmbedWithPrimaryNavbar = embedType === 'AppEmbed' && showPrimaryNavbar === true;
1391
- const shouldDisableCache = overrideOrgId || disablePreauthCache || isAppEmbedWithPrimaryNavbar;
1451
+ const isAppEmbedWithPrimaryNavbar =
1452
+ embedType === 'AppEmbed' && showPrimaryNavbar === true;
1453
+ const shouldDisableCache =
1454
+ overrideOrgId || disablePreauthCache || isAppEmbedWithPrimaryNavbar;
1392
1455
 
1393
1456
  if (shouldDisableCache) {
1394
- mockGetPreauthInfo = jest.spyOn(sessionInfoService, 'getPreauthInfo')
1457
+ mockGetPreauthInfo = jest
1458
+ .spyOn(sessionInfoService, 'getPreauthInfo')
1395
1459
  .mockImplementation(jest.fn());
1396
1460
  } else {
1397
- mockGetPreauthInfo = jest.spyOn(sessionInfoService, 'getPreauthInfo')
1398
- .mockResolvedValue({ info: { test: 'data' } } as any);
1461
+ mockGetPreauthInfo = jest
1462
+ .spyOn(sessionInfoService, 'getPreauthInfo')
1463
+ .mockResolvedValue({ info: { test: 'data' } } as any);
1399
1464
  }
1400
1465
 
1401
- const mockPreauthInfoFetch = jest.spyOn(authService, 'fetchPreauthInfoService')
1466
+ const mockPreauthInfoFetch = jest
1467
+ .spyOn(authService, 'fetchPreauthInfoService')
1402
1468
  .mockResolvedValueOnce({
1403
1469
  ok: true,
1404
1470
  headers: new Headers({ 'content-type': 'application/json' }),
@@ -1533,7 +1599,12 @@ describe('Unit test case for ts embed', () => {
1533
1599
  });
1534
1600
 
1535
1601
  test('should disable preauth cache for FullAppEmbed with disablePreauthCache = true', async () => {
1536
- const { mockGetPreauthInfo } = await setupPreauthTest('AppEmbed', false, undefined, true);
1602
+ const { mockGetPreauthInfo } = await setupPreauthTest(
1603
+ 'AppEmbed',
1604
+ false,
1605
+ undefined,
1606
+ true,
1607
+ );
1537
1608
 
1538
1609
  await executeAfterWait(() => {
1539
1610
  expect(mockGetPreauthInfo).toHaveBeenCalledTimes(0);
@@ -1599,9 +1670,12 @@ describe('Unit test case for ts embed', () => {
1599
1670
 
1600
1671
  test('mixpanel should call with VISUAL_SDK_RENDER_FAILED', () => {
1601
1672
  expect(mockMixPanelEvent).toHaveBeenCalledWith(MIXPANEL_EVENT.VISUAL_SDK_RENDER_START);
1602
- expect(mockMixPanelEvent).toHaveBeenCalledWith(MIXPANEL_EVENT.VISUAL_SDK_RENDER_FAILED, {
1603
- error: 'false',
1604
- });
1673
+ expect(mockMixPanelEvent).toHaveBeenCalledWith(
1674
+ MIXPANEL_EVENT.VISUAL_SDK_RENDER_FAILED,
1675
+ {
1676
+ error: 'false',
1677
+ },
1678
+ );
1605
1679
  });
1606
1680
  });
1607
1681
 
@@ -1968,8 +2042,8 @@ describe('Unit test case for ts embed', () => {
1968
2042
  await appEmbed.render();
1969
2043
  expectUrlMatchesWithParams(
1970
2044
  getIFrameSrc(),
1971
- `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
1972
- + `&foo=bar&baz=1&bool=true${defaultParamsPost}#/home`,
2045
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
2046
+ `&foo=bar&baz=1&bool=true${defaultParamsPost}#/home`,
1973
2047
  );
1974
2048
  });
1975
2049
 
@@ -1997,8 +2071,8 @@ describe('Unit test case for ts embed', () => {
1997
2071
  await appEmbed.render();
1998
2072
  expectUrlMatchesWithParams(
1999
2073
  getIFrameSrc(),
2000
- `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
2001
- + `&foo=bar&foo2=bar2&foo3=false&baz=1&bool=true${defaultParamsPost}#/home`,
2074
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
2075
+ `&foo=bar&foo2=bar2&foo3=false&baz=1&bool=true${defaultParamsPost}#/home`,
2002
2076
  );
2003
2077
  });
2004
2078
 
@@ -2013,8 +2087,8 @@ describe('Unit test case for ts embed', () => {
2013
2087
  await appEmbed.render();
2014
2088
  expectUrlMatchesWithParams(
2015
2089
  getIFrameSrc(),
2016
- `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
2017
- + `&showAlerts=true${defaultParamsPost}#/home`,
2090
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
2091
+ `&showAlerts=true${defaultParamsPost}#/home`,
2018
2092
  );
2019
2093
  });
2020
2094
  it('Sets the locale param', async () => {
@@ -2028,8 +2102,8 @@ describe('Unit test case for ts embed', () => {
2028
2102
  await appEmbed.render();
2029
2103
  expectUrlMatchesWithParams(
2030
2104
  getIFrameSrc(),
2031
- `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
2032
- + `&locale=ja-JP${defaultParamsPost}#/home`,
2105
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
2106
+ `&locale=ja-JP${defaultParamsPost}#/home`,
2033
2107
  );
2034
2108
  });
2035
2109
  it('Sets both params when enableLinkOverridesV2 is set', async () => {
@@ -2047,7 +2121,7 @@ describe('Unit test case for ts embed', () => {
2047
2121
  `http://${thoughtSpotHost}/?embedApp=true&${defaultParamsForPinboardEmbed}&enableLinkOverridesV2=true&linkOverride=true${defaultParamsPost}#/embed/viz/test-lb`,
2048
2122
  );
2049
2123
  });
2050
- it('Sets only linkOverride when enableLinkOverridesV2 is not set', async () => {
2124
+ it('Auto-upgrades V1 linkOverride to V2 (sends both flags)', async () => {
2051
2125
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2052
2126
  frameParams: {
2053
2127
  width: '100%',
@@ -2057,10 +2131,40 @@ describe('Unit test case for ts embed', () => {
2057
2131
  linkOverride: true,
2058
2132
  });
2059
2133
  await liveboardEmbed.render();
2060
- expectUrlMatchesWithParams(
2061
- getIFrameSrc(),
2062
- `http://${thoughtSpotHost}/?embedApp=true&${defaultParamsForPinboardEmbed}&linkOverride=true${defaultParamsPost}#/embed/viz/test-lb`,
2063
- );
2134
+ const src = getIFrameSrc();
2135
+ expect(src).toContain('linkOverride=true');
2136
+ expect(src).toContain('enableLinkOverridesV2=true');
2137
+ });
2138
+ it('Auto-disables V2 link overrides when disableRedirectionLinksInNewTab is true', async () => {
2139
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2140
+ frameParams: {
2141
+ width: '100%',
2142
+ height: '100%',
2143
+ },
2144
+ liveboardId: 'test-lb',
2145
+ enableLinkOverridesV2: true,
2146
+ disableRedirectionLinksInNewTab: true,
2147
+ });
2148
+ await liveboardEmbed.render();
2149
+ const src = getIFrameSrc();
2150
+ expect(src).not.toContain('enableLinkOverridesV2=true');
2151
+ expect(src).not.toContain('linkOverride=true');
2152
+ expect(src).toContain('disableRedirectionLinksInNewTab=true');
2153
+ });
2154
+ it('Auto-disables V1 link override when disableRedirectionLinksInNewTab is true', async () => {
2155
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2156
+ frameParams: {
2157
+ width: '100%',
2158
+ height: '100%',
2159
+ },
2160
+ liveboardId: 'test-lb',
2161
+ linkOverride: true,
2162
+ disableRedirectionLinksInNewTab: true,
2163
+ });
2164
+ await liveboardEmbed.render();
2165
+ const src = getIFrameSrc();
2166
+ expect(src).not.toContain('linkOverride=true');
2167
+ expect(src).toContain('disableRedirectionLinksInNewTab=true');
2064
2168
  });
2065
2169
  it('Sets the iconSprite url', async () => {
2066
2170
  const appEmbed = new AppEmbed(getRootEl(), {
@@ -2075,8 +2179,8 @@ describe('Unit test case for ts embed', () => {
2075
2179
  await appEmbed.render();
2076
2180
  expectUrlMatchesWithParams(
2077
2181
  getIFrameSrc(),
2078
- `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
2079
- + `&iconSprite=iconSprite.com${defaultParamsPost}#/home`,
2182
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
2183
+ `&iconSprite=iconSprite.com${defaultParamsPost}#/home`,
2080
2184
  );
2081
2185
  });
2082
2186
 
@@ -2356,8 +2460,9 @@ describe('Unit test case for ts embed', () => {
2356
2460
  tsEmbedDiv.style.height = '100px';
2357
2461
 
2358
2462
  let resizeObserverCb: any;
2359
- (window as any).ResizeObserver = window.ResizeObserver
2360
- || jest.fn().mockImplementation((resizeObserverCbParam) => {
2463
+ (window as any).ResizeObserver =
2464
+ window.ResizeObserver ||
2465
+ jest.fn().mockImplementation((resizeObserverCbParam) => {
2361
2466
  resizeObserverCb = resizeObserverCbParam;
2362
2467
  return {
2363
2468
  disconnect: jest.fn(),
@@ -2371,9 +2476,12 @@ describe('Unit test case for ts embed', () => {
2371
2476
  libEmbed.showPreRender();
2372
2477
  expect(warnSpy).toHaveBeenCalledTimes(1);
2373
2478
 
2479
+ // The ResizeObserver now tracks the placeholder inside this.el,
2480
+ // not this.el itself, so pass it as the target.
2481
+ const preRenderPlaceholder = tsEmbedDiv.firstElementChild as HTMLElement;
2374
2482
  resizeObserverCb([
2375
2483
  {
2376
- target: tsEmbedDiv,
2484
+ target: preRenderPlaceholder,
2377
2485
  contentRect: { height: 297, width: 987 },
2378
2486
  },
2379
2487
  ]);
@@ -2438,8 +2546,9 @@ describe('Unit test case for ts embed', () => {
2438
2546
  it('should set overflow:hidden when hidePreRender and remove when showPreRender', async () => {
2439
2547
  createRootEleForEmbed();
2440
2548
 
2441
- (window as any).ResizeObserver = window.ResizeObserver
2442
- || jest.fn().mockImplementation(() => ({
2549
+ (window as any).ResizeObserver =
2550
+ window.ResizeObserver ||
2551
+ jest.fn().mockImplementation(() => ({
2443
2552
  disconnect: jest.fn(),
2444
2553
  observe: jest.fn(),
2445
2554
  unobserve: jest.fn(),
@@ -2476,8 +2585,9 @@ describe('Unit test case for ts embed', () => {
2476
2585
  it('it should connect with another object', async () => {
2477
2586
  createRootEleForEmbed();
2478
2587
  mockMessageChannel();
2479
- (window as any).ResizeObserver = window.ResizeObserver
2480
- || jest.fn().mockImplementation(() => ({
2588
+ (window as any).ResizeObserver =
2589
+ window.ResizeObserver ||
2590
+ jest.fn().mockImplementation(() => ({
2481
2591
  disconnect: jest.fn(),
2482
2592
  observe: jest.fn(),
2483
2593
  unobserve: jest.fn(),
@@ -2489,7 +2599,6 @@ describe('Unit test case for ts embed', () => {
2489
2599
 
2490
2600
  libEmbed.preRender();
2491
2601
  await waitFor(() => !!getIFrameEl());
2492
- const warnSpy = jest.spyOn(logger, 'warn');
2493
2602
  const newEmbed = new LiveboardEmbed('#tsEmbedDiv', {
2494
2603
  preRenderId: 'i-am-preRendered',
2495
2604
  liveboardId: 'awdawda',
@@ -2497,9 +2606,10 @@ describe('Unit test case for ts embed', () => {
2497
2606
  frameParams: { height: 90 },
2498
2607
  });
2499
2608
 
2500
- newEmbed.showPreRender();
2609
+ await newEmbed.showPreRender();
2501
2610
 
2502
- expect(warnSpy).toHaveBeenCalledTimes(2);
2611
+ // Verify newEmbed successfully connected to the existing preRender
2612
+ expect((newEmbed as any).isPreRenderConnected()).toBe(true);
2503
2613
  });
2504
2614
  it('showPreRender should not preRender if not available', async () => {
2505
2615
  createRootEleForEmbed();
@@ -2676,7 +2786,10 @@ describe('Unit test case for ts embed', () => {
2676
2786
  document.body.innerHTML = getDocumentBody();
2677
2787
  });
2678
2788
  test('Pre-render should wait for init to complete', async () => {
2679
- const lib = new LiveboardEmbed(getRootEl(), { preRenderId: 'test', liveboardId: 'test' });
2789
+ const lib = new LiveboardEmbed(getRootEl(), {
2790
+ preRenderId: 'test',
2791
+ liveboardId: 'test',
2792
+ });
2680
2793
  lib.preRender();
2681
2794
  await executeAfterWait(() => {
2682
2795
  expect(errorSpy).toHaveBeenCalledWith(ERROR_MESSAGE.RENDER_CALLED_BEFORE_INIT);
@@ -2751,10 +2864,12 @@ describe('Unit test case for ts embed', () => {
2751
2864
  jest.clearAllMocks();
2752
2865
  document.body.innerHTML = getDocumentBody();
2753
2866
  mockPort.postMessage.mockClear();
2754
- jest.spyOn(authToken, 'getAuthenticationToken').mockResolvedValue('mock-test-token-placeholder');
2867
+ jest.spyOn(authToken, 'getAuthenticationToken').mockResolvedValue(
2868
+ 'mock-test-token-placeholder',
2869
+ );
2755
2870
 
2756
2871
  jest.spyOn(baseInstance, 'handleAuth').mockImplementation(() => Promise.resolve(true));
2757
- jest.spyOn(baseInstance, 'notifyAuthFailure').mockImplementation(() => { });
2872
+ jest.spyOn(baseInstance, 'notifyAuthFailure').mockImplementation(() => {});
2758
2873
  });
2759
2874
 
2760
2875
  const renderAndTriggerAuthExpire = async () => {
@@ -2867,7 +2982,7 @@ describe('Unit test case for ts embed', () => {
2867
2982
 
2868
2983
  afterEach(() => {
2869
2984
  expect(baseInstance.notifyAuthFailure).toHaveBeenCalledWith(
2870
- authInstance.AuthFailureType.EXPIRY
2985
+ authInstance.AuthFailureType.EXPIRY,
2871
2986
  );
2872
2987
  });
2873
2988
  });
@@ -2880,8 +2995,10 @@ describe('Unit test case for ts embed', () => {
2880
2995
  jest.clearAllMocks();
2881
2996
  document.body.innerHTML = getDocumentBody();
2882
2997
  mockPort.postMessage.mockClear();
2883
- jest.spyOn(authToken, 'getAuthenticationToken').mockResolvedValue('mock-test-token-placeholder');
2884
- jest.spyOn(processData, 'processAuthFailure').mockImplementation(() => ({} as any));
2998
+ jest.spyOn(authToken, 'getAuthenticationToken').mockResolvedValue(
2999
+ 'mock-test-token-placeholder',
3000
+ );
3001
+ jest.spyOn(processData, 'processAuthFailure').mockImplementation(() => ({}) as any);
2885
3002
  jest.spyOn(logger, 'error').mockImplementation(() => {});
2886
3003
  });
2887
3004
 
@@ -2911,7 +3028,7 @@ describe('Unit test case for ts embed', () => {
2911
3028
  await executeAfterWait(() => {
2912
3029
  expect(authToken.getAuthenticationToken).toHaveBeenCalledWith(
2913
3030
  expect.any(Object),
2914
- true
3031
+ true,
2915
3032
  );
2916
3033
  });
2917
3034
  });
@@ -2928,7 +3045,7 @@ describe('Unit test case for ts embed', () => {
2928
3045
  await executeAfterWait(() => {
2929
3046
  expect(authToken.getAuthenticationToken).toHaveBeenCalledWith(
2930
3047
  expect.any(Object),
2931
- true
3048
+ true,
2932
3049
  );
2933
3050
  });
2934
3051
  });
@@ -2963,17 +3080,19 @@ describe('Unit test case for ts embed', () => {
2963
3080
  await executeAfterWait(() => {
2964
3081
  expect(authToken.getAuthenticationToken).toHaveBeenCalledWith(
2965
3082
  expect.any(Object),
2966
- true
3083
+ true,
2967
3084
  );
2968
3085
  // Check that logger.error was called with the token refresh
2969
3086
  // error
2970
3087
  const errorCalls = (logger.error as jest.Mock).mock.calls.filter(
2971
- (call) => call[0]?.includes(ERROR_MESSAGE.INVALID_TOKEN_ERROR) && call[0]?.includes('Token fetch failed')
3088
+ (call) =>
3089
+ call[0]?.includes(ERROR_MESSAGE.INVALID_TOKEN_ERROR) &&
3090
+ call[0]?.includes('Token fetch failed'),
2972
3091
  );
2973
3092
  expect(errorCalls.length).toBeGreaterThan(0);
2974
3093
  expect(processData.processAuthFailure).toHaveBeenCalledWith(
2975
3094
  error,
2976
- expect.any(Element)
3095
+ expect.any(Element),
2977
3096
  );
2978
3097
  expect(mockPort.postMessage).not.toHaveBeenCalled();
2979
3098
  });
@@ -3162,7 +3281,9 @@ describe('Unit test case for ts embed', () => {
3162
3281
  isEmbedContainerLoaded: true,
3163
3282
  };
3164
3283
 
3165
- jest.spyOn(searchEmbed as any, 'getPreRenderObj').mockReturnValue(mockPreRenderObj as any);
3284
+ jest.spyOn(searchEmbed as any, 'getPreRenderObj').mockReturnValue(
3285
+ mockPreRenderObj as any,
3286
+ );
3166
3287
 
3167
3288
  const result = searchEmbed['checkEmbedContainerLoaded']();
3168
3289
 
@@ -3174,8 +3295,8 @@ describe('Unit test case for ts embed', () => {
3174
3295
  const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3175
3296
  const loggerSpy = jest.spyOn(logger, 'info');
3176
3297
 
3177
- // Mock insertedDomEl to have the embed object
3178
- (searchEmbed as any).insertedDomEl = {
3298
+ // getPreRenderObj reads the embed reference from preRenderWrapper
3299
+ (searchEmbed as any).preRenderWrapper = {
3179
3300
  [searchEmbed['embedNodeKey']]: searchEmbed,
3180
3301
  };
3181
3302
 
@@ -3238,7 +3359,8 @@ describe('Unit test case for ts embed', () => {
3238
3359
  const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3239
3360
  searchEmbed.isEmbedContainerLoaded = true;
3240
3361
 
3241
- const triggerSpy = jest.spyOn(searchEmbed, 'trigger')
3362
+ const triggerSpy = jest
3363
+ .spyOn(searchEmbed, 'trigger')
3242
3364
  .mockResolvedValue(mockContext);
3243
3365
 
3244
3366
  const context = await searchEmbed.getCurrentContext();
@@ -3251,7 +3373,8 @@ describe('Unit test case for ts embed', () => {
3251
3373
  const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3252
3374
  searchEmbed.isEmbedContainerLoaded = false;
3253
3375
 
3254
- const triggerSpy = jest.spyOn(searchEmbed, 'trigger')
3376
+ const triggerSpy = jest
3377
+ .spyOn(searchEmbed, 'trigger')
3255
3378
  .mockResolvedValue(mockContext);
3256
3379
 
3257
3380
  const contextPromise = searchEmbed.getCurrentContext();
@@ -3303,7 +3426,9 @@ describe('Unit test case for ts embed', () => {
3303
3426
  test('should handle handleEmbedContainerLoaded with EmbedListenerReady source', () => {
3304
3427
  const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3305
3428
 
3306
- const handler = searchEmbed['createEmbedContainerHandler'](EmbedEvent.EmbedListenerReady);
3429
+ const handler = searchEmbed['createEmbedContainerHandler'](
3430
+ EmbedEvent.EmbedListenerReady,
3431
+ );
3307
3432
 
3308
3433
  expect(searchEmbed.isEmbedContainerLoaded).toBe(false);
3309
3434
 
@@ -3373,7 +3498,7 @@ describe('Unit test case for ts embed', () => {
3373
3498
  new Error('Auth failed'),
3374
3499
  );
3375
3500
  const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3376
- const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
3501
+ const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
3377
3502
  await searchEmbed.render();
3378
3503
  await executeAfterWait(() => {
3379
3504
  expect(getRootEl().innerHTML).toContain('Not logged in');
@@ -3495,11 +3620,13 @@ describe('Unit test case for ts embed', () => {
3495
3620
  await appEmbed.render();
3496
3621
 
3497
3622
  jest.spyOn(appEmbed, 'trigger').mockRejectedValue(new Error('trigger failed'));
3498
- const removeChildSpy = jest.spyOn(Node.prototype, 'removeChild').mockImplementation(() => getRootEl());
3623
+ const removeChildSpy = jest
3624
+ .spyOn(Node.prototype, 'removeChild')
3625
+ .mockImplementation(() => getRootEl());
3499
3626
 
3500
3627
  appEmbed.destroy();
3501
3628
 
3502
- await new Promise(resolve => setTimeout(resolve, 50));
3629
+ await new Promise((resolve) => setTimeout(resolve, 50));
3503
3630
 
3504
3631
  expect(removeChildSpy).toHaveBeenCalled();
3505
3632
 
@@ -3532,7 +3659,9 @@ describe('Unit test case for ts embed', () => {
3532
3659
  await appEmbed.render();
3533
3660
 
3534
3661
  const triggerSpy = jest.spyOn(appEmbed, 'trigger').mockResolvedValue(null);
3535
- const removeChildSpy = jest.spyOn(Node.prototype, 'removeChild').mockImplementation(() => getRootEl());
3662
+ const removeChildSpy = jest
3663
+ .spyOn(Node.prototype, 'removeChild')
3664
+ .mockImplementation(() => getRootEl());
3536
3665
 
3537
3666
  appEmbed.destroy();
3538
3667
 
@@ -3556,7 +3685,9 @@ describe('Unit test case for ts embed', () => {
3556
3685
  await appEmbed.render();
3557
3686
 
3558
3687
  const triggerSpy = jest.spyOn(appEmbed, 'trigger').mockResolvedValue(null);
3559
- const removeChildSpy = jest.spyOn(Node.prototype, 'removeChild').mockImplementation(() => getRootEl());
3688
+ const removeChildSpy = jest
3689
+ .spyOn(Node.prototype, 'removeChild')
3690
+ .mockImplementation(() => getRootEl());
3560
3691
 
3561
3692
  appEmbed.destroy();
3562
3693
 
@@ -3564,7 +3695,7 @@ describe('Unit test case for ts embed', () => {
3564
3695
  expect(triggerSpy).toHaveBeenCalledWith(HostEvent.DestroyEmbed);
3565
3696
 
3566
3697
  // Wait for the timeout to complete
3567
- await new Promise(resolve => setTimeout(resolve, 1100));
3698
+ await new Promise((resolve) => setTimeout(resolve, 1100));
3568
3699
 
3569
3700
  expect(removeChildSpy).toHaveBeenCalled();
3570
3701
  });
@@ -3585,15 +3716,19 @@ describe('Unit test case for ts embed', () => {
3585
3716
  await appEmbed.render();
3586
3717
 
3587
3718
  // Mock trigger to resolve quickly (before timeout)
3588
- const triggerSpy = jest.spyOn(appEmbed, 'trigger').mockImplementation(() =>
3589
- new Promise(resolve => setTimeout(() => resolve(null), 100))
3590
- );
3591
- const removeChildSpy = jest.spyOn(Node.prototype, 'removeChild').mockImplementation(() => getRootEl());
3719
+ const triggerSpy = jest
3720
+ .spyOn(appEmbed, 'trigger')
3721
+ .mockImplementation(
3722
+ () => new Promise((resolve) => setTimeout(() => resolve(null), 100)),
3723
+ );
3724
+ const removeChildSpy = jest
3725
+ .spyOn(Node.prototype, 'removeChild')
3726
+ .mockImplementation(() => getRootEl());
3592
3727
 
3593
3728
  appEmbed.destroy();
3594
3729
 
3595
3730
  // Wait for the trigger to complete
3596
- await new Promise(resolve => setTimeout(resolve, 200));
3731
+ await new Promise((resolve) => setTimeout(resolve, 200));
3597
3732
 
3598
3733
  expect(triggerSpy).toHaveBeenCalledWith(HostEvent.DestroyEmbed);
3599
3734
  expect(removeChildSpy).toHaveBeenCalled();
@@ -3615,15 +3750,19 @@ describe('Unit test case for ts embed', () => {
3615
3750
  await appEmbed.render();
3616
3751
 
3617
3752
  // Mock trigger to take longer than timeout
3618
- const triggerSpy = jest.spyOn(appEmbed, 'trigger').mockImplementation(() =>
3619
- new Promise(resolve => setTimeout(() => resolve(null), 500))
3620
- );
3621
- const removeChildSpy = jest.spyOn(Node.prototype, 'removeChild').mockImplementation(() => getRootEl());
3753
+ const triggerSpy = jest
3754
+ .spyOn(appEmbed, 'trigger')
3755
+ .mockImplementation(
3756
+ () => new Promise((resolve) => setTimeout(() => resolve(null), 500)),
3757
+ );
3758
+ const removeChildSpy = jest
3759
+ .spyOn(Node.prototype, 'removeChild')
3760
+ .mockImplementation(() => getRootEl());
3622
3761
 
3623
3762
  appEmbed.destroy();
3624
3763
 
3625
3764
  // Wait for the timeout to complete
3626
- await new Promise(resolve => setTimeout(resolve, 200));
3765
+ await new Promise((resolve) => setTimeout(resolve, 200));
3627
3766
 
3628
3767
  expect(triggerSpy).toHaveBeenCalledWith(HostEvent.DestroyEmbed);
3629
3768
  expect(removeChildSpy).toHaveBeenCalled();
@@ -3655,11 +3794,11 @@ describe('Unit test case for ts embed', () => {
3655
3794
  body: JSON.stringify({
3656
3795
  variables: {
3657
3796
  session: { sessionId: 'session-123' },
3658
- contextBookId: 'viz-456'
3659
- }
3660
- })
3661
- }
3662
- })
3797
+ contextBookId: 'viz-456',
3798
+ },
3799
+ }),
3800
+ },
3801
+ }),
3663
3802
  };
3664
3803
 
3665
3804
  const mockPort: any = {
@@ -3696,8 +3835,8 @@ describe('Unit test case for ts embed', () => {
3696
3835
  type: EmbedEvent.ApiIntercept,
3697
3836
  data: JSON.stringify({
3698
3837
  input: '/prism/?op=GetChartWithData',
3699
- init: {}
3700
- })
3838
+ init: {},
3839
+ }),
3701
3840
  };
3702
3841
 
3703
3842
  const mockPort: any = {
@@ -3738,8 +3877,8 @@ describe('Unit test case for ts embed', () => {
3738
3877
  type: EmbedEvent.ApiIntercept,
3739
3878
  data: JSON.stringify({
3740
3879
  input: '/prism/?op=GetChartWithData',
3741
- init: {}
3742
- })
3880
+ init: {},
3881
+ }),
3743
3882
  };
3744
3883
 
3745
3884
  const mockPort: any = {
@@ -3761,7 +3900,7 @@ describe('Unit test case for ts embed', () => {
3761
3900
  // handleInterceptEvent
3762
3901
  const result = await capturedGetUnsavedAnswerTml({
3763
3902
  sessionId: 'session-123',
3764
- vizId: 'viz-456'
3903
+ vizId: 'viz-456',
3765
3904
  });
3766
3905
 
3767
3906
  expect(mockProcessTrigger).toHaveBeenCalled();
@@ -3772,8 +3911,8 @@ describe('Unit test case for ts embed', () => {
3772
3911
  type: 'getUnsavedAnswerTML',
3773
3912
  parameters: {
3774
3913
  sessionId: 'session-123',
3775
- vizId: 'viz-456'
3776
- }
3914
+ vizId: 'viz-456',
3915
+ },
3777
3916
  });
3778
3917
  expect(result).toEqual(mockTmlResponse);
3779
3918
  });
@@ -3793,8 +3932,8 @@ describe('Unit test case for ts embed', () => {
3793
3932
  type: EmbedEvent.ApiIntercept,
3794
3933
  data: JSON.stringify({
3795
3934
  input: '/api/test',
3796
- init: {}
3797
- })
3935
+ init: {},
3936
+ }),
3798
3937
  };
3799
3938
 
3800
3939
  const mockPort: any = {
@@ -3823,8 +3962,8 @@ describe('Unit test case for ts embed', () => {
3823
3962
  type: EmbedEvent.ApiIntercept,
3824
3963
  data: JSON.stringify({
3825
3964
  input: '/prism/?op=GetChartWithData',
3826
- init: {}
3827
- })
3965
+ init: {},
3966
+ }),
3828
3967
  };
3829
3968
 
3830
3969
  const mockPort: any = {
@@ -3876,16 +4015,16 @@ describe('Unit test case for ts embed', () => {
3876
4015
  type: EmbedEvent.ApiIntercept,
3877
4016
  data: JSON.stringify({
3878
4017
  input: '/prism/?op=GetChartWithData',
3879
- init: {}
3880
- })
4018
+ init: {},
4019
+ }),
3881
4020
  };
3882
4021
 
3883
4022
  const mockEventData2 = {
3884
4023
  type: EmbedEvent.ApiIntercept,
3885
4024
  data: JSON.stringify({
3886
4025
  input: '/prism/?op=LoadContextBook',
3887
- init: {}
3888
- })
4026
+ init: {},
4027
+ }),
3889
4028
  };
3890
4029
 
3891
4030
  const mockPort: any = {
@@ -3921,8 +4060,8 @@ describe('Unit test case for ts embed', () => {
3921
4060
  type: EmbedEvent.ApiIntercept,
3922
4061
  data: JSON.stringify({
3923
4062
  input: '/prism/?op=GetChartWithData',
3924
- init: {}
3925
- })
4063
+ init: {},
4064
+ }),
3926
4065
  };
3927
4066
 
3928
4067
  const mockPort: any = {
@@ -3962,8 +4101,8 @@ describe('Unit test case for ts embed', () => {
3962
4101
  type: EmbedEvent.ApiIntercept,
3963
4102
  data: JSON.stringify({
3964
4103
  input: '/prism/?op=GetChartWithData',
3965
- init: {}
3966
- })
4104
+ init: {},
4105
+ }),
3967
4106
  };
3968
4107
 
3969
4108
  const mockPort: any = {
@@ -3980,7 +4119,7 @@ describe('Unit test case for ts embed', () => {
3980
4119
 
3981
4120
  const result = await capturedGetUnsavedAnswerTml({
3982
4121
  sessionId: 'session-123',
3983
- vizId: 'viz-456'
4122
+ vizId: 'viz-456',
3984
4123
  });
3985
4124
 
3986
4125
  expect(result).toBeUndefined();
@@ -3998,8 +4137,8 @@ describe('Unit test case for ts embed', () => {
3998
4137
  type: EmbedEvent.ApiIntercept,
3999
4138
  data: JSON.stringify({
4000
4139
  input: '/prism/?op=LoadContextBook',
4001
- init: {}
4002
- })
4140
+ init: {},
4141
+ }),
4003
4142
  };
4004
4143
 
4005
4144
  const mockPort: any = {
@@ -4016,14 +4155,13 @@ describe('Unit test case for ts embed', () => {
4016
4155
  expect(mockHandleInterceptEvent).toHaveBeenCalledWith(
4017
4156
  expect.objectContaining({
4018
4157
  eventData: mockEventData,
4019
- })
4158
+ }),
4020
4159
  );
4021
4160
  });
4022
4161
  });
4023
4162
  });
4024
4163
  });
4025
4164
 
4026
-
4027
4165
  describe('Additional Coverage Tests', () => {
4028
4166
  beforeAll(() => {
4029
4167
  init({
@@ -4128,9 +4266,7 @@ describe('Trigger method edge cases', () => {
4128
4266
  });
4129
4267
 
4130
4268
  test('should return null when trigger is called before iframe is ready', async () => {
4131
- jest.spyOn(baseInstance, 'getAuthPromise').mockRejectedValueOnce(
4132
- new Error('Auth failed'),
4133
- );
4269
+ jest.spyOn(baseInstance, 'getAuthPromise').mockRejectedValueOnce(new Error('Auth failed'));
4134
4270
  const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
4135
4271
  jest.spyOn(logger, 'debug');
4136
4272
  await searchEmbed.render();
@@ -4298,12 +4434,16 @@ describe('Fullscreen change handler behavior', () => {
4298
4434
  });
4299
4435
 
4300
4436
  describe('ShowPreRender with UpdateEmbedParams', () => {
4301
- const setupPreRenderTest = async (preRenderId: string, initialConfig: Partial<LiveboardViewConfig>) => {
4437
+ const setupPreRenderTest = async (
4438
+ preRenderId: string,
4439
+ initialConfig: Partial<LiveboardViewConfig>,
4440
+ ) => {
4302
4441
  createRootEleForEmbed();
4303
4442
  mockMessageChannel();
4304
4443
 
4305
- (window as any).ResizeObserver = window.ResizeObserver
4306
- || jest.fn().mockImplementation(() => ({
4444
+ (window as any).ResizeObserver =
4445
+ window.ResizeObserver ||
4446
+ jest.fn().mockImplementation(() => ({
4307
4447
  disconnect: jest.fn(),
4308
4448
  observe: jest.fn(),
4309
4449
  unobserve: jest.fn(),
@@ -4513,7 +4653,8 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4513
4653
  });
4514
4654
  });
4515
4655
 
4516
- // Matches the structure produced by createValidationError / embedErrorDetails
4656
+ // Matches the structure produced by createValidationError /
4657
+ // embedErrorDetails
4517
4658
  const makeNestedValidationData = (message = 'invalid payload') => ({
4518
4659
  type: EmbedEvent.Error,
4519
4660
  data: {
@@ -4524,7 +4665,8 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4524
4665
  },
4525
4666
  });
4526
4667
 
4527
- // Matches the flat structure where errorType sits at the top level of data
4668
+ // Matches the flat structure where errorType sits at the top level of
4669
+ // data
4528
4670
  const makeFlatValidationData = (message = 'invalid payload') => ({
4529
4671
  errorType: EmbedErrorCodes.HOST_EVENT_VALIDATION,
4530
4672
  message,
@@ -4559,12 +4701,13 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4559
4701
  const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: true });
4560
4702
  embed.on(EmbedEvent.Error, errorHandler);
4561
4703
 
4562
- (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData('nested error'));
4704
+ (embed as any).executeCallbacks(
4705
+ EmbedEvent.Error,
4706
+ makeNestedValidationData('nested error'),
4707
+ );
4563
4708
 
4564
4709
  expect(errorHandler).not.toHaveBeenCalled();
4565
- expect(logger.warn).toHaveBeenCalledWith(
4566
- 'Host Event Validation failed: nested error',
4567
- );
4710
+ expect(logger.warn).toHaveBeenCalledWith('Host Event Validation failed: nested error');
4568
4711
  });
4569
4712
 
4570
4713
  test('skips Error event when errorType is resolved from data.errorType (flat format)', () => {
@@ -4580,7 +4723,10 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4580
4723
 
4581
4724
  test('delivers Error event to handler when useHostEventsV2 is true and shouldBypassPayloadValidation is undefined', () => {
4582
4725
  const errorHandler = jest.fn();
4583
- const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: undefined });
4726
+ const embed = makeEmbed({
4727
+ useHostEventsV2: true,
4728
+ shouldBypassPayloadValidation: undefined,
4729
+ });
4584
4730
  embed.on(EmbedEvent.Error, errorHandler);
4585
4731
 
4586
4732
  (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
@@ -4590,7 +4736,10 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4590
4736
 
4591
4737
  test('delivers Error event to handler when useHostEventsV2 is true and shouldBypassPayloadValidation is false', () => {
4592
4738
  const errorHandler = jest.fn();
4593
- const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
4739
+ const embed = makeEmbed({
4740
+ useHostEventsV2: true,
4741
+ shouldBypassPayloadValidation: false,
4742
+ });
4594
4743
  embed.on(EmbedEvent.Error, errorHandler);
4595
4744
 
4596
4745
  (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
@@ -4601,7 +4750,10 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4601
4750
  test('skips Error event when useHostEventsV2 is false regardless of shouldBypassPayloadValidation', () => {
4602
4751
  jest.spyOn(logger, 'warn');
4603
4752
  const errorHandler = jest.fn();
4604
- const embed = makeEmbed({ useHostEventsV2: false, shouldBypassPayloadValidation: undefined });
4753
+ const embed = makeEmbed({
4754
+ useHostEventsV2: false,
4755
+ shouldBypassPayloadValidation: undefined,
4756
+ });
4605
4757
  embed.on(EmbedEvent.Error, errorHandler);
4606
4758
 
4607
4759
  (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
@@ -4625,13 +4777,18 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4625
4777
  test('skips Error event when useHostEventsV2 is false and shouldBypassPayloadValidation is true', () => {
4626
4778
  jest.spyOn(logger, 'warn');
4627
4779
  const errorHandler = jest.fn();
4628
- const embed = makeEmbed({ useHostEventsV2: false, shouldBypassPayloadValidation: true });
4780
+ const embed = makeEmbed({
4781
+ useHostEventsV2: false,
4782
+ shouldBypassPayloadValidation: true,
4783
+ });
4629
4784
  embed.on(EmbedEvent.Error, errorHandler);
4630
4785
 
4631
4786
  (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
4632
4787
 
4633
4788
  expect(errorHandler).not.toHaveBeenCalled();
4634
- expect(logger.warn).toHaveBeenCalledWith('Host Event Validation failed: invalid payload');
4789
+ expect(logger.warn).toHaveBeenCalledWith(
4790
+ 'Host Event Validation failed: invalid payload',
4791
+ );
4635
4792
  });
4636
4793
 
4637
4794
  test('skips via handleError when shouldBypassPayloadValidation is true', () => {
@@ -4656,7 +4813,10 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4656
4813
 
4657
4814
  test('delivers Error event to EmbedEvent.ALL handler when not skipped', () => {
4658
4815
  const allHandler = jest.fn();
4659
- const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
4816
+ const embed = makeEmbed({
4817
+ useHostEventsV2: true,
4818
+ shouldBypassPayloadValidation: false,
4819
+ });
4660
4820
  embed.on(EmbedEvent.ALL, allHandler);
4661
4821
 
4662
4822
  (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
@@ -4666,7 +4826,10 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4666
4826
 
4667
4827
  test('does not skip non-Error events even with HOST_EVENT_VALIDATION error code', () => {
4668
4828
  const customActionHandler = jest.fn();
4669
- const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
4829
+ const embed = makeEmbed({
4830
+ useHostEventsV2: true,
4831
+ shouldBypassPayloadValidation: false,
4832
+ });
4670
4833
  embed.on(EmbedEvent.CustomAction, customActionHandler);
4671
4834
 
4672
4835
  (embed as any).executeCallbacks(EmbedEvent.CustomAction, {
@@ -4678,7 +4841,10 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4678
4841
 
4679
4842
  test('does not skip Error events with unrelated error codes', () => {
4680
4843
  const errorHandler = jest.fn();
4681
- const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
4844
+ const embed = makeEmbed({
4845
+ useHostEventsV2: true,
4846
+ shouldBypassPayloadValidation: false,
4847
+ });
4682
4848
  embed.on(EmbedEvent.Error, errorHandler);
4683
4849
 
4684
4850
  (embed as any).executeCallbacks(EmbedEvent.Error, {
@@ -4689,4 +4855,171 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4689
4855
  expect(errorHandler).toHaveBeenCalled();
4690
4856
  });
4691
4857
  });
4858
+
4859
+ describe('constructor init-path branching (SCAL-315058)', () => {
4860
+ beforeEach(() => {
4861
+ init({
4862
+ thoughtSpotHost: 'tshost',
4863
+ authType: AuthType.None,
4864
+ });
4865
+ });
4866
+
4867
+ it('sets hostElement from domSelector', () => {
4868
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
4869
+ expect((embed as any).hostElement).toBe(getRootEl());
4870
+ });
4871
+
4872
+ it('skips isReadyForRenderPromise when init already completed', () => {
4873
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(true);
4874
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
4875
+ expect((embed as any).shouldWaitForRenderPromise).toBe(false);
4876
+ expect((embed as any).isReadyForRenderPromise).toBeUndefined();
4877
+ });
4878
+
4879
+ it('sets isReadyForRenderPromise when init not yet completed', () => {
4880
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(false);
4881
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
4882
+ expect((embed as any).shouldWaitForRenderPromise).toBe(true);
4883
+ expect((embed as any).isReadyForRenderPromise).toBeInstanceOf(Promise);
4884
+ });
4885
+
4886
+ it('afterInit runs synchronously when init already completed', () => {
4887
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(true);
4888
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
4889
+ // thoughtSpotHost is set by afterInit;
4890
+ // must be non-empty after constructor
4891
+ expect((embed as any).thoughtSpotHost).toBeTruthy();
4892
+ });
4893
+
4894
+ it('does not set isReadyForRenderPromise when shouldWaitForRenderPromise is false', () => {
4895
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(true);
4896
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
4897
+ expect((embed as any).shouldWaitForRenderPromise).toBe(false);
4898
+ // isReadyForRenderPromise must be undefined
4899
+ // so no unnecessary await occurs
4900
+ expect((embed as any).isReadyForRenderPromise).toBeUndefined();
4901
+ });
4902
+
4903
+ it('shouldWaitForRenderPromise flips to false after promise settles', async () => {
4904
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(false);
4905
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
4906
+ expect((embed as any).shouldWaitForRenderPromise).toBe(true);
4907
+ await (embed as any).isReadyForRenderPromise;
4908
+ expect((embed as any).shouldWaitForRenderPromise).toBe(false);
4909
+ });
4910
+
4911
+ it('calls throwInitError when getInitPromise rejects', async () => {
4912
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(false);
4913
+ jest.spyOn(baseInstance, 'getInitPromise').mockReturnValue(
4914
+ Promise.reject(new Error('init failed')),
4915
+ );
4916
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
4917
+ const throwInitErrorSpy = jest.spyOn(embed as any, 'throwInitError');
4918
+ await (embed as any).isReadyForRenderPromise;
4919
+ expect(throwInitErrorSpy).toHaveBeenCalled();
4920
+ });
4921
+
4922
+ it('shouldWaitForRenderPromise flips to false even when getInitPromise rejects', async () => {
4923
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(false);
4924
+ jest.spyOn(baseInstance, 'getInitPromise').mockReturnValue(
4925
+ Promise.reject(new Error('init failed')),
4926
+ );
4927
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
4928
+ await (embed as any).isReadyForRenderPromise;
4929
+ expect((embed as any).shouldWaitForRenderPromise).toBe(false);
4930
+ });
4931
+ });
4932
+
4933
+ describe('preRender ID object includes placeHolder (SCAL-315058)', () => {
4934
+ beforeAll(() => {
4935
+ init({
4936
+ thoughtSpotHost: 'tshost',
4937
+ authType: AuthType.None,
4938
+ });
4939
+ });
4940
+
4941
+ it('getPreRenderIds returns placeHolder key', () => {
4942
+ createRootEleForEmbed();
4943
+ const embed = new LiveboardEmbed('#tsEmbedDiv', {
4944
+ preRenderId: 'ph-test',
4945
+ liveboardId: 'lb-id',
4946
+ });
4947
+ const ids = embed.getPreRenderIds();
4948
+ expect(ids.placeHolder).toBe('tsEmbed-pre-render-placeholder-ph-test');
4949
+ });
4950
+ });
4951
+
4952
+ describe('isPreRenderConnected logic (SCAL-315058)', () => {
4953
+ beforeAll(() => {
4954
+ init({
4955
+ thoughtSpotHost: 'tshost',
4956
+ authType: AuthType.None,
4957
+ });
4958
+ });
4959
+
4960
+ it('returns false when preRenderWrapper is absent', () => {
4961
+ createRootEleForEmbed();
4962
+ const embed = new LiveboardEmbed('#tsEmbedDiv', {
4963
+ preRenderId: 'conn-test',
4964
+ liveboardId: 'lb-id',
4965
+ });
4966
+ expect((embed as any).isPreRenderConnected()).toBe(false);
4967
+ });
4968
+
4969
+ it('returns true once preRenderWrapper and preRenderChild are set', () => {
4970
+ createRootEleForEmbed();
4971
+ const embed = new LiveboardEmbed('#tsEmbedDiv', {
4972
+ preRenderId: 'conn-test-2',
4973
+ liveboardId: 'lb-id',
4974
+ });
4975
+ (embed as any).preRenderWrapper = document.createElement('div');
4976
+ (embed as any).preRenderChild = document.createElement('div');
4977
+ expect((embed as any).isPreRenderConnected()).toBe(true);
4978
+ });
4979
+ });
4980
+
4981
+ describe('showPreRender inserts placeholder into hostElement (SCAL-315058)', () => {
4982
+ beforeAll(() => {
4983
+ // Clear spy implementations that may have leaked from prior
4984
+ // describe blocks (e.g. getIsInitCompleted/getInitPromise mocks set
4985
+ // in 'constructor init-path branching' persist across describes
4986
+ // because clearAllMocks only clears call history, not
4987
+ // implementations).
4988
+ jest.restoreAllMocks();
4989
+ jest.spyOn(authInstance, 'postLoginService').mockResolvedValue(undefined);
4990
+ init({
4991
+ thoughtSpotHost: 'tshost',
4992
+ authType: AuthType.None,
4993
+ });
4994
+ (window as any).ResizeObserver = jest.fn().mockImplementation(() => ({
4995
+ disconnect: jest.fn(),
4996
+ observe: jest.fn(),
4997
+ unobserve: jest.fn(),
4998
+ }));
4999
+ });
5000
+
5001
+ it('showPreRender creates a placeholder element with the correct id', async () => {
5002
+ createRootEleForEmbed();
5003
+ const embed = new LiveboardEmbed('#tsEmbedDiv', {
5004
+ preRenderId: 'ph-lifecycle-test',
5005
+ liveboardId: 'lb-id',
5006
+ });
5007
+ await embed.preRender();
5008
+ await waitFor(
5009
+ () => !!document.querySelector('#tsEmbed-pre-render-child-ph-lifecycle-test'),
5010
+ );
5011
+
5012
+ await embed.showPreRender();
5013
+
5014
+ const placeholderEle = (
5015
+ embed as any
5016
+ ).getPreRenderPlaceHolderElement() as HTMLDivElement;
5017
+ expect(placeholderEle).not.toBeNull();
5018
+ expect(placeholderEle.id).toBe('tsEmbed-pre-render-placeholder-ph-lifecycle-test');
5019
+ // placeholder is attached to a parent element (inside hostElement)
5020
+ expect(placeholderEle.parentElement).not.toBeNull();
5021
+
5022
+ embed.destroy();
5023
+ });
5024
+ });
4692
5025
  });