@thoughtspot/visual-embed-sdk 1.50.0 → 1.51.0
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/css-variables.d.ts +95 -0
- package/cjs/src/css-variables.d.ts.map +1 -1
- package/cjs/src/embed/app.d.ts +136 -15
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +59 -37
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +419 -149
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/conversation.d.ts +230 -1
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js +30 -16
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/conversation.spec.js +221 -17
- package/cjs/src/embed/conversation.spec.js.map +1 -1
- package/cjs/src/embed/events.spec.js +635 -0
- package/cjs/src/embed/events.spec.js.map +1 -1
- package/cjs/src/embed/host-events.spec.js +151 -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 +1 -0
- 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 +218 -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 +42 -1
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +26 -6
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +83 -2
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/embed/spotter-utils.d.ts +8 -1
- package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
- package/cjs/src/embed/spotter-utils.js +14 -1
- package/cjs/src/embed/spotter-utils.js.map +1 -1
- package/cjs/src/embed/spotter-utils.spec.js +61 -0
- package/cjs/src/embed/spotter-utils.spec.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 +29 -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.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/util.spec.js +251 -0
- package/cjs/src/react/util.spec.js.map +1 -1
- package/cjs/src/types.d.ts +573 -11
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +519 -2
- 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/dist/{index-B5xoN6Cp.js → index-aBzH5ZwF.js} +1 -1
- package/dist/src/css-variables.d.ts +95 -0
- package/dist/src/css-variables.d.ts.map +1 -1
- package/dist/src/embed/app.d.ts +136 -15
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/conversation.d.ts +230 -1
- 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 +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 +42 -1
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/spotter-utils.d.ts +8 -1
- package/dist/src/embed/spotter-utils.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/mixpanel-service.d.ts +4 -0
- package/dist/src/mixpanel-service.d.ts.map +1 -1
- package/dist/src/types.d.ts +573 -11
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +936 -142
- package/dist/tsembed-react.js +935 -141
- package/dist/tsembed.es.js +935 -142
- package/dist/tsembed.js +933 -140
- package/dist/visual-embed-sdk-react-full.d.ts +1147 -44
- package/dist/visual-embed-sdk-react.d.ts +1153 -46
- package/dist/visual-embed-sdk.d.ts +1161 -46
- package/lib/package.json +1 -1
- package/lib/src/css-variables.d.ts +95 -0
- package/lib/src/css-variables.d.ts.map +1 -1
- package/lib/src/embed/app.d.ts +136 -15
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +60 -38
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +420 -150
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/conversation.d.ts +230 -1
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js +31 -17
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/conversation.spec.js +222 -18
- package/lib/src/embed/conversation.spec.js.map +1 -1
- package/lib/src/embed/events.spec.js +635 -0
- package/lib/src/embed/events.spec.js.map +1 -1
- package/lib/src/embed/host-events.spec.js +152 -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 +1 -0
- 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 +218 -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 +42 -1
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +26 -6
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +83 -2
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/spotter-utils.d.ts +8 -1
- package/lib/src/embed/spotter-utils.d.ts.map +1 -1
- package/lib/src/embed/spotter-utils.js +12 -0
- package/lib/src/embed/spotter-utils.js.map +1 -1
- package/lib/src/embed/spotter-utils.spec.js +62 -1
- package/lib/src/embed/spotter-utils.spec.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 +29 -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.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/util.spec.js +251 -0
- package/lib/src/react/util.spec.js.map +1 -1
- package/lib/src/types.d.ts +573 -11
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +519 -2
- 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/visual-embed-sdk.d.ts +1161 -46
- package/package.json +1 -1
- package/src/css-variables.ts +118 -0
- package/src/embed/app.spec.ts +530 -183
- package/src/embed/app.ts +209 -66
- package/src/embed/conversation.spec.ts +275 -21
- package/src/embed/conversation.ts +262 -17
- package/src/embed/events.spec.ts +757 -1
- package/src/embed/host-events.spec.ts +194 -0
- package/src/embed/hostEventClient/contracts.ts +57 -14
- package/src/embed/hostEventClient/host-event-client.spec.ts +338 -0
- package/src/embed/hostEventClient/host-event-client.ts +16 -0
- package/src/embed/hostEventClient/utils.spec.ts +204 -0
- package/src/embed/hostEventClient/utils.ts +37 -10
- package/src/embed/liveboard.spec.ts +108 -2
- package/src/embed/liveboard.ts +82 -14
- package/src/embed/spotter-utils.spec.ts +68 -1
- package/src/embed/spotter-utils.ts +16 -1
- 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 +30 -4
- package/src/errors.ts +1 -0
- package/src/index.ts +3 -1
- package/src/mixpanel-service.ts +4 -0
- package/src/react/util.spec.tsx +255 -0
- package/src/types.ts +575 -8
- package/src/utils/processData.spec.ts +97 -0
- package/src/utils/processTrigger.spec.ts +91 -0
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
init,
|
|
8
8
|
AuthType,
|
|
9
9
|
LiveboardEmbed,
|
|
10
|
+
AppEmbed,
|
|
10
11
|
HostEvent,
|
|
11
12
|
EmbedErrorCodes,
|
|
12
13
|
} from '../index';
|
|
@@ -148,6 +149,37 @@ describe('HostEvent.OpenFilter', () => {
|
|
|
148
149
|
});
|
|
149
150
|
});
|
|
150
151
|
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// 10b – OpenParameter
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
describe('HostEvent.OpenParameter', () => {
|
|
156
|
+
test('postMessage type is openParameter (camelCase)', async () => {
|
|
157
|
+
mockMessageChannel();
|
|
158
|
+
const { lb, iframe } = await renderLiveboard();
|
|
159
|
+
await executeAfterWait(() => {
|
|
160
|
+
lb.trigger(HostEvent.OpenParameter, { parameter: { parameterId: 'p1' } } as any);
|
|
161
|
+
expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
|
|
162
|
+
expect.objectContaining({ type: 'openParameter' }),
|
|
163
|
+
thoughtSpotHost,
|
|
164
|
+
expect.anything(),
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('accepts a payload with only parameterName', async () => {
|
|
170
|
+
mockMessageChannel();
|
|
171
|
+
const { lb, iframe } = await renderLiveboard();
|
|
172
|
+
await executeAfterWait(() => {
|
|
173
|
+
lb.trigger(HostEvent.OpenParameter, { parameter: { parameterName: 'Param 1' } } as any);
|
|
174
|
+
expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
|
|
175
|
+
expect.objectContaining({ type: 'openParameter' }),
|
|
176
|
+
thoughtSpotHost,
|
|
177
|
+
expect.anything(),
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
151
183
|
// ---------------------------------------------------------------------------
|
|
152
184
|
// 11 – AddColumns
|
|
153
185
|
// ---------------------------------------------------------------------------
|
|
@@ -1633,3 +1665,165 @@ describe('context parameter forwarded in postMessage payload', () => {
|
|
|
1633
1665
|
});
|
|
1634
1666
|
});
|
|
1635
1667
|
});
|
|
1668
|
+
|
|
1669
|
+
// ---------------------------------------------------------------------------
|
|
1670
|
+
// SetVisibleVizs — array vizId injection (NOT in the parametrized arrayEvents)
|
|
1671
|
+
// ---------------------------------------------------------------------------
|
|
1672
|
+
describe('HostEvent.SetVisibleVizs — array payload vizId injection', () => {
|
|
1673
|
+
test('vizId is added as a non-indexed property on the array — array.length unchanged', async () => {
|
|
1674
|
+
mockMessageChannel();
|
|
1675
|
+
const { lb, iframe } = await renderLiveboard({ vizId });
|
|
1676
|
+
const arrayPayload = ['viz1', 'viz2'];
|
|
1677
|
+
await executeAfterWait(() => {
|
|
1678
|
+
lb.trigger(HostEvent.SetVisibleVizs, arrayPayload as any);
|
|
1679
|
+
const call = (iframe.contentWindow.postMessage as jest.Mock).mock.calls[0];
|
|
1680
|
+
const sentData = call[0].data;
|
|
1681
|
+
expect((sentData as any).vizId).toBe(vizId);
|
|
1682
|
+
expect(sentData.length).toBe(2);
|
|
1683
|
+
});
|
|
1684
|
+
});
|
|
1685
|
+
|
|
1686
|
+
test('without viewConfig.vizId — array payload is sent as-is (no vizId property)', async () => {
|
|
1687
|
+
mockMessageChannel();
|
|
1688
|
+
const { lb, iframe } = await renderLiveboard(); // no vizId
|
|
1689
|
+
const arrayPayload = ['viz1'];
|
|
1690
|
+
await executeAfterWait(() => {
|
|
1691
|
+
lb.trigger(HostEvent.SetVisibleVizs, arrayPayload as any);
|
|
1692
|
+
const call = (iframe.contentWindow.postMessage as jest.Mock).mock.calls[0];
|
|
1693
|
+
const sentData = call[0].data;
|
|
1694
|
+
expect((sentData as any).vizId).toBeUndefined();
|
|
1695
|
+
});
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1698
|
+
test('returns null and calls handleError when !isRendered', async () => {
|
|
1699
|
+
const lb = unrenderedLiveboard();
|
|
1700
|
+
const result = await lb.trigger(HostEvent.SetVisibleVizs, ['viz1'] as any);
|
|
1701
|
+
expect(result).toBeNull();
|
|
1702
|
+
expect((lb as any).handleError).toHaveBeenCalledWith(
|
|
1703
|
+
expect.objectContaining({ code: EmbedErrorCodes.RENDER_NOT_CALLED }),
|
|
1704
|
+
);
|
|
1705
|
+
});
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
// ---------------------------------------------------------------------------
|
|
1709
|
+
// UpdateRuntimeFilters — AppEmbed path (no vizId injection)
|
|
1710
|
+
// ---------------------------------------------------------------------------
|
|
1711
|
+
describe('HostEvent.UpdateRuntimeFilters — AppEmbed path', () => {
|
|
1712
|
+
async function renderApp() {
|
|
1713
|
+
const app = new AppEmbed(getRootEl(), {
|
|
1714
|
+
frameParams: { width: '100%', height: '100%' },
|
|
1715
|
+
});
|
|
1716
|
+
await app.render();
|
|
1717
|
+
const iframe = getIFrameEl();
|
|
1718
|
+
jest.spyOn(iframe.contentWindow, 'postMessage');
|
|
1719
|
+
return { app, iframe };
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
test('AppEmbed sends payload directly without vizId injection', async () => {
|
|
1723
|
+
mockMessageChannel();
|
|
1724
|
+
const { app, iframe } = await renderApp();
|
|
1725
|
+
const filters = [{ columnName: 'col', operator: 'EQ', values: ['v1'] }];
|
|
1726
|
+
await executeAfterWait(() => {
|
|
1727
|
+
app.trigger(HostEvent.UpdateRuntimeFilters, filters as any);
|
|
1728
|
+
const call = (iframe.contentWindow.postMessage as jest.Mock).mock.calls[0];
|
|
1729
|
+
const sentData = call[0].data;
|
|
1730
|
+
// Array payload with no vizId property injected
|
|
1731
|
+
expect((sentData as any).vizId).toBeUndefined();
|
|
1732
|
+
expect(sentData).toEqual(filters);
|
|
1733
|
+
});
|
|
1734
|
+
});
|
|
1735
|
+
});
|
|
1736
|
+
|
|
1737
|
+
// ---------------------------------------------------------------------------
|
|
1738
|
+
// Present — vizId injection on LiveboardEmbed
|
|
1739
|
+
// ---------------------------------------------------------------------------
|
|
1740
|
+
describe('HostEvent.Present — vizId injection on LiveboardEmbed', () => {
|
|
1741
|
+
test('viewConfig.vizId is injected into the Present payload', async () => {
|
|
1742
|
+
mockMessageChannel();
|
|
1743
|
+
const { lb, iframe } = await renderLiveboard({ vizId });
|
|
1744
|
+
await executeAfterWait(() => {
|
|
1745
|
+
lb.trigger(HostEvent.Present, {} as any);
|
|
1746
|
+
expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
|
|
1747
|
+
expect.objectContaining({
|
|
1748
|
+
data: expect.objectContaining({ vizId }),
|
|
1749
|
+
}),
|
|
1750
|
+
thoughtSpotHost,
|
|
1751
|
+
expect.anything(),
|
|
1752
|
+
);
|
|
1753
|
+
});
|
|
1754
|
+
});
|
|
1755
|
+
});
|
|
1756
|
+
|
|
1757
|
+
// ---------------------------------------------------------------------------
|
|
1758
|
+
// DownloadAsCsv — additional cases
|
|
1759
|
+
// ---------------------------------------------------------------------------
|
|
1760
|
+
describe('HostEvent.DownloadAsCsv — additional cases', () => {
|
|
1761
|
+
test('viewConfig.vizId overwrites an explicit payload vizId', async () => {
|
|
1762
|
+
mockMessageChannel();
|
|
1763
|
+
const { lb, iframe } = await renderLiveboard({ vizId });
|
|
1764
|
+
await executeAfterWait(() => {
|
|
1765
|
+
lb.trigger(HostEvent.DownloadAsCsv, { vizId: 'explicit-id' } as any);
|
|
1766
|
+
const call = (iframe.contentWindow.postMessage as jest.Mock).mock.calls[0];
|
|
1767
|
+
const sentData = call[0].data;
|
|
1768
|
+
expect(sentData.vizId).toBe(vizId); // viewConfig.vizId wins
|
|
1769
|
+
});
|
|
1770
|
+
});
|
|
1771
|
+
|
|
1772
|
+
test('returns null and calls handleError when !isRendered', async () => {
|
|
1773
|
+
const lb = unrenderedLiveboard();
|
|
1774
|
+
const result = await lb.trigger(HostEvent.DownloadAsCsv, {} as any);
|
|
1775
|
+
expect(result).toBeNull();
|
|
1776
|
+
expect((lb as any).handleError).toHaveBeenCalledWith(
|
|
1777
|
+
expect.objectContaining({ code: EmbedErrorCodes.RENDER_NOT_CALLED }),
|
|
1778
|
+
);
|
|
1779
|
+
});
|
|
1780
|
+
|
|
1781
|
+
test('returns null and logs debug when !iFrame', async () => {
|
|
1782
|
+
const lb = new LiveboardEmbed(getRootEl(), {
|
|
1783
|
+
frameParams: { width: '100%', height: '100%' },
|
|
1784
|
+
liveboardId,
|
|
1785
|
+
});
|
|
1786
|
+
(lb as any).isRendered = true;
|
|
1787
|
+
const result = await lb.trigger(HostEvent.DownloadAsCsv, {} as any);
|
|
1788
|
+
expect(result).toBeNull();
|
|
1789
|
+
});
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1792
|
+
// ---------------------------------------------------------------------------
|
|
1793
|
+
// Spotter share-conversation HostEvents
|
|
1794
|
+
// ---------------------------------------------------------------------------
|
|
1795
|
+
describe('Spotter share-conversation HostEvents', () => {
|
|
1796
|
+
const shareEvents: Array<[string, HostEvent, string, any]> = [
|
|
1797
|
+
['ShareSpotterConversation', HostEvent.ShareSpotterConversation, 'ShareSpotterConversation', { conversationId: 'abc' }],
|
|
1798
|
+
['CloseSpotterShareConversation', HostEvent.CloseSpotterShareConversation, 'CloseSpotterShareConversation', {}],
|
|
1799
|
+
['ExitSpotterSharedConversation', HostEvent.ExitSpotterSharedConversation, 'ExitSpotterSharedConversation', {}],
|
|
1800
|
+
];
|
|
1801
|
+
|
|
1802
|
+
test.each(shareEvents)(
|
|
1803
|
+
'%s: postMessage type is the matching string value',
|
|
1804
|
+
async (name, event, typeString, payload) => {
|
|
1805
|
+
mockMessageChannel();
|
|
1806
|
+
const { lb, iframe } = await renderLiveboard();
|
|
1807
|
+
await executeAfterWait(() => {
|
|
1808
|
+
lb.trigger(event, payload as any);
|
|
1809
|
+
expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
|
|
1810
|
+
expect.objectContaining({ type: typeString }),
|
|
1811
|
+
thoughtSpotHost,
|
|
1812
|
+
expect.anything(),
|
|
1813
|
+
);
|
|
1814
|
+
});
|
|
1815
|
+
},
|
|
1816
|
+
);
|
|
1817
|
+
|
|
1818
|
+
test.each(shareEvents)(
|
|
1819
|
+
'%s: returns null and calls handleError when !isRendered',
|
|
1820
|
+
async (name, event, typeString, payload) => {
|
|
1821
|
+
const lb = unrenderedLiveboard();
|
|
1822
|
+
const result = await lb.trigger(event, payload as any);
|
|
1823
|
+
expect(result).toBeNull();
|
|
1824
|
+
expect((lb as any).handleError).toHaveBeenCalledWith(
|
|
1825
|
+
expect.objectContaining({ code: EmbedErrorCodes.RENDER_NOT_CALLED }),
|
|
1826
|
+
);
|
|
1827
|
+
},
|
|
1828
|
+
);
|
|
1829
|
+
});
|
|
@@ -7,6 +7,49 @@ export interface LiveboardTab {
|
|
|
7
7
|
[key: string]: any;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
export interface LiveboardGroup {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Levels at which a filter or parameter can be applied.
|
|
18
|
+
*/
|
|
19
|
+
export enum ApplicabilityLevel {
|
|
20
|
+
Liveboard = 'LIVEBOARD',
|
|
21
|
+
Tab = 'TAB',
|
|
22
|
+
Group = 'GROUP',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Scopes a filter or parameter to a specific target.
|
|
27
|
+
* At `LIVEBOARD` level the filter applies to the whole Liveboard, so `targetId`
|
|
28
|
+
* is not required.
|
|
29
|
+
*/
|
|
30
|
+
export interface Applicability {
|
|
31
|
+
level: ApplicabilityLevel;
|
|
32
|
+
targetId?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface FilterUpdate {
|
|
36
|
+
column: string;
|
|
37
|
+
oper: string;
|
|
38
|
+
values: string[];
|
|
39
|
+
type?: string;
|
|
40
|
+
applicability?: Applicability;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface LiveboardFilter {
|
|
44
|
+
applicability?: Applicability;
|
|
45
|
+
[key: string]: any;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface LiveboardParameter {
|
|
49
|
+
applicability?: Applicability;
|
|
50
|
+
[key: string]: any;
|
|
51
|
+
}
|
|
52
|
+
|
|
10
53
|
export enum UIPassthroughEvent {
|
|
11
54
|
PinAnswerToLiveboard = 'addVizToPinboard',
|
|
12
55
|
SaveAnswer = 'saveAnswer',
|
|
@@ -23,6 +66,7 @@ export enum UIPassthroughEvent {
|
|
|
23
66
|
GetParameters = 'getParameters',
|
|
24
67
|
GetTML = 'getTML',
|
|
25
68
|
GetTabs = 'getTabs',
|
|
69
|
+
GetGroups = 'getGroups',
|
|
26
70
|
GetExportRequestForCurrentPinboard = 'getExportRequestForCurrentPinboard',
|
|
27
71
|
}
|
|
28
72
|
|
|
@@ -103,7 +147,7 @@ export type UIPassthroughContractBase = {
|
|
|
103
147
|
vizId?: string;
|
|
104
148
|
};
|
|
105
149
|
response: {
|
|
106
|
-
liveboardFilters:
|
|
150
|
+
liveboardFilters: LiveboardFilter[];
|
|
107
151
|
runtimeFilters: RuntimeFilter[];
|
|
108
152
|
};
|
|
109
153
|
};
|
|
@@ -116,7 +160,7 @@ export type UIPassthroughContractBase = {
|
|
|
116
160
|
[UIPassthroughEvent.GetParameters]: {
|
|
117
161
|
request: Record<string, never>;
|
|
118
162
|
response: {
|
|
119
|
-
parameters:
|
|
163
|
+
parameters: LiveboardParameter[];
|
|
120
164
|
};
|
|
121
165
|
};
|
|
122
166
|
[UIPassthroughEvent.GetTML]: {
|
|
@@ -134,6 +178,14 @@ export type UIPassthroughContractBase = {
|
|
|
134
178
|
Tabs: LiveboardTab[];
|
|
135
179
|
};
|
|
136
180
|
};
|
|
181
|
+
[UIPassthroughEvent.GetGroups]: {
|
|
182
|
+
request: Record<string, never>;
|
|
183
|
+
response: {
|
|
184
|
+
orderedGroupIds: string[];
|
|
185
|
+
numberOfGroups: number;
|
|
186
|
+
Groups: LiveboardGroup[];
|
|
187
|
+
};
|
|
188
|
+
};
|
|
137
189
|
[UIPassthroughEvent.GetExportRequestForCurrentPinboard]: {
|
|
138
190
|
request: Record<string, never>;
|
|
139
191
|
response: {
|
|
@@ -143,18 +195,8 @@ export type UIPassthroughContractBase = {
|
|
|
143
195
|
};
|
|
144
196
|
[UIPassthroughEvent.UpdateFilters]: {
|
|
145
197
|
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
|
-
}[];
|
|
198
|
+
filter?: FilterUpdate;
|
|
199
|
+
filters?: FilterUpdate[];
|
|
158
200
|
};
|
|
159
201
|
response: unknown;
|
|
160
202
|
};
|
|
@@ -198,6 +240,7 @@ export type EmbedApiHostEventMapping = {
|
|
|
198
240
|
[HostEvent.GetParameters]: UIPassthroughEvent.GetParameters;
|
|
199
241
|
[HostEvent.GetTML]: UIPassthroughEvent.GetTML;
|
|
200
242
|
[HostEvent.GetTabs]: UIPassthroughEvent.GetTabs;
|
|
243
|
+
[HostEvent.GetGroups]: UIPassthroughEvent.GetGroups;
|
|
201
244
|
[HostEvent.getExportRequestForCurrentPinboard]: UIPassthroughEvent.GetExportRequestForCurrentPinboard;
|
|
202
245
|
}
|
|
203
246
|
|
|
@@ -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,266 @@ 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
|
+
});
|
|
819
1157
|
});
|