@thoughtspot/visual-embed-sdk 1.50.0 → 1.51.0-test

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 (285) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/config.d.ts +1 -0
  3. package/cjs/src/config.d.ts.map +1 -1
  4. package/cjs/src/config.js +2 -1
  5. package/cjs/src/config.js.map +1 -1
  6. package/cjs/src/css-variables.d.ts +122 -0
  7. package/cjs/src/css-variables.d.ts.map +1 -1
  8. package/cjs/src/embed/app.d.ts +176 -24
  9. package/cjs/src/embed/app.d.ts.map +1 -1
  10. package/cjs/src/embed/app.js +86 -38
  11. package/cjs/src/embed/app.js.map +1 -1
  12. package/cjs/src/embed/app.spec.js +672 -150
  13. package/cjs/src/embed/app.spec.js.map +1 -1
  14. package/cjs/src/embed/base.d.ts +1 -1
  15. package/cjs/src/embed/base.js +1 -1
  16. package/cjs/src/embed/conversation.d.ts +401 -2
  17. package/cjs/src/embed/conversation.d.ts.map +1 -1
  18. package/cjs/src/embed/conversation.js +32 -16
  19. package/cjs/src/embed/conversation.js.map +1 -1
  20. package/cjs/src/embed/conversation.spec.js +346 -17
  21. package/cjs/src/embed/conversation.spec.js.map +1 -1
  22. package/cjs/src/embed/events.spec.js +638 -0
  23. package/cjs/src/embed/events.spec.js.map +1 -1
  24. package/cjs/src/embed/host-events.spec.js +187 -0
  25. package/cjs/src/embed/host-events.spec.js.map +1 -1
  26. package/cjs/src/embed/hostEventClient/contracts.d.ts +51 -14
  27. package/cjs/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  28. package/cjs/src/embed/hostEventClient/contracts.js +11 -1
  29. package/cjs/src/embed/hostEventClient/contracts.js.map +1 -1
  30. package/cjs/src/embed/hostEventClient/host-event-client.d.ts +2 -1
  31. package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  32. package/cjs/src/embed/hostEventClient/host-event-client.js +9 -0
  33. package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
  34. package/cjs/src/embed/hostEventClient/host-event-client.spec.js +236 -0
  35. package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  36. package/cjs/src/embed/hostEventClient/utils.d.ts +2 -0
  37. package/cjs/src/embed/hostEventClient/utils.d.ts.map +1 -1
  38. package/cjs/src/embed/hostEventClient/utils.js +32 -2
  39. package/cjs/src/embed/hostEventClient/utils.js.map +1 -1
  40. package/cjs/src/embed/hostEventClient/utils.spec.js +178 -0
  41. package/cjs/src/embed/hostEventClient/utils.spec.js.map +1 -1
  42. package/cjs/src/embed/liveboard.d.ts +81 -13
  43. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  44. package/cjs/src/embed/liveboard.js +50 -9
  45. package/cjs/src/embed/liveboard.js.map +1 -1
  46. package/cjs/src/embed/liveboard.spec.js +432 -4
  47. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  48. package/cjs/src/embed/spotter-utils.d.ts +15 -1
  49. package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
  50. package/cjs/src/embed/spotter-utils.js +28 -1
  51. package/cjs/src/embed/spotter-utils.js.map +1 -1
  52. package/cjs/src/embed/spotter-utils.spec.js +129 -0
  53. package/cjs/src/embed/spotter-utils.spec.js.map +1 -1
  54. package/cjs/src/embed/spotter-viz-utils.d.ts +37 -4
  55. package/cjs/src/embed/spotter-viz-utils.d.ts.map +1 -1
  56. package/cjs/src/embed/spotter-viz-utils.js.map +1 -1
  57. package/cjs/src/embed/spotter-viz-utils.spec.js +17 -0
  58. package/cjs/src/embed/spotter-viz-utils.spec.js.map +1 -1
  59. package/cjs/src/embed/ts-embed.d.ts +16 -8
  60. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  61. package/cjs/src/embed/ts-embed.js +96 -37
  62. package/cjs/src/embed/ts-embed.js.map +1 -1
  63. package/cjs/src/embed/ts-embed.spec.js +683 -3
  64. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  65. package/cjs/src/errors.d.ts +1 -0
  66. package/cjs/src/errors.d.ts.map +1 -1
  67. package/cjs/src/errors.js +1 -0
  68. package/cjs/src/errors.js.map +1 -1
  69. package/cjs/src/index.d.ts +3 -3
  70. package/cjs/src/index.d.ts.map +1 -1
  71. package/cjs/src/index.js +4 -2
  72. package/cjs/src/index.js.map +1 -1
  73. package/cjs/src/mixpanel-service.d.ts +4 -0
  74. package/cjs/src/mixpanel-service.d.ts.map +1 -1
  75. package/cjs/src/mixpanel-service.js +4 -0
  76. package/cjs/src/mixpanel-service.js.map +1 -1
  77. package/cjs/src/react/all-types-export.d.ts +1 -1
  78. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  79. package/cjs/src/react/all-types-export.js +2 -1
  80. package/cjs/src/react/all-types-export.js.map +1 -1
  81. package/cjs/src/react/all-types-export.parity.spec.d.ts +2 -0
  82. package/cjs/src/react/all-types-export.parity.spec.d.ts.map +1 -0
  83. package/cjs/src/react/all-types-export.parity.spec.js +194 -0
  84. package/cjs/src/react/all-types-export.parity.spec.js.map +1 -0
  85. package/cjs/src/react/index.d.ts +66 -32
  86. package/cjs/src/react/index.d.ts.map +1 -1
  87. package/cjs/src/react/index.js +5 -3
  88. package/cjs/src/react/index.js.map +1 -1
  89. package/cjs/src/react/index.spec.js +101 -0
  90. package/cjs/src/react/index.spec.js.map +1 -1
  91. package/cjs/src/react/util.spec.js +251 -0
  92. package/cjs/src/react/util.spec.js.map +1 -1
  93. package/cjs/src/types.d.ts +902 -28
  94. package/cjs/src/types.d.ts.map +1 -1
  95. package/cjs/src/types.js +701 -14
  96. package/cjs/src/types.js.map +1 -1
  97. package/cjs/src/utils/processData.spec.js +93 -0
  98. package/cjs/src/utils/processData.spec.js.map +1 -1
  99. package/cjs/src/utils/processTrigger.spec.js +61 -0
  100. package/cjs/src/utils/processTrigger.spec.js.map +1 -1
  101. package/cjs/src/utils.spec.js +4 -0
  102. package/cjs/src/utils.spec.js.map +1 -1
  103. package/dist/{index-B5xoN6Cp.js → index-Bivg-mDx.js} +1 -1
  104. package/dist/src/config.d.ts +1 -0
  105. package/dist/src/config.d.ts.map +1 -1
  106. package/dist/src/css-variables.d.ts +122 -0
  107. package/dist/src/css-variables.d.ts.map +1 -1
  108. package/dist/src/embed/app.d.ts +176 -24
  109. package/dist/src/embed/app.d.ts.map +1 -1
  110. package/dist/src/embed/base.d.ts +1 -1
  111. package/dist/src/embed/conversation.d.ts +401 -2
  112. package/dist/src/embed/conversation.d.ts.map +1 -1
  113. package/dist/src/embed/hostEventClient/contracts.d.ts +51 -14
  114. package/dist/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  115. package/dist/src/embed/hostEventClient/host-event-client.d.ts +2 -1
  116. package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  117. package/dist/src/embed/hostEventClient/utils.d.ts +2 -0
  118. package/dist/src/embed/hostEventClient/utils.d.ts.map +1 -1
  119. package/dist/src/embed/liveboard.d.ts +81 -13
  120. package/dist/src/embed/liveboard.d.ts.map +1 -1
  121. package/dist/src/embed/spotter-utils.d.ts +15 -1
  122. package/dist/src/embed/spotter-utils.d.ts.map +1 -1
  123. package/dist/src/embed/spotter-viz-utils.d.ts +37 -4
  124. package/dist/src/embed/spotter-viz-utils.d.ts.map +1 -1
  125. package/dist/src/embed/ts-embed.d.ts +16 -8
  126. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  127. package/dist/src/errors.d.ts +1 -0
  128. package/dist/src/errors.d.ts.map +1 -1
  129. package/dist/src/index.d.ts +3 -3
  130. package/dist/src/index.d.ts.map +1 -1
  131. package/dist/src/mixpanel-service.d.ts +4 -0
  132. package/dist/src/mixpanel-service.d.ts.map +1 -1
  133. package/dist/src/react/all-types-export.d.ts +1 -1
  134. package/dist/src/react/all-types-export.d.ts.map +1 -1
  135. package/dist/src/react/all-types-export.parity.spec.d.ts +2 -0
  136. package/dist/src/react/all-types-export.parity.spec.d.ts.map +1 -0
  137. package/dist/src/react/index.d.ts +66 -32
  138. package/dist/src/react/index.d.ts.map +1 -1
  139. package/dist/src/types.d.ts +902 -28
  140. package/dist/src/types.d.ts.map +1 -1
  141. package/dist/tsembed-react.es.js +1269 -206
  142. package/dist/tsembed-react.js +1268 -205
  143. package/dist/tsembed.es.js +1364 -304
  144. package/dist/tsembed.js +1362 -302
  145. package/dist/visual-embed-sdk-react-full.d.ts +1867 -157
  146. package/dist/visual-embed-sdk-react.d.ts +1872 -161
  147. package/dist/visual-embed-sdk.d.ts +1815 -127
  148. package/lib/package.json +1 -1
  149. package/lib/src/config.d.ts +1 -0
  150. package/lib/src/config.d.ts.map +1 -1
  151. package/lib/src/config.js +1 -0
  152. package/lib/src/config.js.map +1 -1
  153. package/lib/src/css-variables.d.ts +122 -0
  154. package/lib/src/css-variables.d.ts.map +1 -1
  155. package/lib/src/embed/app.d.ts +176 -24
  156. package/lib/src/embed/app.d.ts.map +1 -1
  157. package/lib/src/embed/app.js +87 -39
  158. package/lib/src/embed/app.js.map +1 -1
  159. package/lib/src/embed/app.spec.js +673 -151
  160. package/lib/src/embed/app.spec.js.map +1 -1
  161. package/lib/src/embed/base.d.ts +1 -1
  162. package/lib/src/embed/base.js +1 -1
  163. package/lib/src/embed/conversation.d.ts +401 -2
  164. package/lib/src/embed/conversation.d.ts.map +1 -1
  165. package/lib/src/embed/conversation.js +33 -17
  166. package/lib/src/embed/conversation.js.map +1 -1
  167. package/lib/src/embed/conversation.spec.js +347 -18
  168. package/lib/src/embed/conversation.spec.js.map +1 -1
  169. package/lib/src/embed/events.spec.js +638 -0
  170. package/lib/src/embed/events.spec.js.map +1 -1
  171. package/lib/src/embed/host-events.spec.js +188 -1
  172. package/lib/src/embed/host-events.spec.js.map +1 -1
  173. package/lib/src/embed/hostEventClient/contracts.d.ts +51 -14
  174. package/lib/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  175. package/lib/src/embed/hostEventClient/contracts.js +10 -0
  176. package/lib/src/embed/hostEventClient/contracts.js.map +1 -1
  177. package/lib/src/embed/hostEventClient/host-event-client.d.ts +2 -1
  178. package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  179. package/lib/src/embed/hostEventClient/host-event-client.js +10 -1
  180. package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
  181. package/lib/src/embed/hostEventClient/host-event-client.spec.js +236 -0
  182. package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  183. package/lib/src/embed/hostEventClient/utils.d.ts +2 -0
  184. package/lib/src/embed/hostEventClient/utils.d.ts.map +1 -1
  185. package/lib/src/embed/hostEventClient/utils.js +28 -1
  186. package/lib/src/embed/hostEventClient/utils.js.map +1 -1
  187. package/lib/src/embed/hostEventClient/utils.spec.js +179 -1
  188. package/lib/src/embed/hostEventClient/utils.spec.js.map +1 -1
  189. package/lib/src/embed/liveboard.d.ts +81 -13
  190. package/lib/src/embed/liveboard.d.ts.map +1 -1
  191. package/lib/src/embed/liveboard.js +50 -9
  192. package/lib/src/embed/liveboard.js.map +1 -1
  193. package/lib/src/embed/liveboard.spec.js +432 -4
  194. package/lib/src/embed/liveboard.spec.js.map +1 -1
  195. package/lib/src/embed/spotter-utils.d.ts +15 -1
  196. package/lib/src/embed/spotter-utils.d.ts.map +1 -1
  197. package/lib/src/embed/spotter-utils.js +25 -0
  198. package/lib/src/embed/spotter-utils.js.map +1 -1
  199. package/lib/src/embed/spotter-utils.spec.js +130 -1
  200. package/lib/src/embed/spotter-utils.spec.js.map +1 -1
  201. package/lib/src/embed/spotter-viz-utils.d.ts +37 -4
  202. package/lib/src/embed/spotter-viz-utils.d.ts.map +1 -1
  203. package/lib/src/embed/spotter-viz-utils.js.map +1 -1
  204. package/lib/src/embed/spotter-viz-utils.spec.js +17 -0
  205. package/lib/src/embed/spotter-viz-utils.spec.js.map +1 -1
  206. package/lib/src/embed/ts-embed.d.ts +16 -8
  207. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  208. package/lib/src/embed/ts-embed.js +96 -37
  209. package/lib/src/embed/ts-embed.js.map +1 -1
  210. package/lib/src/embed/ts-embed.spec.js +683 -3
  211. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  212. package/lib/src/errors.d.ts +1 -0
  213. package/lib/src/errors.d.ts.map +1 -1
  214. package/lib/src/errors.js +1 -0
  215. package/lib/src/errors.js.map +1 -1
  216. package/lib/src/index.d.ts +3 -3
  217. package/lib/src/index.d.ts.map +1 -1
  218. package/lib/src/index.js +3 -3
  219. package/lib/src/index.js.map +1 -1
  220. package/lib/src/mixpanel-service.d.ts +4 -0
  221. package/lib/src/mixpanel-service.d.ts.map +1 -1
  222. package/lib/src/mixpanel-service.js +4 -0
  223. package/lib/src/mixpanel-service.js.map +1 -1
  224. package/lib/src/react/all-types-export.d.ts +1 -1
  225. package/lib/src/react/all-types-export.d.ts.map +1 -1
  226. package/lib/src/react/all-types-export.js +1 -1
  227. package/lib/src/react/all-types-export.js.map +1 -1
  228. package/lib/src/react/all-types-export.parity.spec.d.ts +2 -0
  229. package/lib/src/react/all-types-export.parity.spec.d.ts.map +1 -0
  230. package/lib/src/react/all-types-export.parity.spec.js +191 -0
  231. package/lib/src/react/all-types-export.parity.spec.js.map +1 -0
  232. package/lib/src/react/index.d.ts +66 -32
  233. package/lib/src/react/index.d.ts.map +1 -1
  234. package/lib/src/react/index.js +5 -3
  235. package/lib/src/react/index.js.map +1 -1
  236. package/lib/src/react/index.spec.js +102 -1
  237. package/lib/src/react/index.spec.js.map +1 -1
  238. package/lib/src/react/util.spec.js +251 -0
  239. package/lib/src/react/util.spec.js.map +1 -1
  240. package/lib/src/types.d.ts +902 -28
  241. package/lib/src/types.d.ts.map +1 -1
  242. package/lib/src/types.js +701 -14
  243. package/lib/src/types.js.map +1 -1
  244. package/lib/src/utils/processData.spec.js +93 -0
  245. package/lib/src/utils/processData.spec.js.map +1 -1
  246. package/lib/src/utils/processTrigger.spec.js +61 -0
  247. package/lib/src/utils/processTrigger.spec.js.map +1 -1
  248. package/lib/src/utils.spec.js +4 -0
  249. package/lib/src/utils.spec.js.map +1 -1
  250. package/lib/src/visual-embed-sdk.d.ts +1815 -127
  251. package/package.json +1 -1
  252. package/src/config.ts +1 -0
  253. package/src/css-variables.ts +150 -0
  254. package/src/embed/app.spec.ts +877 -196
  255. package/src/embed/app.ts +276 -75
  256. package/src/embed/base.ts +1 -1
  257. package/src/embed/conversation.spec.ts +411 -21
  258. package/src/embed/conversation.ts +441 -18
  259. package/src/embed/events.spec.ts +761 -1
  260. package/src/embed/host-events.spec.ts +242 -0
  261. package/src/embed/hostEventClient/contracts.ts +57 -14
  262. package/src/embed/hostEventClient/host-event-client.spec.ts +372 -0
  263. package/src/embed/hostEventClient/host-event-client.ts +18 -2
  264. package/src/embed/hostEventClient/utils.spec.ts +204 -0
  265. package/src/embed/hostEventClient/utils.ts +37 -10
  266. package/src/embed/liveboard.spec.ts +565 -4
  267. package/src/embed/liveboard.ts +155 -28
  268. package/src/embed/spotter-utils.spec.ts +140 -1
  269. package/src/embed/spotter-utils.ts +36 -1
  270. package/src/embed/spotter-viz-utils.spec.ts +17 -0
  271. package/src/embed/spotter-viz-utils.ts +37 -4
  272. package/src/embed/ts-embed.spec.ts +885 -3
  273. package/src/embed/ts-embed.ts +111 -39
  274. package/src/errors.ts +1 -0
  275. package/src/index.ts +45 -2
  276. package/src/mixpanel-service.ts +4 -0
  277. package/src/react/all-types-export.parity.spec.ts +208 -0
  278. package/src/react/all-types-export.ts +22 -0
  279. package/src/react/index.spec.tsx +158 -0
  280. package/src/react/index.tsx +70 -33
  281. package/src/react/util.spec.tsx +255 -0
  282. package/src/types.ts +904 -24
  283. package/src/utils/processData.spec.ts +97 -0
  284. package/src/utils/processTrigger.spec.ts +91 -0
  285. package/src/utils.spec.ts +5 -0
@@ -311,6 +311,82 @@ describe('HostEventClient', () => {
311
311
  );
312
312
  });
313
313
 
314
+ it('should dispatch UpdateParameters over the legacy channel', async () => {
315
+ const { client, mockIframe } = createHostEventClient();
316
+ const payload = [
317
+ { name: 'param1', value: 10, applicability: { level: 'TAB', targetId: 'tab-guid-1' } },
318
+ ] as any;
319
+ mockProcessTrigger.mockResolvedValueOnce({ success: true });
320
+
321
+ const result = await client.triggerHostEvent(HostEvent.UpdateParameters, payload);
322
+
323
+ expect(mockProcessTrigger).toHaveBeenCalledTimes(1);
324
+ expect(mockProcessTrigger).toHaveBeenCalledWith(
325
+ mockIframe,
326
+ HostEvent.UpdateParameters,
327
+ mockThoughtSpotHost,
328
+ payload,
329
+ undefined,
330
+ );
331
+ expect(result).toEqual({ success: true });
332
+ });
333
+
334
+ it('should forward UpdateParameters with null applicability', async () => {
335
+ const { client, mockIframe } = createHostEventClient();
336
+ const payload = [{ name: 'param1', value: 10, applicability: null }] as any;
337
+ mockProcessTrigger.mockResolvedValueOnce({ success: true });
338
+
339
+ await client.triggerHostEvent(HostEvent.UpdateParameters, payload);
340
+
341
+ expect(mockProcessTrigger).toHaveBeenCalledWith(
342
+ mockIframe,
343
+ HostEvent.UpdateParameters,
344
+ mockThoughtSpotHost,
345
+ payload,
346
+ undefined,
347
+ );
348
+ });
349
+
350
+ it('should throw when UpdateParameters entry has invalid applicability', async () => {
351
+ const { client } = createHostEventClient();
352
+ const invalidPayload = [
353
+ { name: 'param1', value: 10, applicability: { level: 'TAB' } }, // missing targetId
354
+ ] as any;
355
+
356
+ await expect(client.triggerHostEvent(HostEvent.UpdateParameters, invalidPayload))
357
+ .rejects.toThrow('UpdateParameters received an invalid applicability');
358
+ expect(mockProcessTrigger).not.toHaveBeenCalled();
359
+ });
360
+
361
+ it('should pass context to UpdateParameters event', async () => {
362
+ const { client, mockIframe } = createHostEventClient();
363
+ const payload = [{ name: 'param1', value: 10 }] as any;
364
+ const context = { vizId: 'viz-1' } as any;
365
+ mockProcessTrigger.mockResolvedValueOnce({ success: true });
366
+
367
+ await client.triggerHostEvent(HostEvent.UpdateParameters, payload, context);
368
+
369
+ expect(mockProcessTrigger).toHaveBeenCalledWith(
370
+ mockIframe,
371
+ HostEvent.UpdateParameters,
372
+ mockThoughtSpotHost,
373
+ payload,
374
+ context,
375
+ );
376
+ });
377
+
378
+ it('should route GetGroups through passthrough and return data', async () => {
379
+ const { client } = createHostEventClient();
380
+ const mockResponse = [{ value: { orderedGroupIds: ['g1', 'g2'], numberOfGroups: 2, Groups: [] as any[] } }];
381
+ mockProcessTrigger
382
+ .mockResolvedValueOnce(mockGetAvailablePassthroughs())
383
+ .mockResolvedValueOnce(mockResponse);
384
+
385
+ const result = await client.triggerHostEvent(HostEvent.GetGroups, {});
386
+
387
+ expect(result).toEqual({ orderedGroupIds: ['g1', 'g2'], numberOfGroups: 2, Groups: [] });
388
+ });
389
+
314
390
  it('should throw when DrillDown payload has no valid points', async () => {
315
391
  const { client } = createHostEventClient();
316
392
  const invalidPayload = {} as any;
@@ -816,4 +892,300 @@ describe('HostEventClient', () => {
816
892
  expect(result).toEqual({ ok: true });
817
893
  });
818
894
  });
895
+
896
+ describe('availablePassthroughKeysCache reuse', () => {
897
+ const allKeys = () => [{ value: { keys: Object.values(UIPassthroughEvent) } }];
898
+
899
+ it('GetFilters: second call reuses cache — GetAvailableUIPassthroughs called only once', async () => {
900
+ const { client } = createHostEventClient();
901
+ const filterResponse = [{ value: { liveboardFilters: [] as any[], runtimeFilters: [] as any[] } }];
902
+ mockProcessTrigger
903
+ .mockResolvedValueOnce(allKeys())
904
+ .mockResolvedValueOnce(filterResponse)
905
+ .mockResolvedValueOnce(filterResponse);
906
+
907
+ await client.triggerHostEvent(HostEvent.GetFilters, {});
908
+ await client.triggerHostEvent(HostEvent.GetFilters, {});
909
+
910
+ // 3 calls total: 1 GetAvailableUIPassthroughs + 2 GetFilters
911
+ expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
912
+ expect(mockProcessTrigger).not.toHaveBeenNthCalledWith(
913
+ 3,
914
+ expect.anything(),
915
+ HostEvent.UIPassthrough,
916
+ mockThoughtSpotHost,
917
+ expect.objectContaining({ type: UIPassthroughEvent.GetAvailableUIPassthroughs }),
918
+ undefined,
919
+ );
920
+ });
921
+
922
+ it('GetTabs: second call reuses cache — GetAvailableUIPassthroughs called only once', async () => {
923
+ const { client } = createHostEventClient();
924
+ const tabResponse = [{ value: { orderedTabIds: [] as any[], numberOfTabs: 0, Tabs: [] as any[] } }];
925
+ mockProcessTrigger
926
+ .mockResolvedValueOnce(allKeys())
927
+ .mockResolvedValueOnce(tabResponse)
928
+ .mockResolvedValueOnce(tabResponse);
929
+
930
+ await client.triggerHostEvent(HostEvent.GetTabs, {});
931
+ await client.triggerHostEvent(HostEvent.GetTabs, {});
932
+
933
+ expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
934
+ });
935
+
936
+ it('GetParameters: second call reuses cache — GetAvailableUIPassthroughs called only once', async () => {
937
+ const { client } = createHostEventClient();
938
+ const paramResponse = [{ value: { parameters: [] as any[] } }];
939
+ mockProcessTrigger
940
+ .mockResolvedValueOnce(allKeys())
941
+ .mockResolvedValueOnce(paramResponse)
942
+ .mockResolvedValueOnce(paramResponse);
943
+
944
+ await client.triggerHostEvent(HostEvent.GetParameters, {});
945
+ await client.triggerHostEvent(HostEvent.GetParameters, {});
946
+
947
+ expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
948
+ });
949
+
950
+ it('GetAnswerSession: second call reuses cache — GetAvailableUIPassthroughs called only once', async () => {
951
+ const { client } = createHostEventClient();
952
+ const sessionResponse = [{ value: { session: 's1' } }];
953
+ mockProcessTrigger
954
+ .mockResolvedValueOnce(allKeys())
955
+ .mockResolvedValueOnce(sessionResponse)
956
+ .mockResolvedValueOnce(sessionResponse);
957
+
958
+ await client.triggerHostEvent(HostEvent.GetAnswerSession, {});
959
+ await client.triggerHostEvent(HostEvent.GetAnswerSession, {});
960
+
961
+ expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
962
+ });
963
+
964
+ it('UIPassthrough direct trigger does not populate the cache', async () => {
965
+ const { client } = createHostEventClient();
966
+ mockProcessTrigger.mockResolvedValue([{ value: { result: 'ok' } }]);
967
+
968
+ await client.triggerHostEvent(HostEvent.UIPassthrough, {});
969
+ // Cache not populated — next GetFilters call must still fetch keys
970
+ mockProcessTrigger.mockResolvedValueOnce(allKeys());
971
+ mockProcessTrigger.mockResolvedValueOnce([{ value: { liveboardFilters: [], runtimeFilters: [] as any[] } }]);
972
+ await client.triggerHostEvent(HostEvent.GetFilters, {});
973
+
974
+ // UIPassthrough call + GetAvailableUIPassthroughs + GetFilters = 3
975
+ expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
976
+ });
977
+
978
+ it('GetAnswerSession: catch in getAvailableUIPassthroughKeys does not cache, so next call retries GetAvailableUIPassthroughs', async () => {
979
+ const { client } = createHostEventClient();
980
+ mockProcessTrigger.mockReset();
981
+
982
+ // Flow per triggerHostEvent call when GetAvailableUIPassthroughs rejects:
983
+ // 1. getAvailableUIPassthroughKeys → processTrigger(UIPassthrough, GetAvailableUIPassthroughs) → rejects → returns []
984
+ // 2. keys=[] so getDataWithPassthroughFallback is called →
985
+ // triggerUIPassthroughApi(GetAnswerSession) → processTrigger(UIPassthrough) → null → hostEventFallback called
986
+ // 3. hostEventFallback → processTrigger(GetAnswerSession) → legacy result
987
+
988
+ // First triggerHostEvent: 3 calls
989
+ mockProcessTrigger.mockRejectedValueOnce(new Error('network error')); // call 1
990
+ mockProcessTrigger.mockResolvedValueOnce(null); // call 2
991
+ mockProcessTrigger.mockResolvedValueOnce({ session: 'leg1' }); // call 3
992
+
993
+ await client.triggerHostEvent(HostEvent.GetAnswerSession, { vizId: '1' });
994
+
995
+ // Second triggerHostEvent: cache was NOT set, so GetAvailableUIPassthroughs is retried: 3 more calls
996
+ mockProcessTrigger.mockRejectedValueOnce(new Error('network error')); // call 4
997
+ mockProcessTrigger.mockResolvedValueOnce(null); // call 5
998
+ mockProcessTrigger.mockResolvedValueOnce({ session: 'leg2' }); // call 6
999
+
1000
+ await client.triggerHostEvent(HostEvent.GetAnswerSession, { vizId: '2' });
1001
+
1002
+ // 6 total calls: GetAvailableUIPassthroughs was called TWICE (cache not set after catch)
1003
+ expect(mockProcessTrigger).toHaveBeenCalledTimes(6);
1004
+ const availableCallsCount = mockProcessTrigger.mock.calls.filter(
1005
+ (call) => call[3]?.type === UIPassthroughEvent.GetAvailableUIPassthroughs,
1006
+ ).length;
1007
+ expect(availableCallsCount).toBe(2);
1008
+ });
1009
+ });
1010
+
1011
+ describe('GetTML edge cases', () => {
1012
+ const allKeys = () => [{ value: { keys: Object.values(UIPassthroughEvent) } }];
1013
+
1014
+ it('includeNonExecutedSearchTokens flag is forwarded in UIPassthrough parameters', async () => {
1015
+ const { client, mockIframe } = createHostEventClient();
1016
+ mockProcessTrigger
1017
+ .mockResolvedValueOnce(allKeys())
1018
+ .mockResolvedValueOnce([{ value: { tml: 'data' } }]);
1019
+
1020
+ await client.triggerHostEvent(HostEvent.GetTML, { includeNonExecutedSearchTokens: true } as any);
1021
+
1022
+ expect(mockProcessTrigger).toHaveBeenNthCalledWith(
1023
+ 2,
1024
+ mockIframe,
1025
+ HostEvent.UIPassthrough,
1026
+ mockThoughtSpotHost,
1027
+ {
1028
+ type: UIPassthroughEvent.GetTML,
1029
+ parameters: { includeNonExecutedSearchTokens: true },
1030
+ },
1031
+ undefined,
1032
+ );
1033
+ });
1034
+
1035
+ it('value.error (singular) throws — distinct from value.errors', async () => {
1036
+ const { client } = createHostEventClient();
1037
+ mockProcessTrigger
1038
+ .mockResolvedValueOnce(allKeys())
1039
+ .mockResolvedValueOnce([{ value: { error: 'TML fetch failed' } }]);
1040
+
1041
+ await expect(client.triggerHostEvent(HostEvent.GetTML, {}))
1042
+ .rejects.toThrow('TML fetch failed');
1043
+ });
1044
+
1045
+ it('GetTML key absent from non-empty keys array falls back to legacy hostEventFallback', async () => {
1046
+ const { client, mockIframe } = createHostEventClient();
1047
+ // Return keys that do NOT include GetTML
1048
+ mockProcessTrigger
1049
+ .mockResolvedValueOnce([{ value: { keys: [UIPassthroughEvent.GetFilters] } }])
1050
+ .mockResolvedValueOnce({ answer: { search_query: 'revenue' } });
1051
+
1052
+ const result = await client.triggerHostEvent(HostEvent.GetTML, {});
1053
+
1054
+ expect(mockProcessTrigger).toHaveBeenNthCalledWith(
1055
+ 2,
1056
+ mockIframe,
1057
+ HostEvent.GetTML,
1058
+ mockThoughtSpotHost,
1059
+ {},
1060
+ undefined,
1061
+ );
1062
+ expect(result).toEqual({ answer: { search_query: 'revenue' } });
1063
+ });
1064
+ });
1065
+
1066
+ describe('SaveAnswer key-presence edge cases', () => {
1067
+ const allKeys = () => [{ value: { keys: Object.values(UIPassthroughEvent) } }];
1068
+ const saveResponse = [{
1069
+ value: {
1070
+ saveResponse: {
1071
+ data: {
1072
+ Answer__save: {
1073
+ answer: { id: 'ans123' },
1074
+ },
1075
+ },
1076
+ },
1077
+ },
1078
+ refId: 'viz1',
1079
+ }];
1080
+
1081
+ it('name+description both empty string route through passthrough, not Save fallback', async () => {
1082
+ const { client, mockIframe } = createHostEventClient();
1083
+ mockProcessTrigger
1084
+ .mockResolvedValueOnce(allKeys())
1085
+ .mockResolvedValueOnce(saveResponse);
1086
+
1087
+ await client.triggerHostEvent(HostEvent.SaveAnswer, { name: '', description: '' } as any);
1088
+
1089
+ expect(mockProcessTrigger).toHaveBeenNthCalledWith(
1090
+ 2,
1091
+ mockIframe,
1092
+ HostEvent.UIPassthrough,
1093
+ mockThoughtSpotHost,
1094
+ { type: UIPassthroughEvent.SaveAnswer, parameters: { name: '', description: '' } },
1095
+ undefined,
1096
+ );
1097
+ });
1098
+
1099
+ it('description: undefined (key present) routes through passthrough, not Save fallback', async () => {
1100
+ const { client, mockIframe } = createHostEventClient();
1101
+ mockProcessTrigger
1102
+ .mockResolvedValueOnce(allKeys())
1103
+ .mockResolvedValueOnce(saveResponse);
1104
+
1105
+ await client.triggerHostEvent(HostEvent.SaveAnswer, { name: 'Test', description: undefined } as any);
1106
+
1107
+ expect(mockProcessTrigger).toHaveBeenNthCalledWith(
1108
+ 2,
1109
+ mockIframe,
1110
+ HostEvent.UIPassthrough,
1111
+ mockThoughtSpotHost,
1112
+ { type: UIPassthroughEvent.SaveAnswer, parameters: { name: 'Test', description: undefined } },
1113
+ undefined,
1114
+ );
1115
+ });
1116
+
1117
+ it('name key absent falls back to HostEvent.Save', async () => {
1118
+ const { client, mockIframe } = createHostEventClient();
1119
+ mockProcessTrigger
1120
+ .mockResolvedValueOnce(allKeys())
1121
+ .mockResolvedValueOnce([saveResponse[0].value]);
1122
+
1123
+ await client.triggerHostEvent(HostEvent.SaveAnswer, { description: 'desc only' } as any);
1124
+
1125
+ expect(mockProcessTrigger).toHaveBeenCalledWith(
1126
+ mockIframe,
1127
+ HostEvent.Save,
1128
+ mockThoughtSpotHost,
1129
+ { description: 'desc only' },
1130
+ undefined,
1131
+ );
1132
+ });
1133
+
1134
+ it('answerId is undefined when saveResponse.data.Answer__save.answer.id is missing', async () => {
1135
+ const { client } = createHostEventClient();
1136
+ const noIdSaveResponse = [{
1137
+ value: {
1138
+ saveResponse: {
1139
+ data: {
1140
+ Answer__save: {
1141
+ answer: {},
1142
+ },
1143
+ },
1144
+ },
1145
+ },
1146
+ refId: 'viz1',
1147
+ }];
1148
+ mockProcessTrigger
1149
+ .mockResolvedValueOnce(allKeys())
1150
+ .mockResolvedValueOnce(noIdSaveResponse);
1151
+
1152
+ const result = await client.triggerHostEvent(HostEvent.SaveAnswer, { name: 'Test', description: 'Desc' } as any);
1153
+
1154
+ expect(result.answerId).toBeUndefined();
1155
+ });
1156
+ });
1157
+
1158
+ describe('trigger typing contracts (compile-time)', () => {
1159
+ // These assertions are enforced by ts-jest at compile time; the runtime
1160
+ // expect() is trivial. They lock the PayloadT default on
1161
+ // triggerHostEvent: no-payload calls must infer the contract response
1162
+ // instead of collapsing to `any`.
1163
+ type IsAny<T> = 0 extends 1 & T ? true : false;
1164
+ type Extends<A, B> = A extends B ? true : false;
1165
+
1166
+ it('infers typed response for no-payload contracted events and keeps any for uncontracted', () => {
1167
+ const { client } = createHostEventClient();
1168
+
1169
+ // Contracted event, called with no payload — the fixed case.
1170
+ const getTabs = () => client.triggerHostEvent(HostEvent.GetTabs);
1171
+ type TabsResponse = Awaited<ReturnType<typeof getTabs>>;
1172
+ const tabsIsTyped: IsAny<TabsResponse> = false;
1173
+ const tabsHasContractFields: Extends<
1174
+ TabsResponse,
1175
+ { numberOfTabs: number; orderedTabIds: string[] }
1176
+ > = true;
1177
+ const probeUnknownField = (r: TabsResponse) =>
1178
+ // @ts-expect-error — field not on the GetTabs contract (was silent when `any`)
1179
+ r.tabCount;
1180
+ void probeUnknownField;
1181
+
1182
+ // Uncontracted event — must remain `any` with or without payload.
1183
+ const reload = () => client.triggerHostEvent(HostEvent.Reload);
1184
+ type ReloadResponse = Awaited<ReturnType<typeof reload>>;
1185
+ const reloadStaysAny: IsAny<ReloadResponse> = true;
1186
+
1187
+ expect([tabsIsTyped, tabsHasContractFields, reloadStaysAny])
1188
+ .toEqual([false, true, true]);
1189
+ });
1190
+ });
819
1191
  });
@@ -3,8 +3,10 @@ import { processTrigger as processTriggerService } from '../../utils/processTrig
3
3
  import { getEmbedConfig } from '../embedConfig';
4
4
  import {
5
5
  isValidUpdateFiltersPayload,
6
+ isValidUpdateParametersPayload,
6
7
  isValidDrillDownPayload,
7
8
  throwUpdateFiltersValidationError,
9
+ throwUpdateParametersValidationError,
8
10
  throwDrillDownValidationError,
9
11
  } from './utils';
10
12
  import {
@@ -36,6 +38,7 @@ const PASSTHROUGH_MAP: Partial<Record<HostEvent, UIPassthroughEvent>> = {
36
38
  [HostEvent.GetParameters]: UIPassthroughEvent.GetParameters,
37
39
  [HostEvent.GetTML]: UIPassthroughEvent.GetTML,
38
40
  [HostEvent.GetTabs]: UIPassthroughEvent.GetTabs,
41
+ [HostEvent.GetGroups]: UIPassthroughEvent.GetGroups,
39
42
  [HostEvent.getExportRequestForCurrentPinboard]: UIPassthroughEvent.GetExportRequestForCurrentPinboard,
40
43
  };
41
44
 
@@ -58,6 +61,7 @@ export class HostEventClient {
58
61
  [HostEvent.Pin]: (p, c) => this.handlePinEvent(p, c),
59
62
  [HostEvent.SaveAnswer]: (p, c) => this.handleSaveAnswerEvent(p, c),
60
63
  [HostEvent.UpdateFilters]: (p, c) => this.handleUpdateFiltersEvent(p, c),
64
+ [HostEvent.UpdateParameters]: (p, c) => this.handleUpdateParametersEvent(p, c),
61
65
  [HostEvent.DrillDown]: (p, c) => this.handleDrillDownEvent(p, c),
62
66
  };
63
67
  }
@@ -243,6 +247,18 @@ export class HostEventClient {
243
247
  return this.handleHostEventWithParam(UIPassthroughEvent.UpdateFilters, payload, context as ContextType);
244
248
  }
245
249
 
250
+ protected handleUpdateParametersEvent(
251
+ payload: HostEventRequest<HostEvent.UpdateParameters>,
252
+ context?: ContextType,
253
+ ): Promise<any> {
254
+ if (!isValidUpdateParametersPayload(payload)) {
255
+ throwUpdateParametersValidationError();
256
+ }
257
+
258
+ // UpdateParameters has no UI passthrough contract; dispatch over the legacy channel
259
+ return this.hostEventFallback(HostEvent.UpdateParameters, payload, context);
260
+ }
261
+
246
262
  protected handleDrillDownEvent(
247
263
  payload: HostEventRequest<HostEvent.DrillDown>,
248
264
  context?: ContextType,
@@ -265,8 +281,8 @@ export class HostEventClient {
265
281
  */
266
282
  public async triggerHostEvent<
267
283
  HostEventT extends HostEvent,
268
- PayloadT,
269
- ContextT extends ContextType,
284
+ PayloadT = HostEventRequest<HostEventT>,
285
+ ContextT extends ContextType = ContextType,
270
286
  >(
271
287
  hostEvent: HostEventT,
272
288
  payload?: TriggerPayload<PayloadT, HostEventT>,
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  isValidUpdateFiltersPayload,
3
+ isValidUpdateParametersPayload,
3
4
  isValidDrillDownPayload,
4
5
  createValidationError,
5
6
  throwUpdateFiltersValidationError,
7
+ throwUpdateParametersValidationError,
6
8
  throwDrillDownValidationError,
7
9
  } from './utils';
8
10
  import { ERROR_MESSAGE } from '../../errors';
@@ -114,6 +116,201 @@ describe('hostEventClient utils', () => {
114
116
  filters: [{ column: 'y' }], // invalid
115
117
  } as any)).toBe(true);
116
118
  });
119
+
120
+ it('returns true for filter without applicability', () => {
121
+ expect(isValidUpdateFiltersPayload({
122
+ filter: { column: 'x', oper: 'EQ', values: ['a'] },
123
+ } as any)).toBe(true);
124
+ });
125
+
126
+ it('returns true for filter with valid applicability', () => {
127
+ expect(isValidUpdateFiltersPayload({
128
+ filter: {
129
+ column: 'x',
130
+ oper: 'EQ',
131
+ values: ['a'],
132
+ applicability: { level: 'TAB', targetId: 'tab-guid-1' },
133
+ },
134
+ } as any)).toBe(true);
135
+ });
136
+
137
+ it('returns true for filters array with applicability on some filters', () => {
138
+ expect(isValidUpdateFiltersPayload({
139
+ filters: [
140
+ {
141
+ column: 'x',
142
+ oper: 'IN',
143
+ values: ['a', 'b'],
144
+ applicability: { level: 'TAB', targetId: 'tab-guid-1' },
145
+ },
146
+ { column: 'y', oper: 'EQ', values: ['c'] },
147
+ ],
148
+ } as any)).toBe(true);
149
+ });
150
+
151
+ it('returns false for applicability missing targetId', () => {
152
+ expect(isValidUpdateFiltersPayload({
153
+ filter: {
154
+ column: 'x',
155
+ oper: 'EQ',
156
+ values: ['a'],
157
+ applicability: { level: 'TAB' },
158
+ },
159
+ } as any)).toBe(false);
160
+ });
161
+
162
+ it('returns false for applicability with empty targetId', () => {
163
+ expect(isValidUpdateFiltersPayload({
164
+ filter: {
165
+ column: 'x',
166
+ oper: 'EQ',
167
+ values: ['a'],
168
+ applicability: { level: 'TAB', targetId: '' },
169
+ },
170
+ } as any)).toBe(false);
171
+ });
172
+
173
+ it('returns false for null applicability', () => {
174
+ expect(isValidUpdateFiltersPayload({
175
+ filter: {
176
+ column: 'x',
177
+ oper: 'EQ',
178
+ values: ['a'],
179
+ applicability: null,
180
+ },
181
+ } as any)).toBe(false);
182
+ });
183
+
184
+ it('returns false for non-object applicability', () => {
185
+ expect(isValidUpdateFiltersPayload({
186
+ filter: {
187
+ column: 'x',
188
+ oper: 'EQ',
189
+ values: ['a'],
190
+ applicability: 'TAB',
191
+ },
192
+ } as any)).toBe(false);
193
+ });
194
+
195
+ it('returns true for LIVEBOARD level applicability without targetId', () => {
196
+ expect(isValidUpdateFiltersPayload({
197
+ filter: {
198
+ column: 'x',
199
+ oper: 'EQ',
200
+ values: ['a'],
201
+ applicability: { level: 'LIVEBOARD' },
202
+ },
203
+ } as any)).toBe(true);
204
+ });
205
+
206
+ it('returns false for applicability missing level', () => {
207
+ expect(isValidUpdateFiltersPayload({
208
+ filter: {
209
+ column: 'x',
210
+ oper: 'EQ',
211
+ values: ['a'],
212
+ applicability: { targetId: 'tab-guid-1' },
213
+ },
214
+ } as any)).toBe(false);
215
+ });
216
+
217
+ it('returns false for applicability with non-string level', () => {
218
+ expect(isValidUpdateFiltersPayload({
219
+ filter: {
220
+ column: 'x',
221
+ oper: 'EQ',
222
+ values: ['a'],
223
+ applicability: { level: 123, targetId: 'tab-guid-1' },
224
+ },
225
+ } as any)).toBe(false);
226
+ });
227
+
228
+ it('returns false if one filter in filters array has invalid applicability', () => {
229
+ expect(isValidUpdateFiltersPayload({
230
+ filters: [
231
+ { column: 'x', oper: 'EQ', values: ['a'] },
232
+ {
233
+ column: 'y',
234
+ oper: 'EQ',
235
+ values: ['b'],
236
+ applicability: { level: 'TAB' }, // missing targetId
237
+ },
238
+ ],
239
+ } as any)).toBe(false);
240
+ });
241
+ });
242
+
243
+ // =========================
244
+ // UpdateParameters Validation
245
+ // =========================
246
+ describe('isValidUpdateParametersPayload', () => {
247
+ it('returns true for undefined payload', () => {
248
+ expect(isValidUpdateParametersPayload(undefined)).toBe(true);
249
+ });
250
+
251
+ it('returns true for non-array payload', () => {
252
+ expect(isValidUpdateParametersPayload({ name: 'p', value: 1 })).toBe(true);
253
+ });
254
+
255
+ it('returns true for parameters without applicability', () => {
256
+ expect(isValidUpdateParametersPayload([
257
+ { name: 'p1', value: 1 },
258
+ { name: 'p2', value: 'a' },
259
+ ])).toBe(true);
260
+ });
261
+
262
+ it('returns true for null applicability', () => {
263
+ expect(isValidUpdateParametersPayload([
264
+ { name: 'p', value: 1, applicability: null },
265
+ ])).toBe(true);
266
+ });
267
+
268
+ it('returns true for valid TAB applicability with targetId', () => {
269
+ expect(isValidUpdateParametersPayload([
270
+ { name: 'p', value: 1, applicability: { level: 'TAB', targetId: 'tab-guid-1' } },
271
+ ])).toBe(true);
272
+ });
273
+
274
+ it('returns true for LIVEBOARD level applicability without targetId', () => {
275
+ expect(isValidUpdateParametersPayload([
276
+ { name: 'p', value: 1, applicability: { level: 'LIVEBOARD' } },
277
+ ])).toBe(true);
278
+ });
279
+
280
+ it('returns false for applicability missing targetId', () => {
281
+ expect(isValidUpdateParametersPayload([
282
+ { name: 'p', value: 1, applicability: { level: 'TAB' } },
283
+ ])).toBe(false);
284
+ });
285
+
286
+ it('returns false for applicability with empty targetId', () => {
287
+ expect(isValidUpdateParametersPayload([
288
+ { name: 'p', value: 1, applicability: { level: 'GROUP', targetId: ' ' } },
289
+ ])).toBe(false);
290
+ });
291
+
292
+ it('returns false for applicability with invalid level', () => {
293
+ expect(isValidUpdateParametersPayload([
294
+ { name: 'p', value: 1, applicability: { level: 'VIZ', targetId: 'guid-1' } },
295
+ ])).toBe(false);
296
+ });
297
+
298
+ it('returns false for non-object applicability', () => {
299
+ expect(isValidUpdateParametersPayload([
300
+ { name: 'p', value: 1, applicability: 'TAB' },
301
+ ])).toBe(false);
302
+ });
303
+
304
+ it('returns false if one parameter has invalid applicability', () => {
305
+ expect(isValidUpdateParametersPayload([
306
+ { name: 'p1', value: 1 },
307
+ { name: 'p2', value: 2, applicability: { level: 'GROUP' } }, // missing targetId
308
+ ])).toBe(false);
309
+ });
310
+
311
+ it('returns true for non-object entries', () => {
312
+ expect(isValidUpdateParametersPayload(['p', 1, null])).toBe(true);
313
+ });
117
314
  });
118
315
 
119
316
  // =========================
@@ -216,6 +413,13 @@ describe('hostEventClient utils', () => {
216
413
  });
217
414
  });
218
415
 
416
+ describe('throwUpdateParametersValidationError', () => {
417
+ it('throws with UPDATEPARAMETERS_INVALID_PAYLOAD message', () => {
418
+ expect(() => throwUpdateParametersValidationError())
419
+ .toThrow(ERROR_MESSAGE.UPDATEPARAMETERS_INVALID_PAYLOAD);
420
+ });
421
+ });
422
+
219
423
  describe('throwDrillDownValidationError', () => {
220
424
  it('throws with DRILLDOWN_INVALID_PAYLOAD message', () => {
221
425
  expect(() => throwDrillDownValidationError())