@thoughtspot/visual-embed-sdk 1.49.3 → 1.50.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/authToken.spec.js +5 -0
  3. package/cjs/src/authToken.spec.js.map +1 -1
  4. package/cjs/src/css-variables.d.ts +59 -0
  5. package/cjs/src/css-variables.d.ts.map +1 -1
  6. package/cjs/src/embed/app.d.ts +31 -2
  7. package/cjs/src/embed/app.d.ts.map +1 -1
  8. package/cjs/src/embed/app.js +19 -2
  9. package/cjs/src/embed/app.js.map +1 -1
  10. package/cjs/src/embed/app.spec.js +112 -2
  11. package/cjs/src/embed/app.spec.js.map +1 -1
  12. package/cjs/src/embed/conversation.d.ts +38 -0
  13. package/cjs/src/embed/conversation.d.ts.map +1 -1
  14. package/cjs/src/embed/conversation.js +12 -2
  15. package/cjs/src/embed/conversation.js.map +1 -1
  16. package/cjs/src/embed/conversation.spec.js +53 -0
  17. package/cjs/src/embed/conversation.spec.js.map +1 -1
  18. package/cjs/src/embed/embedConfig.spec.d.ts +2 -0
  19. package/cjs/src/embed/embedConfig.spec.d.ts.map +1 -0
  20. package/cjs/src/embed/embedConfig.spec.js +74 -0
  21. package/cjs/src/embed/embedConfig.spec.js.map +1 -0
  22. package/cjs/src/embed/events.spec.js +593 -0
  23. package/cjs/src/embed/events.spec.js.map +1 -1
  24. package/cjs/src/embed/host-events.spec.d.ts +2 -0
  25. package/cjs/src/embed/host-events.spec.d.ts.map +1 -0
  26. package/cjs/src/embed/host-events.spec.js +1325 -0
  27. package/cjs/src/embed/host-events.spec.js.map +1 -0
  28. package/cjs/src/embed/hostEventClient/contracts.d.ts +36 -14
  29. package/cjs/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  30. package/cjs/src/embed/hostEventClient/contracts.js +10 -1
  31. package/cjs/src/embed/hostEventClient/contracts.js.map +1 -1
  32. package/cjs/src/embed/hostEventClient/host-event-client.d.ts +1 -0
  33. package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  34. package/cjs/src/embed/hostEventClient/host-event-client.js +8 -0
  35. package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
  36. package/cjs/src/embed/hostEventClient/host-event-client.spec.js +209 -0
  37. package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  38. package/cjs/src/embed/hostEventClient/utils.d.ts +2 -0
  39. package/cjs/src/embed/hostEventClient/utils.d.ts.map +1 -1
  40. package/cjs/src/embed/hostEventClient/utils.js +32 -2
  41. package/cjs/src/embed/hostEventClient/utils.js.map +1 -1
  42. package/cjs/src/embed/hostEventClient/utils.spec.js +178 -0
  43. package/cjs/src/embed/hostEventClient/utils.spec.js.map +1 -1
  44. package/cjs/src/embed/liveboard.d.ts +6 -0
  45. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  46. package/cjs/src/embed/liveboard.js.map +1 -1
  47. package/cjs/src/embed/spotter-viz-utils.d.ts +34 -0
  48. package/cjs/src/embed/spotter-viz-utils.d.ts.map +1 -1
  49. package/cjs/src/embed/spotter-viz-utils.js.map +1 -1
  50. package/cjs/src/embed/spotter-viz-utils.spec.js +17 -0
  51. package/cjs/src/embed/spotter-viz-utils.spec.js.map +1 -1
  52. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  53. package/cjs/src/embed/ts-embed.js +14 -7
  54. package/cjs/src/embed/ts-embed.js.map +1 -1
  55. package/cjs/src/embed/ts-embed.spec.js +146 -0
  56. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  57. package/cjs/src/errors.d.ts +1 -0
  58. package/cjs/src/errors.d.ts.map +1 -1
  59. package/cjs/src/errors.js +1 -0
  60. package/cjs/src/errors.js.map +1 -1
  61. package/cjs/src/index.d.ts +2 -2
  62. package/cjs/src/index.d.ts.map +1 -1
  63. package/cjs/src/index.js +3 -2
  64. package/cjs/src/index.js.map +1 -1
  65. package/cjs/src/mixpanel-service.spec.js +14 -0
  66. package/cjs/src/mixpanel-service.spec.js.map +1 -1
  67. package/cjs/src/react/util.spec.js +251 -0
  68. package/cjs/src/react/util.spec.js.map +1 -1
  69. package/cjs/src/types.d.ts +212 -8
  70. package/cjs/src/types.d.ts.map +1 -1
  71. package/cjs/src/types.js +185 -3
  72. package/cjs/src/types.js.map +1 -1
  73. package/cjs/src/utils/custom-actions.spec.js +26 -0
  74. package/cjs/src/utils/custom-actions.spec.js.map +1 -1
  75. package/cjs/src/utils/processData.spec.js +123 -0
  76. package/cjs/src/utils/processData.spec.js.map +1 -1
  77. package/cjs/src/utils/processTrigger.spec.js +61 -0
  78. package/cjs/src/utils/processTrigger.spec.js.map +1 -1
  79. package/cjs/src/utils/reporting.spec.js +21 -0
  80. package/cjs/src/utils/reporting.spec.js.map +1 -1
  81. package/cjs/src/utils/sessionInfoService.spec.d.ts +2 -0
  82. package/cjs/src/utils/sessionInfoService.spec.d.ts.map +1 -0
  83. package/cjs/src/utils/sessionInfoService.spec.js +130 -0
  84. package/cjs/src/utils/sessionInfoService.spec.js.map +1 -0
  85. package/cjs/src/utils.spec.js +158 -0
  86. package/cjs/src/utils.spec.js.map +1 -1
  87. package/dist/{index-DsSQndIB.js → index-fCne7kmU.js} +1 -1
  88. package/dist/src/css-variables.d.ts +59 -0
  89. package/dist/src/css-variables.d.ts.map +1 -1
  90. package/dist/src/embed/app.d.ts +31 -2
  91. package/dist/src/embed/app.d.ts.map +1 -1
  92. package/dist/src/embed/conversation.d.ts +38 -0
  93. package/dist/src/embed/conversation.d.ts.map +1 -1
  94. package/dist/src/embed/embedConfig.spec.d.ts +2 -0
  95. package/dist/src/embed/embedConfig.spec.d.ts.map +1 -0
  96. package/dist/src/embed/host-events.spec.d.ts +2 -0
  97. package/dist/src/embed/host-events.spec.d.ts.map +1 -0
  98. package/dist/src/embed/hostEventClient/contracts.d.ts +36 -14
  99. package/dist/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  100. package/dist/src/embed/hostEventClient/host-event-client.d.ts +1 -0
  101. package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  102. package/dist/src/embed/hostEventClient/utils.d.ts +2 -0
  103. package/dist/src/embed/hostEventClient/utils.d.ts.map +1 -1
  104. package/dist/src/embed/liveboard.d.ts +6 -0
  105. package/dist/src/embed/liveboard.d.ts.map +1 -1
  106. package/dist/src/embed/spotter-viz-utils.d.ts +34 -0
  107. package/dist/src/embed/spotter-viz-utils.d.ts.map +1 -1
  108. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  109. package/dist/src/errors.d.ts +1 -0
  110. package/dist/src/errors.d.ts.map +1 -1
  111. package/dist/src/index.d.ts +2 -2
  112. package/dist/src/index.d.ts.map +1 -1
  113. package/dist/src/types.d.ts +212 -8
  114. package/dist/src/types.d.ts.map +1 -1
  115. package/dist/src/utils/sessionInfoService.spec.d.ts +2 -0
  116. package/dist/src/utils/sessionInfoService.spec.d.ts.map +1 -0
  117. package/dist/tsembed-react.es.js +482 -86
  118. package/dist/tsembed-react.js +481 -85
  119. package/dist/tsembed.es.js +483 -87
  120. package/dist/tsembed.js +481 -85
  121. package/dist/visual-embed-sdk-react-full.d.ts +407 -23
  122. package/dist/visual-embed-sdk-react.d.ts +407 -23
  123. package/dist/visual-embed-sdk.d.ts +411 -23
  124. package/lib/package.json +1 -1
  125. package/lib/src/authToken.spec.js +5 -0
  126. package/lib/src/authToken.spec.js.map +1 -1
  127. package/lib/src/css-variables.d.ts +59 -0
  128. package/lib/src/css-variables.d.ts.map +1 -1
  129. package/lib/src/embed/app.d.ts +31 -2
  130. package/lib/src/embed/app.d.ts.map +1 -1
  131. package/lib/src/embed/app.js +19 -2
  132. package/lib/src/embed/app.js.map +1 -1
  133. package/lib/src/embed/app.spec.js +112 -2
  134. package/lib/src/embed/app.spec.js.map +1 -1
  135. package/lib/src/embed/conversation.d.ts +38 -0
  136. package/lib/src/embed/conversation.d.ts.map +1 -1
  137. package/lib/src/embed/conversation.js +11 -1
  138. package/lib/src/embed/conversation.js.map +1 -1
  139. package/lib/src/embed/conversation.spec.js +54 -1
  140. package/lib/src/embed/conversation.spec.js.map +1 -1
  141. package/lib/src/embed/embedConfig.spec.d.ts +2 -0
  142. package/lib/src/embed/embedConfig.spec.d.ts.map +1 -0
  143. package/lib/src/embed/embedConfig.spec.js +72 -0
  144. package/lib/src/embed/embedConfig.spec.js.map +1 -0
  145. package/lib/src/embed/events.spec.js +593 -0
  146. package/lib/src/embed/events.spec.js.map +1 -1
  147. package/lib/src/embed/host-events.spec.d.ts +2 -0
  148. package/lib/src/embed/host-events.spec.d.ts.map +1 -0
  149. package/lib/src/embed/host-events.spec.js +1322 -0
  150. package/lib/src/embed/host-events.spec.js.map +1 -0
  151. package/lib/src/embed/hostEventClient/contracts.d.ts +36 -14
  152. package/lib/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  153. package/lib/src/embed/hostEventClient/contracts.js +9 -0
  154. package/lib/src/embed/hostEventClient/contracts.js.map +1 -1
  155. package/lib/src/embed/hostEventClient/host-event-client.d.ts +1 -0
  156. package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  157. package/lib/src/embed/hostEventClient/host-event-client.js +9 -1
  158. package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
  159. package/lib/src/embed/hostEventClient/host-event-client.spec.js +209 -0
  160. package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  161. package/lib/src/embed/hostEventClient/utils.d.ts +2 -0
  162. package/lib/src/embed/hostEventClient/utils.d.ts.map +1 -1
  163. package/lib/src/embed/hostEventClient/utils.js +28 -1
  164. package/lib/src/embed/hostEventClient/utils.js.map +1 -1
  165. package/lib/src/embed/hostEventClient/utils.spec.js +179 -1
  166. package/lib/src/embed/hostEventClient/utils.spec.js.map +1 -1
  167. package/lib/src/embed/liveboard.d.ts +6 -0
  168. package/lib/src/embed/liveboard.d.ts.map +1 -1
  169. package/lib/src/embed/liveboard.js.map +1 -1
  170. package/lib/src/embed/spotter-viz-utils.d.ts +34 -0
  171. package/lib/src/embed/spotter-viz-utils.d.ts.map +1 -1
  172. package/lib/src/embed/spotter-viz-utils.js.map +1 -1
  173. package/lib/src/embed/spotter-viz-utils.spec.js +17 -0
  174. package/lib/src/embed/spotter-viz-utils.spec.js.map +1 -1
  175. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  176. package/lib/src/embed/ts-embed.js +14 -7
  177. package/lib/src/embed/ts-embed.js.map +1 -1
  178. package/lib/src/embed/ts-embed.spec.js +146 -0
  179. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  180. package/lib/src/errors.d.ts +1 -0
  181. package/lib/src/errors.d.ts.map +1 -1
  182. package/lib/src/errors.js +1 -0
  183. package/lib/src/errors.js.map +1 -1
  184. package/lib/src/index.d.ts +2 -2
  185. package/lib/src/index.d.ts.map +1 -1
  186. package/lib/src/index.js +2 -2
  187. package/lib/src/index.js.map +1 -1
  188. package/lib/src/mixpanel-service.spec.js +14 -0
  189. package/lib/src/mixpanel-service.spec.js.map +1 -1
  190. package/lib/src/react/util.spec.js +251 -0
  191. package/lib/src/react/util.spec.js.map +1 -1
  192. package/lib/src/types.d.ts +212 -8
  193. package/lib/src/types.d.ts.map +1 -1
  194. package/lib/src/types.js +185 -3
  195. package/lib/src/types.js.map +1 -1
  196. package/lib/src/utils/custom-actions.spec.js +26 -0
  197. package/lib/src/utils/custom-actions.spec.js.map +1 -1
  198. package/lib/src/utils/processData.spec.js +123 -0
  199. package/lib/src/utils/processData.spec.js.map +1 -1
  200. package/lib/src/utils/processTrigger.spec.js +61 -0
  201. package/lib/src/utils/processTrigger.spec.js.map +1 -1
  202. package/lib/src/utils/reporting.spec.js +21 -0
  203. package/lib/src/utils/reporting.spec.js.map +1 -1
  204. package/lib/src/utils/sessionInfoService.spec.d.ts +2 -0
  205. package/lib/src/utils/sessionInfoService.spec.d.ts.map +1 -0
  206. package/lib/src/utils/sessionInfoService.spec.js +127 -0
  207. package/lib/src/utils/sessionInfoService.spec.js.map +1 -0
  208. package/lib/src/utils.spec.js +159 -1
  209. package/lib/src/utils.spec.js.map +1 -1
  210. package/lib/src/visual-embed-sdk.d.ts +411 -23
  211. package/package.json +1 -1
  212. package/src/authToken.spec.ts +6 -0
  213. package/src/css-variables.ts +73 -0
  214. package/src/embed/app.spec.ts +155 -2
  215. package/src/embed/app.ts +44 -2
  216. package/src/embed/conversation.spec.ts +64 -1
  217. package/src/embed/conversation.ts +41 -0
  218. package/src/embed/embedConfig.spec.ts +79 -0
  219. package/src/embed/events.spec.ts +705 -1
  220. package/src/embed/host-events.spec.ts +1759 -0
  221. package/src/embed/hostEventClient/contracts.ts +41 -14
  222. package/src/embed/hostEventClient/host-event-client.spec.ts +326 -0
  223. package/src/embed/hostEventClient/host-event-client.ts +15 -0
  224. package/src/embed/hostEventClient/utils.spec.ts +204 -0
  225. package/src/embed/hostEventClient/utils.ts +37 -10
  226. package/src/embed/liveboard.ts +6 -0
  227. package/src/embed/spotter-viz-utils.spec.ts +17 -0
  228. package/src/embed/spotter-viz-utils.ts +34 -0
  229. package/src/embed/ts-embed.spec.ts +186 -0
  230. package/src/embed/ts-embed.ts +12 -4
  231. package/src/errors.ts +1 -0
  232. package/src/index.ts +2 -1
  233. package/src/mixpanel-service.spec.ts +15 -0
  234. package/src/react/util.spec.tsx +255 -0
  235. package/src/types.ts +215 -6
  236. package/src/utils/custom-actions.spec.ts +28 -0
  237. package/src/utils/processData.spec.ts +129 -0
  238. package/src/utils/processTrigger.spec.ts +91 -0
  239. package/src/utils/reporting.spec.ts +27 -0
  240. package/src/utils/sessionInfoService.spec.ts +151 -0
  241. package/src/utils.spec.ts +195 -0
@@ -0,0 +1,1759 @@
1
+ /**
2
+ * Unit tests for HostEvents that had no prior coverage.
3
+ * Covers: postMessage type correctness, !isRendered guard (returns null + handleError),
4
+ * and vizId injection on LiveboardEmbed when viewConfig.vizId is set.
5
+ */
6
+ import {
7
+ init,
8
+ AuthType,
9
+ LiveboardEmbed,
10
+ AppEmbed,
11
+ HostEvent,
12
+ EmbedErrorCodes,
13
+ } from '../index';
14
+ import {
15
+ executeAfterWait,
16
+ getDocumentBody,
17
+ getIFrameEl,
18
+ getRootEl,
19
+ mockMessageChannel,
20
+ messageChannelMock,
21
+ } from '../test/test-utils';
22
+ import * as authInstance from '../auth';
23
+
24
+ const thoughtSpotHost = 'https://tshost';
25
+ const liveboardId = 'eca215d4-0d2c-4a55-90e3-d81ef6848ae0';
26
+ const vizId = '6e73f724-660e-11eb-ae93-0242ac130002';
27
+
28
+ beforeAll(() => {
29
+ init({ thoughtSpotHost, authType: AuthType.None });
30
+ jest.spyOn(authInstance, 'postLoginService').mockImplementation(() => Promise.resolve(true as any));
31
+ });
32
+
33
+ beforeEach(() => {
34
+ document.body.innerHTML = getDocumentBody();
35
+ });
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Helper: render a LiveboardEmbed and return it with the iframe spy
39
+ // ---------------------------------------------------------------------------
40
+ async function renderLiveboard(extraConfig: Record<string, any> = {}) {
41
+ const lb = new LiveboardEmbed(getRootEl(), {
42
+ frameParams: { width: '100%', height: '100%' },
43
+ liveboardId,
44
+ ...extraConfig,
45
+ });
46
+ await lb.render();
47
+ const iframe = getIFrameEl();
48
+ jest.spyOn(iframe.contentWindow, 'postMessage');
49
+ return { lb, iframe };
50
+ }
51
+
52
+ // Helper: create a LiveboardEmbed that has NOT been rendered, with handleError
53
+ // mocked to prevent the jest-setup console.error → throw.
54
+ function unrenderedLiveboard() {
55
+ const lb = new LiveboardEmbed(getRootEl(), {
56
+ frameParams: { width: '100%', height: '100%' },
57
+ liveboardId,
58
+ });
59
+ jest.spyOn(lb as any, 'handleError').mockImplementation(() => {});
60
+ return lb;
61
+ }
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // 3 – Filter
65
+ // ---------------------------------------------------------------------------
66
+ describe('HostEvent.Filter', () => {
67
+ test('postMessage type is filter (all lowercase)', async () => {
68
+ mockMessageChannel();
69
+ const { lb, iframe } = await renderLiveboard();
70
+ await executeAfterWait(() => {
71
+ lb.trigger(HostEvent.Filter, { columnName: 'col', operator: 'EQ', values: ['v'] } as any);
72
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
73
+ expect.objectContaining({ type: 'filter' }),
74
+ thoughtSpotHost,
75
+ expect.anything(),
76
+ );
77
+ });
78
+ });
79
+
80
+ test('returns null and calls handleError when !isRendered', async () => {
81
+ const lb = unrenderedLiveboard();
82
+ const handleErrorSpy = (lb as any).handleError as jest.Mock;
83
+ const result = await lb.trigger(HostEvent.Filter, {} as any);
84
+ expect(result).toBeNull();
85
+ expect(handleErrorSpy).toHaveBeenCalledWith(
86
+ expect.objectContaining({ code: EmbedErrorCodes.RENDER_NOT_CALLED }),
87
+ );
88
+ });
89
+ });
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // 8 – UpdateRuntimeFilters
93
+ // ---------------------------------------------------------------------------
94
+ describe('HostEvent.UpdateRuntimeFilters', () => {
95
+ test('postMessage type is UpdateRuntimeFilters (PascalCase)', async () => {
96
+ mockMessageChannel();
97
+ const { lb, iframe } = await renderLiveboard();
98
+ await executeAfterWait(() => {
99
+ lb.trigger(HostEvent.UpdateRuntimeFilters, [] as any);
100
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
101
+ expect.objectContaining({ type: 'UpdateRuntimeFilters' }),
102
+ thoughtSpotHost,
103
+ expect.anything(),
104
+ );
105
+ });
106
+ });
107
+
108
+ test('returns null when !isRendered', async () => {
109
+ const lb = unrenderedLiveboard();
110
+ const result = await lb.trigger(HostEvent.UpdateRuntimeFilters, [] as any);
111
+ expect(result).toBeNull();
112
+ });
113
+
114
+ test('vizId is injected when viewConfig.vizId is set and payload is object', async () => {
115
+ mockMessageChannel();
116
+ const { lb, iframe } = await renderLiveboard({ vizId });
117
+ await executeAfterWait(() => {
118
+ lb.trigger(HostEvent.UpdateRuntimeFilters, [{}] as any);
119
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
120
+ expect.objectContaining({ type: 'UpdateRuntimeFilters' }),
121
+ thoughtSpotHost,
122
+ expect.anything(),
123
+ );
124
+ });
125
+ });
126
+ });
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // 10 – OpenFilter
130
+ // ---------------------------------------------------------------------------
131
+ describe('HostEvent.OpenFilter', () => {
132
+ test('postMessage type is openFilter (camelCase)', async () => {
133
+ mockMessageChannel();
134
+ const { lb, iframe } = await renderLiveboard();
135
+ await executeAfterWait(() => {
136
+ lb.trigger(HostEvent.OpenFilter, { columnId: 'col1' } as any);
137
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
138
+ expect.objectContaining({ type: 'openFilter' }),
139
+ thoughtSpotHost,
140
+ expect.anything(),
141
+ );
142
+ });
143
+ });
144
+
145
+ test('returns null when !isRendered', async () => {
146
+ const lb = unrenderedLiveboard();
147
+ const result = await lb.trigger(HostEvent.OpenFilter, {} as any);
148
+ expect(result).toBeNull();
149
+ });
150
+ });
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // 11 – AddColumns
154
+ // ---------------------------------------------------------------------------
155
+ describe('HostEvent.AddColumns', () => {
156
+ test('postMessage type is addColumns (camelCase)', async () => {
157
+ mockMessageChannel();
158
+ const { lb, iframe } = await renderLiveboard();
159
+ await executeAfterWait(() => {
160
+ lb.trigger(HostEvent.AddColumns, { columnIds: ['c1'] } as any);
161
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
162
+ expect.objectContaining({ type: 'addColumns' }),
163
+ thoughtSpotHost,
164
+ expect.anything(),
165
+ );
166
+ });
167
+ });
168
+
169
+ test('returns null when !isRendered', async () => {
170
+ const lb = unrenderedLiveboard();
171
+ const result = await lb.trigger(HostEvent.AddColumns, {} as any);
172
+ expect(result).toBeNull();
173
+ });
174
+ });
175
+
176
+ // ---------------------------------------------------------------------------
177
+ // 12 – RemoveColumn
178
+ // ---------------------------------------------------------------------------
179
+ describe('HostEvent.RemoveColumn', () => {
180
+ test('postMessage type is removeColumn (camelCase)', async () => {
181
+ mockMessageChannel();
182
+ const { lb, iframe } = await renderLiveboard();
183
+ await executeAfterWait(() => {
184
+ lb.trigger(HostEvent.RemoveColumn, { columnId: 'c1' } as any);
185
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
186
+ expect.objectContaining({ type: 'removeColumn' }),
187
+ thoughtSpotHost,
188
+ expect.anything(),
189
+ );
190
+ });
191
+ });
192
+
193
+ test('returns null when !isRendered', async () => {
194
+ const lb = unrenderedLiveboard();
195
+ const result = await lb.trigger(HostEvent.RemoveColumn, {} as any);
196
+ expect(result).toBeNull();
197
+ });
198
+ });
199
+
200
+ // ---------------------------------------------------------------------------
201
+ // 15 – LiveboardInfo
202
+ // ---------------------------------------------------------------------------
203
+ describe('HostEvent.LiveboardInfo', () => {
204
+ test('postMessage type is pinboardInfo', async () => {
205
+ mockMessageChannel();
206
+ const { lb, iframe } = await renderLiveboard();
207
+ await executeAfterWait(() => {
208
+ lb.trigger(HostEvent.LiveboardInfo);
209
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
210
+ expect.objectContaining({ type: 'pinboardInfo' }),
211
+ thoughtSpotHost,
212
+ expect.anything(),
213
+ );
214
+ });
215
+ });
216
+
217
+ test('returns null when !isRendered', async () => {
218
+ const lb = unrenderedLiveboard();
219
+ const result = await lb.trigger(HostEvent.LiveboardInfo);
220
+ expect(result).toBeNull();
221
+ });
222
+ });
223
+
224
+ // ---------------------------------------------------------------------------
225
+ // 16 – Schedule
226
+ // ---------------------------------------------------------------------------
227
+ describe('HostEvent.Schedule', () => {
228
+ test('postMessage type is subscription', async () => {
229
+ mockMessageChannel();
230
+ const { lb, iframe } = await renderLiveboard();
231
+ await executeAfterWait(() => {
232
+ lb.trigger(HostEvent.Schedule);
233
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
234
+ expect.objectContaining({ type: 'subscription' }),
235
+ thoughtSpotHost,
236
+ expect.anything(),
237
+ );
238
+ });
239
+ });
240
+
241
+ test('returns null when !isRendered', async () => {
242
+ const lb = unrenderedLiveboard();
243
+ const result = await lb.trigger(HostEvent.Schedule);
244
+ expect(result).toBeNull();
245
+ });
246
+ });
247
+
248
+ // ---------------------------------------------------------------------------
249
+ // 17 – SchedulesList
250
+ // ---------------------------------------------------------------------------
251
+ describe('HostEvent.SchedulesList', () => {
252
+ test('postMessage type is schedule-list (hyphenated)', async () => {
253
+ mockMessageChannel();
254
+ const { lb, iframe } = await renderLiveboard();
255
+ await executeAfterWait(() => {
256
+ lb.trigger(HostEvent.SchedulesList);
257
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
258
+ expect.objectContaining({ type: 'schedule-list' }),
259
+ thoughtSpotHost,
260
+ expect.anything(),
261
+ );
262
+ });
263
+ });
264
+
265
+ test('returns null when !isRendered', async () => {
266
+ const lb = unrenderedLiveboard();
267
+ const result = await lb.trigger(HostEvent.SchedulesList);
268
+ expect(result).toBeNull();
269
+ });
270
+ });
271
+
272
+ // ---------------------------------------------------------------------------
273
+ // 18 – ExportTML
274
+ // ---------------------------------------------------------------------------
275
+ describe('HostEvent.ExportTML', () => {
276
+ test('postMessage type is exportTSL (uses TSL abbreviation)', async () => {
277
+ mockMessageChannel();
278
+ const { lb, iframe } = await renderLiveboard();
279
+ await executeAfterWait(() => {
280
+ lb.trigger(HostEvent.ExportTML);
281
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
282
+ expect.objectContaining({ type: 'exportTSL' }),
283
+ thoughtSpotHost,
284
+ expect.anything(),
285
+ );
286
+ });
287
+ });
288
+
289
+ test('returns null when !isRendered', async () => {
290
+ const lb = unrenderedLiveboard();
291
+ const result = await lb.trigger(HostEvent.ExportTML);
292
+ expect(result).toBeNull();
293
+ });
294
+ });
295
+
296
+ // ---------------------------------------------------------------------------
297
+ // 19 – EditTML
298
+ // ---------------------------------------------------------------------------
299
+ describe('HostEvent.EditTML', () => {
300
+ test('postMessage type is editTSL', async () => {
301
+ mockMessageChannel();
302
+ const { lb, iframe } = await renderLiveboard();
303
+ await executeAfterWait(() => {
304
+ lb.trigger(HostEvent.EditTML);
305
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
306
+ expect.objectContaining({ type: 'editTSL' }),
307
+ thoughtSpotHost,
308
+ expect.anything(),
309
+ );
310
+ });
311
+ });
312
+
313
+ test('returns null when !isRendered', async () => {
314
+ const lb = unrenderedLiveboard();
315
+ const result = await lb.trigger(HostEvent.EditTML);
316
+ expect(result).toBeNull();
317
+ });
318
+ });
319
+
320
+ // ---------------------------------------------------------------------------
321
+ // 20 – UpdateTML
322
+ // ---------------------------------------------------------------------------
323
+ describe('HostEvent.UpdateTML', () => {
324
+ test('postMessage type is updateTSL', async () => {
325
+ mockMessageChannel();
326
+ const { lb, iframe } = await renderLiveboard();
327
+ await executeAfterWait(() => {
328
+ lb.trigger(HostEvent.UpdateTML);
329
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
330
+ expect.objectContaining({ type: 'updateTSL' }),
331
+ thoughtSpotHost,
332
+ expect.anything(),
333
+ );
334
+ });
335
+ });
336
+
337
+ test('returns null when !isRendered', async () => {
338
+ const lb = unrenderedLiveboard();
339
+ const result = await lb.trigger(HostEvent.UpdateTML);
340
+ expect(result).toBeNull();
341
+ });
342
+ });
343
+
344
+ // ---------------------------------------------------------------------------
345
+ // 21 – DownloadAsPdf
346
+ // ---------------------------------------------------------------------------
347
+ describe('HostEvent.DownloadAsPdf', () => {
348
+ test('postMessage type is downloadAsPdf (camelCase)', async () => {
349
+ mockMessageChannel();
350
+ const { lb, iframe } = await renderLiveboard();
351
+ await executeAfterWait(() => {
352
+ lb.trigger(HostEvent.DownloadAsPdf);
353
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
354
+ expect.objectContaining({ type: 'downloadAsPdf' }),
355
+ thoughtSpotHost,
356
+ expect.anything(),
357
+ );
358
+ });
359
+ });
360
+
361
+ test('returns null when !isRendered', async () => {
362
+ const lb = unrenderedLiveboard();
363
+ const result = await lb.trigger(HostEvent.DownloadAsPdf);
364
+ expect(result).toBeNull();
365
+ });
366
+ });
367
+
368
+ // ---------------------------------------------------------------------------
369
+ // 22 – DownloadLiveboardAsContinuousPDF
370
+ // ---------------------------------------------------------------------------
371
+ describe('HostEvent.DownloadLiveboardAsContinuousPDF', () => {
372
+ test('postMessage type is downloadLiveboardAsContinuousPDF', async () => {
373
+ mockMessageChannel();
374
+ const { lb, iframe } = await renderLiveboard();
375
+ await executeAfterWait(() => {
376
+ lb.trigger(HostEvent.DownloadLiveboardAsContinuousPDF);
377
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
378
+ expect.objectContaining({ type: 'downloadLiveboardAsContinuousPDF' }),
379
+ thoughtSpotHost,
380
+ expect.anything(),
381
+ );
382
+ });
383
+ });
384
+
385
+ test('returns null when !isRendered', async () => {
386
+ const lb = unrenderedLiveboard();
387
+ const result = await lb.trigger(HostEvent.DownloadLiveboardAsContinuousPDF);
388
+ expect(result).toBeNull();
389
+ });
390
+ });
391
+
392
+ // ---------------------------------------------------------------------------
393
+ // 23 – AIHighlights
394
+ // ---------------------------------------------------------------------------
395
+ describe('HostEvent.AIHighlights', () => {
396
+ test('postMessage type is AIHighlights (PascalCase)', async () => {
397
+ mockMessageChannel();
398
+ const { lb, iframe } = await renderLiveboard();
399
+ await executeAfterWait(() => {
400
+ lb.trigger(HostEvent.AIHighlights);
401
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
402
+ expect.objectContaining({ type: 'AIHighlights' }),
403
+ thoughtSpotHost,
404
+ expect.anything(),
405
+ );
406
+ });
407
+ });
408
+
409
+ test('returns null when !isRendered', async () => {
410
+ const lb = unrenderedLiveboard();
411
+ const result = await lb.trigger(HostEvent.AIHighlights);
412
+ expect(result).toBeNull();
413
+ });
414
+ });
415
+
416
+ // ---------------------------------------------------------------------------
417
+ // 24 – MakeACopy
418
+ // ---------------------------------------------------------------------------
419
+ describe('HostEvent.MakeACopy', () => {
420
+ test('postMessage type is makeACopy (camelCase)', async () => {
421
+ mockMessageChannel();
422
+ const { lb, iframe } = await renderLiveboard();
423
+ await executeAfterWait(() => {
424
+ lb.trigger(HostEvent.MakeACopy);
425
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
426
+ expect.objectContaining({ type: 'makeACopy' }),
427
+ thoughtSpotHost,
428
+ expect.anything(),
429
+ );
430
+ });
431
+ });
432
+
433
+ test('returns null when !isRendered', async () => {
434
+ const lb = unrenderedLiveboard();
435
+ const result = await lb.trigger(HostEvent.MakeACopy);
436
+ expect(result).toBeNull();
437
+ });
438
+ });
439
+
440
+ // ---------------------------------------------------------------------------
441
+ // 25 – Remove (maps to 'delete' string)
442
+ // ---------------------------------------------------------------------------
443
+ describe('HostEvent.Remove', () => {
444
+ test("postMessage type is 'delete' (NOT 'Remove')", async () => {
445
+ mockMessageChannel();
446
+ const { lb, iframe } = await renderLiveboard();
447
+ await executeAfterWait(() => {
448
+ lb.trigger(HostEvent.Remove);
449
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
450
+ expect.objectContaining({ type: 'delete' }),
451
+ thoughtSpotHost,
452
+ expect.anything(),
453
+ );
454
+ });
455
+ });
456
+
457
+ test('returns null when !isRendered', async () => {
458
+ const lb = unrenderedLiveboard();
459
+ const result = await lb.trigger(HostEvent.Remove);
460
+ expect(result).toBeNull();
461
+ });
462
+ });
463
+
464
+ // ---------------------------------------------------------------------------
465
+ // 26 – Explore
466
+ // ---------------------------------------------------------------------------
467
+ describe('HostEvent.Explore', () => {
468
+ test('postMessage type is explore (all lowercase)', async () => {
469
+ mockMessageChannel();
470
+ const { lb, iframe } = await renderLiveboard();
471
+ await executeAfterWait(() => {
472
+ lb.trigger(HostEvent.Explore, { vizId: 'v1' } as any);
473
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
474
+ expect.objectContaining({ type: 'explore' }),
475
+ thoughtSpotHost,
476
+ expect.anything(),
477
+ );
478
+ });
479
+ });
480
+
481
+ test('returns null when !isRendered', async () => {
482
+ const lb = unrenderedLiveboard();
483
+ const result = await lb.trigger(HostEvent.Explore, {} as any);
484
+ expect(result).toBeNull();
485
+ });
486
+ });
487
+
488
+ // ---------------------------------------------------------------------------
489
+ // 27 – CreateMonitor
490
+ // ---------------------------------------------------------------------------
491
+ describe('HostEvent.CreateMonitor', () => {
492
+ test('postMessage type is createMonitor (camelCase)', async () => {
493
+ mockMessageChannel();
494
+ const { lb, iframe } = await renderLiveboard();
495
+ await executeAfterWait(() => {
496
+ lb.trigger(HostEvent.CreateMonitor);
497
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
498
+ expect.objectContaining({ type: 'createMonitor' }),
499
+ thoughtSpotHost,
500
+ expect.anything(),
501
+ );
502
+ });
503
+ });
504
+
505
+ test('returns null when !isRendered', async () => {
506
+ const lb = unrenderedLiveboard();
507
+ const result = await lb.trigger(HostEvent.CreateMonitor);
508
+ expect(result).toBeNull();
509
+ });
510
+ });
511
+
512
+ // ---------------------------------------------------------------------------
513
+ // 28 – ManageMonitor
514
+ // ---------------------------------------------------------------------------
515
+ describe('HostEvent.ManageMonitor', () => {
516
+ test('postMessage type is manageMonitor (camelCase)', async () => {
517
+ mockMessageChannel();
518
+ const { lb, iframe } = await renderLiveboard();
519
+ await executeAfterWait(() => {
520
+ lb.trigger(HostEvent.ManageMonitor);
521
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
522
+ expect.objectContaining({ type: 'manageMonitor' }),
523
+ thoughtSpotHost,
524
+ expect.anything(),
525
+ );
526
+ });
527
+ });
528
+
529
+ test('returns null when !isRendered', async () => {
530
+ const lb = unrenderedLiveboard();
531
+ const result = await lb.trigger(HostEvent.ManageMonitor);
532
+ expect(result).toBeNull();
533
+ });
534
+ });
535
+
536
+ // ---------------------------------------------------------------------------
537
+ // 29 – Edit
538
+ // ---------------------------------------------------------------------------
539
+ describe('HostEvent.Edit', () => {
540
+ test('postMessage type is edit (all lowercase)', async () => {
541
+ mockMessageChannel();
542
+ const { lb, iframe } = await renderLiveboard();
543
+ await executeAfterWait(() => {
544
+ lb.trigger(HostEvent.Edit);
545
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
546
+ expect.objectContaining({ type: 'edit' }),
547
+ thoughtSpotHost,
548
+ expect.anything(),
549
+ );
550
+ });
551
+ });
552
+
553
+ test('returns null when !isRendered', async () => {
554
+ const lb = unrenderedLiveboard();
555
+ const result = await lb.trigger(HostEvent.Edit);
556
+ expect(result).toBeNull();
557
+ });
558
+ });
559
+
560
+ // ---------------------------------------------------------------------------
561
+ // 30 – CopyLink (maps to 'embedDocument')
562
+ // ---------------------------------------------------------------------------
563
+ describe('HostEvent.CopyLink', () => {
564
+ test("postMessage type is 'embedDocument' (NOT 'CopyLink')", async () => {
565
+ mockMessageChannel();
566
+ const { lb, iframe } = await renderLiveboard();
567
+ await executeAfterWait(() => {
568
+ lb.trigger(HostEvent.CopyLink);
569
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
570
+ expect.objectContaining({ type: 'embedDocument' }),
571
+ thoughtSpotHost,
572
+ expect.anything(),
573
+ );
574
+ });
575
+ });
576
+
577
+ test('returns null when !isRendered', async () => {
578
+ const lb = unrenderedLiveboard();
579
+ const result = await lb.trigger(HostEvent.CopyLink);
580
+ expect(result).toBeNull();
581
+ });
582
+ });
583
+
584
+ // ---------------------------------------------------------------------------
585
+ // 33 – ShowUnderlyingData
586
+ // ---------------------------------------------------------------------------
587
+ describe('HostEvent.ShowUnderlyingData', () => {
588
+ test('postMessage type is showUnderlyingData (camelCase)', async () => {
589
+ mockMessageChannel();
590
+ const { lb, iframe } = await renderLiveboard();
591
+ await executeAfterWait(() => {
592
+ lb.trigger(HostEvent.ShowUnderlyingData);
593
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
594
+ expect.objectContaining({ type: 'showUnderlyingData' }),
595
+ thoughtSpotHost,
596
+ expect.anything(),
597
+ );
598
+ });
599
+ });
600
+
601
+ test('returns null when !isRendered', async () => {
602
+ const lb = unrenderedLiveboard();
603
+ const result = await lb.trigger(HostEvent.ShowUnderlyingData);
604
+ expect(result).toBeNull();
605
+ });
606
+ });
607
+
608
+ // ---------------------------------------------------------------------------
609
+ // 34 – Delete (maps to 'onDeleteAnswer')
610
+ // ---------------------------------------------------------------------------
611
+ describe('HostEvent.Delete', () => {
612
+ test("postMessage type is 'onDeleteAnswer' (NOT 'delete' or 'Delete')", async () => {
613
+ mockMessageChannel();
614
+ const { lb, iframe } = await renderLiveboard();
615
+ await executeAfterWait(() => {
616
+ lb.trigger(HostEvent.Delete);
617
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
618
+ expect.objectContaining({ type: 'onDeleteAnswer' }),
619
+ thoughtSpotHost,
620
+ expect.anything(),
621
+ );
622
+ });
623
+ });
624
+
625
+ test('returns null when !isRendered', async () => {
626
+ const lb = unrenderedLiveboard();
627
+ const result = await lb.trigger(HostEvent.Delete);
628
+ expect(result).toBeNull();
629
+ });
630
+ });
631
+
632
+ // ---------------------------------------------------------------------------
633
+ // 35 – SpotIQAnalyze
634
+ // ---------------------------------------------------------------------------
635
+ describe('HostEvent.SpotIQAnalyze', () => {
636
+ test('postMessage type is spotIQAnalyze (camelCase)', async () => {
637
+ mockMessageChannel();
638
+ const { lb, iframe } = await renderLiveboard();
639
+ await executeAfterWait(() => {
640
+ lb.trigger(HostEvent.SpotIQAnalyze);
641
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
642
+ expect.objectContaining({ type: 'spotIQAnalyze' }),
643
+ thoughtSpotHost,
644
+ expect.anything(),
645
+ );
646
+ });
647
+ });
648
+
649
+ test('returns null when !isRendered', async () => {
650
+ const lb = unrenderedLiveboard();
651
+ const result = await lb.trigger(HostEvent.SpotIQAnalyze);
652
+ expect(result).toBeNull();
653
+ });
654
+ });
655
+
656
+ // ---------------------------------------------------------------------------
657
+ // 36 – Download (maps to 'downloadAsPng', same as DownloadAsPng)
658
+ // ---------------------------------------------------------------------------
659
+ describe('HostEvent.Download', () => {
660
+ test("postMessage type is 'downloadAsPng' (same string as DownloadAsPng)", async () => {
661
+ mockMessageChannel();
662
+ const { lb, iframe } = await renderLiveboard();
663
+ await executeAfterWait(() => {
664
+ lb.trigger(HostEvent.Download);
665
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
666
+ expect.objectContaining({ type: 'downloadAsPng' }),
667
+ thoughtSpotHost,
668
+ expect.anything(),
669
+ );
670
+ });
671
+ });
672
+
673
+ test('Download and DownloadAsPng share the identical string value', () => {
674
+ expect(HostEvent.Download).toBe(HostEvent.DownloadAsPng);
675
+ });
676
+
677
+ test('returns null when !isRendered', async () => {
678
+ const lb = unrenderedLiveboard();
679
+ const result = await lb.trigger(HostEvent.Download);
680
+ expect(result).toBeNull();
681
+ });
682
+ });
683
+
684
+ // ---------------------------------------------------------------------------
685
+ // 37 – DownloadAsPng
686
+ // ---------------------------------------------------------------------------
687
+ describe('HostEvent.DownloadAsPng', () => {
688
+ test('postMessage type is downloadAsPng', async () => {
689
+ mockMessageChannel();
690
+ const { lb, iframe } = await renderLiveboard();
691
+ await executeAfterWait(() => {
692
+ lb.trigger(HostEvent.DownloadAsPng);
693
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
694
+ expect.objectContaining({ type: 'downloadAsPng' }),
695
+ thoughtSpotHost,
696
+ expect.anything(),
697
+ );
698
+ });
699
+ });
700
+
701
+ test('returns null when !isRendered', async () => {
702
+ const lb = unrenderedLiveboard();
703
+ const result = await lb.trigger(HostEvent.DownloadAsPng);
704
+ expect(result).toBeNull();
705
+ });
706
+ });
707
+
708
+ // ---------------------------------------------------------------------------
709
+ // 39 – DownloadAsXlsx
710
+ // ---------------------------------------------------------------------------
711
+ describe('HostEvent.DownloadAsXlsx', () => {
712
+ test('postMessage type is downloadAsXLSX (capital X-L-S-X)', async () => {
713
+ mockMessageChannel();
714
+ const { lb, iframe } = await renderLiveboard();
715
+ await executeAfterWait(() => {
716
+ lb.trigger(HostEvent.DownloadAsXlsx);
717
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
718
+ expect.objectContaining({ type: 'downloadAsXLSX' }),
719
+ thoughtSpotHost,
720
+ expect.anything(),
721
+ );
722
+ });
723
+ });
724
+
725
+ test('returns null when !isRendered', async () => {
726
+ const lb = unrenderedLiveboard();
727
+ const result = await lb.trigger(HostEvent.DownloadAsXlsx);
728
+ expect(result).toBeNull();
729
+ });
730
+ });
731
+
732
+ // ---------------------------------------------------------------------------
733
+ // 40 – Share
734
+ // ---------------------------------------------------------------------------
735
+ describe('HostEvent.Share', () => {
736
+ test('postMessage type is share (all lowercase)', async () => {
737
+ mockMessageChannel();
738
+ const { lb, iframe } = await renderLiveboard();
739
+ await executeAfterWait(() => {
740
+ lb.trigger(HostEvent.Share);
741
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
742
+ expect.objectContaining({ type: 'share' }),
743
+ thoughtSpotHost,
744
+ expect.anything(),
745
+ );
746
+ });
747
+ });
748
+
749
+ test('returns null when !isRendered', async () => {
750
+ const lb = unrenderedLiveboard();
751
+ const result = await lb.trigger(HostEvent.Share);
752
+ expect(result).toBeNull();
753
+ });
754
+ });
755
+
756
+ // ---------------------------------------------------------------------------
757
+ // 42 – SyncToSheets
758
+ // ---------------------------------------------------------------------------
759
+ describe('HostEvent.SyncToSheets', () => {
760
+ test('postMessage type is sync-to-sheets (hyphenated, all lowercase)', async () => {
761
+ mockMessageChannel();
762
+ const { lb, iframe } = await renderLiveboard();
763
+ await executeAfterWait(() => {
764
+ lb.trigger(HostEvent.SyncToSheets);
765
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
766
+ expect.objectContaining({ type: 'sync-to-sheets' }),
767
+ thoughtSpotHost,
768
+ expect.anything(),
769
+ );
770
+ });
771
+ });
772
+
773
+ test('returns null when !isRendered', async () => {
774
+ const lb = unrenderedLiveboard();
775
+ const result = await lb.trigger(HostEvent.SyncToSheets);
776
+ expect(result).toBeNull();
777
+ });
778
+ });
779
+
780
+ // ---------------------------------------------------------------------------
781
+ // 43 – SyncToOtherApps
782
+ // ---------------------------------------------------------------------------
783
+ describe('HostEvent.SyncToOtherApps', () => {
784
+ test('postMessage type is sync-to-other-apps (hyphenated)', async () => {
785
+ mockMessageChannel();
786
+ const { lb, iframe } = await renderLiveboard();
787
+ await executeAfterWait(() => {
788
+ lb.trigger(HostEvent.SyncToOtherApps);
789
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
790
+ expect.objectContaining({ type: 'sync-to-other-apps' }),
791
+ thoughtSpotHost,
792
+ expect.anything(),
793
+ );
794
+ });
795
+ });
796
+
797
+ test('returns null when !isRendered', async () => {
798
+ const lb = unrenderedLiveboard();
799
+ const result = await lb.trigger(HostEvent.SyncToOtherApps);
800
+ expect(result).toBeNull();
801
+ });
802
+ });
803
+
804
+ // ---------------------------------------------------------------------------
805
+ // 44 – ManagePipelines
806
+ // ---------------------------------------------------------------------------
807
+ describe('HostEvent.ManagePipelines', () => {
808
+ test("postMessage type is 'manage-pipeline' (singular, hyphenated)", async () => {
809
+ mockMessageChannel();
810
+ const { lb, iframe } = await renderLiveboard();
811
+ await executeAfterWait(() => {
812
+ lb.trigger(HostEvent.ManagePipelines);
813
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
814
+ expect.objectContaining({ type: 'manage-pipeline' }),
815
+ thoughtSpotHost,
816
+ expect.anything(),
817
+ );
818
+ });
819
+ });
820
+
821
+ test('returns null when !isRendered', async () => {
822
+ const lb = unrenderedLiveboard();
823
+ const result = await lb.trigger(HostEvent.ManagePipelines);
824
+ expect(result).toBeNull();
825
+ });
826
+ });
827
+
828
+ // ---------------------------------------------------------------------------
829
+ // 45 – ResetSearch
830
+ // ---------------------------------------------------------------------------
831
+ describe('HostEvent.ResetSearch', () => {
832
+ test('postMessage type is resetSearch (camelCase)', async () => {
833
+ mockMessageChannel();
834
+ const { lb, iframe } = await renderLiveboard();
835
+ await executeAfterWait(() => {
836
+ lb.trigger(HostEvent.ResetSearch);
837
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
838
+ expect.objectContaining({ type: 'resetSearch' }),
839
+ thoughtSpotHost,
840
+ expect.anything(),
841
+ );
842
+ });
843
+ });
844
+
845
+ test('returns null when !isRendered', async () => {
846
+ const lb = unrenderedLiveboard();
847
+ const result = await lb.trigger(HostEvent.ResetSearch);
848
+ expect(result).toBeNull();
849
+ });
850
+ });
851
+
852
+ // ---------------------------------------------------------------------------
853
+ // 49 – SetVisibleTabs
854
+ // ---------------------------------------------------------------------------
855
+ describe('HostEvent.SetVisibleTabs', () => {
856
+ test("postMessage type is 'SetPinboardVisibleTabs' (NOT 'SetVisibleTabs')", async () => {
857
+ mockMessageChannel();
858
+ const { lb, iframe } = await renderLiveboard();
859
+ await executeAfterWait(() => {
860
+ lb.trigger(HostEvent.SetVisibleTabs, ['tab1'] as any);
861
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
862
+ expect.objectContaining({ type: 'SetPinboardVisibleTabs' }),
863
+ thoughtSpotHost,
864
+ expect.anything(),
865
+ );
866
+ });
867
+ });
868
+
869
+ test('returns null when !isRendered', async () => {
870
+ const lb = unrenderedLiveboard();
871
+ const result = await lb.trigger(HostEvent.SetVisibleTabs, [] as any);
872
+ expect(result).toBeNull();
873
+ });
874
+ });
875
+
876
+ // ---------------------------------------------------------------------------
877
+ // 50 – SetHiddenTabs
878
+ // ---------------------------------------------------------------------------
879
+ describe('HostEvent.SetHiddenTabs', () => {
880
+ test("postMessage type is 'SetPinboardHiddenTabs' (NOT 'SetHiddenTabs')", async () => {
881
+ mockMessageChannel();
882
+ const { lb, iframe } = await renderLiveboard();
883
+ await executeAfterWait(() => {
884
+ lb.trigger(HostEvent.SetHiddenTabs, ['tab1'] as any);
885
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
886
+ expect.objectContaining({ type: 'SetPinboardHiddenTabs' }),
887
+ thoughtSpotHost,
888
+ expect.anything(),
889
+ );
890
+ });
891
+ });
892
+
893
+ test('returns null when !isRendered', async () => {
894
+ const lb = unrenderedLiveboard();
895
+ const result = await lb.trigger(HostEvent.SetHiddenTabs, [] as any);
896
+ expect(result).toBeNull();
897
+ });
898
+ });
899
+
900
+ // ---------------------------------------------------------------------------
901
+ // 52 – AskSage
902
+ // ---------------------------------------------------------------------------
903
+ describe('HostEvent.AskSage', () => {
904
+ test('postMessage type is AskSage (PascalCase)', async () => {
905
+ mockMessageChannel();
906
+ const { lb, iframe } = await renderLiveboard();
907
+ await executeAfterWait(() => {
908
+ lb.trigger(HostEvent.AskSage);
909
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
910
+ expect.objectContaining({ type: 'AskSage' }),
911
+ thoughtSpotHost,
912
+ expect.anything(),
913
+ );
914
+ });
915
+ });
916
+
917
+ test('returns null when !isRendered', async () => {
918
+ const lb = unrenderedLiveboard();
919
+ const result = await lb.trigger(HostEvent.AskSage);
920
+ expect(result).toBeNull();
921
+ });
922
+ });
923
+
924
+ // ---------------------------------------------------------------------------
925
+ // 53 – UpdateCrossFilter
926
+ // ---------------------------------------------------------------------------
927
+ describe('HostEvent.UpdateCrossFilter', () => {
928
+ test('postMessage type is UpdateCrossFilter (PascalCase)', async () => {
929
+ mockMessageChannel();
930
+ const { lb, iframe } = await renderLiveboard();
931
+ await executeAfterWait(() => {
932
+ lb.trigger(HostEvent.UpdateCrossFilter, {} as any);
933
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
934
+ expect.objectContaining({ type: 'UpdateCrossFilter' }),
935
+ thoughtSpotHost,
936
+ expect.anything(),
937
+ );
938
+ });
939
+ });
940
+
941
+ test('returns null when !isRendered', async () => {
942
+ const lb = unrenderedLiveboard();
943
+ const result = await lb.trigger(HostEvent.UpdateCrossFilter, {} as any);
944
+ expect(result).toBeNull();
945
+ });
946
+ });
947
+
948
+ // ---------------------------------------------------------------------------
949
+ // 56 – UpdateParameters
950
+ // ---------------------------------------------------------------------------
951
+ describe('HostEvent.UpdateParameters', () => {
952
+ test('postMessage type is UpdateParameters (PascalCase)', async () => {
953
+ mockMessageChannel();
954
+ const { lb, iframe } = await renderLiveboard();
955
+ await executeAfterWait(() => {
956
+ lb.trigger(HostEvent.UpdateParameters, {} as any);
957
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
958
+ expect.objectContaining({ type: 'UpdateParameters' }),
959
+ thoughtSpotHost,
960
+ expect.anything(),
961
+ );
962
+ });
963
+ });
964
+
965
+ test('returns null when !isRendered', async () => {
966
+ const lb = unrenderedLiveboard();
967
+ const result = await lb.trigger(HostEvent.UpdateParameters, {} as any);
968
+ expect(result).toBeNull();
969
+ });
970
+ });
971
+
972
+ // ---------------------------------------------------------------------------
973
+ // 60 – SelectPersonalizedView (maps to British spelling)
974
+ // ---------------------------------------------------------------------------
975
+ describe('HostEvent.SelectPersonalizedView', () => {
976
+ test("postMessage type is 'SelectPersonalisedView' (British spelling)", async () => {
977
+ mockMessageChannel();
978
+ const { lb, iframe } = await renderLiveboard();
979
+ await executeAfterWait(() => {
980
+ lb.trigger(HostEvent.SelectPersonalizedView, {} as any);
981
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
982
+ expect.objectContaining({ type: 'SelectPersonalisedView' }),
983
+ thoughtSpotHost,
984
+ expect.anything(),
985
+ );
986
+ });
987
+ });
988
+
989
+ test('returns null when !isRendered', async () => {
990
+ const lb = unrenderedLiveboard();
991
+ const result = await lb.trigger(HostEvent.SelectPersonalizedView, {} as any);
992
+ expect(result).toBeNull();
993
+ });
994
+ });
995
+
996
+ // ---------------------------------------------------------------------------
997
+ // 64 – TransformTableVizData
998
+ // ---------------------------------------------------------------------------
999
+ describe('HostEvent.TransformTableVizData', () => {
1000
+ test('postMessage type is TransformTableVizData (PascalCase)', async () => {
1001
+ mockMessageChannel();
1002
+ const { lb, iframe } = await renderLiveboard();
1003
+ await executeAfterWait(() => {
1004
+ lb.trigger(HostEvent.TransformTableVizData, {} as any);
1005
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1006
+ expect.objectContaining({ type: 'TransformTableVizData' }),
1007
+ thoughtSpotHost,
1008
+ expect.anything(),
1009
+ );
1010
+ });
1011
+ });
1012
+
1013
+ test('returns null when !isRendered', async () => {
1014
+ const lb = unrenderedLiveboard();
1015
+ const result = await lb.trigger(HostEvent.TransformTableVizData, {} as any);
1016
+ expect(result).toBeNull();
1017
+ });
1018
+ });
1019
+
1020
+ // ---------------------------------------------------------------------------
1021
+ // 65 – SpotterSearch
1022
+ // ---------------------------------------------------------------------------
1023
+ describe('HostEvent.SpotterSearch', () => {
1024
+ test('postMessage type is SpotterSearch (PascalCase)', async () => {
1025
+ mockMessageChannel();
1026
+ const { lb, iframe } = await renderLiveboard();
1027
+ await executeAfterWait(() => {
1028
+ lb.trigger(HostEvent.SpotterSearch, {} as any);
1029
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1030
+ expect.objectContaining({ type: 'SpotterSearch' }),
1031
+ thoughtSpotHost,
1032
+ expect.anything(),
1033
+ );
1034
+ });
1035
+ });
1036
+
1037
+ test('returns null when !isRendered', async () => {
1038
+ const lb = unrenderedLiveboard();
1039
+ const result = await lb.trigger(HostEvent.SpotterSearch, {} as any);
1040
+ expect(result).toBeNull();
1041
+ });
1042
+ });
1043
+
1044
+ // ---------------------------------------------------------------------------
1045
+ // 66 – EditLastPrompt
1046
+ // ---------------------------------------------------------------------------
1047
+ describe('HostEvent.EditLastPrompt', () => {
1048
+ test('postMessage type is EditLastPrompt (PascalCase)', async () => {
1049
+ mockMessageChannel();
1050
+ const { lb, iframe } = await renderLiveboard();
1051
+ await executeAfterWait(() => {
1052
+ lb.trigger(HostEvent.EditLastPrompt, {} as any);
1053
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1054
+ expect.objectContaining({ type: 'EditLastPrompt' }),
1055
+ thoughtSpotHost,
1056
+ expect.anything(),
1057
+ );
1058
+ });
1059
+ });
1060
+
1061
+ test('returns null when !isRendered', async () => {
1062
+ const lb = unrenderedLiveboard();
1063
+ const result = await lb.trigger(HostEvent.EditLastPrompt, {} as any);
1064
+ expect(result).toBeNull();
1065
+ });
1066
+ });
1067
+
1068
+ // ---------------------------------------------------------------------------
1069
+ // 67 – PreviewSpotterData
1070
+ // ---------------------------------------------------------------------------
1071
+ describe('HostEvent.PreviewSpotterData', () => {
1072
+ test('postMessage type is PreviewSpotterData (PascalCase)', async () => {
1073
+ mockMessageChannel();
1074
+ const { lb, iframe } = await renderLiveboard();
1075
+ await executeAfterWait(() => {
1076
+ lb.trigger(HostEvent.PreviewSpotterData, {} as any);
1077
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1078
+ expect.objectContaining({ type: 'PreviewSpotterData' }),
1079
+ thoughtSpotHost,
1080
+ expect.anything(),
1081
+ );
1082
+ });
1083
+ });
1084
+
1085
+ test('returns null when !isRendered', async () => {
1086
+ const lb = unrenderedLiveboard();
1087
+ const result = await lb.trigger(HostEvent.PreviewSpotterData, {} as any);
1088
+ expect(result).toBeNull();
1089
+ });
1090
+ });
1091
+
1092
+ // ---------------------------------------------------------------------------
1093
+ // 68 – AddToCoaching
1094
+ // ---------------------------------------------------------------------------
1095
+ describe('HostEvent.AddToCoaching', () => {
1096
+ test('postMessage type is addToCoaching (camelCase)', async () => {
1097
+ mockMessageChannel();
1098
+ const { lb, iframe } = await renderLiveboard();
1099
+ await executeAfterWait(() => {
1100
+ lb.trigger(HostEvent.AddToCoaching, {} as any);
1101
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1102
+ expect.objectContaining({ type: 'addToCoaching' }),
1103
+ thoughtSpotHost,
1104
+ expect.anything(),
1105
+ );
1106
+ });
1107
+ });
1108
+
1109
+ test('returns null when !isRendered', async () => {
1110
+ const lb = unrenderedLiveboard();
1111
+ const result = await lb.trigger(HostEvent.AddToCoaching, {} as any);
1112
+ expect(result).toBeNull();
1113
+ });
1114
+ });
1115
+
1116
+ // ---------------------------------------------------------------------------
1117
+ // 69 – DataModelInstructions
1118
+ // ---------------------------------------------------------------------------
1119
+ describe('HostEvent.DataModelInstructions', () => {
1120
+ test('postMessage type is DataModelInstructions (PascalCase)', async () => {
1121
+ mockMessageChannel();
1122
+ const { lb, iframe } = await renderLiveboard();
1123
+ await executeAfterWait(() => {
1124
+ lb.trigger(HostEvent.DataModelInstructions, {} as any);
1125
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1126
+ expect.objectContaining({ type: 'DataModelInstructions' }),
1127
+ thoughtSpotHost,
1128
+ expect.anything(),
1129
+ );
1130
+ });
1131
+ });
1132
+
1133
+ test('returns null when !isRendered', async () => {
1134
+ const lb = unrenderedLiveboard();
1135
+ const result = await lb.trigger(HostEvent.DataModelInstructions, {} as any);
1136
+ expect(result).toBeNull();
1137
+ });
1138
+ });
1139
+
1140
+ // ---------------------------------------------------------------------------
1141
+ // 70 – ResetSpotterConversation
1142
+ // ---------------------------------------------------------------------------
1143
+ describe('HostEvent.ResetSpotterConversation', () => {
1144
+ test('postMessage type is ResetSpotterConversation (PascalCase)', async () => {
1145
+ mockMessageChannel();
1146
+ const { lb, iframe } = await renderLiveboard();
1147
+ await executeAfterWait(() => {
1148
+ lb.trigger(HostEvent.ResetSpotterConversation, {} as any);
1149
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1150
+ expect.objectContaining({ type: 'ResetSpotterConversation' }),
1151
+ thoughtSpotHost,
1152
+ expect.anything(),
1153
+ );
1154
+ });
1155
+ });
1156
+
1157
+ test('returns null when !isRendered', async () => {
1158
+ const lb = unrenderedLiveboard();
1159
+ const result = await lb.trigger(HostEvent.ResetSpotterConversation, {} as any);
1160
+ expect(result).toBeNull();
1161
+ });
1162
+ });
1163
+
1164
+ // ---------------------------------------------------------------------------
1165
+ // 71 – DeleteLastPrompt
1166
+ // ---------------------------------------------------------------------------
1167
+ describe('HostEvent.DeleteLastPrompt', () => {
1168
+ test('postMessage type is DeleteLastPrompt (PascalCase)', async () => {
1169
+ mockMessageChannel();
1170
+ const { lb, iframe } = await renderLiveboard();
1171
+ await executeAfterWait(() => {
1172
+ lb.trigger(HostEvent.DeleteLastPrompt, {} as any);
1173
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1174
+ expect.objectContaining({ type: 'DeleteLastPrompt' }),
1175
+ thoughtSpotHost,
1176
+ expect.anything(),
1177
+ );
1178
+ });
1179
+ });
1180
+
1181
+ test('returns null when !isRendered', async () => {
1182
+ const lb = unrenderedLiveboard();
1183
+ const result = await lb.trigger(HostEvent.DeleteLastPrompt, {} as any);
1184
+ expect(result).toBeNull();
1185
+ });
1186
+ });
1187
+
1188
+ // ---------------------------------------------------------------------------
1189
+ // 72 – AnswerChartSwitcher
1190
+ // ---------------------------------------------------------------------------
1191
+ describe('HostEvent.AnswerChartSwitcher', () => {
1192
+ test('postMessage type is answerChartSwitcher (camelCase)', async () => {
1193
+ mockMessageChannel();
1194
+ const { lb, iframe } = await renderLiveboard();
1195
+ await executeAfterWait(() => {
1196
+ lb.trigger(HostEvent.AnswerChartSwitcher, {} as any);
1197
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1198
+ expect.objectContaining({ type: 'answerChartSwitcher' }),
1199
+ thoughtSpotHost,
1200
+ expect.anything(),
1201
+ );
1202
+ });
1203
+ });
1204
+
1205
+ test('returns null when !isRendered', async () => {
1206
+ const lb = unrenderedLiveboard();
1207
+ const result = await lb.trigger(HostEvent.AnswerChartSwitcher, {} as any);
1208
+ expect(result).toBeNull();
1209
+ });
1210
+ });
1211
+
1212
+ // ---------------------------------------------------------------------------
1213
+ // 75 – AskSpotter
1214
+ // ---------------------------------------------------------------------------
1215
+ describe('HostEvent.AskSpotter', () => {
1216
+ test('postMessage type is AskSpotter (PascalCase)', async () => {
1217
+ mockMessageChannel();
1218
+ const { lb, iframe } = await renderLiveboard();
1219
+ await executeAfterWait(() => {
1220
+ lb.trigger(HostEvent.AskSpotter, {} as any);
1221
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1222
+ expect.objectContaining({ type: 'AskSpotter' }),
1223
+ thoughtSpotHost,
1224
+ expect.anything(),
1225
+ );
1226
+ });
1227
+ });
1228
+
1229
+ test('returns null when !isRendered', async () => {
1230
+ const lb = unrenderedLiveboard();
1231
+ const result = await lb.trigger(HostEvent.AskSpotter, {} as any);
1232
+ expect(result).toBeNull();
1233
+ });
1234
+ });
1235
+
1236
+ // ---------------------------------------------------------------------------
1237
+ // 78 – StartNewSpotterConversation
1238
+ // ---------------------------------------------------------------------------
1239
+ describe('HostEvent.StartNewSpotterConversation', () => {
1240
+ test('postMessage type is StartNewSpotterConversation (PascalCase)', async () => {
1241
+ mockMessageChannel();
1242
+ const { lb, iframe } = await renderLiveboard();
1243
+ await executeAfterWait(() => {
1244
+ lb.trigger(HostEvent.StartNewSpotterConversation, {} as any);
1245
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1246
+ expect.objectContaining({ type: 'StartNewSpotterConversation' }),
1247
+ thoughtSpotHost,
1248
+ expect.anything(),
1249
+ );
1250
+ });
1251
+ });
1252
+
1253
+ test('returns null when !isRendered', async () => {
1254
+ const lb = unrenderedLiveboard();
1255
+ const result = await lb.trigger(HostEvent.StartNewSpotterConversation, {} as any);
1256
+ expect(result).toBeNull();
1257
+ });
1258
+ });
1259
+
1260
+ // ---------------------------------------------------------------------------
1261
+ // 80 – SendTestScheduleEmail
1262
+ // ---------------------------------------------------------------------------
1263
+ describe('HostEvent.SendTestScheduleEmail', () => {
1264
+ test('postMessage type is sendTestScheduleEmail (camelCase)', async () => {
1265
+ mockMessageChannel();
1266
+ const { lb, iframe } = await renderLiveboard();
1267
+ await executeAfterWait(() => {
1268
+ lb.trigger(HostEvent.SendTestScheduleEmail, {} as any);
1269
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1270
+ expect.objectContaining({ type: 'sendTestScheduleEmail' }),
1271
+ thoughtSpotHost,
1272
+ expect.anything(),
1273
+ );
1274
+ });
1275
+ });
1276
+
1277
+ test('returns null when !isRendered', async () => {
1278
+ const lb = unrenderedLiveboard();
1279
+ const result = await lb.trigger(HostEvent.SendTestScheduleEmail, {} as any);
1280
+ expect(result).toBeNull();
1281
+ });
1282
+ });
1283
+
1284
+ // ---------------------------------------------------------------------------
1285
+ // 81 – SpotterVizSendUserMessage
1286
+ // ---------------------------------------------------------------------------
1287
+ describe('HostEvent.SpotterVizSendUserMessage', () => {
1288
+ test('postMessage type is SpotterVizSendUserMessage (PascalCase)', async () => {
1289
+ mockMessageChannel();
1290
+ const { lb, iframe } = await renderLiveboard();
1291
+ await executeAfterWait(() => {
1292
+ lb.trigger(HostEvent.SpotterVizSendUserMessage, {} as any);
1293
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1294
+ expect.objectContaining({ type: 'SpotterVizSendUserMessage' }),
1295
+ thoughtSpotHost,
1296
+ expect.anything(),
1297
+ );
1298
+ });
1299
+ });
1300
+
1301
+ test('returns null when !isRendered', async () => {
1302
+ const lb = unrenderedLiveboard();
1303
+ const result = await lb.trigger(HostEvent.SpotterVizSendUserMessage, {} as any);
1304
+ expect(result).toBeNull();
1305
+ });
1306
+ });
1307
+
1308
+ // ---------------------------------------------------------------------------
1309
+ // 82 – InitSpotterVizConversation
1310
+ // ---------------------------------------------------------------------------
1311
+ describe('HostEvent.InitSpotterVizConversation', () => {
1312
+ test('postMessage type is InitSpotterVizConversation (PascalCase)', async () => {
1313
+ mockMessageChannel();
1314
+ const { lb, iframe } = await renderLiveboard();
1315
+ await executeAfterWait(() => {
1316
+ lb.trigger(HostEvent.InitSpotterVizConversation, {} as any);
1317
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1318
+ expect.objectContaining({ type: 'InitSpotterVizConversation' }),
1319
+ thoughtSpotHost,
1320
+ expect.anything(),
1321
+ );
1322
+ });
1323
+ });
1324
+
1325
+ test('returns null when !isRendered', async () => {
1326
+ const lb = unrenderedLiveboard();
1327
+ const result = await lb.trigger(HostEvent.InitSpotterVizConversation, {} as any);
1328
+ expect(result).toBeNull();
1329
+ });
1330
+ });
1331
+
1332
+ // ---------------------------------------------------------------------------
1333
+ // 83 – RefreshLiveboardBrowserCache
1334
+ // ---------------------------------------------------------------------------
1335
+ describe('HostEvent.RefreshLiveboardBrowserCache', () => {
1336
+ test('postMessage type is refreshLiveboardBrowserCache (camelCase)', async () => {
1337
+ mockMessageChannel();
1338
+ const { lb, iframe } = await renderLiveboard();
1339
+ await executeAfterWait(() => {
1340
+ lb.trigger(HostEvent.RefreshLiveboardBrowserCache, {} as any);
1341
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1342
+ expect.objectContaining({ type: 'refreshLiveboardBrowserCache' }),
1343
+ thoughtSpotHost,
1344
+ expect.anything(),
1345
+ );
1346
+ });
1347
+ });
1348
+
1349
+ test('returns null when !isRendered', async () => {
1350
+ const lb = unrenderedLiveboard();
1351
+ const result = await lb.trigger(HostEvent.RefreshLiveboardBrowserCache, {} as any);
1352
+ expect(result).toBeNull();
1353
+ });
1354
+ });
1355
+
1356
+ // ---------------------------------------------------------------------------
1357
+ // vizId injection tests — verifies LiveboardEmbed injects viewConfig.vizId
1358
+ // ---------------------------------------------------------------------------
1359
+ describe('vizId injection on LiveboardEmbed trigger (object payload)', () => {
1360
+ const eventsWithVizId: Array<[string, HostEvent, any]> = [
1361
+ ['Filter', HostEvent.Filter, { columnName: 'c' }],
1362
+ ['OpenFilter', HostEvent.OpenFilter, { columnId: 'c1' }],
1363
+ ['AddColumns', HostEvent.AddColumns, { columnIds: ['c1'] }],
1364
+ ['RemoveColumn', HostEvent.RemoveColumn, { columnId: 'c1' }],
1365
+ ['LiveboardInfo', HostEvent.LiveboardInfo, {}],
1366
+ ['Schedule', HostEvent.Schedule, {}],
1367
+ ['SchedulesList', HostEvent.SchedulesList, {}],
1368
+ ['ExportTML', HostEvent.ExportTML, {}],
1369
+ ['EditTML', HostEvent.EditTML, {}],
1370
+ ['UpdateTML', HostEvent.UpdateTML, {}],
1371
+ ['DownloadAsPdf', HostEvent.DownloadAsPdf, {}],
1372
+ ['DownloadLiveboardAsContinuousPDF', HostEvent.DownloadLiveboardAsContinuousPDF, {}],
1373
+ ['AIHighlights', HostEvent.AIHighlights, {}],
1374
+ ['MakeACopy', HostEvent.MakeACopy, {}],
1375
+ ['Remove', HostEvent.Remove, {}],
1376
+ ['Explore', HostEvent.Explore, {}],
1377
+ ['CreateMonitor', HostEvent.CreateMonitor, {}],
1378
+ ['ManageMonitor', HostEvent.ManageMonitor, {}],
1379
+ ['Edit', HostEvent.Edit, {}],
1380
+ ['CopyLink', HostEvent.CopyLink, {}],
1381
+ ['ShowUnderlyingData', HostEvent.ShowUnderlyingData, {}],
1382
+ ['Delete', HostEvent.Delete, {}],
1383
+ ['SpotIQAnalyze', HostEvent.SpotIQAnalyze, {}],
1384
+ ['Download', HostEvent.Download, {}],
1385
+ ['DownloadAsPng', HostEvent.DownloadAsPng, {}],
1386
+ ['DownloadAsXlsx', HostEvent.DownloadAsXlsx, {}],
1387
+ ['Share', HostEvent.Share, {}],
1388
+ ['SyncToSheets', HostEvent.SyncToSheets, {}],
1389
+ ['SyncToOtherApps', HostEvent.SyncToOtherApps, {}],
1390
+ ['ManagePipelines', HostEvent.ManagePipelines, {}],
1391
+ ['SelectPersonalizedView', HostEvent.SelectPersonalizedView, {}],
1392
+ ['UpdateCrossFilter', HostEvent.UpdateCrossFilter, {}],
1393
+ ['UpdateParameters', HostEvent.UpdateParameters, {}],
1394
+ ['TransformTableVizData', HostEvent.TransformTableVizData, {}],
1395
+ ['PreviewSpotterData', HostEvent.PreviewSpotterData, {}],
1396
+ ['AnswerChartSwitcher', HostEvent.AnswerChartSwitcher, {}],
1397
+ ];
1398
+
1399
+ test.each(eventsWithVizId)(
1400
+ '%s: viewConfig.vizId is injected into the payload as data.vizId',
1401
+ async (name, event, payload) => {
1402
+ mockMessageChannel();
1403
+ const { lb, iframe } = await renderLiveboard({ vizId });
1404
+ await executeAfterWait(() => {
1405
+ lb.trigger(event, payload as any);
1406
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1407
+ expect.objectContaining({
1408
+ data: expect.objectContaining({ vizId }),
1409
+ }),
1410
+ thoughtSpotHost,
1411
+ expect.anything(),
1412
+ );
1413
+ });
1414
+ },
1415
+ );
1416
+ });
1417
+
1418
+ // ---------------------------------------------------------------------------
1419
+ // Array payload vizId injection — vizId added as a non-indexed property on the
1420
+ // array object (array.length remains unchanged)
1421
+ // ---------------------------------------------------------------------------
1422
+ describe('vizId injection on LiveboardEmbed trigger (array payload)', () => {
1423
+ const arrayEvents: Array<[string, HostEvent]> = [
1424
+ ['UpdateRuntimeFilters', HostEvent.UpdateRuntimeFilters],
1425
+ ['SetVisibleTabs', HostEvent.SetVisibleTabs],
1426
+ ['SetHiddenTabs', HostEvent.SetHiddenTabs],
1427
+ ];
1428
+
1429
+ test.each(arrayEvents)(
1430
+ '%s: vizId is added as a non-indexed property on the array — array.length unchanged',
1431
+ async (name, event) => {
1432
+ mockMessageChannel();
1433
+ const { lb, iframe } = await renderLiveboard({ vizId });
1434
+ const arrayPayload = ['item1', 'item2'];
1435
+ await executeAfterWait(() => {
1436
+ lb.trigger(event, arrayPayload as any);
1437
+ const call = (iframe.contentWindow.postMessage as jest.Mock).mock.calls[0];
1438
+ const sentData = call[0].data;
1439
+ // vizId is set as a property directly on the array object
1440
+ expect((sentData as any).vizId).toBe(vizId);
1441
+ // array.length is unchanged
1442
+ expect(sentData.length).toBe(2);
1443
+ });
1444
+ },
1445
+ );
1446
+
1447
+ test.each(arrayEvents)(
1448
+ '%s: without viewConfig.vizId — array payload is sent as-is (no vizId property)',
1449
+ async (name, event) => {
1450
+ mockMessageChannel();
1451
+ const { lb, iframe } = await renderLiveboard(); // no vizId
1452
+ const arrayPayload = ['item1'];
1453
+ await executeAfterWait(() => {
1454
+ lb.trigger(event, arrayPayload as any);
1455
+ const call = (iframe.contentWindow.postMessage as jest.Mock).mock.calls[0];
1456
+ const sentData = call[0].data;
1457
+ expect((sentData as any).vizId).toBeUndefined();
1458
+ });
1459
+ },
1460
+ );
1461
+ });
1462
+
1463
+ // ---------------------------------------------------------------------------
1464
+ // UpdateRuntimeFilters — without vizId, array payload sent as-is
1465
+ // ---------------------------------------------------------------------------
1466
+ describe('HostEvent.UpdateRuntimeFilters — additional cases', () => {
1467
+ test('without viewConfig.vizId — RuntimeFilter array payload sent as-is', async () => {
1468
+ mockMessageChannel();
1469
+ const { lb, iframe } = await renderLiveboard(); // no vizId
1470
+ const filters = [{ columnName: 'col', operator: 'EQ', values: ['v1'] }];
1471
+ await executeAfterWait(() => {
1472
+ lb.trigger(HostEvent.UpdateRuntimeFilters, filters as any);
1473
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1474
+ expect.objectContaining({ type: 'UpdateRuntimeFilters' }),
1475
+ thoughtSpotHost,
1476
+ expect.anything(),
1477
+ );
1478
+ });
1479
+ });
1480
+ });
1481
+
1482
+ // ---------------------------------------------------------------------------
1483
+ // iframe MessageChannel response resolves the trigger promise
1484
+ // ---------------------------------------------------------------------------
1485
+ describe('trigger promise resolves with iframe response', () => {
1486
+ const responseEvents: Array<[string, HostEvent, any]> = [
1487
+ ['AddColumns', HostEvent.AddColumns, { columnIds: ['c1'] }],
1488
+ ['RemoveColumn', HostEvent.RemoveColumn, { columnId: 'c1' }],
1489
+ ['UpdateTML', HostEvent.UpdateTML, {}],
1490
+ ['AIHighlights', HostEvent.AIHighlights, {}],
1491
+ ['AskSage', HostEvent.AskSage, {}],
1492
+ ['AskSpotter', HostEvent.AskSpotter, {}],
1493
+ ['TransformTableVizData', HostEvent.TransformTableVizData, {}],
1494
+ ['SpotterSearch', HostEvent.SpotterSearch, {}],
1495
+ ];
1496
+
1497
+ test.each(responseEvents)(
1498
+ '%s: trigger promise resolves with the full responseData from iframe',
1499
+ async (name, event, payload) => {
1500
+ mockMessageChannel();
1501
+ const { lb } = await renderLiveboard();
1502
+ const responseData = { test: 'data-from-iframe' };
1503
+
1504
+ // Start the trigger (which internally posts to the MessageChannel)
1505
+ const triggerPromise = lb.trigger(event, payload as any);
1506
+
1507
+ // Simulate the iframe responding via port1
1508
+ messageChannelMock.port1.onmessage({ data: responseData });
1509
+
1510
+ const result = await triggerPromise;
1511
+ expect(result).toEqual(responseData);
1512
+ },
1513
+ );
1514
+ });
1515
+
1516
+ // ---------------------------------------------------------------------------
1517
+ // Context parameter forwarding
1518
+ // ---------------------------------------------------------------------------
1519
+ describe('context parameter forwarded in postMessage payload', () => {
1520
+ test('ExportTML: context is forwarded as context field in postMessage', async () => {
1521
+ mockMessageChannel();
1522
+ const { lb, iframe } = await renderLiveboard();
1523
+ await executeAfterWait(() => {
1524
+ lb.trigger(HostEvent.ExportTML, {} as any, 'Liveboard' as any);
1525
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1526
+ expect.objectContaining({ context: 'Liveboard' }),
1527
+ thoughtSpotHost,
1528
+ expect.anything(),
1529
+ );
1530
+ });
1531
+ });
1532
+
1533
+ test('EditTML: context is forwarded as context field in postMessage', async () => {
1534
+ mockMessageChannel();
1535
+ const { lb, iframe } = await renderLiveboard();
1536
+ await executeAfterWait(() => {
1537
+ lb.trigger(HostEvent.EditTML, {} as any, 'Search' as any);
1538
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1539
+ expect.objectContaining({ context: 'Search' }),
1540
+ thoughtSpotHost,
1541
+ expect.anything(),
1542
+ );
1543
+ });
1544
+ });
1545
+
1546
+ test('DownloadAsPdf: context is forwarded as context field in postMessage', async () => {
1547
+ mockMessageChannel();
1548
+ const { lb, iframe } = await renderLiveboard();
1549
+ await executeAfterWait(() => {
1550
+ lb.trigger(HostEvent.DownloadAsPdf, {} as any, 'Liveboard' as any);
1551
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1552
+ expect.objectContaining({ context: 'Liveboard' }),
1553
+ thoughtSpotHost,
1554
+ expect.anything(),
1555
+ );
1556
+ });
1557
+ });
1558
+
1559
+ test('MakeACopy: context is forwarded as context field in postMessage', async () => {
1560
+ mockMessageChannel();
1561
+ const { lb, iframe } = await renderLiveboard();
1562
+ await executeAfterWait(() => {
1563
+ lb.trigger(HostEvent.MakeACopy, {} as any, 'Answer' as any);
1564
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1565
+ expect.objectContaining({ context: 'Answer' }),
1566
+ thoughtSpotHost,
1567
+ expect.anything(),
1568
+ );
1569
+ });
1570
+ });
1571
+
1572
+ test('CreateMonitor: context is forwarded as context field in postMessage', async () => {
1573
+ mockMessageChannel();
1574
+ const { lb, iframe } = await renderLiveboard();
1575
+ await executeAfterWait(() => {
1576
+ lb.trigger(HostEvent.CreateMonitor, {} as any, 'Liveboard' as any);
1577
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1578
+ expect.objectContaining({ context: 'Liveboard' }),
1579
+ thoughtSpotHost,
1580
+ expect.anything(),
1581
+ );
1582
+ });
1583
+ });
1584
+
1585
+ test('ShowUnderlyingData: context is forwarded as context field in postMessage', async () => {
1586
+ mockMessageChannel();
1587
+ const { lb, iframe } = await renderLiveboard();
1588
+ await executeAfterWait(() => {
1589
+ lb.trigger(HostEvent.ShowUnderlyingData, {} as any, 'Liveboard' as any);
1590
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1591
+ expect.objectContaining({ context: 'Liveboard' }),
1592
+ thoughtSpotHost,
1593
+ expect.anything(),
1594
+ );
1595
+ });
1596
+ });
1597
+
1598
+ test('SpotIQAnalyze: context is forwarded as context field in postMessage', async () => {
1599
+ mockMessageChannel();
1600
+ const { lb, iframe } = await renderLiveboard();
1601
+ await executeAfterWait(() => {
1602
+ lb.trigger(HostEvent.SpotIQAnalyze, {} as any, 'Answer' as any);
1603
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1604
+ expect.objectContaining({ context: 'Answer' }),
1605
+ thoughtSpotHost,
1606
+ expect.anything(),
1607
+ );
1608
+ });
1609
+ });
1610
+
1611
+ test('Edit: context is forwarded as context field in postMessage', async () => {
1612
+ mockMessageChannel();
1613
+ const { lb, iframe } = await renderLiveboard();
1614
+ await executeAfterWait(() => {
1615
+ lb.trigger(HostEvent.Edit, {} as any, 'Liveboard' as any);
1616
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1617
+ expect.objectContaining({ context: 'Liveboard' }),
1618
+ thoughtSpotHost,
1619
+ expect.anything(),
1620
+ );
1621
+ });
1622
+ });
1623
+
1624
+ test('CopyLink: context is forwarded as context field in postMessage', async () => {
1625
+ mockMessageChannel();
1626
+ const { lb, iframe } = await renderLiveboard();
1627
+ await executeAfterWait(() => {
1628
+ lb.trigger(HostEvent.CopyLink, {} as any, 'Liveboard' as any);
1629
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1630
+ expect.objectContaining({ context: 'Liveboard' }),
1631
+ thoughtSpotHost,
1632
+ expect.anything(),
1633
+ );
1634
+ });
1635
+ });
1636
+ });
1637
+
1638
+ // ---------------------------------------------------------------------------
1639
+ // SetVisibleVizs — array vizId injection (NOT in the parametrized arrayEvents)
1640
+ // ---------------------------------------------------------------------------
1641
+ describe('HostEvent.SetVisibleVizs — array payload vizId injection', () => {
1642
+ test('vizId is added as a non-indexed property on the array — array.length unchanged', async () => {
1643
+ mockMessageChannel();
1644
+ const { lb, iframe } = await renderLiveboard({ vizId });
1645
+ const arrayPayload = ['viz1', 'viz2'];
1646
+ await executeAfterWait(() => {
1647
+ lb.trigger(HostEvent.SetVisibleVizs, arrayPayload as any);
1648
+ const call = (iframe.contentWindow.postMessage as jest.Mock).mock.calls[0];
1649
+ const sentData = call[0].data;
1650
+ expect((sentData as any).vizId).toBe(vizId);
1651
+ expect(sentData.length).toBe(2);
1652
+ });
1653
+ });
1654
+
1655
+ test('without viewConfig.vizId — array payload is sent as-is (no vizId property)', async () => {
1656
+ mockMessageChannel();
1657
+ const { lb, iframe } = await renderLiveboard(); // no vizId
1658
+ const arrayPayload = ['viz1'];
1659
+ await executeAfterWait(() => {
1660
+ lb.trigger(HostEvent.SetVisibleVizs, arrayPayload as any);
1661
+ const call = (iframe.contentWindow.postMessage as jest.Mock).mock.calls[0];
1662
+ const sentData = call[0].data;
1663
+ expect((sentData as any).vizId).toBeUndefined();
1664
+ });
1665
+ });
1666
+
1667
+ test('returns null and calls handleError when !isRendered', async () => {
1668
+ const lb = unrenderedLiveboard();
1669
+ const result = await lb.trigger(HostEvent.SetVisibleVizs, ['viz1'] as any);
1670
+ expect(result).toBeNull();
1671
+ expect((lb as any).handleError).toHaveBeenCalledWith(
1672
+ expect.objectContaining({ code: EmbedErrorCodes.RENDER_NOT_CALLED }),
1673
+ );
1674
+ });
1675
+ });
1676
+
1677
+ // ---------------------------------------------------------------------------
1678
+ // UpdateRuntimeFilters — AppEmbed path (no vizId injection)
1679
+ // ---------------------------------------------------------------------------
1680
+ describe('HostEvent.UpdateRuntimeFilters — AppEmbed path', () => {
1681
+ async function renderApp() {
1682
+ const app = new AppEmbed(getRootEl(), {
1683
+ frameParams: { width: '100%', height: '100%' },
1684
+ });
1685
+ await app.render();
1686
+ const iframe = getIFrameEl();
1687
+ jest.spyOn(iframe.contentWindow, 'postMessage');
1688
+ return { app, iframe };
1689
+ }
1690
+
1691
+ test('AppEmbed sends payload directly without vizId injection', async () => {
1692
+ mockMessageChannel();
1693
+ const { app, iframe } = await renderApp();
1694
+ const filters = [{ columnName: 'col', operator: 'EQ', values: ['v1'] }];
1695
+ await executeAfterWait(() => {
1696
+ app.trigger(HostEvent.UpdateRuntimeFilters, filters as any);
1697
+ const call = (iframe.contentWindow.postMessage as jest.Mock).mock.calls[0];
1698
+ const sentData = call[0].data;
1699
+ // Array payload with no vizId property injected
1700
+ expect((sentData as any).vizId).toBeUndefined();
1701
+ expect(sentData).toEqual(filters);
1702
+ });
1703
+ });
1704
+ });
1705
+
1706
+ // ---------------------------------------------------------------------------
1707
+ // Present — vizId injection on LiveboardEmbed
1708
+ // ---------------------------------------------------------------------------
1709
+ describe('HostEvent.Present — vizId injection on LiveboardEmbed', () => {
1710
+ test('viewConfig.vizId is injected into the Present payload', async () => {
1711
+ mockMessageChannel();
1712
+ const { lb, iframe } = await renderLiveboard({ vizId });
1713
+ await executeAfterWait(() => {
1714
+ lb.trigger(HostEvent.Present, {} as any);
1715
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1716
+ expect.objectContaining({
1717
+ data: expect.objectContaining({ vizId }),
1718
+ }),
1719
+ thoughtSpotHost,
1720
+ expect.anything(),
1721
+ );
1722
+ });
1723
+ });
1724
+ });
1725
+
1726
+ // ---------------------------------------------------------------------------
1727
+ // DownloadAsCsv — additional cases
1728
+ // ---------------------------------------------------------------------------
1729
+ describe('HostEvent.DownloadAsCsv — additional cases', () => {
1730
+ test('viewConfig.vizId overwrites an explicit payload vizId', async () => {
1731
+ mockMessageChannel();
1732
+ const { lb, iframe } = await renderLiveboard({ vizId });
1733
+ await executeAfterWait(() => {
1734
+ lb.trigger(HostEvent.DownloadAsCsv, { vizId: 'explicit-id' } as any);
1735
+ const call = (iframe.contentWindow.postMessage as jest.Mock).mock.calls[0];
1736
+ const sentData = call[0].data;
1737
+ expect(sentData.vizId).toBe(vizId); // viewConfig.vizId wins
1738
+ });
1739
+ });
1740
+
1741
+ test('returns null and calls handleError when !isRendered', async () => {
1742
+ const lb = unrenderedLiveboard();
1743
+ const result = await lb.trigger(HostEvent.DownloadAsCsv, {} as any);
1744
+ expect(result).toBeNull();
1745
+ expect((lb as any).handleError).toHaveBeenCalledWith(
1746
+ expect.objectContaining({ code: EmbedErrorCodes.RENDER_NOT_CALLED }),
1747
+ );
1748
+ });
1749
+
1750
+ test('returns null and logs debug when !iFrame', async () => {
1751
+ const lb = new LiveboardEmbed(getRootEl(), {
1752
+ frameParams: { width: '100%', height: '100%' },
1753
+ liveboardId,
1754
+ });
1755
+ (lb as any).isRendered = true;
1756
+ const result = await lb.trigger(HostEvent.DownloadAsCsv, {} as any);
1757
+ expect(result).toBeNull();
1758
+ });
1759
+ });