@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
@@ -46,6 +46,7 @@ beforeAll(() => {
46
46
  const customisations = {
47
47
  style: {
48
48
  customCSS: {},
49
+ customCSSUrl: undefined,
49
50
  },
50
51
  content: {},
51
52
  };
@@ -78,6 +79,9 @@ const getMockAppInitPayload = (data) => {
78
79
  customVariablesForThirdPartyTools,
79
80
  interceptTimeout: undefined,
80
81
  interceptUrls: [],
82
+ shouldBypassPayloadValidation: undefined,
83
+ useHostEventsV2: undefined,
84
+ embedExpiryInAuthToken: true,
81
85
  };
82
86
  return {
83
87
  type: EmbedEvent.APP_INIT,
@@ -366,7 +370,10 @@ describe('Unit test case for ts embed', () => {
366
370
  });
367
371
  await executeAfterWait(() => {
368
372
  expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
369
- reorderedHomepageModules: [HomepageModule.MyLibrary, HomepageModule.Watchlist],
373
+ reorderedHomepageModules: [
374
+ HomepageModule.MyLibrary,
375
+ HomepageModule.Watchlist,
376
+ ],
370
377
  }));
371
378
  });
372
379
  });
@@ -512,10 +519,38 @@ describe('Unit test case for ts embed', () => {
512
519
  });
513
520
  await executeAfterWait(() => {
514
521
  expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({
515
- hiddenHomeLeftNavItems: [HomeLeftNavItem.Home, HomeLeftNavItem.MonitorSubscription],
522
+ hiddenHomeLeftNavItems: [
523
+ HomeLeftNavItem.Home,
524
+ HomeLeftNavItem.MonitorSubscription,
525
+ ],
516
526
  }));
517
527
  });
518
528
  });
529
+ test.each([
530
+ ['not set', undefined, true],
531
+ ['false', false, false],
532
+ ['true', true, true],
533
+ ])('embedExpiryInAuthToken is %s when refreshAuthTokenOnNearExpiry is %s', async (_label, refreshAuthTokenOnNearExpiry, expectedEmbedExpiry) => {
534
+ const mockEmbedEventPayload = {
535
+ type: EmbedEvent.APP_INIT,
536
+ data: {},
537
+ };
538
+ const searchEmbed = new AppEmbed(getRootEl(), {
539
+ ...defaultViewConfig,
540
+ refreshAuthTokenOnNearExpiry,
541
+ });
542
+ searchEmbed.render();
543
+ const mockPort = {
544
+ postMessage: jest.fn(),
545
+ };
546
+ await executeAfterWait(() => {
547
+ const iframe = getIFrameEl();
548
+ postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
549
+ });
550
+ await executeAfterWait(() => {
551
+ expect(mockPort.postMessage).toHaveBeenCalledWith(getMockAppInitPayload({ embedExpiryInAuthToken: expectedEmbedExpiry }));
552
+ });
553
+ });
519
554
  test('when Embed event status have start status', (done) => {
520
555
  const mockEmbedEventPayload = {
521
556
  type: EmbedEvent.Save,
@@ -715,10 +750,8 @@ describe('Unit test case for ts embed', () => {
715
750
  customVariablesForThirdPartyTools: {},
716
751
  }));
717
752
  const customisationContent = mockPort.postMessage.mock.calls[0][0].data.customisations.content;
718
- expect(customisationContent.stringIDsUrl)
719
- .toBe('https://sample-string-ids-url.com');
720
- expect(customisationContent.stringIDs)
721
- .toEqual({
753
+ expect(customisationContent.stringIDsUrl).toBe('https://sample-string-ids-url.com');
754
+ expect(customisationContent.stringIDs).toEqual({
722
755
  'liveboard.header.title': 'Dashboard name',
723
756
  });
724
757
  });
@@ -728,7 +761,10 @@ describe('Unit test case for ts embed', () => {
728
761
  type: EmbedEvent.APP_INIT,
729
762
  data: {},
730
763
  };
731
- const searchEmbed = new SearchEmbed(getRootEl(), { ...defaultViewConfig, exposeTranslationIDs: true });
764
+ const searchEmbed = new SearchEmbed(getRootEl(), {
765
+ ...defaultViewConfig,
766
+ exposeTranslationIDs: true,
767
+ });
732
768
  searchEmbed.render();
733
769
  const mockPort = {
734
770
  postMessage: jest.fn(),
@@ -769,16 +805,16 @@ describe('Unit test case for ts embed', () => {
769
805
  name: 'Valid Action',
770
806
  target: CustomActionTarget.LIVEBOARD,
771
807
  position: CustomActionsPosition.PRIMARY,
772
- metadataIds: { liveboardIds: ['lb123'] }
808
+ metadataIds: { liveboardIds: ['lb123'] },
773
809
  },
774
810
  {
775
811
  id: 'action2',
776
812
  name: 'Another Valid Action',
777
813
  target: CustomActionTarget.VIZ,
778
814
  position: CustomActionsPosition.MENU,
779
- metadataIds: { vizIds: ['viz456'] }
780
- }
781
- ]
815
+ metadataIds: { vizIds: ['viz456'] },
816
+ },
817
+ ],
782
818
  });
783
819
  searchEmbed.render();
784
820
  const mockPort = {
@@ -804,15 +840,15 @@ describe('Unit test case for ts embed', () => {
804
840
  name: 'Another Valid Action',
805
841
  target: CustomActionTarget.VIZ,
806
842
  position: CustomActionsPosition.MENU,
807
- metadataIds: { vizIds: ['viz456'] }
843
+ metadataIds: { vizIds: ['viz456'] },
808
844
  },
809
845
  {
810
846
  id: 'action1',
811
847
  name: 'Valid Action',
812
848
  target: CustomActionTarget.LIVEBOARD,
813
849
  position: CustomActionsPosition.PRIMARY,
814
- metadataIds: { liveboardIds: ['lb123'] }
815
- }
850
+ metadataIds: { liveboardIds: ['lb123'] },
851
+ },
816
852
  ],
817
853
  customVariablesForThirdPartyTools: {},
818
854
  }));
@@ -825,14 +861,14 @@ describe('Unit test case for ts embed', () => {
825
861
  id: 'action1',
826
862
  name: 'Valid Action',
827
863
  target: CustomActionTarget.LIVEBOARD,
828
- position: CustomActionsPosition.PRIMARY
864
+ position: CustomActionsPosition.PRIMARY,
829
865
  }),
830
866
  expect.objectContaining({
831
867
  id: 'action2',
832
868
  name: 'Another Valid Action',
833
869
  target: CustomActionTarget.VIZ,
834
- position: CustomActionsPosition.MENU
835
- })
870
+ position: CustomActionsPosition.MENU,
871
+ }),
836
872
  ]));
837
873
  // Verify actions are sorted by name (alphabetically)
838
874
  expect(appInitData.customActions[0].name).toBe('Another Valid Action');
@@ -886,7 +922,10 @@ describe('Unit test case for ts embed', () => {
886
922
  type: EmbedEvent.APP_INIT,
887
923
  data: {},
888
924
  };
889
- const searchEmbed = new SearchEmbed(getRootEl(), { ...defaultViewConfig, preRenderId: 'test' });
925
+ const searchEmbed = new SearchEmbed(getRootEl(), {
926
+ ...defaultViewConfig,
927
+ preRenderId: 'test',
928
+ });
890
929
  searchEmbed.preRender();
891
930
  const mockPort = {
892
931
  postMessage: jest.fn(),
@@ -937,7 +976,10 @@ describe('Unit test case for ts embed', () => {
937
976
  type: EmbedEvent.AuthExpire,
938
977
  data: {},
939
978
  };
940
- const searchEmbed = new SearchEmbed(getRootEl(), { ...defaultViewConfig, preRenderId: 'test' });
979
+ const searchEmbed = new SearchEmbed(getRootEl(), {
980
+ ...defaultViewConfig,
981
+ preRenderId: 'test',
982
+ });
941
983
  jest.spyOn(baseInstance, 'notifyAuthFailure');
942
984
  searchEmbed.preRender();
943
985
  const loggerSpy = jest.spyOn(logger, 'error').mockImplementation(() => { });
@@ -1077,9 +1119,13 @@ describe('Unit test case for ts embed', () => {
1077
1119
  // resetCachedPreauthInfo();
1078
1120
  let mockGetPreauthInfo = null;
1079
1121
  if (overrideOrgId) {
1080
- mockGetPreauthInfo = jest.spyOn(sessionInfoService, 'getPreauthInfo').mockImplementation(jest.fn());
1122
+ mockGetPreauthInfo = jest
1123
+ .spyOn(sessionInfoService, 'getPreauthInfo')
1124
+ .mockImplementation(jest.fn());
1081
1125
  }
1082
- const mockPreauthInfoFetch = jest.spyOn(authService, 'fetchPreauthInfoService').mockResolvedValueOnce({
1126
+ const mockPreauthInfoFetch = jest
1127
+ .spyOn(authService, 'fetchPreauthInfoService')
1128
+ .mockResolvedValueOnce({
1083
1129
  ok: true,
1084
1130
  headers: new Headers({ 'content-type': 'application/json' }),
1085
1131
  json: async () => ({
@@ -1114,14 +1160,14 @@ describe('Unit test case for ts embed', () => {
1114
1160
  };
1115
1161
  };
1116
1162
  test('should call InfoSuccess Event on preauth call success', async () => {
1117
- const { mockPreauthInfoFetch, iFrame, } = await setup(true);
1163
+ const { mockPreauthInfoFetch, iFrame } = await setup(true);
1118
1164
  expect(mockPreauthInfoFetch).toHaveBeenCalledTimes(1);
1119
1165
  await executeAfterWait(() => {
1120
1166
  expect(mockProcessTrigger).toHaveBeenCalledWith(iFrame, HostEvent.InfoSuccess, 'http://tshost', expect.objectContaining({ info: expect.any(Object) }), undefined);
1121
1167
  });
1122
1168
  });
1123
1169
  test('should not call InfoSuccess Event if overrideOrgId is true', async () => {
1124
- const { mockGetPreauthInfo, } = await setup(true, 123);
1170
+ const { mockGetPreauthInfo } = await setup(true, 123);
1125
1171
  expect(mockGetPreauthInfo).toHaveBeenCalledTimes(0);
1126
1172
  });
1127
1173
  });
@@ -1154,14 +1200,17 @@ describe('Unit test case for ts embed', () => {
1154
1200
  const isAppEmbedWithPrimaryNavbar = embedType === 'AppEmbed' && showPrimaryNavbar === true;
1155
1201
  const shouldDisableCache = overrideOrgId || disablePreauthCache || isAppEmbedWithPrimaryNavbar;
1156
1202
  if (shouldDisableCache) {
1157
- mockGetPreauthInfo = jest.spyOn(sessionInfoService, 'getPreauthInfo')
1203
+ mockGetPreauthInfo = jest
1204
+ .spyOn(sessionInfoService, 'getPreauthInfo')
1158
1205
  .mockImplementation(jest.fn());
1159
1206
  }
1160
1207
  else {
1161
- mockGetPreauthInfo = jest.spyOn(sessionInfoService, 'getPreauthInfo')
1208
+ mockGetPreauthInfo = jest
1209
+ .spyOn(sessionInfoService, 'getPreauthInfo')
1162
1210
  .mockResolvedValue({ info: { test: 'data' } });
1163
1211
  }
1164
- const mockPreauthInfoFetch = jest.spyOn(authService, 'fetchPreauthInfoService')
1212
+ const mockPreauthInfoFetch = jest
1213
+ .spyOn(authService, 'fetchPreauthInfoService')
1165
1214
  .mockResolvedValueOnce({
1166
1215
  ok: true,
1167
1216
  headers: new Headers({ 'content-type': 'application/json' }),
@@ -1633,8 +1682,8 @@ describe('Unit test case for ts embed', () => {
1633
1682
  },
1634
1683
  });
1635
1684
  await appEmbed.render();
1636
- expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
1637
- + `&foo=bar&baz=1&bool=true${defaultParamsPost}#/home`);
1685
+ expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
1686
+ `&foo=bar&baz=1&bool=true${defaultParamsPost}#/home`);
1638
1687
  });
1639
1688
  it('should set the additional flags correctly on the iframe src from init and view config', async () => {
1640
1689
  init({
@@ -1658,8 +1707,8 @@ describe('Unit test case for ts embed', () => {
1658
1707
  },
1659
1708
  });
1660
1709
  await appEmbed.render();
1661
- expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
1662
- + `&foo=bar&foo2=bar2&foo3=false&baz=1&bool=true${defaultParamsPost}#/home`);
1710
+ expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
1711
+ `&foo=bar&foo2=bar2&foo3=false&baz=1&bool=true${defaultParamsPost}#/home`);
1663
1712
  });
1664
1713
  it('Sets the showAlerts param', async () => {
1665
1714
  const appEmbed = new AppEmbed(getRootEl(), {
@@ -1670,8 +1719,8 @@ describe('Unit test case for ts embed', () => {
1670
1719
  showAlerts: true,
1671
1720
  });
1672
1721
  await appEmbed.render();
1673
- expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
1674
- + `&showAlerts=true${defaultParamsPost}#/home`);
1722
+ expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
1723
+ `&showAlerts=true${defaultParamsPost}#/home`);
1675
1724
  });
1676
1725
  it('Sets the locale param', async () => {
1677
1726
  const appEmbed = new AppEmbed(getRootEl(), {
@@ -1682,8 +1731,8 @@ describe('Unit test case for ts embed', () => {
1682
1731
  locale: 'ja-JP',
1683
1732
  });
1684
1733
  await appEmbed.render();
1685
- expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
1686
- + `&locale=ja-JP${defaultParamsPost}#/home`);
1734
+ expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
1735
+ `&locale=ja-JP${defaultParamsPost}#/home`);
1687
1736
  });
1688
1737
  it('Sets both params when enableLinkOverridesV2 is set', async () => {
1689
1738
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
@@ -1697,7 +1746,37 @@ describe('Unit test case for ts embed', () => {
1697
1746
  await liveboardEmbed.render();
1698
1747
  expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&${defaultParamsForPinboardEmbed}&enableLinkOverridesV2=true&linkOverride=true${defaultParamsPost}#/embed/viz/test-lb`);
1699
1748
  });
1700
- it('Sets only linkOverride when enableLinkOverridesV2 is not set', async () => {
1749
+ it('Auto-upgrades V1 linkOverride to V2 (sends both flags)', async () => {
1750
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1751
+ frameParams: {
1752
+ width: '100%',
1753
+ height: '100%',
1754
+ },
1755
+ liveboardId: 'test-lb',
1756
+ linkOverride: true,
1757
+ });
1758
+ await liveboardEmbed.render();
1759
+ const src = getIFrameSrc();
1760
+ expect(src).toContain('linkOverride=true');
1761
+ expect(src).toContain('enableLinkOverridesV2=true');
1762
+ });
1763
+ it('Auto-disables V2 link overrides when disableRedirectionLinksInNewTab is true', async () => {
1764
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1765
+ frameParams: {
1766
+ width: '100%',
1767
+ height: '100%',
1768
+ },
1769
+ liveboardId: 'test-lb',
1770
+ enableLinkOverridesV2: true,
1771
+ disableRedirectionLinksInNewTab: true,
1772
+ });
1773
+ await liveboardEmbed.render();
1774
+ const src = getIFrameSrc();
1775
+ expect(src).not.toContain('enableLinkOverridesV2=true');
1776
+ expect(src).not.toContain('linkOverride=true');
1777
+ expect(src).toContain('disableRedirectionLinksInNewTab=true');
1778
+ });
1779
+ it('Auto-disables V1 link override when disableRedirectionLinksInNewTab is true', async () => {
1701
1780
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1702
1781
  frameParams: {
1703
1782
  width: '100%',
@@ -1705,9 +1784,12 @@ describe('Unit test case for ts embed', () => {
1705
1784
  },
1706
1785
  liveboardId: 'test-lb',
1707
1786
  linkOverride: true,
1787
+ disableRedirectionLinksInNewTab: true,
1708
1788
  });
1709
1789
  await liveboardEmbed.render();
1710
- expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&${defaultParamsForPinboardEmbed}&linkOverride=true${defaultParamsPost}#/embed/viz/test-lb`);
1790
+ const src = getIFrameSrc();
1791
+ expect(src).not.toContain('linkOverride=true');
1792
+ expect(src).toContain('disableRedirectionLinksInNewTab=true');
1711
1793
  });
1712
1794
  it('Sets the iconSprite url', async () => {
1713
1795
  const appEmbed = new AppEmbed(getRootEl(), {
@@ -1720,8 +1802,8 @@ describe('Unit test case for ts embed', () => {
1720
1802
  },
1721
1803
  });
1722
1804
  await appEmbed.render();
1723
- expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
1724
- + `&iconSprite=iconSprite.com${defaultParamsPost}#/home`);
1805
+ expectUrlMatchesWithParams(getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
1806
+ `&iconSprite=iconSprite.com${defaultParamsPost}#/home`);
1725
1807
  });
1726
1808
  it('inserts as sibling of root node if configured', async () => {
1727
1809
  const appEmbed = new AppEmbed(getRootEl(), {
@@ -1954,22 +2036,26 @@ describe('Unit test case for ts embed', () => {
1954
2036
  tsEmbedDiv.style.width = '100px';
1955
2037
  tsEmbedDiv.style.height = '100px';
1956
2038
  let resizeObserverCb;
1957
- window.ResizeObserver = window.ResizeObserver
1958
- || jest.fn().mockImplementation((resizeObserverCbParam) => {
1959
- resizeObserverCb = resizeObserverCbParam;
1960
- return {
1961
- disconnect: jest.fn(),
1962
- observe: jest.fn(),
1963
- unobserve: jest.fn(),
1964
- };
1965
- });
2039
+ window.ResizeObserver =
2040
+ window.ResizeObserver ||
2041
+ jest.fn().mockImplementation((resizeObserverCbParam) => {
2042
+ resizeObserverCb = resizeObserverCbParam;
2043
+ return {
2044
+ disconnect: jest.fn(),
2045
+ observe: jest.fn(),
2046
+ unobserve: jest.fn(),
2047
+ };
2048
+ });
1966
2049
  // show preRender
1967
2050
  const warnSpy = jest.spyOn(logger, 'warn');
1968
2051
  libEmbed.showPreRender();
1969
2052
  expect(warnSpy).toHaveBeenCalledTimes(1);
2053
+ // The ResizeObserver now tracks the placeholder inside this.el,
2054
+ // not this.el itself, so pass it as the target.
2055
+ const preRenderPlaceholder = tsEmbedDiv.firstElementChild;
1970
2056
  resizeObserverCb([
1971
2057
  {
1972
- target: tsEmbedDiv,
2058
+ target: preRenderPlaceholder,
1973
2059
  contentRect: { height: 297, width: 987 },
1974
2060
  },
1975
2061
  ]);
@@ -2020,12 +2106,13 @@ describe('Unit test case for ts embed', () => {
2020
2106
  });
2021
2107
  it('should set overflow:hidden when hidePreRender and remove when showPreRender', async () => {
2022
2108
  createRootEleForEmbed();
2023
- window.ResizeObserver = window.ResizeObserver
2024
- || jest.fn().mockImplementation(() => ({
2025
- disconnect: jest.fn(),
2026
- observe: jest.fn(),
2027
- unobserve: jest.fn(),
2028
- }));
2109
+ window.ResizeObserver =
2110
+ window.ResizeObserver ||
2111
+ jest.fn().mockImplementation(() => ({
2112
+ disconnect: jest.fn(),
2113
+ observe: jest.fn(),
2114
+ unobserve: jest.fn(),
2115
+ }));
2029
2116
  const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
2030
2117
  preRenderId: 'overflow-test',
2031
2118
  liveboardId: 'myLiveboardId',
@@ -2051,27 +2138,28 @@ describe('Unit test case for ts embed', () => {
2051
2138
  it('it should connect with another object', async () => {
2052
2139
  createRootEleForEmbed();
2053
2140
  mockMessageChannel();
2054
- window.ResizeObserver = window.ResizeObserver
2055
- || jest.fn().mockImplementation(() => ({
2056
- disconnect: jest.fn(),
2057
- observe: jest.fn(),
2058
- unobserve: jest.fn(),
2059
- }));
2141
+ window.ResizeObserver =
2142
+ window.ResizeObserver ||
2143
+ jest.fn().mockImplementation(() => ({
2144
+ disconnect: jest.fn(),
2145
+ observe: jest.fn(),
2146
+ unobserve: jest.fn(),
2147
+ }));
2060
2148
  const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
2061
2149
  preRenderId: 'i-am-preRendered',
2062
2150
  liveboardId: 'myLiveboardId',
2063
2151
  });
2064
2152
  libEmbed.preRender();
2065
2153
  await waitFor(() => !!getIFrameEl());
2066
- const warnSpy = jest.spyOn(logger, 'warn');
2067
2154
  const newEmbed = new LiveboardEmbed('#tsEmbedDiv', {
2068
2155
  preRenderId: 'i-am-preRendered',
2069
2156
  liveboardId: 'awdawda',
2070
2157
  hiddenActions: [Action.AddFilter],
2071
2158
  frameParams: { height: 90 },
2072
2159
  });
2073
- newEmbed.showPreRender();
2074
- expect(warnSpy).toHaveBeenCalledTimes(2);
2160
+ await newEmbed.showPreRender();
2161
+ // Verify newEmbed successfully connected to the existing preRender
2162
+ expect(newEmbed.isPreRenderConnected()).toBe(true);
2075
2163
  });
2076
2164
  it('showPreRender should not preRender if not available', async () => {
2077
2165
  createRootEleForEmbed();
@@ -2222,7 +2310,10 @@ describe('Unit test case for ts embed', () => {
2222
2310
  document.body.innerHTML = getDocumentBody();
2223
2311
  });
2224
2312
  test('Pre-render should wait for init to complete', async () => {
2225
- const lib = new LiveboardEmbed(getRootEl(), { preRenderId: 'test', liveboardId: 'test' });
2313
+ const lib = new LiveboardEmbed(getRootEl(), {
2314
+ preRenderId: 'test',
2315
+ liveboardId: 'test',
2316
+ });
2226
2317
  lib.preRender();
2227
2318
  await executeAfterWait(() => {
2228
2319
  expect(errorSpy).toHaveBeenCalledWith(ERROR_MESSAGE.RENDER_CALLED_BEFORE_INIT);
@@ -2452,7 +2543,11 @@ describe('Unit test case for ts embed', () => {
2452
2543
  expect(authToken.getAuthenticationToken).toHaveBeenCalledWith(expect.any(Object), true);
2453
2544
  // Check that logger.error was called with the token refresh
2454
2545
  // error
2455
- const errorCalls = logger.error.mock.calls.filter((call) => { var _a, _b; return ((_a = call[0]) === null || _a === void 0 ? void 0 : _a.includes(ERROR_MESSAGE.INVALID_TOKEN_ERROR)) && ((_b = call[0]) === null || _b === void 0 ? void 0 : _b.includes('Token fetch failed')); });
2546
+ const errorCalls = logger.error.mock.calls.filter((call) => {
2547
+ var _a, _b;
2548
+ return ((_a = call[0]) === null || _a === void 0 ? void 0 : _a.includes(ERROR_MESSAGE.INVALID_TOKEN_ERROR)) &&
2549
+ ((_b = call[0]) === null || _b === void 0 ? void 0 : _b.includes('Token fetch failed'));
2550
+ });
2456
2551
  expect(errorCalls.length).toBeGreaterThan(0);
2457
2552
  expect(processData.processAuthFailure).toHaveBeenCalledWith(error, expect.any(Element));
2458
2553
  expect(mockPort.postMessage).not.toHaveBeenCalled();
@@ -2614,8 +2709,8 @@ describe('Unit test case for ts embed', () => {
2614
2709
  test('should return getPreRenderObj and log if same object', () => {
2615
2710
  const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
2616
2711
  const loggerSpy = jest.spyOn(logger, 'info');
2617
- // Mock insertedDomEl to have the embed object
2618
- searchEmbed.insertedDomEl = {
2712
+ // getPreRenderObj reads the embed reference from preRenderWrapper
2713
+ searchEmbed.preRenderWrapper = {
2619
2714
  [searchEmbed['embedNodeKey']]: searchEmbed,
2620
2715
  };
2621
2716
  const result = searchEmbed['getPreRenderObj']();
@@ -2664,7 +2759,8 @@ describe('Unit test case for ts embed', () => {
2664
2759
  test('should return context when embed container is already loaded', async () => {
2665
2760
  const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
2666
2761
  searchEmbed.isEmbedContainerLoaded = true;
2667
- const triggerSpy = jest.spyOn(searchEmbed, 'trigger')
2762
+ const triggerSpy = jest
2763
+ .spyOn(searchEmbed, 'trigger')
2668
2764
  .mockResolvedValue(mockContext);
2669
2765
  const context = await searchEmbed.getCurrentContext();
2670
2766
  expect(context).toEqual(mockContext);
@@ -2673,7 +2769,8 @@ describe('Unit test case for ts embed', () => {
2673
2769
  test('should wait for embed container to load before returning context', async () => {
2674
2770
  const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
2675
2771
  searchEmbed.isEmbedContainerLoaded = false;
2676
- const triggerSpy = jest.spyOn(searchEmbed, 'trigger')
2772
+ const triggerSpy = jest
2773
+ .spyOn(searchEmbed, 'trigger')
2677
2774
  .mockResolvedValue(mockContext);
2678
2775
  const contextPromise = searchEmbed.getCurrentContext();
2679
2776
  // Context should not be resolved yet
@@ -2859,9 +2956,11 @@ describe('Unit test case for ts embed', () => {
2859
2956
  });
2860
2957
  await appEmbed.render();
2861
2958
  jest.spyOn(appEmbed, 'trigger').mockRejectedValue(new Error('trigger failed'));
2862
- const removeChildSpy = jest.spyOn(Node.prototype, 'removeChild').mockImplementation(() => getRootEl());
2959
+ const removeChildSpy = jest
2960
+ .spyOn(Node.prototype, 'removeChild')
2961
+ .mockImplementation(() => getRootEl());
2863
2962
  appEmbed.destroy();
2864
- await new Promise(resolve => setTimeout(resolve, 50));
2963
+ await new Promise((resolve) => setTimeout(resolve, 50));
2865
2964
  expect(removeChildSpy).toHaveBeenCalled();
2866
2965
  embedConfig.setEmbedConfig(originalEmbedConfig);
2867
2966
  });
@@ -2886,7 +2985,9 @@ describe('Unit test case for ts embed', () => {
2886
2985
  });
2887
2986
  await appEmbed.render();
2888
2987
  const triggerSpy = jest.spyOn(appEmbed, 'trigger').mockResolvedValue(null);
2889
- const removeChildSpy = jest.spyOn(Node.prototype, 'removeChild').mockImplementation(() => getRootEl());
2988
+ const removeChildSpy = jest
2989
+ .spyOn(Node.prototype, 'removeChild')
2990
+ .mockImplementation(() => getRootEl());
2890
2991
  appEmbed.destroy();
2891
2992
  expect(triggerSpy).toHaveBeenCalledWith(HostEvent.DestroyEmbed);
2892
2993
  expect(removeChildSpy).toHaveBeenCalled();
@@ -2905,12 +3006,14 @@ describe('Unit test case for ts embed', () => {
2905
3006
  });
2906
3007
  await appEmbed.render();
2907
3008
  const triggerSpy = jest.spyOn(appEmbed, 'trigger').mockResolvedValue(null);
2908
- const removeChildSpy = jest.spyOn(Node.prototype, 'removeChild').mockImplementation(() => getRootEl());
3009
+ const removeChildSpy = jest
3010
+ .spyOn(Node.prototype, 'removeChild')
3011
+ .mockImplementation(() => getRootEl());
2909
3012
  appEmbed.destroy();
2910
3013
  // Should be called immediately when config is enabled
2911
3014
  expect(triggerSpy).toHaveBeenCalledWith(HostEvent.DestroyEmbed);
2912
3015
  // Wait for the timeout to complete
2913
- await new Promise(resolve => setTimeout(resolve, 1100));
3016
+ await new Promise((resolve) => setTimeout(resolve, 1100));
2914
3017
  expect(removeChildSpy).toHaveBeenCalled();
2915
3018
  });
2916
3019
  it('should handle Promise.race with successful cleanup completion', async () => {
@@ -2927,11 +3030,15 @@ describe('Unit test case for ts embed', () => {
2927
3030
  });
2928
3031
  await appEmbed.render();
2929
3032
  // Mock trigger to resolve quickly (before timeout)
2930
- const triggerSpy = jest.spyOn(appEmbed, 'trigger').mockImplementation(() => new Promise(resolve => setTimeout(() => resolve(null), 100)));
2931
- const removeChildSpy = jest.spyOn(Node.prototype, 'removeChild').mockImplementation(() => getRootEl());
3033
+ const triggerSpy = jest
3034
+ .spyOn(appEmbed, 'trigger')
3035
+ .mockImplementation(() => new Promise((resolve) => setTimeout(() => resolve(null), 100)));
3036
+ const removeChildSpy = jest
3037
+ .spyOn(Node.prototype, 'removeChild')
3038
+ .mockImplementation(() => getRootEl());
2932
3039
  appEmbed.destroy();
2933
3040
  // Wait for the trigger to complete
2934
- await new Promise(resolve => setTimeout(resolve, 200));
3041
+ await new Promise((resolve) => setTimeout(resolve, 200));
2935
3042
  expect(triggerSpy).toHaveBeenCalledWith(HostEvent.DestroyEmbed);
2936
3043
  expect(removeChildSpy).toHaveBeenCalled();
2937
3044
  });
@@ -2949,11 +3056,15 @@ describe('Unit test case for ts embed', () => {
2949
3056
  });
2950
3057
  await appEmbed.render();
2951
3058
  // Mock trigger to take longer than timeout
2952
- const triggerSpy = jest.spyOn(appEmbed, 'trigger').mockImplementation(() => new Promise(resolve => setTimeout(() => resolve(null), 500)));
2953
- const removeChildSpy = jest.spyOn(Node.prototype, 'removeChild').mockImplementation(() => getRootEl());
3059
+ const triggerSpy = jest
3060
+ .spyOn(appEmbed, 'trigger')
3061
+ .mockImplementation(() => new Promise((resolve) => setTimeout(() => resolve(null), 500)));
3062
+ const removeChildSpy = jest
3063
+ .spyOn(Node.prototype, 'removeChild')
3064
+ .mockImplementation(() => getRootEl());
2954
3065
  appEmbed.destroy();
2955
3066
  // Wait for the timeout to complete
2956
- await new Promise(resolve => setTimeout(resolve, 200));
3067
+ await new Promise((resolve) => setTimeout(resolve, 200));
2957
3068
  expect(triggerSpy).toHaveBeenCalledWith(HostEvent.DestroyEmbed);
2958
3069
  expect(removeChildSpy).toHaveBeenCalled();
2959
3070
  });
@@ -2981,11 +3092,11 @@ describe('Unit test case for ts embed', () => {
2981
3092
  body: JSON.stringify({
2982
3093
  variables: {
2983
3094
  session: { sessionId: 'session-123' },
2984
- contextBookId: 'viz-456'
2985
- }
2986
- })
2987
- }
2988
- })
3095
+ contextBookId: 'viz-456',
3096
+ },
3097
+ }),
3098
+ },
3099
+ }),
2989
3100
  };
2990
3101
  const mockPort = {
2991
3102
  postMessage: jest.fn(),
@@ -3016,8 +3127,8 @@ describe('Unit test case for ts embed', () => {
3016
3127
  type: EmbedEvent.ApiIntercept,
3017
3128
  data: JSON.stringify({
3018
3129
  input: '/prism/?op=GetChartWithData',
3019
- init: {}
3020
- })
3130
+ init: {},
3131
+ }),
3021
3132
  };
3022
3133
  const mockPort = {
3023
3134
  postMessage: jest.fn(),
@@ -3049,8 +3160,8 @@ describe('Unit test case for ts embed', () => {
3049
3160
  type: EmbedEvent.ApiIntercept,
3050
3161
  data: JSON.stringify({
3051
3162
  input: '/prism/?op=GetChartWithData',
3052
- init: {}
3053
- })
3163
+ init: {},
3164
+ }),
3054
3165
  };
3055
3166
  const mockPort = {
3056
3167
  postMessage: jest.fn(),
@@ -3067,7 +3178,7 @@ describe('Unit test case for ts embed', () => {
3067
3178
  // handleInterceptEvent
3068
3179
  const result = await capturedGetUnsavedAnswerTml({
3069
3180
  sessionId: 'session-123',
3070
- vizId: 'viz-456'
3181
+ vizId: 'viz-456',
3071
3182
  });
3072
3183
  expect(mockProcessTrigger).toHaveBeenCalled();
3073
3184
  const callArgs = mockProcessTrigger.mock.calls[0];
@@ -3077,8 +3188,8 @@ describe('Unit test case for ts embed', () => {
3077
3188
  type: 'getUnsavedAnswerTML',
3078
3189
  parameters: {
3079
3190
  sessionId: 'session-123',
3080
- vizId: 'viz-456'
3081
- }
3191
+ vizId: 'viz-456',
3192
+ },
3082
3193
  });
3083
3194
  expect(result).toEqual(mockTmlResponse);
3084
3195
  });
@@ -3095,8 +3206,8 @@ describe('Unit test case for ts embed', () => {
3095
3206
  type: EmbedEvent.ApiIntercept,
3096
3207
  data: JSON.stringify({
3097
3208
  input: '/api/test',
3098
- init: {}
3099
- })
3209
+ init: {},
3210
+ }),
3100
3211
  };
3101
3212
  const mockPort = {
3102
3213
  postMessage: jest.fn(),
@@ -3120,8 +3231,8 @@ describe('Unit test case for ts embed', () => {
3120
3231
  type: EmbedEvent.ApiIntercept,
3121
3232
  data: JSON.stringify({
3122
3233
  input: '/prism/?op=GetChartWithData',
3123
- init: {}
3124
- })
3234
+ init: {},
3235
+ }),
3125
3236
  };
3126
3237
  const mockPort = {
3127
3238
  postMessage: jest.fn(),
@@ -3162,15 +3273,15 @@ describe('Unit test case for ts embed', () => {
3162
3273
  type: EmbedEvent.ApiIntercept,
3163
3274
  data: JSON.stringify({
3164
3275
  input: '/prism/?op=GetChartWithData',
3165
- init: {}
3166
- })
3276
+ init: {},
3277
+ }),
3167
3278
  };
3168
3279
  const mockEventData2 = {
3169
3280
  type: EmbedEvent.ApiIntercept,
3170
3281
  data: JSON.stringify({
3171
3282
  input: '/prism/?op=LoadContextBook',
3172
- init: {}
3173
- })
3283
+ init: {},
3284
+ }),
3174
3285
  };
3175
3286
  const mockPort = {
3176
3287
  postMessage: jest.fn(),
@@ -3199,8 +3310,8 @@ describe('Unit test case for ts embed', () => {
3199
3310
  type: EmbedEvent.ApiIntercept,
3200
3311
  data: JSON.stringify({
3201
3312
  input: '/prism/?op=GetChartWithData',
3202
- init: {}
3203
- })
3313
+ init: {},
3314
+ }),
3204
3315
  };
3205
3316
  const mockPort = {
3206
3317
  postMessage: jest.fn(),
@@ -3231,8 +3342,8 @@ describe('Unit test case for ts embed', () => {
3231
3342
  type: EmbedEvent.ApiIntercept,
3232
3343
  data: JSON.stringify({
3233
3344
  input: '/prism/?op=GetChartWithData',
3234
- init: {}
3235
- })
3345
+ init: {},
3346
+ }),
3236
3347
  };
3237
3348
  const mockPort = {
3238
3349
  postMessage: jest.fn(),
@@ -3245,7 +3356,7 @@ describe('Unit test case for ts embed', () => {
3245
3356
  expect(capturedGetUnsavedAnswerTml).toBeDefined();
3246
3357
  const result = await capturedGetUnsavedAnswerTml({
3247
3358
  sessionId: 'session-123',
3248
- vizId: 'viz-456'
3359
+ vizId: 'viz-456',
3249
3360
  });
3250
3361
  expect(result).toBeUndefined();
3251
3362
  });
@@ -3260,8 +3371,8 @@ describe('Unit test case for ts embed', () => {
3260
3371
  type: EmbedEvent.ApiIntercept,
3261
3372
  data: JSON.stringify({
3262
3373
  input: '/prism/?op=LoadContextBook',
3263
- init: {}
3264
- })
3374
+ init: {},
3375
+ }),
3265
3376
  };
3266
3377
  const mockPort = {
3267
3378
  postMessage: jest.fn(),
@@ -3495,12 +3606,13 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
3495
3606
  const setupPreRenderTest = async (preRenderId, initialConfig) => {
3496
3607
  createRootEleForEmbed();
3497
3608
  mockMessageChannel();
3498
- window.ResizeObserver = window.ResizeObserver
3499
- || jest.fn().mockImplementation(() => ({
3500
- disconnect: jest.fn(),
3501
- observe: jest.fn(),
3502
- unobserve: jest.fn(),
3503
- }));
3609
+ window.ResizeObserver =
3610
+ window.ResizeObserver ||
3611
+ jest.fn().mockImplementation(() => ({
3612
+ disconnect: jest.fn(),
3613
+ observe: jest.fn(),
3614
+ unobserve: jest.fn(),
3615
+ }));
3504
3616
  const embed1 = new LiveboardEmbed('#tsEmbedDiv', {
3505
3617
  preRenderId,
3506
3618
  ...initialConfig,
@@ -3651,7 +3763,8 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
3651
3763
  authType: AuthType.None,
3652
3764
  });
3653
3765
  });
3654
- // Matches the structure produced by createValidationError / embedErrorDetails
3766
+ // Matches the structure produced by createValidationError /
3767
+ // embedErrorDetails
3655
3768
  const makeNestedValidationData = (message = 'invalid payload') => ({
3656
3769
  type: EmbedEvent.Error,
3657
3770
  data: {
@@ -3661,7 +3774,8 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
3661
3774
  error: message,
3662
3775
  },
3663
3776
  });
3664
- // Matches the flat structure where errorType sits at the top level of data
3777
+ // Matches the flat structure where errorType sits at the top level of
3778
+ // data
3665
3779
  const makeFlatValidationData = (message = 'invalid payload') => ({
3666
3780
  errorType: EmbedErrorCodes.HOST_EVENT_VALIDATION,
3667
3781
  message,
@@ -3702,14 +3816,20 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
3702
3816
  });
3703
3817
  test('delivers Error event to handler when useHostEventsV2 is true and shouldBypassPayloadValidation is undefined', () => {
3704
3818
  const errorHandler = jest.fn();
3705
- const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: undefined });
3819
+ const embed = makeEmbed({
3820
+ useHostEventsV2: true,
3821
+ shouldBypassPayloadValidation: undefined,
3822
+ });
3706
3823
  embed.on(EmbedEvent.Error, errorHandler);
3707
3824
  embed.executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
3708
3825
  expect(errorHandler).toHaveBeenCalled();
3709
3826
  });
3710
3827
  test('delivers Error event to handler when useHostEventsV2 is true and shouldBypassPayloadValidation is false', () => {
3711
3828
  const errorHandler = jest.fn();
3712
- const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
3829
+ const embed = makeEmbed({
3830
+ useHostEventsV2: true,
3831
+ shouldBypassPayloadValidation: false,
3832
+ });
3713
3833
  embed.on(EmbedEvent.Error, errorHandler);
3714
3834
  embed.executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
3715
3835
  expect(errorHandler).toHaveBeenCalled();
@@ -3717,7 +3837,10 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
3717
3837
  test('skips Error event when useHostEventsV2 is false regardless of shouldBypassPayloadValidation', () => {
3718
3838
  jest.spyOn(logger, 'warn');
3719
3839
  const errorHandler = jest.fn();
3720
- const embed = makeEmbed({ useHostEventsV2: false, shouldBypassPayloadValidation: undefined });
3840
+ const embed = makeEmbed({
3841
+ useHostEventsV2: false,
3842
+ shouldBypassPayloadValidation: undefined,
3843
+ });
3721
3844
  embed.on(EmbedEvent.Error, errorHandler);
3722
3845
  embed.executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
3723
3846
  expect(errorHandler).not.toHaveBeenCalled();
@@ -3733,7 +3856,10 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
3733
3856
  test('skips Error event when useHostEventsV2 is false and shouldBypassPayloadValidation is true', () => {
3734
3857
  jest.spyOn(logger, 'warn');
3735
3858
  const errorHandler = jest.fn();
3736
- const embed = makeEmbed({ useHostEventsV2: false, shouldBypassPayloadValidation: true });
3859
+ const embed = makeEmbed({
3860
+ useHostEventsV2: false,
3861
+ shouldBypassPayloadValidation: true,
3862
+ });
3737
3863
  embed.on(EmbedEvent.Error, errorHandler);
3738
3864
  embed.executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
3739
3865
  expect(errorHandler).not.toHaveBeenCalled();
@@ -3758,14 +3884,20 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
3758
3884
  });
3759
3885
  test('delivers Error event to EmbedEvent.ALL handler when not skipped', () => {
3760
3886
  const allHandler = jest.fn();
3761
- const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
3887
+ const embed = makeEmbed({
3888
+ useHostEventsV2: true,
3889
+ shouldBypassPayloadValidation: false,
3890
+ });
3762
3891
  embed.on(EmbedEvent.ALL, allHandler);
3763
3892
  embed.executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
3764
3893
  expect(allHandler).toHaveBeenCalled();
3765
3894
  });
3766
3895
  test('does not skip non-Error events even with HOST_EVENT_VALIDATION error code', () => {
3767
3896
  const customActionHandler = jest.fn();
3768
- const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
3897
+ const embed = makeEmbed({
3898
+ useHostEventsV2: true,
3899
+ shouldBypassPayloadValidation: false,
3900
+ });
3769
3901
  embed.on(EmbedEvent.CustomAction, customActionHandler);
3770
3902
  embed.executeCallbacks(EmbedEvent.CustomAction, {
3771
3903
  data: { code: EmbedErrorCodes.HOST_EVENT_VALIDATION },
@@ -3774,7 +3906,10 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
3774
3906
  });
3775
3907
  test('does not skip Error events with unrelated error codes', () => {
3776
3908
  const errorHandler = jest.fn();
3777
- const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
3909
+ const embed = makeEmbed({
3910
+ useHostEventsV2: true,
3911
+ shouldBypassPayloadValidation: false,
3912
+ });
3778
3913
  embed.on(EmbedEvent.Error, errorHandler);
3779
3914
  embed.executeCallbacks(EmbedEvent.Error, {
3780
3915
  errorType: 'SOME_OTHER_ERROR',
@@ -3783,5 +3918,145 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
3783
3918
  expect(errorHandler).toHaveBeenCalled();
3784
3919
  });
3785
3920
  });
3921
+ describe('constructor init-path branching (SCAL-315058)', () => {
3922
+ beforeEach(() => {
3923
+ init({
3924
+ thoughtSpotHost: 'tshost',
3925
+ authType: AuthType.None,
3926
+ });
3927
+ });
3928
+ it('sets hostElement from domSelector', () => {
3929
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
3930
+ expect(embed.hostElement).toBe(getRootEl());
3931
+ });
3932
+ it('skips isReadyForRenderPromise when init already completed', () => {
3933
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(true);
3934
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
3935
+ expect(embed.shouldWaitForRenderPromise).toBe(false);
3936
+ expect(embed.isReadyForRenderPromise).toBeUndefined();
3937
+ });
3938
+ it('sets isReadyForRenderPromise when init not yet completed', () => {
3939
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(false);
3940
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
3941
+ expect(embed.shouldWaitForRenderPromise).toBe(true);
3942
+ expect(embed.isReadyForRenderPromise).toBeInstanceOf(Promise);
3943
+ });
3944
+ it('afterInit runs synchronously when init already completed', () => {
3945
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(true);
3946
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
3947
+ // thoughtSpotHost is set by afterInit;
3948
+ // must be non-empty after constructor
3949
+ expect(embed.thoughtSpotHost).toBeTruthy();
3950
+ });
3951
+ it('does not set isReadyForRenderPromise when shouldWaitForRenderPromise is false', () => {
3952
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(true);
3953
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
3954
+ expect(embed.shouldWaitForRenderPromise).toBe(false);
3955
+ // isReadyForRenderPromise must be undefined
3956
+ // so no unnecessary await occurs
3957
+ expect(embed.isReadyForRenderPromise).toBeUndefined();
3958
+ });
3959
+ it('shouldWaitForRenderPromise flips to false after promise settles', async () => {
3960
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(false);
3961
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
3962
+ expect(embed.shouldWaitForRenderPromise).toBe(true);
3963
+ await embed.isReadyForRenderPromise;
3964
+ expect(embed.shouldWaitForRenderPromise).toBe(false);
3965
+ });
3966
+ it('calls throwInitError when getInitPromise rejects', async () => {
3967
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(false);
3968
+ jest.spyOn(baseInstance, 'getInitPromise').mockReturnValue(Promise.reject(new Error('init failed')));
3969
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
3970
+ const throwInitErrorSpy = jest.spyOn(embed, 'throwInitError');
3971
+ await embed.isReadyForRenderPromise;
3972
+ expect(throwInitErrorSpy).toHaveBeenCalled();
3973
+ });
3974
+ it('shouldWaitForRenderPromise flips to false even when getInitPromise rejects', async () => {
3975
+ jest.spyOn(baseInstance, 'getIsInitCompleted').mockReturnValue(false);
3976
+ jest.spyOn(baseInstance, 'getInitPromise').mockReturnValue(Promise.reject(new Error('init failed')));
3977
+ const embed = new SearchEmbed(getRootEl(), defaultViewConfig);
3978
+ await embed.isReadyForRenderPromise;
3979
+ expect(embed.shouldWaitForRenderPromise).toBe(false);
3980
+ });
3981
+ });
3982
+ describe('preRender ID object includes placeHolder (SCAL-315058)', () => {
3983
+ beforeAll(() => {
3984
+ init({
3985
+ thoughtSpotHost: 'tshost',
3986
+ authType: AuthType.None,
3987
+ });
3988
+ });
3989
+ it('getPreRenderIds returns placeHolder key', () => {
3990
+ createRootEleForEmbed();
3991
+ const embed = new LiveboardEmbed('#tsEmbedDiv', {
3992
+ preRenderId: 'ph-test',
3993
+ liveboardId: 'lb-id',
3994
+ });
3995
+ const ids = embed.getPreRenderIds();
3996
+ expect(ids.placeHolder).toBe('tsEmbed-pre-render-placeholder-ph-test');
3997
+ });
3998
+ });
3999
+ describe('isPreRenderConnected logic (SCAL-315058)', () => {
4000
+ beforeAll(() => {
4001
+ init({
4002
+ thoughtSpotHost: 'tshost',
4003
+ authType: AuthType.None,
4004
+ });
4005
+ });
4006
+ it('returns false when preRenderWrapper is absent', () => {
4007
+ createRootEleForEmbed();
4008
+ const embed = new LiveboardEmbed('#tsEmbedDiv', {
4009
+ preRenderId: 'conn-test',
4010
+ liveboardId: 'lb-id',
4011
+ });
4012
+ expect(embed.isPreRenderConnected()).toBe(false);
4013
+ });
4014
+ it('returns true once preRenderWrapper and preRenderChild are set', () => {
4015
+ createRootEleForEmbed();
4016
+ const embed = new LiveboardEmbed('#tsEmbedDiv', {
4017
+ preRenderId: 'conn-test-2',
4018
+ liveboardId: 'lb-id',
4019
+ });
4020
+ embed.preRenderWrapper = document.createElement('div');
4021
+ embed.preRenderChild = document.createElement('div');
4022
+ expect(embed.isPreRenderConnected()).toBe(true);
4023
+ });
4024
+ });
4025
+ describe('showPreRender inserts placeholder into hostElement (SCAL-315058)', () => {
4026
+ beforeAll(() => {
4027
+ // Clear spy implementations that may have leaked from prior
4028
+ // describe blocks (e.g. getIsInitCompleted/getInitPromise mocks set
4029
+ // in 'constructor init-path branching' persist across describes
4030
+ // because clearAllMocks only clears call history, not
4031
+ // implementations).
4032
+ jest.restoreAllMocks();
4033
+ jest.spyOn(authInstance, 'postLoginService').mockResolvedValue(undefined);
4034
+ init({
4035
+ thoughtSpotHost: 'tshost',
4036
+ authType: AuthType.None,
4037
+ });
4038
+ window.ResizeObserver = jest.fn().mockImplementation(() => ({
4039
+ disconnect: jest.fn(),
4040
+ observe: jest.fn(),
4041
+ unobserve: jest.fn(),
4042
+ }));
4043
+ });
4044
+ it('showPreRender creates a placeholder element with the correct id', async () => {
4045
+ createRootEleForEmbed();
4046
+ const embed = new LiveboardEmbed('#tsEmbedDiv', {
4047
+ preRenderId: 'ph-lifecycle-test',
4048
+ liveboardId: 'lb-id',
4049
+ });
4050
+ await embed.preRender();
4051
+ await waitFor(() => !!document.querySelector('#tsEmbed-pre-render-child-ph-lifecycle-test'));
4052
+ await embed.showPreRender();
4053
+ const placeholderEle = embed.getPreRenderPlaceHolderElement();
4054
+ expect(placeholderEle).not.toBeNull();
4055
+ expect(placeholderEle.id).toBe('tsEmbed-pre-render-placeholder-ph-lifecycle-test');
4056
+ // placeholder is attached to a parent element (inside hostElement)
4057
+ expect(placeholderEle.parentElement).not.toBeNull();
4058
+ embed.destroy();
4059
+ });
4060
+ });
3786
4061
  });
3787
4062
  //# sourceMappingURL=ts-embed.spec.js.map