@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.
- package/cjs/package.json +1 -1
- package/cjs/src/config.d.ts +1 -0
- package/cjs/src/config.d.ts.map +1 -1
- package/cjs/src/config.js +2 -1
- package/cjs/src/config.js.map +1 -1
- package/cjs/src/css-variables.d.ts +122 -0
- package/cjs/src/css-variables.d.ts.map +1 -1
- package/cjs/src/embed/app.d.ts +176 -24
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +86 -38
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +672 -150
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/base.d.ts +1 -1
- package/cjs/src/embed/base.js +1 -1
- package/cjs/src/embed/conversation.d.ts +401 -2
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js +32 -16
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/conversation.spec.js +346 -17
- package/cjs/src/embed/conversation.spec.js.map +1 -1
- package/cjs/src/embed/events.spec.js +638 -0
- package/cjs/src/embed/events.spec.js.map +1 -1
- package/cjs/src/embed/host-events.spec.js +187 -0
- package/cjs/src/embed/host-events.spec.js.map +1 -1
- package/cjs/src/embed/hostEventClient/contracts.d.ts +51 -14
- package/cjs/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/contracts.js +11 -1
- package/cjs/src/embed/hostEventClient/contracts.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts +2 -1
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.js +9 -0
- package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.spec.js +236 -0
- package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
- package/cjs/src/embed/hostEventClient/utils.d.ts +2 -0
- package/cjs/src/embed/hostEventClient/utils.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/utils.js +32 -2
- package/cjs/src/embed/hostEventClient/utils.js.map +1 -1
- package/cjs/src/embed/hostEventClient/utils.spec.js +178 -0
- package/cjs/src/embed/hostEventClient/utils.spec.js.map +1 -1
- package/cjs/src/embed/liveboard.d.ts +81 -13
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +50 -9
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +432 -4
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/embed/spotter-utils.d.ts +15 -1
- package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
- package/cjs/src/embed/spotter-utils.js +28 -1
- package/cjs/src/embed/spotter-utils.js.map +1 -1
- package/cjs/src/embed/spotter-utils.spec.js +129 -0
- package/cjs/src/embed/spotter-utils.spec.js.map +1 -1
- package/cjs/src/embed/spotter-viz-utils.d.ts +37 -4
- package/cjs/src/embed/spotter-viz-utils.d.ts.map +1 -1
- package/cjs/src/embed/spotter-viz-utils.js.map +1 -1
- package/cjs/src/embed/spotter-viz-utils.spec.js +17 -0
- package/cjs/src/embed/spotter-viz-utils.spec.js.map +1 -1
- package/cjs/src/embed/ts-embed.d.ts +16 -8
- package/cjs/src/embed/ts-embed.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.js +96 -37
- package/cjs/src/embed/ts-embed.js.map +1 -1
- package/cjs/src/embed/ts-embed.spec.js +683 -3
- package/cjs/src/embed/ts-embed.spec.js.map +1 -1
- package/cjs/src/errors.d.ts +1 -0
- package/cjs/src/errors.d.ts.map +1 -1
- package/cjs/src/errors.js +1 -0
- package/cjs/src/errors.js.map +1 -1
- package/cjs/src/index.d.ts +3 -3
- package/cjs/src/index.d.ts.map +1 -1
- package/cjs/src/index.js +4 -2
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/mixpanel-service.d.ts +4 -0
- package/cjs/src/mixpanel-service.d.ts.map +1 -1
- package/cjs/src/mixpanel-service.js +4 -0
- package/cjs/src/mixpanel-service.js.map +1 -1
- package/cjs/src/react/all-types-export.d.ts +1 -1
- package/cjs/src/react/all-types-export.d.ts.map +1 -1
- package/cjs/src/react/all-types-export.js +2 -1
- package/cjs/src/react/all-types-export.js.map +1 -1
- package/cjs/src/react/all-types-export.parity.spec.d.ts +2 -0
- package/cjs/src/react/all-types-export.parity.spec.d.ts.map +1 -0
- package/cjs/src/react/all-types-export.parity.spec.js +194 -0
- package/cjs/src/react/all-types-export.parity.spec.js.map +1 -0
- package/cjs/src/react/index.d.ts +66 -32
- package/cjs/src/react/index.d.ts.map +1 -1
- package/cjs/src/react/index.js +5 -3
- package/cjs/src/react/index.js.map +1 -1
- package/cjs/src/react/index.spec.js +101 -0
- package/cjs/src/react/index.spec.js.map +1 -1
- package/cjs/src/react/util.spec.js +251 -0
- package/cjs/src/react/util.spec.js.map +1 -1
- package/cjs/src/types.d.ts +902 -28
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +701 -14
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/processData.spec.js +93 -0
- package/cjs/src/utils/processData.spec.js.map +1 -1
- package/cjs/src/utils/processTrigger.spec.js +61 -0
- package/cjs/src/utils/processTrigger.spec.js.map +1 -1
- package/cjs/src/utils.spec.js +4 -0
- package/cjs/src/utils.spec.js.map +1 -1
- package/dist/{index-B5xoN6Cp.js → index-Bivg-mDx.js} +1 -1
- package/dist/src/config.d.ts +1 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/css-variables.d.ts +122 -0
- package/dist/src/css-variables.d.ts.map +1 -1
- package/dist/src/embed/app.d.ts +176 -24
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/base.d.ts +1 -1
- package/dist/src/embed/conversation.d.ts +401 -2
- package/dist/src/embed/conversation.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/contracts.d.ts +51 -14
- package/dist/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/host-event-client.d.ts +2 -1
- package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/utils.d.ts +2 -0
- package/dist/src/embed/hostEventClient/utils.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +81 -13
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/spotter-utils.d.ts +15 -1
- package/dist/src/embed/spotter-utils.d.ts.map +1 -1
- package/dist/src/embed/spotter-viz-utils.d.ts +37 -4
- package/dist/src/embed/spotter-viz-utils.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.d.ts +16 -8
- package/dist/src/embed/ts-embed.d.ts.map +1 -1
- package/dist/src/errors.d.ts +1 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/mixpanel-service.d.ts +4 -0
- package/dist/src/mixpanel-service.d.ts.map +1 -1
- package/dist/src/react/all-types-export.d.ts +1 -1
- package/dist/src/react/all-types-export.d.ts.map +1 -1
- package/dist/src/react/all-types-export.parity.spec.d.ts +2 -0
- package/dist/src/react/all-types-export.parity.spec.d.ts.map +1 -0
- package/dist/src/react/index.d.ts +66 -32
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +902 -28
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +1269 -206
- package/dist/tsembed-react.js +1268 -205
- package/dist/tsembed.es.js +1364 -304
- package/dist/tsembed.js +1362 -302
- package/dist/visual-embed-sdk-react-full.d.ts +1867 -157
- package/dist/visual-embed-sdk-react.d.ts +1872 -161
- package/dist/visual-embed-sdk.d.ts +1815 -127
- package/lib/package.json +1 -1
- package/lib/src/config.d.ts +1 -0
- package/lib/src/config.d.ts.map +1 -1
- package/lib/src/config.js +1 -0
- package/lib/src/config.js.map +1 -1
- package/lib/src/css-variables.d.ts +122 -0
- package/lib/src/css-variables.d.ts.map +1 -1
- package/lib/src/embed/app.d.ts +176 -24
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +87 -39
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +673 -151
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/base.d.ts +1 -1
- package/lib/src/embed/base.js +1 -1
- package/lib/src/embed/conversation.d.ts +401 -2
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js +33 -17
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/conversation.spec.js +347 -18
- package/lib/src/embed/conversation.spec.js.map +1 -1
- package/lib/src/embed/events.spec.js +638 -0
- package/lib/src/embed/events.spec.js.map +1 -1
- package/lib/src/embed/host-events.spec.js +188 -1
- package/lib/src/embed/host-events.spec.js.map +1 -1
- package/lib/src/embed/hostEventClient/contracts.d.ts +51 -14
- package/lib/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/contracts.js +10 -0
- package/lib/src/embed/hostEventClient/contracts.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.d.ts +2 -1
- package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.js +10 -1
- package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.spec.js +236 -0
- package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
- package/lib/src/embed/hostEventClient/utils.d.ts +2 -0
- package/lib/src/embed/hostEventClient/utils.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/utils.js +28 -1
- package/lib/src/embed/hostEventClient/utils.js.map +1 -1
- package/lib/src/embed/hostEventClient/utils.spec.js +179 -1
- package/lib/src/embed/hostEventClient/utils.spec.js.map +1 -1
- package/lib/src/embed/liveboard.d.ts +81 -13
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +50 -9
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +432 -4
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/spotter-utils.d.ts +15 -1
- package/lib/src/embed/spotter-utils.d.ts.map +1 -1
- package/lib/src/embed/spotter-utils.js +25 -0
- package/lib/src/embed/spotter-utils.js.map +1 -1
- package/lib/src/embed/spotter-utils.spec.js +130 -1
- package/lib/src/embed/spotter-utils.spec.js.map +1 -1
- package/lib/src/embed/spotter-viz-utils.d.ts +37 -4
- package/lib/src/embed/spotter-viz-utils.d.ts.map +1 -1
- package/lib/src/embed/spotter-viz-utils.js.map +1 -1
- package/lib/src/embed/spotter-viz-utils.spec.js +17 -0
- package/lib/src/embed/spotter-viz-utils.spec.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts +16 -8
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +96 -37
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +683 -3
- package/lib/src/embed/ts-embed.spec.js.map +1 -1
- package/lib/src/errors.d.ts +1 -0
- package/lib/src/errors.d.ts.map +1 -1
- package/lib/src/errors.js +1 -0
- package/lib/src/errors.js.map +1 -1
- package/lib/src/index.d.ts +3 -3
- package/lib/src/index.d.ts.map +1 -1
- package/lib/src/index.js +3 -3
- package/lib/src/index.js.map +1 -1
- package/lib/src/mixpanel-service.d.ts +4 -0
- package/lib/src/mixpanel-service.d.ts.map +1 -1
- package/lib/src/mixpanel-service.js +4 -0
- package/lib/src/mixpanel-service.js.map +1 -1
- package/lib/src/react/all-types-export.d.ts +1 -1
- package/lib/src/react/all-types-export.d.ts.map +1 -1
- package/lib/src/react/all-types-export.js +1 -1
- package/lib/src/react/all-types-export.js.map +1 -1
- package/lib/src/react/all-types-export.parity.spec.d.ts +2 -0
- package/lib/src/react/all-types-export.parity.spec.d.ts.map +1 -0
- package/lib/src/react/all-types-export.parity.spec.js +191 -0
- package/lib/src/react/all-types-export.parity.spec.js.map +1 -0
- package/lib/src/react/index.d.ts +66 -32
- package/lib/src/react/index.d.ts.map +1 -1
- package/lib/src/react/index.js +5 -3
- package/lib/src/react/index.js.map +1 -1
- package/lib/src/react/index.spec.js +102 -1
- package/lib/src/react/index.spec.js.map +1 -1
- package/lib/src/react/util.spec.js +251 -0
- package/lib/src/react/util.spec.js.map +1 -1
- package/lib/src/types.d.ts +902 -28
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +701 -14
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/processData.spec.js +93 -0
- package/lib/src/utils/processData.spec.js.map +1 -1
- package/lib/src/utils/processTrigger.spec.js +61 -0
- package/lib/src/utils/processTrigger.spec.js.map +1 -1
- package/lib/src/utils.spec.js +4 -0
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +1815 -127
- package/package.json +1 -1
- package/src/config.ts +1 -0
- package/src/css-variables.ts +150 -0
- package/src/embed/app.spec.ts +877 -196
- package/src/embed/app.ts +276 -75
- package/src/embed/base.ts +1 -1
- package/src/embed/conversation.spec.ts +411 -21
- package/src/embed/conversation.ts +441 -18
- package/src/embed/events.spec.ts +761 -1
- package/src/embed/host-events.spec.ts +242 -0
- package/src/embed/hostEventClient/contracts.ts +57 -14
- package/src/embed/hostEventClient/host-event-client.spec.ts +372 -0
- package/src/embed/hostEventClient/host-event-client.ts +18 -2
- package/src/embed/hostEventClient/utils.spec.ts +204 -0
- package/src/embed/hostEventClient/utils.ts +37 -10
- package/src/embed/liveboard.spec.ts +565 -4
- package/src/embed/liveboard.ts +155 -28
- package/src/embed/spotter-utils.spec.ts +140 -1
- package/src/embed/spotter-utils.ts +36 -1
- package/src/embed/spotter-viz-utils.spec.ts +17 -0
- package/src/embed/spotter-viz-utils.ts +37 -4
- package/src/embed/ts-embed.spec.ts +885 -3
- package/src/embed/ts-embed.ts +111 -39
- package/src/errors.ts +1 -0
- package/src/index.ts +45 -2
- package/src/mixpanel-service.ts +4 -0
- package/src/react/all-types-export.parity.spec.ts +208 -0
- package/src/react/all-types-export.ts +22 -0
- package/src/react/index.spec.tsx +158 -0
- package/src/react/index.tsx +70 -33
- package/src/react/util.spec.tsx +255 -0
- package/src/types.ts +904 -24
- package/src/utils/processData.spec.ts +97 -0
- package/src/utils/processTrigger.spec.ts +91 -0
- 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())
|