@thoughtspot/visual-embed-sdk
Version:
ThoughtSpot Embed SDK
566 lines • 33.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const types_1 = require("../../types");
const processTrigger_1 = require("../../utils/processTrigger");
const EmbedConfigService = tslib_1.__importStar(require("../embedConfig"));
const contracts_1 = require("./contracts");
const host_event_client_1 = require("./host-event-client");
jest.mock('../../utils/processTrigger');
const mockProcessTrigger = processTrigger_1.processTrigger;
const createHostEventClient = () => {
const mockIframe = document.createElement('iframe');
const client = new host_event_client_1.HostEventClient(mockIframe);
return { client, mockIframe };
};
describe('HostEventClient', () => {
const mockThoughtSpotHost = 'http://localhost';
beforeEach(() => {
jest.spyOn(EmbedConfigService, 'getEmbedConfig').mockReturnValue({ thoughtSpotHost: mockThoughtSpotHost, authType: types_1.AuthType.None });
});
afterEach(() => {
jest.clearAllMocks();
});
describe('executeUIPassthroughApi', () => {
it('should call processTrigger with correct parameters and return response', async () => {
const { client, mockIframe } = createHostEventClient();
const apiName = contracts_1.UIPassthroughEvent.PinAnswerToLiveboard;
const parameters = {
newVizName: 'testViz',
};
const triggerResponse = Promise.resolve([
{ value: { pinboardId: 'testPinboard', tabId: 'testTab', vizId: 'testVizId' } },
]);
mockProcessTrigger.mockResolvedValue(triggerResponse);
const result = await client.triggerUIPassthroughApi(apiName, parameters);
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, {
type: apiName,
parameters,
}, undefined);
expect(result).toEqual(await triggerResponse);
});
});
describe('handleUIPassthroughForHostEvent', () => {
it('should return the value from the first valid response', async () => {
const { client } = createHostEventClient();
const apiName = contracts_1.UIPassthroughEvent.PinAnswerToLiveboard;
const parameters = {
newVizName: 'testViz',
};
const triggerResponse = Promise.resolve([
{ value: { pinboardId: 'testPinboard', tabId: 'testTab', vizId: 'testVizId' } },
]);
mockProcessTrigger.mockResolvedValue(triggerResponse);
const result = await client.handleHostEventWithParam(apiName, parameters);
expect(result).toEqual({
pinboardId: 'testPinboard',
tabId: 'testTab',
vizId: 'testVizId',
});
});
it('should throw an error if no valid response is found', async () => {
const { client } = createHostEventClient();
const apiName = contracts_1.UIPassthroughEvent.PinAnswerToLiveboard;
const parameters = {
newVizName: 'testViz',
};
const triggerResponse = [];
mockProcessTrigger.mockResolvedValue(triggerResponse);
await expect(client.handleHostEventWithParam(apiName, parameters))
.rejects.toEqual({ error: 'No answer found.' });
});
it('should throw an error if no valid response is found for vizId', async () => {
const { client } = createHostEventClient();
const apiName = contracts_1.UIPassthroughEvent.PinAnswerToLiveboard;
const parameters = {
newVizName: 'testViz',
vizId: 'testVizId',
};
const triggerResponse = [];
mockProcessTrigger.mockResolvedValue(triggerResponse);
await expect(client.handleHostEventWithParam(apiName, parameters))
.rejects.toEqual({ error: 'No answer found for vizId: testVizId.' });
});
it('should throw an error if the response contains errors', async () => {
const { client } = createHostEventClient();
const apiName = contracts_1.UIPassthroughEvent.PinAnswerToLiveboard;
const parameters = {
newVizName: 'testViz',
};
const triggerResponse = [
{ error: 'Some error' },
];
mockProcessTrigger.mockResolvedValue(triggerResponse);
await expect(client.handleHostEventWithParam(apiName, parameters))
.rejects.toEqual({ error: 'Some error' });
});
});
describe('executeHostEvent', () => {
const mockGetAvailablePassthroughs = () => [{ value: { keys: Object.values(contracts_1.UIPassthroughEvent) } }];
it('should call handleUIPassthroughForHostEvent for Pin event', async () => {
const { client, mockIframe } = createHostEventClient();
const hostEvent = types_1.HostEvent.Pin;
const payload = {
newVizName: 'testViz',
};
const mockResponse = {
value: {
pinboardId: 'testPinboard',
liveboardId: 'testPinboard',
tabId: 'testTab',
vizId: 'testVizId',
},
};
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([mockResponse]);
const result = await client.triggerHostEvent(hostEvent, payload);
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.UIPassthrough, 'http://localhost', { parameters: payload, type: contracts_1.UIPassthroughEvent.PinAnswerToLiveboard }, undefined);
expect(result).toEqual(mockResponse.value);
});
it('should call handleUIPassthroughForHostEvent for SaveAnswer event', async () => {
const { client, mockIframe } = createHostEventClient();
const hostEvent = types_1.HostEvent.SaveAnswer;
const payload = {
name: 'Test Answer',
description: 'Test Description',
vizId: 'testVizId',
};
const mockResponse = [{
value: {
saveResponse: {
data: {
Answer__save: {
answer: {
id: 'newAnswer',
},
},
},
},
},
refId: 'testVizId',
}];
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce(mockResponse);
const result = await client.triggerHostEvent(hostEvent, payload);
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, {
parameters: payload,
type: 'saveAnswer',
}, undefined);
expect(result).toEqual({ answerId: 'newAnswer', ...mockResponse[0].value });
});
it('should call handleHostEventWithParam for UpdateFilters event', async () => {
const { client, mockIframe } = createHostEventClient();
const hostEvent = types_1.HostEvent.UpdateFilters;
const payload = {
vizId: 'viz-123',
filter: {
column: 'region',
oper: 'EQ',
values: ['North'],
},
};
const mockResponse = [{ value: { success: true } }];
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce(mockResponse);
const result = await client.triggerHostEvent(hostEvent, payload);
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, {
type: contracts_1.UIPassthroughEvent.UpdateFilters,
parameters: payload,
}, undefined);
expect(result).toEqual({ success: true });
});
it('should call handleHostEventWithParam for DrillDown event', async () => {
const { client, mockIframe } = createHostEventClient();
const hostEvent = types_1.HostEvent.DrillDown;
const payload = {
points: { clickedPoint: 'point-1', selectedPoints: ['sel-1'] },
autoDrillDown: true,
};
const mockResponse = [{ value: { drillDownApplied: true } }];
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce(mockResponse);
const result = await client.triggerHostEvent(hostEvent, payload);
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, {
type: contracts_1.UIPassthroughEvent.Drilldown,
parameters: payload,
}, undefined);
expect(result).toEqual({ drillDownApplied: true });
});
it('should accept UpdateFilters with filters array', async () => {
const { client, mockIframe } = createHostEventClient();
const payload = {
filters: [
{ column: '(Sample) Retail - Apparel::city', oper: 'IN', values: ['atlanta'] },
{ column: '(Sample) Retail - Apparel::Region', oper: 'IN', values: ['West', 'Midwest'] },
],
};
const mockResponse = [{ value: { success: true } }];
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce(mockResponse);
const result = await client.triggerHostEvent(types_1.HostEvent.UpdateFilters, payload);
expect(mockProcessTrigger).toHaveBeenNthCalledWith(2, mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, { type: contracts_1.UIPassthroughEvent.UpdateFilters, parameters: payload }, undefined);
expect(result).toEqual({ success: true });
});
it('should throw when UpdateFilters payload has no valid filter', async () => {
const { client } = createHostEventClient();
const invalidPayload = {};
mockProcessTrigger.mockResolvedValueOnce(mockGetAvailablePassthroughs());
await expect(client.triggerHostEvent(types_1.HostEvent.UpdateFilters, invalidPayload))
.rejects.toThrow('UpdateFilters requires a valid filter or filters array');
expect(mockProcessTrigger).toHaveBeenCalledTimes(1);
});
it('should pass context to UpdateFilters event', async () => {
const { client, mockIframe } = createHostEventClient();
const payload = { vizId: 'viz-1', filter: { column: 'x', oper: 'EQ', values: ['a'] } };
const context = { answerId: 'ans-1' };
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([{ value: {} }]);
await client.triggerHostEvent(types_1.HostEvent.UpdateFilters, payload, context);
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, { type: contracts_1.UIPassthroughEvent.UpdateFilters, parameters: payload }, context);
});
it('should throw when DrillDown payload has no valid points', async () => {
const { client } = createHostEventClient();
const invalidPayload = {};
mockProcessTrigger.mockResolvedValueOnce(mockGetAvailablePassthroughs());
await expect(client.triggerHostEvent(types_1.HostEvent.DrillDown, invalidPayload))
.rejects.toThrow('DrillDown requires a valid points object');
expect(mockProcessTrigger).toHaveBeenCalledTimes(1);
});
it('should pass context to DrillDown event', async () => {
const { client, mockIframe } = createHostEventClient();
const payload = { points: { clickedPoint: 'point-1' }, vizId: 'viz-2' };
const context = { liveboardId: 'lb-1' };
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([{ value: {} }]);
await client.triggerHostEvent(types_1.HostEvent.DrillDown, payload, context);
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, { type: contracts_1.UIPassthroughEvent.Drilldown, parameters: payload }, context);
});
it('should skip to fallback when passthrough is not in available keys', async () => {
const { client, mockIframe } = createHostEventClient();
mockProcessTrigger
.mockResolvedValueOnce([{ value: { keys: ['getFilters'] } }])
.mockResolvedValueOnce({ session: 'legacySession' });
const result = await client.triggerHostEvent(types_1.HostEvent.GetAnswerSession, { vizId: '123' });
expect(mockProcessTrigger).toHaveBeenCalledTimes(2);
expect(mockProcessTrigger).toHaveBeenNthCalledWith(2, mockIframe, types_1.HostEvent.GetAnswerSession, mockThoughtSpotHost, { vizId: '123' }, undefined);
expect(result).toEqual({ session: 'legacySession' });
});
it('should call hostEventFallback for unmapped events', async () => {
const { client } = createHostEventClient();
const hostEvent = 'testEvent';
const payload = { data: 'testData' };
const mockResponse = { fallbackResponse: 'data' };
jest.spyOn(client, 'hostEventFallback').mockResolvedValue(mockResponse);
const result = await client.triggerHostEvent(hostEvent, payload);
expect(client.hostEventFallback).toHaveBeenCalledWith(hostEvent, payload, undefined);
expect(result).toEqual(mockResponse);
});
it('should route GetAnswerSession through passthrough and return data', async () => {
const { client, mockIframe } = createHostEventClient();
const mockResponse = [{ value: { session: 'testSession', embedAnswerData: { id: '1' } } }];
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce(mockResponse);
const result = await client.triggerHostEvent(types_1.HostEvent.GetAnswerSession, { vizId: '123' });
expect(mockProcessTrigger).toHaveBeenNthCalledWith(2, mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, { type: contracts_1.UIPassthroughEvent.GetAnswerSession, parameters: { vizId: '123' } }, undefined);
expect(result).toEqual({ session: 'testSession', embedAnswerData: { id: '1' } });
});
it('should fall back to legacy host event when passthrough returns no data for GetAnswerSession', async () => {
const { client } = createHostEventClient();
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([])
.mockResolvedValueOnce({ session: 'fallbackSession' });
const result = await client.triggerHostEvent(types_1.HostEvent.GetAnswerSession, { vizId: '123' });
expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
expect(mockProcessTrigger).toHaveBeenNthCalledWith(3, expect.anything(), types_1.HostEvent.GetAnswerSession, mockThoughtSpotHost, { vizId: '123' }, undefined);
expect(result).toEqual({ session: 'fallbackSession' });
});
it('should throw real errors from passthrough without falling back', async () => {
const { client } = createHostEventClient();
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([{ error: 'Permission denied' }]);
await expect(client.triggerHostEvent(types_1.HostEvent.GetAnswerSession, {}))
.rejects.toThrow('Permission denied');
expect(mockProcessTrigger).toHaveBeenCalledTimes(2);
});
it('should route GetFilters through passthrough and return data', async () => {
const { client, mockIframe } = createHostEventClient();
const mockResponse = [{ value: { liveboardFilters: [{ id: 'f1' }], runtimeFilters: [] } }];
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce(mockResponse);
const result = await client.triggerHostEvent(types_1.HostEvent.GetFilters, {});
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, { type: contracts_1.UIPassthroughEvent.GetFilters, parameters: {} }, undefined);
expect(result).toEqual({ liveboardFilters: [{ id: 'f1' }], runtimeFilters: [] });
});
it('should route GetTabs through passthrough and return data', async () => {
const { client } = createHostEventClient();
const mockResponse = [{ value: { orderedTabIds: ['t1', 't2'], numberOfTabs: 2, Tabs: [] } }];
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce(mockResponse);
const result = await client.triggerHostEvent(types_1.HostEvent.GetTabs, {});
expect(result).toEqual({ orderedTabIds: ['t1', 't2'], numberOfTabs: 2, Tabs: [] });
});
it('should route GetTML through passthrough and return data', async () => {
const { client } = createHostEventClient();
const tmlData = { answer: { search_query: 'revenue by region' } };
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([{ value: tmlData }]);
const result = await client.triggerHostEvent(types_1.HostEvent.GetTML, {});
expect(result).toEqual(tmlData);
});
it('should route GetIframeUrl through passthrough and return data', async () => {
const { client } = createHostEventClient();
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([{ value: { iframeUrl: 'https://ts.example.com/embed' } }]);
const result = await client.triggerHostEvent(types_1.HostEvent.GetIframeUrl, {});
expect(result).toEqual({ iframeUrl: 'https://ts.example.com/embed' });
});
it('should route GetParameters through passthrough and return data', async () => {
const { client } = createHostEventClient();
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([{ value: { parameters: [{ name: 'p1' }] } }]);
const result = await client.triggerHostEvent(types_1.HostEvent.GetParameters, {});
expect(result).toEqual({ parameters: [{ name: 'p1' }] });
});
it('should route getExportRequestForCurrentPinboard through passthrough and return data', async () => {
const { client } = createHostEventClient();
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([{ value: { data: { v2Content: 'exportData' }, type: 'getExportRequestForCurrentPinboard' } }]);
const result = await client.triggerHostEvent(types_1.HostEvent.getExportRequestForCurrentPinboard, {});
expect(result).toEqual({ data: { v2Content: 'exportData' }, type: 'getExportRequestForCurrentPinboard' });
});
it('should fall back to legacy for GetFilters when passthrough returns null', async () => {
const { client } = createHostEventClient();
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce(null)
.mockResolvedValueOnce({ liveboardFilters: [], runtimeFilters: [] });
const result = await client.triggerHostEvent(types_1.HostEvent.GetFilters, {});
expect(mockProcessTrigger).toHaveBeenCalledTimes(3);
expect(result).toEqual({ liveboardFilters: [], runtimeFilters: [] });
});
it('should call fallback for Pin event', async () => {
const { client, mockIframe } = createHostEventClient();
const hostEvent = types_1.HostEvent.Pin;
const payload = {};
const mockResponse = {
value: {
pinboardId: 'testPinboard',
tabId: 'testTab',
vizId: 'testVizId',
},
};
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([mockResponse]);
const result = await client.triggerHostEvent(hostEvent, payload);
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.Pin, mockThoughtSpotHost, {}, undefined);
expect(result).toEqual([mockResponse]);
});
it('should call fallback for SaveAnswer event', async () => {
const { client, mockIframe } = createHostEventClient();
const hostEvent = types_1.HostEvent.SaveAnswer;
const payload = {};
const mockResponse = {
value: {
pinboardId: 'testPinboard',
tabId: 'testTab',
vizId: 'testVizId',
},
};
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce([mockResponse]);
const result = await client.triggerHostEvent(hostEvent, payload);
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.Save, mockThoughtSpotHost, {}, undefined);
expect(result).toEqual([mockResponse]);
});
it('Pin response support pinboardId as well', async () => {
const { client, mockIframe } = createHostEventClient();
const hostEvent = types_1.HostEvent.Pin;
const payload = {
newVizDescription: 'Test Description',
vizId: 'testVizId',
newVizName: 'Test Answer',
newLiveboardName: 'testLiveboard',
};
const mockResponse = [{
value: {
pinboardId: 'testLiveboard',
liveboardId: 'testPinboard',
tabId: 'testTab',
vizId: 'testVizId',
},
refId: 'testVizId',
}];
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce(mockResponse);
const result = await client.triggerHostEvent(hostEvent, payload);
expect(result.liveboardId).toBe('testLiveboard');
});
it('should request liveboardId as well', async () => {
const { client, mockIframe } = createHostEventClient();
const hostEvent = types_1.HostEvent.Pin;
const payload = {
liveboardId: 'test',
newVizName: 'Test Answer',
newPinboardName: 'testLiveboard1',
newLiveboardName: 'testLiveboard',
};
const mockResponse = [{
value: {
pinboardId: 'testLiveboard',
tabId: 'testTab',
vizId: 'testVizId',
},
refId: 'testVizId',
}];
mockProcessTrigger
.mockResolvedValueOnce(mockGetAvailablePassthroughs())
.mockResolvedValueOnce(mockResponse);
const result = await client.triggerHostEvent(hostEvent, payload);
expect(result.liveboardId).toBe('testLiveboard');
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, {
parameters: { ...payload, pinboardId: 'test', newPinboardName: 'testLiveboard' },
type: 'addVizToPinboard',
}, undefined);
expect(result).toEqual({
pinboardId: 'testLiveboard',
tabId: 'testTab',
vizId: 'testVizId',
liveboardId: 'testLiveboard',
});
});
});
describe('getDataWithPassthroughFallback', () => {
const callMethod = (client, ...args) => client.getDataWithPassthroughFallback(...args);
it('should return unwrapped value when passthrough succeeds', async () => {
const { client } = createHostEventClient();
mockProcessTrigger.mockResolvedValue([{ value: { session: 's1', embedAnswerData: { id: 'a1' } } }]);
const result = await callMethod(client, contracts_1.UIPassthroughEvent.GetAnswerSession, types_1.HostEvent.GetAnswerSession, { vizId: '1' });
expect(result).toEqual({ session: 's1', embedAnswerData: { id: 'a1' } });
expect(mockProcessTrigger).toHaveBeenCalledTimes(1);
});
it('should fall back to legacy when passthrough returns empty array', async () => {
const { client } = createHostEventClient();
const legacyResponse = { session: 'legacy' };
mockProcessTrigger
.mockResolvedValueOnce([])
.mockResolvedValueOnce(legacyResponse);
const result = await callMethod(client, contracts_1.UIPassthroughEvent.GetAnswerSession, types_1.HostEvent.GetAnswerSession, { vizId: '1' });
expect(mockProcessTrigger).toHaveBeenCalledTimes(2);
expect(result).toEqual(legacyResponse);
});
it('should fall back to legacy when passthrough returns null', async () => {
const { client } = createHostEventClient();
const legacyResponse = { parameters: [] };
mockProcessTrigger
.mockResolvedValueOnce(null)
.mockResolvedValueOnce(legacyResponse);
const result = await callMethod(client, contracts_1.UIPassthroughEvent.GetParameters, types_1.HostEvent.GetParameters, {});
expect(mockProcessTrigger).toHaveBeenCalledTimes(2);
expect(result).toEqual(legacyResponse);
});
it('should fall back to legacy when passthrough returns undefined', async () => {
const { client } = createHostEventClient();
const legacyResponse = { iframeUrl: 'https://ts.example.com' };
mockProcessTrigger
.mockResolvedValueOnce(undefined)
.mockResolvedValueOnce(legacyResponse);
const result = await callMethod(client, contracts_1.UIPassthroughEvent.GetIframeUrl, types_1.HostEvent.GetIframeUrl, {});
expect(mockProcessTrigger).toHaveBeenCalledTimes(2);
expect(result).toEqual(legacyResponse);
});
it('should fall back when response array has no matching entries', async () => {
const { client } = createHostEventClient();
const legacyResponse = { v2Content: 'data' };
mockProcessTrigger
.mockResolvedValueOnce([{ refId: 'r1' }])
.mockResolvedValueOnce(legacyResponse);
const result = await callMethod(client, contracts_1.UIPassthroughEvent.GetExportRequestForCurrentPinboard, types_1.HostEvent.getExportRequestForCurrentPinboard, {});
expect(mockProcessTrigger).toHaveBeenCalledTimes(2);
expect(result).toEqual(legacyResponse);
});
it('should throw when response has error field', async () => {
const { client } = createHostEventClient();
mockProcessTrigger.mockResolvedValue([{ error: 'Permission denied' }]);
await expect(callMethod(client, contracts_1.UIPassthroughEvent.GetFilters, types_1.HostEvent.GetFilters, {})).rejects.toThrow('Permission denied');
expect(mockProcessTrigger).toHaveBeenCalledTimes(1);
});
it('should throw when response value contains errors field', async () => {
const { client } = createHostEventClient();
mockProcessTrigger.mockResolvedValue([{ value: { errors: 'Invalid vizId' } }]);
await expect(callMethod(client, contracts_1.UIPassthroughEvent.GetTML, types_1.HostEvent.GetTML, { vizId: 'bad' })).rejects.toThrow('Invalid vizId');
expect(mockProcessTrigger).toHaveBeenCalledTimes(1);
});
it('should throw when response value contains error field', async () => {
const { client } = createHostEventClient();
mockProcessTrigger.mockResolvedValue([{ value: { error: 'Not found' } }]);
await expect(callMethod(client, contracts_1.UIPassthroughEvent.GetTabs, types_1.HostEvent.GetTabs, {})).rejects.toThrow('Not found');
expect(mockProcessTrigger).toHaveBeenCalledTimes(1);
});
it('should stringify object errors instead of producing [object Object]', async () => {
const { client } = createHostEventClient();
const errorObj = { code: 403, reason: 'Forbidden' };
mockProcessTrigger.mockResolvedValue([{ error: errorObj }]);
await expect(callMethod(client, contracts_1.UIPassthroughEvent.GetFilters, types_1.HostEvent.GetFilters, {})).rejects.toThrow(JSON.stringify(errorObj));
expect(mockProcessTrigger).toHaveBeenCalledTimes(1);
});
it('should default payload to empty object when null', async () => {
const { client, mockIframe } = createHostEventClient();
mockProcessTrigger.mockResolvedValue([{ value: { iframeUrl: 'https://ts.example.com' } }]);
await callMethod(client, contracts_1.UIPassthroughEvent.GetIframeUrl, types_1.HostEvent.GetIframeUrl, null);
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.UIPassthrough, mockThoughtSpotHost, { type: contracts_1.UIPassthroughEvent.GetIframeUrl, parameters: {} }, undefined);
});
});
describe('UI passthrough available keys tests', () => {
const mockKeys = () => [{ value: { keys: Object.values(contracts_1.UIPassthroughEvent) } }];
it('triggerHostEvent Pin returns passthrough response', async () => {
const { client } = createHostEventClient();
mockProcessTrigger
.mockResolvedValueOnce(mockKeys())
.mockResolvedValueOnce([{ value: { pinboardId: 'lb1', tabId: 't1', vizId: 'v1' } }]);
const result = await client.triggerHostEvent(types_1.HostEvent.Pin, { newVizName: 'Viz' });
expect(result).toMatchObject({ pinboardId: 'lb1', liveboardId: 'lb1' });
});
it('triggerHostEvent GetAnswerSession returns session', async () => {
const { client } = createHostEventClient();
mockProcessTrigger
.mockResolvedValueOnce(mockKeys())
.mockResolvedValueOnce([{ value: { session: 's1' } }]);
const result = await client.triggerHostEvent(types_1.HostEvent.GetAnswerSession, {});
expect(result).toEqual({ session: 's1' });
});
it('triggerHostEvent unmapped event uses fallback', async () => {
const { client } = createHostEventClient();
mockProcessTrigger.mockResolvedValue({ data: 'legacy' });
const result = await client.triggerHostEvent('unknownEvent', {});
expect(mockProcessTrigger).toHaveBeenCalledWith(expect.anything(), 'unknownEvent', mockThoughtSpotHost, {}, undefined);
expect(result).toEqual({ data: 'legacy' });
});
it('hostEventFallback delegates to processTrigger', async () => {
const { client, mockIframe } = createHostEventClient();
mockProcessTrigger.mockResolvedValue({ ok: true });
const result = await client.hostEventFallback(types_1.HostEvent.Save, { x: 1 });
expect(mockProcessTrigger).toHaveBeenCalledWith(mockIframe, types_1.HostEvent.Save, mockThoughtSpotHost, { x: 1 }, undefined);
expect(result).toEqual({ ok: true });
});
});
});
//# sourceMappingURL=host-event-client.spec.js.map