@thoughtspot/visual-embed-sdk 1.49.3 → 1.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/package.json +1 -1
- package/cjs/src/authToken.spec.js +5 -0
- package/cjs/src/authToken.spec.js.map +1 -1
- package/cjs/src/css-variables.d.ts +59 -0
- package/cjs/src/css-variables.d.ts.map +1 -1
- package/cjs/src/embed/app.d.ts +31 -2
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +19 -2
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +112 -2
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/conversation.d.ts +38 -0
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js +12 -2
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/conversation.spec.js +53 -0
- package/cjs/src/embed/conversation.spec.js.map +1 -1
- package/cjs/src/embed/embedConfig.spec.d.ts +2 -0
- package/cjs/src/embed/embedConfig.spec.d.ts.map +1 -0
- package/cjs/src/embed/embedConfig.spec.js +74 -0
- package/cjs/src/embed/embedConfig.spec.js.map +1 -0
- package/cjs/src/embed/events.spec.js +593 -0
- package/cjs/src/embed/events.spec.js.map +1 -1
- package/cjs/src/embed/host-events.spec.d.ts +2 -0
- package/cjs/src/embed/host-events.spec.d.ts.map +1 -0
- package/cjs/src/embed/host-events.spec.js +1325 -0
- package/cjs/src/embed/host-events.spec.js.map +1 -0
- package/cjs/src/embed/hostEventClient/contracts.d.ts +36 -14
- package/cjs/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/contracts.js +10 -1
- package/cjs/src/embed/hostEventClient/contracts.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts +1 -0
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.js +8 -0
- package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.spec.js +209 -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 +6 -0
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/spotter-viz-utils.d.ts +34 -0
- 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.map +1 -1
- package/cjs/src/embed/ts-embed.js +14 -7
- package/cjs/src/embed/ts-embed.js.map +1 -1
- package/cjs/src/embed/ts-embed.spec.js +146 -0
- 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 +2 -2
- package/cjs/src/index.d.ts.map +1 -1
- package/cjs/src/index.js +3 -2
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/mixpanel-service.spec.js +14 -0
- package/cjs/src/mixpanel-service.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 +212 -8
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +185 -3
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/custom-actions.spec.js +26 -0
- package/cjs/src/utils/custom-actions.spec.js.map +1 -1
- package/cjs/src/utils/processData.spec.js +123 -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/reporting.spec.js +21 -0
- package/cjs/src/utils/reporting.spec.js.map +1 -1
- package/cjs/src/utils/sessionInfoService.spec.d.ts +2 -0
- package/cjs/src/utils/sessionInfoService.spec.d.ts.map +1 -0
- package/cjs/src/utils/sessionInfoService.spec.js +130 -0
- package/cjs/src/utils/sessionInfoService.spec.js.map +1 -0
- package/cjs/src/utils.spec.js +158 -0
- package/cjs/src/utils.spec.js.map +1 -1
- package/dist/{index-DsSQndIB.js → index-fCne7kmU.js} +1 -1
- package/dist/src/css-variables.d.ts +59 -0
- package/dist/src/css-variables.d.ts.map +1 -1
- package/dist/src/embed/app.d.ts +31 -2
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/conversation.d.ts +38 -0
- package/dist/src/embed/conversation.d.ts.map +1 -1
- package/dist/src/embed/embedConfig.spec.d.ts +2 -0
- package/dist/src/embed/embedConfig.spec.d.ts.map +1 -0
- package/dist/src/embed/host-events.spec.d.ts +2 -0
- package/dist/src/embed/host-events.spec.d.ts.map +1 -0
- package/dist/src/embed/hostEventClient/contracts.d.ts +36 -14
- package/dist/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/host-event-client.d.ts +1 -0
- 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 +6 -0
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/spotter-viz-utils.d.ts +34 -0
- package/dist/src/embed/spotter-viz-utils.d.ts.map +1 -1
- 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 +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +212 -8
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/sessionInfoService.spec.d.ts +2 -0
- package/dist/src/utils/sessionInfoService.spec.d.ts.map +1 -0
- package/dist/tsembed-react.es.js +482 -86
- package/dist/tsembed-react.js +481 -85
- package/dist/tsembed.es.js +483 -87
- package/dist/tsembed.js +481 -85
- package/dist/visual-embed-sdk-react-full.d.ts +407 -23
- package/dist/visual-embed-sdk-react.d.ts +407 -23
- package/dist/visual-embed-sdk.d.ts +411 -23
- package/lib/package.json +1 -1
- package/lib/src/authToken.spec.js +5 -0
- package/lib/src/authToken.spec.js.map +1 -1
- package/lib/src/css-variables.d.ts +59 -0
- package/lib/src/css-variables.d.ts.map +1 -1
- package/lib/src/embed/app.d.ts +31 -2
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +19 -2
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +112 -2
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/conversation.d.ts +38 -0
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js +11 -1
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/conversation.spec.js +54 -1
- package/lib/src/embed/conversation.spec.js.map +1 -1
- package/lib/src/embed/embedConfig.spec.d.ts +2 -0
- package/lib/src/embed/embedConfig.spec.d.ts.map +1 -0
- package/lib/src/embed/embedConfig.spec.js +72 -0
- package/lib/src/embed/embedConfig.spec.js.map +1 -0
- package/lib/src/embed/events.spec.js +593 -0
- package/lib/src/embed/events.spec.js.map +1 -1
- package/lib/src/embed/host-events.spec.d.ts +2 -0
- package/lib/src/embed/host-events.spec.d.ts.map +1 -0
- package/lib/src/embed/host-events.spec.js +1322 -0
- package/lib/src/embed/host-events.spec.js.map +1 -0
- package/lib/src/embed/hostEventClient/contracts.d.ts +36 -14
- package/lib/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/contracts.js +9 -0
- package/lib/src/embed/hostEventClient/contracts.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.d.ts +1 -0
- package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.js +9 -1
- package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.spec.js +209 -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 +6 -0
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/spotter-viz-utils.d.ts +34 -0
- 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.map +1 -1
- package/lib/src/embed/ts-embed.js +14 -7
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +146 -0
- 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 +2 -2
- package/lib/src/index.d.ts.map +1 -1
- package/lib/src/index.js +2 -2
- package/lib/src/index.js.map +1 -1
- package/lib/src/mixpanel-service.spec.js +14 -0
- package/lib/src/mixpanel-service.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 +212 -8
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +185 -3
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/custom-actions.spec.js +26 -0
- package/lib/src/utils/custom-actions.spec.js.map +1 -1
- package/lib/src/utils/processData.spec.js +123 -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/reporting.spec.js +21 -0
- package/lib/src/utils/reporting.spec.js.map +1 -1
- package/lib/src/utils/sessionInfoService.spec.d.ts +2 -0
- package/lib/src/utils/sessionInfoService.spec.d.ts.map +1 -0
- package/lib/src/utils/sessionInfoService.spec.js +127 -0
- package/lib/src/utils/sessionInfoService.spec.js.map +1 -0
- package/lib/src/utils.spec.js +159 -1
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +411 -23
- package/package.json +1 -1
- package/src/authToken.spec.ts +6 -0
- package/src/css-variables.ts +73 -0
- package/src/embed/app.spec.ts +155 -2
- package/src/embed/app.ts +44 -2
- package/src/embed/conversation.spec.ts +64 -1
- package/src/embed/conversation.ts +41 -0
- package/src/embed/embedConfig.spec.ts +79 -0
- package/src/embed/events.spec.ts +705 -1
- package/src/embed/host-events.spec.ts +1759 -0
- package/src/embed/hostEventClient/contracts.ts +41 -14
- package/src/embed/hostEventClient/host-event-client.spec.ts +326 -0
- package/src/embed/hostEventClient/host-event-client.ts +15 -0
- package/src/embed/hostEventClient/utils.spec.ts +204 -0
- package/src/embed/hostEventClient/utils.ts +37 -10
- package/src/embed/liveboard.ts +6 -0
- package/src/embed/spotter-viz-utils.spec.ts +17 -0
- package/src/embed/spotter-viz-utils.ts +34 -0
- package/src/embed/ts-embed.spec.ts +186 -0
- package/src/embed/ts-embed.ts +12 -4
- package/src/errors.ts +1 -0
- package/src/index.ts +2 -1
- package/src/mixpanel-service.spec.ts +15 -0
- package/src/react/util.spec.tsx +255 -0
- package/src/types.ts +215 -6
- package/src/utils/custom-actions.spec.ts +28 -0
- package/src/utils/processData.spec.ts +129 -0
- package/src/utils/processTrigger.spec.ts +91 -0
- package/src/utils/reporting.spec.ts +27 -0
- package/src/utils/sessionInfoService.spec.ts +151 -0
- package/src/utils.spec.ts +195 -0
|
@@ -7,6 +7,43 @@ export interface LiveboardTab {
|
|
|
7
7
|
[key: string]: any;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Levels at which a filter or parameter can be applied.
|
|
12
|
+
*/
|
|
13
|
+
export enum ApplicabilityLevel {
|
|
14
|
+
Liveboard = 'LIVEBOARD',
|
|
15
|
+
Tab = 'TAB',
|
|
16
|
+
Group = 'GROUP',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Scopes a filter or parameter to a specific target.
|
|
21
|
+
* At `LIVEBOARD` level the filter applies to the whole Liveboard, so `targetId`
|
|
22
|
+
* is not required.
|
|
23
|
+
*/
|
|
24
|
+
export interface Applicability {
|
|
25
|
+
level: ApplicabilityLevel;
|
|
26
|
+
targetId?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface FilterUpdate {
|
|
30
|
+
column: string;
|
|
31
|
+
oper: string;
|
|
32
|
+
values: string[];
|
|
33
|
+
type?: string;
|
|
34
|
+
applicability?: Applicability;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface LiveboardFilter {
|
|
38
|
+
applicability?: Applicability;
|
|
39
|
+
[key: string]: any;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface LiveboardParameter {
|
|
43
|
+
applicability?: Applicability;
|
|
44
|
+
[key: string]: any;
|
|
45
|
+
}
|
|
46
|
+
|
|
10
47
|
export enum UIPassthroughEvent {
|
|
11
48
|
PinAnswerToLiveboard = 'addVizToPinboard',
|
|
12
49
|
SaveAnswer = 'saveAnswer',
|
|
@@ -103,7 +140,7 @@ export type UIPassthroughContractBase = {
|
|
|
103
140
|
vizId?: string;
|
|
104
141
|
};
|
|
105
142
|
response: {
|
|
106
|
-
liveboardFilters:
|
|
143
|
+
liveboardFilters: LiveboardFilter[];
|
|
107
144
|
runtimeFilters: RuntimeFilter[];
|
|
108
145
|
};
|
|
109
146
|
};
|
|
@@ -116,7 +153,7 @@ export type UIPassthroughContractBase = {
|
|
|
116
153
|
[UIPassthroughEvent.GetParameters]: {
|
|
117
154
|
request: Record<string, never>;
|
|
118
155
|
response: {
|
|
119
|
-
parameters:
|
|
156
|
+
parameters: LiveboardParameter[];
|
|
120
157
|
};
|
|
121
158
|
};
|
|
122
159
|
[UIPassthroughEvent.GetTML]: {
|
|
@@ -143,18 +180,8 @@ export type UIPassthroughContractBase = {
|
|
|
143
180
|
};
|
|
144
181
|
[UIPassthroughEvent.UpdateFilters]: {
|
|
145
182
|
request: {
|
|
146
|
-
filter?:
|
|
147
|
-
|
|
148
|
-
oper: string;
|
|
149
|
-
values: string[];
|
|
150
|
-
type?: string;
|
|
151
|
-
};
|
|
152
|
-
filters?: {
|
|
153
|
-
column: string;
|
|
154
|
-
oper: string;
|
|
155
|
-
values: string[];
|
|
156
|
-
type?: string;
|
|
157
|
-
}[];
|
|
183
|
+
filter?: FilterUpdate;
|
|
184
|
+
filters?: FilterUpdate[];
|
|
158
185
|
};
|
|
159
186
|
response: unknown;
|
|
160
187
|
};
|
|
@@ -311,6 +311,70 @@ 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
|
+
|
|
314
378
|
it('should throw when DrillDown payload has no valid points', async () => {
|
|
315
379
|
const { client } = createHostEventClient();
|
|
316
380
|
const invalidPayload = {} as any;
|
|
@@ -816,4 +880,266 @@ describe('HostEventClient', () => {
|
|
|
816
880
|
expect(result).toEqual({ ok: true });
|
|
817
881
|
});
|
|
818
882
|
});
|
|
883
|
+
|
|
884
|
+
describe('availablePassthroughKeysCache reuse', () => {
|
|
885
|
+
const allKeys = () => [{ value: { keys: Object.values(UIPassthroughEvent) } }];
|
|
886
|
+
|
|
887
|
+
it('GetFilters: second call reuses cache — GetAvailableUIPassthroughs called only once', async () => {
|
|
888
|
+
const { client } = createHostEventClient();
|
|
889
|
+
const filterResponse = [{ value: { liveboardFilters: [] as any[], runtimeFilters: [] as any[] } }];
|
|
890
|
+
mockProcessTrigger
|
|
891
|
+
.mockResolvedValueOnce(allKeys())
|
|
892
|
+
.mockResolvedValueOnce(filterResponse)
|
|
893
|
+
.mockResolvedValueOnce(filterResponse);
|
|
894
|
+
|
|
895
|
+
await client.triggerHostEvent(HostEvent.GetFilters, {});
|
|
896
|
+
await client.triggerHostEvent(HostEvent.GetFilters, {});
|
|
897
|
+
|
|
898
|
+
// 3 calls total: 1 GetAvailableUIPassthroughs + 2 GetFilters
|
|
899
|
+
expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
|
|
900
|
+
expect(mockProcessTrigger).not.toHaveBeenNthCalledWith(
|
|
901
|
+
3,
|
|
902
|
+
expect.anything(),
|
|
903
|
+
HostEvent.UIPassthrough,
|
|
904
|
+
mockThoughtSpotHost,
|
|
905
|
+
expect.objectContaining({ type: UIPassthroughEvent.GetAvailableUIPassthroughs }),
|
|
906
|
+
undefined,
|
|
907
|
+
);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
it('GetTabs: second call reuses cache — GetAvailableUIPassthroughs called only once', async () => {
|
|
911
|
+
const { client } = createHostEventClient();
|
|
912
|
+
const tabResponse = [{ value: { orderedTabIds: [] as any[], numberOfTabs: 0, Tabs: [] as any[] } }];
|
|
913
|
+
mockProcessTrigger
|
|
914
|
+
.mockResolvedValueOnce(allKeys())
|
|
915
|
+
.mockResolvedValueOnce(tabResponse)
|
|
916
|
+
.mockResolvedValueOnce(tabResponse);
|
|
917
|
+
|
|
918
|
+
await client.triggerHostEvent(HostEvent.GetTabs, {});
|
|
919
|
+
await client.triggerHostEvent(HostEvent.GetTabs, {});
|
|
920
|
+
|
|
921
|
+
expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
it('GetParameters: second call reuses cache — GetAvailableUIPassthroughs called only once', async () => {
|
|
925
|
+
const { client } = createHostEventClient();
|
|
926
|
+
const paramResponse = [{ value: { parameters: [] as any[] } }];
|
|
927
|
+
mockProcessTrigger
|
|
928
|
+
.mockResolvedValueOnce(allKeys())
|
|
929
|
+
.mockResolvedValueOnce(paramResponse)
|
|
930
|
+
.mockResolvedValueOnce(paramResponse);
|
|
931
|
+
|
|
932
|
+
await client.triggerHostEvent(HostEvent.GetParameters, {});
|
|
933
|
+
await client.triggerHostEvent(HostEvent.GetParameters, {});
|
|
934
|
+
|
|
935
|
+
expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
it('GetAnswerSession: second call reuses cache — GetAvailableUIPassthroughs called only once', async () => {
|
|
939
|
+
const { client } = createHostEventClient();
|
|
940
|
+
const sessionResponse = [{ value: { session: 's1' } }];
|
|
941
|
+
mockProcessTrigger
|
|
942
|
+
.mockResolvedValueOnce(allKeys())
|
|
943
|
+
.mockResolvedValueOnce(sessionResponse)
|
|
944
|
+
.mockResolvedValueOnce(sessionResponse);
|
|
945
|
+
|
|
946
|
+
await client.triggerHostEvent(HostEvent.GetAnswerSession, {});
|
|
947
|
+
await client.triggerHostEvent(HostEvent.GetAnswerSession, {});
|
|
948
|
+
|
|
949
|
+
expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
it('UIPassthrough direct trigger does not populate the cache', async () => {
|
|
953
|
+
const { client } = createHostEventClient();
|
|
954
|
+
mockProcessTrigger.mockResolvedValue([{ value: { result: 'ok' } }]);
|
|
955
|
+
|
|
956
|
+
await client.triggerHostEvent(HostEvent.UIPassthrough, {});
|
|
957
|
+
// Cache not populated — next GetFilters call must still fetch keys
|
|
958
|
+
mockProcessTrigger.mockResolvedValueOnce(allKeys());
|
|
959
|
+
mockProcessTrigger.mockResolvedValueOnce([{ value: { liveboardFilters: [], runtimeFilters: [] as any[] } }]);
|
|
960
|
+
await client.triggerHostEvent(HostEvent.GetFilters, {});
|
|
961
|
+
|
|
962
|
+
// UIPassthrough call + GetAvailableUIPassthroughs + GetFilters = 3
|
|
963
|
+
expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
it('GetAnswerSession: catch in getAvailableUIPassthroughKeys does not cache, so next call retries GetAvailableUIPassthroughs', async () => {
|
|
967
|
+
const { client } = createHostEventClient();
|
|
968
|
+
mockProcessTrigger.mockReset();
|
|
969
|
+
|
|
970
|
+
// Flow per triggerHostEvent call when GetAvailableUIPassthroughs rejects:
|
|
971
|
+
// 1. getAvailableUIPassthroughKeys → processTrigger(UIPassthrough, GetAvailableUIPassthroughs) → rejects → returns []
|
|
972
|
+
// 2. keys=[] so getDataWithPassthroughFallback is called →
|
|
973
|
+
// triggerUIPassthroughApi(GetAnswerSession) → processTrigger(UIPassthrough) → null → hostEventFallback called
|
|
974
|
+
// 3. hostEventFallback → processTrigger(GetAnswerSession) → legacy result
|
|
975
|
+
|
|
976
|
+
// First triggerHostEvent: 3 calls
|
|
977
|
+
mockProcessTrigger.mockRejectedValueOnce(new Error('network error')); // call 1
|
|
978
|
+
mockProcessTrigger.mockResolvedValueOnce(null); // call 2
|
|
979
|
+
mockProcessTrigger.mockResolvedValueOnce({ session: 'leg1' }); // call 3
|
|
980
|
+
|
|
981
|
+
await client.triggerHostEvent(HostEvent.GetAnswerSession, { vizId: '1' });
|
|
982
|
+
|
|
983
|
+
// Second triggerHostEvent: cache was NOT set, so GetAvailableUIPassthroughs is retried: 3 more calls
|
|
984
|
+
mockProcessTrigger.mockRejectedValueOnce(new Error('network error')); // call 4
|
|
985
|
+
mockProcessTrigger.mockResolvedValueOnce(null); // call 5
|
|
986
|
+
mockProcessTrigger.mockResolvedValueOnce({ session: 'leg2' }); // call 6
|
|
987
|
+
|
|
988
|
+
await client.triggerHostEvent(HostEvent.GetAnswerSession, { vizId: '2' });
|
|
989
|
+
|
|
990
|
+
// 6 total calls: GetAvailableUIPassthroughs was called TWICE (cache not set after catch)
|
|
991
|
+
expect(mockProcessTrigger).toHaveBeenCalledTimes(6);
|
|
992
|
+
const availableCallsCount = mockProcessTrigger.mock.calls.filter(
|
|
993
|
+
(call) => call[3]?.type === UIPassthroughEvent.GetAvailableUIPassthroughs,
|
|
994
|
+
).length;
|
|
995
|
+
expect(availableCallsCount).toBe(2);
|
|
996
|
+
});
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
describe('GetTML edge cases', () => {
|
|
1000
|
+
const allKeys = () => [{ value: { keys: Object.values(UIPassthroughEvent) } }];
|
|
1001
|
+
|
|
1002
|
+
it('includeNonExecutedSearchTokens flag is forwarded in UIPassthrough parameters', async () => {
|
|
1003
|
+
const { client, mockIframe } = createHostEventClient();
|
|
1004
|
+
mockProcessTrigger
|
|
1005
|
+
.mockResolvedValueOnce(allKeys())
|
|
1006
|
+
.mockResolvedValueOnce([{ value: { tml: 'data' } }]);
|
|
1007
|
+
|
|
1008
|
+
await client.triggerHostEvent(HostEvent.GetTML, { includeNonExecutedSearchTokens: true } as any);
|
|
1009
|
+
|
|
1010
|
+
expect(mockProcessTrigger).toHaveBeenNthCalledWith(
|
|
1011
|
+
2,
|
|
1012
|
+
mockIframe,
|
|
1013
|
+
HostEvent.UIPassthrough,
|
|
1014
|
+
mockThoughtSpotHost,
|
|
1015
|
+
{
|
|
1016
|
+
type: UIPassthroughEvent.GetTML,
|
|
1017
|
+
parameters: { includeNonExecutedSearchTokens: true },
|
|
1018
|
+
},
|
|
1019
|
+
undefined,
|
|
1020
|
+
);
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
it('value.error (singular) throws — distinct from value.errors', async () => {
|
|
1024
|
+
const { client } = createHostEventClient();
|
|
1025
|
+
mockProcessTrigger
|
|
1026
|
+
.mockResolvedValueOnce(allKeys())
|
|
1027
|
+
.mockResolvedValueOnce([{ value: { error: 'TML fetch failed' } }]);
|
|
1028
|
+
|
|
1029
|
+
await expect(client.triggerHostEvent(HostEvent.GetTML, {}))
|
|
1030
|
+
.rejects.toThrow('TML fetch failed');
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
it('GetTML key absent from non-empty keys array falls back to legacy hostEventFallback', async () => {
|
|
1034
|
+
const { client, mockIframe } = createHostEventClient();
|
|
1035
|
+
// Return keys that do NOT include GetTML
|
|
1036
|
+
mockProcessTrigger
|
|
1037
|
+
.mockResolvedValueOnce([{ value: { keys: [UIPassthroughEvent.GetFilters] } }])
|
|
1038
|
+
.mockResolvedValueOnce({ answer: { search_query: 'revenue' } });
|
|
1039
|
+
|
|
1040
|
+
const result = await client.triggerHostEvent(HostEvent.GetTML, {});
|
|
1041
|
+
|
|
1042
|
+
expect(mockProcessTrigger).toHaveBeenNthCalledWith(
|
|
1043
|
+
2,
|
|
1044
|
+
mockIframe,
|
|
1045
|
+
HostEvent.GetTML,
|
|
1046
|
+
mockThoughtSpotHost,
|
|
1047
|
+
{},
|
|
1048
|
+
undefined,
|
|
1049
|
+
);
|
|
1050
|
+
expect(result).toEqual({ answer: { search_query: 'revenue' } });
|
|
1051
|
+
});
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
describe('SaveAnswer key-presence edge cases', () => {
|
|
1055
|
+
const allKeys = () => [{ value: { keys: Object.values(UIPassthroughEvent) } }];
|
|
1056
|
+
const saveResponse = [{
|
|
1057
|
+
value: {
|
|
1058
|
+
saveResponse: {
|
|
1059
|
+
data: {
|
|
1060
|
+
Answer__save: {
|
|
1061
|
+
answer: { id: 'ans123' },
|
|
1062
|
+
},
|
|
1063
|
+
},
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
refId: 'viz1',
|
|
1067
|
+
}];
|
|
1068
|
+
|
|
1069
|
+
it('name+description both empty string route through passthrough, not Save fallback', async () => {
|
|
1070
|
+
const { client, mockIframe } = createHostEventClient();
|
|
1071
|
+
mockProcessTrigger
|
|
1072
|
+
.mockResolvedValueOnce(allKeys())
|
|
1073
|
+
.mockResolvedValueOnce(saveResponse);
|
|
1074
|
+
|
|
1075
|
+
await client.triggerHostEvent(HostEvent.SaveAnswer, { name: '', description: '' } as any);
|
|
1076
|
+
|
|
1077
|
+
expect(mockProcessTrigger).toHaveBeenNthCalledWith(
|
|
1078
|
+
2,
|
|
1079
|
+
mockIframe,
|
|
1080
|
+
HostEvent.UIPassthrough,
|
|
1081
|
+
mockThoughtSpotHost,
|
|
1082
|
+
{ type: UIPassthroughEvent.SaveAnswer, parameters: { name: '', description: '' } },
|
|
1083
|
+
undefined,
|
|
1084
|
+
);
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
it('description: undefined (key present) routes through passthrough, not Save fallback', async () => {
|
|
1088
|
+
const { client, mockIframe } = createHostEventClient();
|
|
1089
|
+
mockProcessTrigger
|
|
1090
|
+
.mockResolvedValueOnce(allKeys())
|
|
1091
|
+
.mockResolvedValueOnce(saveResponse);
|
|
1092
|
+
|
|
1093
|
+
await client.triggerHostEvent(HostEvent.SaveAnswer, { name: 'Test', description: undefined } as any);
|
|
1094
|
+
|
|
1095
|
+
expect(mockProcessTrigger).toHaveBeenNthCalledWith(
|
|
1096
|
+
2,
|
|
1097
|
+
mockIframe,
|
|
1098
|
+
HostEvent.UIPassthrough,
|
|
1099
|
+
mockThoughtSpotHost,
|
|
1100
|
+
{ type: UIPassthroughEvent.SaveAnswer, parameters: { name: 'Test', description: undefined } },
|
|
1101
|
+
undefined,
|
|
1102
|
+
);
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
it('name key absent falls back to HostEvent.Save', async () => {
|
|
1106
|
+
const { client, mockIframe } = createHostEventClient();
|
|
1107
|
+
mockProcessTrigger
|
|
1108
|
+
.mockResolvedValueOnce(allKeys())
|
|
1109
|
+
.mockResolvedValueOnce([saveResponse[0].value]);
|
|
1110
|
+
|
|
1111
|
+
await client.triggerHostEvent(HostEvent.SaveAnswer, { description: 'desc only' } as any);
|
|
1112
|
+
|
|
1113
|
+
expect(mockProcessTrigger).toHaveBeenCalledWith(
|
|
1114
|
+
mockIframe,
|
|
1115
|
+
HostEvent.Save,
|
|
1116
|
+
mockThoughtSpotHost,
|
|
1117
|
+
{ description: 'desc only' },
|
|
1118
|
+
undefined,
|
|
1119
|
+
);
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
it('answerId is undefined when saveResponse.data.Answer__save.answer.id is missing', async () => {
|
|
1123
|
+
const { client } = createHostEventClient();
|
|
1124
|
+
const noIdSaveResponse = [{
|
|
1125
|
+
value: {
|
|
1126
|
+
saveResponse: {
|
|
1127
|
+
data: {
|
|
1128
|
+
Answer__save: {
|
|
1129
|
+
answer: {},
|
|
1130
|
+
},
|
|
1131
|
+
},
|
|
1132
|
+
},
|
|
1133
|
+
},
|
|
1134
|
+
refId: 'viz1',
|
|
1135
|
+
}];
|
|
1136
|
+
mockProcessTrigger
|
|
1137
|
+
.mockResolvedValueOnce(allKeys())
|
|
1138
|
+
.mockResolvedValueOnce(noIdSaveResponse);
|
|
1139
|
+
|
|
1140
|
+
const result = await client.triggerHostEvent(HostEvent.SaveAnswer, { name: 'Test', description: 'Desc' } as any);
|
|
1141
|
+
|
|
1142
|
+
expect(result.answerId).toBeUndefined();
|
|
1143
|
+
});
|
|
1144
|
+
});
|
|
819
1145
|
});
|
|
@@ -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 {
|
|
@@ -58,6 +60,7 @@ export class HostEventClient {
|
|
|
58
60
|
[HostEvent.Pin]: (p, c) => this.handlePinEvent(p, c),
|
|
59
61
|
[HostEvent.SaveAnswer]: (p, c) => this.handleSaveAnswerEvent(p, c),
|
|
60
62
|
[HostEvent.UpdateFilters]: (p, c) => this.handleUpdateFiltersEvent(p, c),
|
|
63
|
+
[HostEvent.UpdateParameters]: (p, c) => this.handleUpdateParametersEvent(p, c),
|
|
61
64
|
[HostEvent.DrillDown]: (p, c) => this.handleDrillDownEvent(p, c),
|
|
62
65
|
};
|
|
63
66
|
}
|
|
@@ -243,6 +246,18 @@ export class HostEventClient {
|
|
|
243
246
|
return this.handleHostEventWithParam(UIPassthroughEvent.UpdateFilters, payload, context as ContextType);
|
|
244
247
|
}
|
|
245
248
|
|
|
249
|
+
protected handleUpdateParametersEvent(
|
|
250
|
+
payload: HostEventRequest<HostEvent.UpdateParameters>,
|
|
251
|
+
context?: ContextType,
|
|
252
|
+
): Promise<any> {
|
|
253
|
+
if (!isValidUpdateParametersPayload(payload)) {
|
|
254
|
+
throwUpdateParametersValidationError();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// UpdateParameters has no UI passthrough contract; dispatch over the legacy channel
|
|
258
|
+
return this.hostEventFallback(HostEvent.UpdateParameters, payload, context);
|
|
259
|
+
}
|
|
260
|
+
|
|
246
261
|
protected handleDrillDownEvent(
|
|
247
262
|
payload: HostEventRequest<HostEvent.DrillDown>,
|
|
248
263
|
context?: ContextType,
|
|
@@ -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())
|