@thoughtspot/visual-embed-sdk 1.35.2 → 1.35.4

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 (209) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/auth.spec.js +12 -0
  3. package/cjs/src/auth.spec.js.map +1 -1
  4. package/cjs/src/authToken.spec.js +1 -1
  5. package/cjs/src/authToken.spec.js.map +1 -1
  6. package/cjs/src/embed/app.d.ts +1 -1
  7. package/cjs/src/embed/app.d.ts.map +1 -1
  8. package/cjs/src/embed/app.js +4 -3
  9. package/cjs/src/embed/app.js.map +1 -1
  10. package/cjs/src/embed/base.d.ts +1 -1
  11. package/cjs/src/embed/base.js +1 -1
  12. package/cjs/src/embed/bodyless-conversation.spec.js +0 -1
  13. package/cjs/src/embed/bodyless-conversation.spec.js.map +1 -1
  14. package/cjs/src/embed/hostEventClient/contracts.d.ts +79 -0
  15. package/cjs/src/embed/hostEventClient/contracts.d.ts.map +1 -0
  16. package/cjs/src/embed/hostEventClient/contracts.js +14 -0
  17. package/cjs/src/embed/hostEventClient/contracts.js.map +1 -0
  18. package/cjs/src/embed/hostEventClient/host-event-client.d.ts +25 -0
  19. package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -0
  20. package/cjs/src/embed/hostEventClient/host-event-client.js +89 -0
  21. package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -0
  22. package/cjs/src/embed/hostEventClient/host-event-client.spec.d.ts +2 -0
  23. package/cjs/src/embed/hostEventClient/host-event-client.spec.d.ts.map +1 -0
  24. package/cjs/src/embed/hostEventClient/host-event-client.spec.js +191 -0
  25. package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -0
  26. package/cjs/src/embed/liveboard.d.ts +10 -8
  27. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  28. package/cjs/src/embed/liveboard.js +3 -3
  29. package/cjs/src/embed/liveboard.js.map +1 -1
  30. package/cjs/src/embed/liveboard.spec.js +34 -2
  31. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  32. package/cjs/src/embed/search.spec.js +11 -0
  33. package/cjs/src/embed/search.spec.js.map +1 -1
  34. package/cjs/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
  35. package/cjs/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
  36. package/cjs/src/embed/searchEmbed-basic-auth.spec.js +104 -0
  37. package/cjs/src/embed/searchEmbed-basic-auth.spec.js.map +1 -0
  38. package/cjs/src/embed/ts-embed.d.ts +21 -4
  39. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  40. package/cjs/src/embed/ts-embed.js +33 -9
  41. package/cjs/src/embed/ts-embed.js.map +1 -1
  42. package/cjs/src/embed/ts-embed.spec.d.ts.map +1 -1
  43. package/cjs/src/embed/ts-embed.spec.js +38 -1
  44. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  45. package/cjs/src/hostEventsTypeMapping.d.ts +2 -0
  46. package/cjs/src/hostEventsTypeMapping.d.ts.map +1 -0
  47. package/cjs/src/hostEventsTypeMapping.js +4 -0
  48. package/cjs/src/hostEventsTypeMapping.js.map +1 -0
  49. package/cjs/src/index.d.ts +2 -1
  50. package/cjs/src/index.d.ts.map +1 -1
  51. package/cjs/src/index.js +3 -1
  52. package/cjs/src/index.js.map +1 -1
  53. package/cjs/src/react/all-types-export.d.ts +1 -1
  54. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  55. package/cjs/src/react/all-types-export.js +2 -1
  56. package/cjs/src/react/all-types-export.js.map +1 -1
  57. package/cjs/src/types.d.ts +38 -23
  58. package/cjs/src/types.d.ts.map +1 -1
  59. package/cjs/src/types.js +31 -20
  60. package/cjs/src/types.js.map +1 -1
  61. package/cjs/src/utils/embedApi/contracts.d.ts +101 -0
  62. package/cjs/src/utils/embedApi/contracts.d.ts.map +1 -0
  63. package/cjs/src/utils/embedApi/contracts.js +17 -0
  64. package/cjs/src/utils/embedApi/contracts.js.map +1 -0
  65. package/cjs/src/utils/embedApi/embedApiClient.d.ts +12 -0
  66. package/cjs/src/utils/embedApi/embedApiClient.d.ts.map +1 -0
  67. package/cjs/src/utils/embedApi/embedApiClient.js +46 -0
  68. package/cjs/src/utils/embedApi/embedApiClient.js.map +1 -0
  69. package/cjs/src/utils/embedApi/processEmbedApi.d.ts +9 -0
  70. package/cjs/src/utils/embedApi/processEmbedApi.d.ts.map +1 -0
  71. package/cjs/src/utils/embedApi/processEmbedApi.js +18 -0
  72. package/cjs/src/utils/embedApi/processEmbedApi.js.map +1 -0
  73. package/dist/{index-DoPTJLbd.js → index-BBBimG1x.js} +1 -1
  74. package/dist/index-CENLvayL.js +7370 -0
  75. package/dist/index-CbltIawo.js +7370 -0
  76. package/dist/index-CoQfqaHj.js +7370 -0
  77. package/dist/index-CzwzS0P4.js +7370 -0
  78. package/dist/index-DFwi_pV_.js +7370 -0
  79. package/dist/index-DOIjN0N_.js +7370 -0
  80. package/dist/index-DYBx8SuE.js +7370 -0
  81. package/dist/index-DaLHJaLd.js +7370 -0
  82. package/dist/index-nWevLycs.js +7370 -0
  83. package/dist/src/embed/app.d.ts +1 -1
  84. package/dist/src/embed/app.d.ts.map +1 -1
  85. package/dist/src/embed/base.d.ts +1 -1
  86. package/dist/src/embed/hostEventClient/contracts.d.ts +79 -0
  87. package/dist/src/embed/hostEventClient/contracts.d.ts.map +1 -0
  88. package/dist/src/embed/hostEventClient/host-event-client.d.ts +25 -0
  89. package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -0
  90. package/dist/src/embed/hostEventClient/host-event-client.spec.d.ts +2 -0
  91. package/dist/src/embed/hostEventClient/host-event-client.spec.d.ts.map +1 -0
  92. package/dist/src/embed/liveboard.d.ts +10 -8
  93. package/dist/src/embed/liveboard.d.ts.map +1 -1
  94. package/dist/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
  95. package/dist/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
  96. package/dist/src/embed/ts-embed.d.ts +21 -4
  97. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  98. package/dist/src/embed/ts-embed.spec.d.ts.map +1 -1
  99. package/dist/src/hostEventsTypeMapping.d.ts +2 -0
  100. package/dist/src/hostEventsTypeMapping.d.ts.map +1 -0
  101. package/dist/src/index.d.ts +2 -1
  102. package/dist/src/index.d.ts.map +1 -1
  103. package/dist/src/react/all-types-export.d.ts +1 -1
  104. package/dist/src/react/all-types-export.d.ts.map +1 -1
  105. package/dist/src/types.d.ts +38 -23
  106. package/dist/src/types.d.ts.map +1 -1
  107. package/dist/src/utils/embedApi/contracts.d.ts +101 -0
  108. package/dist/src/utils/embedApi/contracts.d.ts.map +1 -0
  109. package/dist/src/utils/embedApi/embedApiClient.d.ts +12 -0
  110. package/dist/src/utils/embedApi/embedApiClient.d.ts.map +1 -0
  111. package/dist/src/utils/embedApi/processEmbedApi.d.ts +9 -0
  112. package/dist/src/utils/embedApi/processEmbedApi.d.ts.map +1 -0
  113. package/dist/tsembed-react.es.js +164 -37
  114. package/dist/tsembed-react.js +163 -36
  115. package/dist/tsembed.es.js +166 -39
  116. package/dist/tsembed.js +164 -37
  117. package/dist/visual-embed-sdk-react-full.d.ts +170 -38
  118. package/dist/visual-embed-sdk-react.d.ts +170 -38
  119. package/dist/visual-embed-sdk.d.ts +170 -38
  120. package/lib/package.json +1 -1
  121. package/lib/src/auth.spec.js +12 -0
  122. package/lib/src/auth.spec.js.map +1 -1
  123. package/lib/src/authToken.spec.js +1 -1
  124. package/lib/src/authToken.spec.js.map +1 -1
  125. package/lib/src/embed/app.d.ts +1 -1
  126. package/lib/src/embed/app.d.ts.map +1 -1
  127. package/lib/src/embed/app.js +4 -3
  128. package/lib/src/embed/app.js.map +1 -1
  129. package/lib/src/embed/base.d.ts +1 -1
  130. package/lib/src/embed/base.js +1 -1
  131. package/lib/src/embed/bodyless-conversation.spec.js +0 -1
  132. package/lib/src/embed/bodyless-conversation.spec.js.map +1 -1
  133. package/lib/src/embed/hostEventClient/contracts.d.ts +79 -0
  134. package/lib/src/embed/hostEventClient/contracts.d.ts.map +1 -0
  135. package/lib/src/embed/hostEventClient/contracts.js +11 -0
  136. package/lib/src/embed/hostEventClient/contracts.js.map +1 -0
  137. package/lib/src/embed/hostEventClient/host-event-client.d.ts +25 -0
  138. package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -0
  139. package/lib/src/embed/hostEventClient/host-event-client.js +85 -0
  140. package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -0
  141. package/lib/src/embed/hostEventClient/host-event-client.spec.d.ts +2 -0
  142. package/lib/src/embed/hostEventClient/host-event-client.spec.d.ts.map +1 -0
  143. package/lib/src/embed/hostEventClient/host-event-client.spec.js +188 -0
  144. package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -0
  145. package/lib/src/embed/liveboard.d.ts +10 -8
  146. package/lib/src/embed/liveboard.d.ts.map +1 -1
  147. package/lib/src/embed/liveboard.js +3 -3
  148. package/lib/src/embed/liveboard.js.map +1 -1
  149. package/lib/src/embed/liveboard.spec.js +34 -2
  150. package/lib/src/embed/liveboard.spec.js.map +1 -1
  151. package/lib/src/embed/search.spec.js +11 -0
  152. package/lib/src/embed/search.spec.js.map +1 -1
  153. package/lib/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
  154. package/lib/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
  155. package/lib/src/embed/searchEmbed-basic-auth.spec.js +101 -0
  156. package/lib/src/embed/searchEmbed-basic-auth.spec.js.map +1 -0
  157. package/lib/src/embed/ts-embed.d.ts +21 -4
  158. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  159. package/lib/src/embed/ts-embed.js +33 -9
  160. package/lib/src/embed/ts-embed.js.map +1 -1
  161. package/lib/src/embed/ts-embed.spec.d.ts.map +1 -1
  162. package/lib/src/embed/ts-embed.spec.js +40 -3
  163. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  164. package/lib/src/hostEventsTypeMapping.d.ts +2 -0
  165. package/lib/src/hostEventsTypeMapping.d.ts.map +1 -0
  166. package/lib/src/hostEventsTypeMapping.js +2 -0
  167. package/lib/src/hostEventsTypeMapping.js.map +1 -0
  168. package/lib/src/index.d.ts +2 -1
  169. package/lib/src/index.d.ts.map +1 -1
  170. package/lib/src/index.js +2 -1
  171. package/lib/src/index.js.map +1 -1
  172. package/lib/src/react/all-types-export.d.ts +1 -1
  173. package/lib/src/react/all-types-export.d.ts.map +1 -1
  174. package/lib/src/react/all-types-export.js +1 -1
  175. package/lib/src/react/all-types-export.js.map +1 -1
  176. package/lib/src/types.d.ts +38 -23
  177. package/lib/src/types.d.ts.map +1 -1
  178. package/lib/src/types.js +31 -20
  179. package/lib/src/types.js.map +1 -1
  180. package/lib/src/utils/embedApi/contracts.d.ts +101 -0
  181. package/lib/src/utils/embedApi/contracts.d.ts.map +1 -0
  182. package/lib/src/utils/embedApi/contracts.js +14 -0
  183. package/lib/src/utils/embedApi/contracts.js.map +1 -0
  184. package/lib/src/utils/embedApi/embedApiClient.d.ts +12 -0
  185. package/lib/src/utils/embedApi/embedApiClient.d.ts.map +1 -0
  186. package/lib/src/utils/embedApi/embedApiClient.js +42 -0
  187. package/lib/src/utils/embedApi/embedApiClient.js.map +1 -0
  188. package/lib/src/utils/embedApi/processEmbedApi.d.ts +9 -0
  189. package/lib/src/utils/embedApi/processEmbedApi.d.ts.map +1 -0
  190. package/lib/src/utils/embedApi/processEmbedApi.js +14 -0
  191. package/lib/src/utils/embedApi/processEmbedApi.js.map +1 -0
  192. package/lib/src/visual-embed-sdk.d.ts +181 -38
  193. package/package.json +1 -1
  194. package/src/auth.spec.ts +13 -0
  195. package/src/authToken.spec.ts +1 -1
  196. package/src/embed/app.ts +7 -4
  197. package/src/embed/base.ts +1 -1
  198. package/src/embed/bodyless-conversation.spec.ts +0 -1
  199. package/src/embed/hostEventClient/contracts.ts +105 -0
  200. package/src/embed/hostEventClient/host-event-client.spec.ts +254 -0
  201. package/src/embed/hostEventClient/host-event-client.ts +139 -0
  202. package/src/embed/liveboard.spec.ts +36 -2
  203. package/src/embed/liveboard.ts +13 -8
  204. package/src/embed/search.spec.ts +15 -0
  205. package/src/embed/ts-embed.spec.ts +53 -1
  206. package/src/embed/ts-embed.ts +60 -16
  207. package/src/index.ts +2 -0
  208. package/src/react/all-types-export.ts +1 -0
  209. package/src/types.ts +37 -22
@@ -0,0 +1,254 @@
1
+ import { getIFrameEl, getRootEl } from '../../test/test-utils';
2
+ import { HostEvent } from '../../types';
3
+ import { processTrigger } from '../../utils/processTrigger';
4
+ import * as EmbedConfigService from '../embedConfig';
5
+ import {
6
+ UIPassthroughEvent,
7
+ UIPassthroughRequest,
8
+ UIPassthroughArrayResponse,
9
+ HostEventRequest,
10
+ } from './contracts';
11
+ import { HostEventClient } from './host-event-client';
12
+
13
+ jest.mock('../../utils/processTrigger');
14
+
15
+ const mockProcessTrigger = processTrigger as jest.Mock;
16
+
17
+ const createHostEventClient = () => {
18
+ const mockIframe = document.createElement('iframe');
19
+ const client = new HostEventClient(mockIframe);
20
+ return { client, mockIframe };
21
+ };
22
+
23
+ describe('HostEventClient', () => {
24
+ const mockThoughtSpotHost = 'http://localhost';
25
+ beforeEach(() => {
26
+ jest.spyOn(EmbedConfigService, 'getEmbedConfig').mockReturnValue({ thoughtSpotHost: mockThoughtSpotHost });
27
+ });
28
+
29
+ afterEach(() => {
30
+ jest.clearAllMocks();
31
+ });
32
+
33
+ describe('executeUIPassthroughApi', () => {
34
+ it('should call processTrigger with correct parameters and return response', async () => {
35
+ const { client, mockIframe } = createHostEventClient();
36
+
37
+ const apiName = UIPassthroughEvent.PinAnswerToLiveboard;
38
+ const parameters: UIPassthroughRequest<typeof apiName> = {
39
+ newVizName: 'testViz',
40
+ };
41
+ const triggerResponse = Promise.resolve([
42
+ { value: { pinboardId: 'testPinboard', tabId: 'testTab', vizId: 'testVizId' } },
43
+ ]);
44
+
45
+ mockProcessTrigger.mockResolvedValue(triggerResponse);
46
+
47
+ const result = await client.triggerUIPassthroughApi(apiName, parameters);
48
+
49
+ expect(mockProcessTrigger).toHaveBeenCalledWith(
50
+ mockIframe,
51
+ HostEvent.UIPassthrough,
52
+ mockThoughtSpotHost,
53
+ {
54
+ type: apiName,
55
+ parameters,
56
+ },
57
+ );
58
+ expect(result).toEqual(await triggerResponse);
59
+ });
60
+ });
61
+
62
+ describe('handleUIPassthroughForHostEvent', () => {
63
+ it('should return the value from the first valid response', async () => {
64
+ const { client } = createHostEventClient();
65
+ const apiName = UIPassthroughEvent.PinAnswerToLiveboard;
66
+ const parameters: UIPassthroughRequest<typeof apiName> = {
67
+ newVizName: 'testViz',
68
+ };
69
+ const triggerResponse = Promise.resolve([
70
+ { value: { pinboardId: 'testPinboard', tabId: 'testTab', vizId: 'testVizId' } },
71
+ ]);
72
+ mockProcessTrigger.mockResolvedValue(triggerResponse);
73
+
74
+ const result = await client.handleHostEventWithParam(apiName, parameters);
75
+
76
+ expect(result).toEqual({
77
+ pinboardId: 'testPinboard',
78
+ tabId: 'testTab',
79
+ vizId: 'testVizId',
80
+ });
81
+ });
82
+
83
+ it('should throw an error if no valid response is found', async () => {
84
+ const { client } = createHostEventClient();
85
+ const apiName = UIPassthroughEvent.PinAnswerToLiveboard;
86
+ const parameters: UIPassthroughRequest<typeof apiName> = {
87
+ newVizName: 'testViz',
88
+ };
89
+ const triggerResponse: UIPassthroughArrayResponse<typeof apiName> = [];
90
+ mockProcessTrigger.mockResolvedValue(triggerResponse);
91
+
92
+ await expect(client.handleHostEventWithParam(apiName, parameters))
93
+ .rejects.toEqual({ error: 'No answer found.' });
94
+ });
95
+
96
+ it('should throw an error if no valid response is found for vizId', async () => {
97
+ const { client } = createHostEventClient();
98
+ const apiName = UIPassthroughEvent.PinAnswerToLiveboard;
99
+ const parameters: UIPassthroughRequest<typeof apiName> = {
100
+ newVizName: 'testViz',
101
+ vizId: 'testVizId',
102
+ };
103
+ const triggerResponse: UIPassthroughArrayResponse<typeof apiName> = [];
104
+ mockProcessTrigger.mockResolvedValue(triggerResponse);
105
+
106
+ await expect(client.handleHostEventWithParam(apiName, parameters))
107
+ .rejects.toEqual({ error: 'No answer found for vizId: testVizId.' });
108
+ });
109
+
110
+ it('should throw an error if the response contains errors', async () => {
111
+ const { client } = createHostEventClient();
112
+ const apiName = UIPassthroughEvent.PinAnswerToLiveboard;
113
+ const parameters: UIPassthroughRequest<typeof apiName> = {
114
+ newVizName: 'testViz',
115
+ };
116
+ const triggerResponse: UIPassthroughArrayResponse<typeof apiName> = [
117
+ { error: 'Some error' },
118
+ ];
119
+ mockProcessTrigger.mockResolvedValue(triggerResponse);
120
+
121
+ await expect(client.handleHostEventWithParam(apiName, parameters))
122
+ .rejects.toEqual({ error: 'Some error' });
123
+ });
124
+ });
125
+
126
+ describe('executeHostEvent', () => {
127
+ it('should call handleUIPassthroughForHostEvent for Pin event', async () => {
128
+ const { client, mockIframe } = createHostEventClient();
129
+ const hostEvent = HostEvent.Pin;
130
+ const payload: HostEventRequest<typeof hostEvent> = {
131
+ newVizName: 'testViz',
132
+ };
133
+ const mockResponse = {
134
+ value: {
135
+ pinboardId: 'testPinboard',
136
+ tabId: 'testTab',
137
+ vizId: 'testVizId',
138
+ },
139
+ };
140
+
141
+ mockProcessTrigger.mockResolvedValue([mockResponse]);
142
+
143
+ const result = await client.triggerHostEvent(hostEvent, payload);
144
+
145
+ expect(mockProcessTrigger).toHaveBeenCalledWith(
146
+ mockIframe,
147
+ HostEvent.UIPassthrough,
148
+ 'http://localhost',
149
+ { parameters: payload, type: UIPassthroughEvent.PinAnswerToLiveboard },
150
+ );
151
+ expect(result).toEqual(mockResponse.value);
152
+ });
153
+
154
+ it('should call handleUIPassthroughForHostEvent for SaveAnswer event', async () => {
155
+ const { client, mockIframe } = createHostEventClient();
156
+ const hostEvent = HostEvent.SaveAnswer;
157
+ const payload: HostEventRequest<typeof hostEvent> = {
158
+ name: 'Test Answer',
159
+ description: 'Test Description',
160
+ vizId: 'testVizId',
161
+ };
162
+ const mockResponse = [{
163
+ value: {
164
+ saveResponse: {
165
+ data: {
166
+ Answer__save: {
167
+ answer: {
168
+ id: 'newAnswer',
169
+ },
170
+ },
171
+ },
172
+ },
173
+ },
174
+ refId: 'testVizId',
175
+ }];
176
+ mockProcessTrigger.mockResolvedValue(mockResponse);
177
+ const result = await client.triggerHostEvent(hostEvent, payload);
178
+
179
+ expect(mockProcessTrigger).toHaveBeenCalledWith(
180
+ mockIframe,
181
+ HostEvent.UIPassthrough,
182
+ mockThoughtSpotHost,
183
+ {
184
+ parameters: payload,
185
+ type: 'saveAnswer',
186
+ },
187
+ );
188
+ expect(result).toEqual({ answerId: 'newAnswer', ...mockResponse[0].value });
189
+ });
190
+
191
+ it('should call hostEventFallback for unmapped events', async () => {
192
+ const { client } = createHostEventClient();
193
+ const hostEvent = 'testEvent' as HostEvent;
194
+ const payload = { data: 'testData' };
195
+ const mockResponse = { fallbackResponse: 'data' };
196
+ jest.spyOn(client, 'hostEventFallback').mockResolvedValue(mockResponse);
197
+
198
+ const result = await client.triggerHostEvent(hostEvent, payload);
199
+
200
+ expect(client.hostEventFallback).toHaveBeenCalledWith(hostEvent, payload);
201
+ expect(result).toEqual(mockResponse);
202
+ });
203
+
204
+ it('should call fallback for Pin event', async () => {
205
+ const { client, mockIframe } = createHostEventClient();
206
+ const hostEvent = HostEvent.Pin;
207
+ const payload: HostEventRequest<typeof hostEvent> = {} as any;
208
+ const mockResponse = {
209
+ value: {
210
+ pinboardId: 'testPinboard',
211
+ tabId: 'testTab',
212
+ vizId: 'testVizId',
213
+ },
214
+ };
215
+
216
+ mockProcessTrigger.mockResolvedValue([mockResponse]);
217
+
218
+ const result = await client.triggerHostEvent(hostEvent, payload);
219
+
220
+ expect(mockProcessTrigger).toHaveBeenCalledWith(
221
+ mockIframe,
222
+ HostEvent.Pin,
223
+ mockThoughtSpotHost,
224
+ {},
225
+ );
226
+ expect(result).toEqual([mockResponse]);
227
+ });
228
+
229
+ it('should call fallback for SaveAnswer event', async () => {
230
+ const { client, mockIframe } = createHostEventClient();
231
+ const hostEvent = HostEvent.SaveAnswer;
232
+ const payload: HostEventRequest<typeof hostEvent> = {} as any;
233
+ const mockResponse = {
234
+ value: {
235
+ pinboardId: 'testPinboard',
236
+ tabId: 'testTab',
237
+ vizId: 'testVizId',
238
+ },
239
+ };
240
+
241
+ mockProcessTrigger.mockResolvedValue([mockResponse]);
242
+
243
+ const result = await client.triggerHostEvent(hostEvent, payload);
244
+
245
+ expect(mockProcessTrigger).toHaveBeenCalledWith(
246
+ mockIframe,
247
+ HostEvent.Save,
248
+ mockThoughtSpotHost,
249
+ {},
250
+ );
251
+ expect(result).toEqual([mockResponse]);
252
+ });
253
+ });
254
+ });
@@ -0,0 +1,139 @@
1
+ import { HostEvent } from '../../types';
2
+ import { processTrigger as processTriggerService } from '../../utils/processTrigger';
3
+ import { getEmbedConfig } from '../embedConfig';
4
+ import {
5
+ UIPassthroughArrayResponse,
6
+ UIPassthroughEvent, HostEventRequest, HostEventResponse,
7
+ UIPassthroughRequest,
8
+ UIPassthroughResponse,
9
+ TriggerPayload,
10
+ TriggerResponse,
11
+ } from './contracts';
12
+
13
+ export class HostEventClient {
14
+ iFrame: HTMLIFrameElement;
15
+
16
+ constructor(iFrame?: HTMLIFrameElement) {
17
+ this.iFrame = iFrame;
18
+ }
19
+
20
+ /**
21
+ * A wrapper over process trigger to
22
+ * @param {HostEvent} message Host event to send
23
+ * @param {any} data Data to send with the host event
24
+ * @returns {Promise<any>} - the response from the process trigger
25
+ */
26
+ protected async processTrigger(message: HostEvent, data: any): Promise<any> {
27
+ if (!this.iFrame) {
28
+ throw new Error('Iframe element is not set');
29
+ }
30
+
31
+ const thoughtspotHost = getEmbedConfig().thoughtSpotHost;
32
+ return processTriggerService(
33
+ this.iFrame,
34
+ message,
35
+ thoughtspotHost,
36
+ data,
37
+ );
38
+ }
39
+
40
+ public async handleHostEventWithParam<UIPassthroughEventT extends UIPassthroughEvent>(
41
+ apiName: UIPassthroughEventT,
42
+ parameters: UIPassthroughRequest<UIPassthroughEventT>,
43
+ ): Promise<UIPassthroughResponse<UIPassthroughEventT>> {
44
+ const response = (await this.triggerUIPassthroughApi(apiName, parameters))
45
+ ?.filter?.((r) => r.error || r.value)[0];
46
+
47
+ if (!response) {
48
+ const error = `No answer found${parameters.vizId ? ` for vizId: ${parameters.vizId}` : ''}.`;
49
+ // eslint-disable-next-line no-throw-literal
50
+ throw { error };
51
+ }
52
+
53
+ const errors = response.error
54
+ || (response.value as any)?.errors
55
+ || (response.value as any)?.error;
56
+
57
+ if (errors) {
58
+ // eslint-disable-next-line no-throw-literal
59
+ throw { error: response.error };
60
+ }
61
+
62
+ return { ...response.value };
63
+ }
64
+
65
+ public async hostEventFallback(
66
+ hostEvent: HostEvent,
67
+ data: any,
68
+ ): Promise<any> {
69
+ return this.processTrigger(hostEvent, data);
70
+ }
71
+
72
+ /**
73
+ * Setter for the iframe element used for host events
74
+ * @param {HTMLIFrameElement} iFrame - the iframe element to set
75
+ */
76
+ public setIframeElement(iFrame: HTMLIFrameElement): void {
77
+ this.iFrame = iFrame;
78
+ }
79
+
80
+ public async triggerUIPassthroughApi<UIPassthroughEventT extends UIPassthroughEvent>(
81
+ apiName: UIPassthroughEventT,
82
+ parameters: UIPassthroughRequest<UIPassthroughEventT>,
83
+ ): Promise<UIPassthroughArrayResponse<UIPassthroughEventT>> {
84
+ const res = await this.processTrigger(HostEvent.UIPassthrough, {
85
+ type: apiName,
86
+ parameters,
87
+ });
88
+
89
+ return res;
90
+ }
91
+
92
+ protected async handlePinEvent(
93
+ payload: HostEventRequest<HostEvent.Pin>,
94
+ ): Promise<HostEventResponse<HostEvent.Pin>> {
95
+ if (!payload || !('newVizName' in payload)) {
96
+ return this.hostEventFallback(HostEvent.Pin, payload);
97
+ }
98
+
99
+ return this.handleHostEventWithParam(
100
+ UIPassthroughEvent.PinAnswerToLiveboard, payload,
101
+ );
102
+ }
103
+
104
+ protected async handleSaveAnswerEvent(
105
+ payload: HostEventRequest<HostEvent.SaveAnswer>,
106
+ ): Promise<any> {
107
+ if (!payload || !('name' in payload) || !('description' in payload)) {
108
+ // Save is the fallback for SaveAnswer
109
+ return this.hostEventFallback(HostEvent.Save, payload);
110
+ }
111
+
112
+ const data = await this.handleHostEventWithParam(
113
+ UIPassthroughEvent.SaveAnswer, payload,
114
+ );
115
+ return {
116
+ ...data,
117
+ answerId: data?.saveResponse?.data?.Answer__save?.answer?.id,
118
+ };
119
+ }
120
+
121
+ public async triggerHostEvent<
122
+ HostEventT extends HostEvent,
123
+ PayloadT,
124
+ >(
125
+ hostEvent: HostEventT,
126
+ payload?: TriggerPayload<PayloadT, HostEventT>,
127
+ ): Promise<TriggerResponse<PayloadT, HostEventT>> {
128
+ switch (hostEvent) {
129
+ case HostEvent.Pin:
130
+ return this.handlePinEvent(payload as HostEventRequest<HostEvent.Pin>) as any;
131
+ case HostEvent.SaveAnswer:
132
+ return this.handleSaveAnswerEvent(
133
+ payload as HostEventRequest<HostEvent.SaveAnswer>,
134
+ ) as any;
135
+ default:
136
+ return this.hostEventFallback(hostEvent, payload);
137
+ }
138
+ }
139
+ }
@@ -484,8 +484,10 @@ describe('Liveboard/viz embed tests', () => {
484
484
  liveboardId,
485
485
  } as LiveboardViewConfig);
486
486
  liveboardEmbed.render();
487
- const result = await liveboardEmbed.trigger(HostEvent.Pin);
488
- expect(mockProcessTrigger).toBeCalled();
487
+ await executeAfterWait(async () => {
488
+ await liveboardEmbed.trigger(HostEvent.Pin);
489
+ expect(mockProcessTrigger).toBeCalled();
490
+ });
489
491
  });
490
492
 
491
493
  test('should render active tab when activeTab present', async () => {
@@ -570,6 +572,27 @@ describe('Liveboard/viz embed tests', () => {
570
572
  done();
571
573
  });
572
574
  });
575
+
576
+ test('navigateToLiveboard with preRender', (done) => {
577
+ mockMessageChannel();
578
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
579
+ ...defaultViewConfig,
580
+ preRenderId: 'test',
581
+ } as LiveboardViewConfig);
582
+ const onSpy = jest.spyOn(liveboardEmbed, 'trigger');
583
+ liveboardEmbed.prerenderGeneric();
584
+ executeAfterWait(() => {
585
+ const iframe = getIFrameEl();
586
+ postMessageToParent(iframe.contentWindow, {
587
+ type: EmbedEvent.APP_INIT,
588
+ });
589
+ });
590
+ executeAfterWait(() => {
591
+ liveboardEmbed.navigateToLiveboard('lb1', 'viz1');
592
+ expect(onSpy).toHaveBeenCalledWith(HostEvent.Navigate, 'embed/viz/lb1/viz1');
593
+ done();
594
+ });
595
+ });
573
596
  test('should set runtime parametere values in url params', async () => {
574
597
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
575
598
  ...defaultViewConfig,
@@ -659,6 +682,17 @@ describe('Liveboard/viz embed tests', () => {
659
682
  });
660
683
  });
661
684
 
685
+ test('get liveboard url value', async () => {
686
+ const libEmbed = new LiveboardEmbed(getRootEl(), {
687
+ liveboardId: '1234',
688
+ });
689
+ await libEmbed.render();
690
+ await executeAfterWait(() => {
691
+ const url = libEmbed.getLiveboardUrl();
692
+ expect(url).toEqual('http://tshost/#/pinboard/1234');
693
+ });
694
+ });
695
+
662
696
  test('Show preview loader should show the loader if viz embed and showPreviewLoader is true', async () => {
663
697
  jest.spyOn(previewService, 'getPreview').mockResolvedValue({
664
698
  vizContent: '<div id=test>test</div>',
@@ -23,6 +23,7 @@ import { getQueryParamString, isUndefined } from '../utils';
23
23
  import { getAuthPromise } from './base';
24
24
  import { V1Embed } from './ts-embed';
25
25
  import { addPreviewStylesIfNotPresent } from '../utils/global-styles';
26
+ import { HostEventRequest, HostEventResponse } from './hostEventClient/contracts';
26
27
 
27
28
  /**
28
29
  * The configuration for the embedded Liveboard or visualization page view.
@@ -248,7 +249,7 @@ export interface LiveboardViewConfig
248
249
  */
249
250
  enableAskSage?: boolean;
250
251
  /**
251
- * This flag is used to enable the 2 column layout in liveboard
252
+ * This flag is used to enable the 2 column layout on a Liveboard
252
253
  * @type {boolean}
253
254
  * @default false
254
255
  * @version SDK: 1.32.0 | ThoughtSpot:10.1.0.cl
@@ -283,7 +284,7 @@ export interface LiveboardViewConfig
283
284
  */
284
285
  showPreviewLoader?: boolean;
285
286
  /**
286
- * This flag is used to enable the compact header in liveboard
287
+ * This flag is used to enable the compact header on a Liveboard
287
288
  * @type {boolean}
288
289
  * @default false
289
290
  * @version SDK: 1.35.0 | ThoughtSpot:10.3.0.cl
@@ -297,7 +298,7 @@ export interface LiveboardViewConfig
297
298
  */
298
299
  isLiveboardCompactHeaderEnabled?: boolean;
299
300
  /**
300
- * This flag is used to show/hide verified Icon in liveboard compact header
301
+ * This flag is used to show/hide verified icon in the Liveboard compact header
301
302
  * @type {boolean}
302
303
  * @default true
303
304
  * @version SDK: 1.35.0 | ThoughtSpot:10.4.0.cl
@@ -311,7 +312,8 @@ export interface LiveboardViewConfig
311
312
  */
312
313
  showLiveboardVerifiedBadge?: boolean;
313
314
  /**
314
- * This flag is used to show/hide re-verify banner in liveboard compact header
315
+ * This flag is used to show/hide the re-verify banner
316
+ * in Liveboard compact header
315
317
  * @type {boolean}
316
318
  * @default true
317
319
  * @version SDK: 1.35.0 | ThoughtSpot:10.4.0.cl
@@ -325,7 +327,7 @@ export interface LiveboardViewConfig
325
327
  */
326
328
  showLiveboardReverifyBanner?: boolean;
327
329
  /**
328
- * This flag is used to enable/disable hide irrelevant filters in liveboard tab
330
+ * This flag is used to enable/disable hide irrelevant filters in a Liveboard tab
329
331
  * @type {boolean}
330
332
  * @default false
331
333
  * @version SDK: 1.36.0 | ThoughtSpot:10.6.0.cl
@@ -636,7 +638,10 @@ export class LiveboardEmbed extends V1Embed {
636
638
  * @param messageType The event type
637
639
  * @param data The payload to send with the message
638
640
  */
639
- public trigger(messageType: HostEvent, data: any = {}): Promise<any> {
641
+ public trigger<HostEventT extends HostEvent>(
642
+ messageType: HostEventT,
643
+ data?: HostEventRequest<HostEventT>,
644
+ ): Promise<HostEventResponse<HostEventT>> {
640
645
  const dataWithVizId = data;
641
646
  if (messageType === HostEvent.SetActiveTab) {
642
647
  this.setActiveTab(data);
@@ -678,8 +683,8 @@ export class LiveboardEmbed extends V1Embed {
678
683
  }
679
684
 
680
685
  /**
681
- * Returns the full url of the liveboard/viz which can be used to open
682
- * this liveboard inside the full Thoughtspot application in a new tab.
686
+ * Returns the full url of the Liveboard/visualization which can be used to open
687
+ * this Liveboard inside the full Thoughtspot application in a new tab.
683
688
  * @returns url string
684
689
  */
685
690
  public getLiveboardUrl(): string {
@@ -16,6 +16,7 @@ import {
16
16
  expectUrlMatchesWithParams,
17
17
  } from '../test/test-utils';
18
18
  import { version } from '../../package.json';
19
+ import { SearchBarEmbed } from './search-bar';
19
20
 
20
21
  const defaultViewConfig = {
21
22
  frameParams: {
@@ -504,6 +505,20 @@ describe('Search embed tests', () => {
504
505
  });
505
506
  });
506
507
 
508
+ test('should set dataPanelCustomGroupsAccordionInitialState to EXPAND_FIRST when passed', async () => {
509
+ const searchEmbed = new SearchBarEmbed(getRootEl() as any, {
510
+ ...defaultViewConfig,
511
+ // eslint-disable-next-line max-len
512
+ });
513
+ searchEmbed.render();
514
+ await executeAfterWait(() => {
515
+ expectUrlMatchesWithParams(
516
+ getIFrameSrc(),
517
+ `http://${thoughtSpotHost}/v2/?${defaultParams}&useLastSelectedSources=false${prefixParams}#/embed/search-bar-embed`,
518
+ );
519
+ });
520
+ });
521
+
507
522
  test('should set dataPanelCustomGroupsAccordionInitialState to EXPAND_FIRST when passed', async () => {
508
523
  const searchEmbed = new SearchEmbed(getRootEl(), {
509
524
  ...defaultViewConfig,
@@ -15,6 +15,7 @@ import {
15
15
  ConversationViewConfig,
16
16
  ConversationEmbed,
17
17
  SearchViewConfig,
18
+ AnswerService,
18
19
  } from '../index';
19
20
  import {
20
21
  Action, HomeLeftNavItem, RuntimeFilter, RuntimeFilterOp, HomepageModule, HostEvent,
@@ -48,7 +49,12 @@ import * as authService from '../utils/authService/authService';
48
49
  import { logger } from '../utils/logger';
49
50
  import { version } from '../../package.json';
50
51
  import { HiddenActionItemByDefaultForSearchEmbed } from './search';
52
+ import { processTrigger } from '../utils/processTrigger';
53
+ import { UIPassthroughEvent } from './hostEventClient/contracts';
51
54
 
55
+ jest.mock('../utils/processTrigger');
56
+
57
+ const mockProcessTrigger = processTrigger as jest.Mock;
52
58
  const defaultViewConfig = {
53
59
  frameParams: {
54
60
  width: 1280,
@@ -126,6 +132,52 @@ describe('Unit test case for ts embed', () => {
126
132
  });
127
133
  });
128
134
  });
135
+
136
+ test('should get answer service', async () => {
137
+ const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
138
+ searchEmbed.render();
139
+ mockProcessTrigger.mockResolvedValue({ session: 'test' });
140
+ await executeAfterWait(async () => {
141
+ expect(await searchEmbed.getAnswerService()).toBeInstanceOf(AnswerService);
142
+ });
143
+ });
144
+
145
+ test('triggerUIPassThrough with params', async () => {
146
+ const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
147
+ searchEmbed.render();
148
+ mockProcessTrigger.mockResolvedValue({ session: 'test' });
149
+ await executeAfterWait(async () => {
150
+ const payload = { newVizName: 'test' };
151
+ expect(
152
+ await searchEmbed.triggerUIPassThrough(
153
+ UIPassthroughEvent.PinAnswerToLiveboard,
154
+ payload,
155
+ ),
156
+ );
157
+ expect(mockProcessTrigger).toHaveBeenCalledWith(
158
+ getIFrameEl(),
159
+ HostEvent.UIPassthrough,
160
+ 'http://tshost',
161
+ {
162
+ parameters: payload,
163
+ type: UIPassthroughEvent.PinAnswerToLiveboard,
164
+ },
165
+ );
166
+ });
167
+ });
168
+
169
+ test('should set proper height, width and min-height to iframe', async () => {
170
+ // we dont have origin specific policies so just checking if
171
+ // policies are ending with ;
172
+ const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
173
+ searchEmbed.render();
174
+ await executeAfterWait(() => {
175
+ const iframe = getIFrameEl();
176
+ expect(iframe.style.width).toBe(`${defaultViewConfig.frameParams.width}px`);
177
+ expect(iframe.style.height).toBe(`${defaultViewConfig.frameParams.height}px`);
178
+ expect(iframe.style.minHeight).toBe(`${defaultViewConfig.frameParams.height}px`);
179
+ });
180
+ });
129
181
  });
130
182
 
131
183
  describe('AuthExpire embedEvent in cookieless authentication authType', () => {
@@ -1338,7 +1390,6 @@ describe('Unit test case for ts embed', () => {
1338
1390
  },
1339
1391
  });
1340
1392
  await appEmbed.render();
1341
- console.log('val ', getIFrameSrc());
1342
1393
  expectUrlMatchesWithParams(
1343
1394
  getIFrameSrc(),
1344
1395
  `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}`
@@ -1804,6 +1855,7 @@ describe('Unit test case for ts embed', () => {
1804
1855
  afterAll(() => {
1805
1856
  const rootEle = document.getElementById('myRoot');
1806
1857
  rootEle.remove();
1858
+ jest.clearAllMocks();
1807
1859
  });
1808
1860
 
1809
1861
  it('should preRender and hide the iframe', async () => {