import { AppEmbed, AppViewConfig, DataPanelCustomColumnGroupsAccordionState, Page, HomePageSearchBarMode, HomePage, ListPage, } from './app'; import { SpotterQueryMode } from './conversation'; import { init } from '../index'; import { Action, AuthType, EmbedEvent, HostEvent, RuntimeFilterOp } from '../types'; import { executeAfterWait, getDocumentBody, getIFrameSrc, getRootEl, getIFrameEl, mockMessageChannel, defaultParams, defaultParamsForPinboardEmbed, defaultParamsWithoutHiddenActions, expectUrlMatchesWithParams, postMessageToParent, testVisualOverridesInEmbed, expectUrlToHaveParamsWithValues, } from '../test/test-utils'; import { version } from '../../package.json'; import * as config from '../config'; import { TsEmbed, V1Embed } from './ts-embed'; import { logger } from '../utils/logger'; import * as auth from '../auth'; const defaultViewConfig = { frameParams: { width: 1280, height: 720, }, }; const thoughtSpotHost = 'tshost'; const defaultParamsPost = ''; // Helper function to create and render AppEmbed with config const createAndRenderAppEmbed = async (viewConfig: AppViewConfig) => { const appEmbed = new AppEmbed(getRootEl(), viewConfig); appEmbed.render(); return appEmbed; }; // Helper function to test URL parameters const testUrlParams = async (viewConfig: AppViewConfig, expectedUrl: string) => { await createAndRenderAppEmbed(viewConfig); await executeAfterWait(() => { expectUrlMatchesWithParams(getIFrameSrc(), expectedUrl); }); }; // Helper function to test the full-height route-change behavior const testSetIframeHeightBehavior = ( currentPath: string, shouldBeCalled: boolean ) => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig) as any; const spySetIFrameHeight = shouldBeCalled ? jest.spyOn(appEmbed, 'setIFrameHeight').mockImplementation(jest.fn()) : jest.spyOn(appEmbed, 'setIFrameHeight'); appEmbed.render(); appEmbed.fullHeightController.handleRouteChange({ data: { currentPath }, type: 'Route', }); if (shouldBeCalled) { expect(spySetIFrameHeight).toHaveBeenCalled(); } else { expect(spySetIFrameHeight).not.toHaveBeenCalled(); } }; // Helper function to create mock iframe with default getBoundingClientRect const createMockIFrame = (rect = { top: 100, left: 150, bottom: 600, right: 800, width: 650, height: 500, }): HTMLIFrameElement => { const mockIFrame = document.createElement('iframe'); mockIFrame.getBoundingClientRect = jest.fn().mockReturnValue(rect); return mockIFrame; }; // Helper function to setup iframe creation spy const setupIFrameCreationSpy = (mockIFrame: HTMLIFrameElement) => { jest.spyOn(document, 'createElement').mockImplementation((tagName) => { if (tagName === 'iframe') { return mockIFrame; } return document.createElement(tagName); }); }; const originalResizeObserver = window.ResizeObserver; beforeAll(() => { init({ thoughtSpotHost, authType: AuthType.None, }); jest.spyOn(auth, 'postLoginService').mockImplementation(() => Promise.resolve(undefined)); (window as any).ResizeObserver = window.ResizeObserver || jest.fn().mockImplementation(() => ({ disconnect: jest.fn(), observe: jest.fn(), unobserve: jest.fn(), })); }); const cleanUp = () => { document.body.innerHTML = getDocumentBody(); window.ResizeObserver = originalResizeObserver; }; describe('App embed tests', () => { beforeEach(() => { cleanUp(); }); test('should render home page by default', async () => { const appEmbed = new AppEmbed(getRootEl(), defaultViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should hide the primary nav bar', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should hide the help and profile buttons from nav bar', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, disableProfileAndHelp: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should hide the application switcher button from nav bar', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, hideApplicationSwitcher: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&applicationSwitcherHidden=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should hide the org switcher button from nav bar', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, hideOrgSwitcher: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&orgSwitcherHidden=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); describe('should render the correct routes for pages', () => { const pageRouteMap = { [Page.Search]: 'answer', [Page.Answers]: 'answers', [Page.Pinboards]: 'pinboards', [Page.Liveboards]: 'pinboards', [Page.Data]: 'data/tables', [Page.Home]: 'home', [Page.SpotIQ]: 'insights/results', [Page.Monitor]: 'insights/monitor-alerts', [Page.Collections]: 'collections', [Page.LiveboardSchedules]: 'home/liveboard-schedules', }; const pageIds = Object.keys(pageRouteMap); for (let i = 0; i < pageIds.length; i++) { const pageId = pageIds[i]; test(`${pageId}`, async () => { const route = pageRouteMap[pageId as keyof typeof pageRouteMap]; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, pageId: pageId as Page, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/${route}`, ); cleanUp(); }); }); } const pageRouteMapForModularHome = { [Page.Search]: 'answer', [Page.Answers]: 'home/answers', [Page.Pinboards]: 'home/liveboards', [Page.Liveboards]: 'home/liveboards', [Page.Data]: 'data/tables', [Page.Home]: 'home', [Page.SpotIQ]: 'home/spotiq-analysis', [Page.Monitor]: 'home/monitor-alerts', [Page.Collections]: 'collections', [Page.LiveboardSchedules]: 'home/liveboard-schedules', }; const pageIdsForModularHomes = Object.keys(pageRouteMapForModularHome); for (let i = 0; i < pageIdsForModularHomes.length; i++) { const pageIdsForModularHome = pageIdsForModularHomes[i]; test(`${pageIdsForModularHome}`, async () => { const route = pageRouteMapForModularHome[pageIdsForModularHome as keyof typeof pageRouteMapForModularHome]; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, modularHomeExperience: true, pageId: pageIdsForModularHome as Page, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/${route}`, ); cleanUp(); }); }); } }); test('should navigate to a path', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, path: 'foo/bar', } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/foo/bar`, ); }); }); test('should apply runtime filters', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: true, runtimeFilters: [ { columnName: 'sales', operator: RuntimeFilterOp.EQ, values: [1000], }, ], excludeRuntimeFiltersfromURL: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=false&profileAndHelpInNavBarHidden=false&col1=sales&op1=EQ&val1=1000&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should not append runtime filters in URL if excludeRuntimeFiltersfromURL is true', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: true, runtimeFilters: [ { columnName: 'sales', operator: RuntimeFilterOp.EQ, values: [1000], }, ], excludeRuntimeFiltersfromURL: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=false&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should append runtime filters in URL if excludeRuntimeFiltersfromURL is undefined', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: true, runtimeFilters: [ { columnName: 'sales', operator: RuntimeFilterOp.EQ, values: [1000], }, ], excludeRuntimeFiltersfromURL: undefined, } as AppViewConfig); appEmbed.render(); const runtimeFilter = 'col1=sales&op1=EQ&val1=1000'; await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=false&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}&${runtimeFilter}#/home`, ); }); }); test('should disable and hide actions', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: true, disabledActions: [Action.Save, Action.Update], disabledActionReason: 'Access denied', hiddenActions: [Action.Download], } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=false&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3&${defaultParamsWithoutHiddenActions}&disableAction=[%22save%22,%22update%22]&disableHint=Access%20denied&hideAction=[%22${Action.ReportError}%22,%22download%22]${defaultParamsPost}#/home`, ); }); }); test('should set enable2ColumnLayout to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, enable2ColumnLayout: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&enable2ColumnLayout=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isLiveboardAlwaysOn12ColLayout to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isLiveboardAlwaysOn12ColLayout: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isLiveboardAlwaysOn12ColLayout=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set coverAndFilterOptionInPDF to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, coverAndFilterOptionInPDF: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&arePdfCoverFilterPageCheckboxesEnabled=false&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isLiveboardStylingAndGroupingEnabled to true in url (deprecated, use isLiveboardMasterpiecesEnabled)', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isLiveboardStylingAndGroupingEnabled: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isLiveboardStylingAndGroupingEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set liveboardGutter in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, liveboardGutter: 8, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&liveboardGutter=8&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isThisPeriodInDateFiltersEnabled to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isThisPeriodInDateFiltersEnabled: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isThisPeriodInDateFiltersEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set enableHomepageAnnouncement to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, enableHomepageAnnouncement: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&enableHomepageAnnouncement=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set enableHomepageAnnouncement to false in url when not provided in AppViewConfig', async () => { const appEmbed = new AppEmbed(getRootEl(), defaultViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&enableHomepageAnnouncement=false&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isPNGInScheduledEmailsEnabled to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isPNGInScheduledEmailsEnabled: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isPNGInScheduledEmailsEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isWYSIWYGLiveboardPDFEnabled to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isContinuousLiveboardPDFEnabled: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isWYSIWYGLiveboardPDFEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should not set isWYSIWYGLiveboardPDFEnabled in url by default', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isLinkParametersEnabled to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isLinkParametersEnabled: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isLinkParametersEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isLinkParametersEnabled to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isLinkParametersEnabled: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isLinkParametersEnabled=false&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('Should add homepageVersion=v4 and updatedSpotterChatPrompt=true when homePage is Focused to the iframe src', async () => { await testUrlParams( { ...defaultViewConfig, discoveryExperience: { homePage: HomePage.Focused, }, } as AppViewConfig, `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v4&updatedSpotterChatPrompt=true${defaultParams}${defaultParamsPost}#/home`, ); }); test('Should respect explicit updatedSpotterChatPrompt=false even when homePage is Focused', async () => { await testUrlParams( { ...defaultViewConfig, discoveryExperience: { homePage: HomePage.Focused, }, updatedSpotterChatPrompt: false, } as AppViewConfig, `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v4&updatedSpotterChatPrompt=false${defaultParams}${defaultParamsPost}#/home`, ); }); test('should set isLiveboardXLSXCSVDownloadEnabled to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isLiveboardXLSXCSVDownloadEnabled: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isLiveboardXLSXCSVDownloadEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set updatedSpotterChatPrompt to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, updatedSpotterChatPrompt: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&updatedSpotterChatPrompt=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set updatedSpotterChatPrompt to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, updatedSpotterChatPrompt: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&updatedSpotterChatPrompt=false&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set showSpotterRadiance to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showSpotterRadiance: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&showSpotterRadiance=true${defaultParamsPost}#/home`, ); }); }); test('should set showSpotterRadiance to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showSpotterRadiance: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&showSpotterRadiance=false${defaultParamsPost}#/home`, ); }); }); test('should set defaultQueryMode to research in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, defaultQueryMode: SpotterQueryMode.RESEARCH, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&defaultQueryMode=research${defaultParamsPost}#/home`, ); }); }); test('should not set defaultQueryMode in url when unset', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false${defaultParamsPost}#/home`, ); }); }); test('should set hideToolResponseCardBranding to true in url via spotterChatConfig', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterChatConfig: { hideToolResponseCardBranding: true, }, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&hideToolResponseCardBranding=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set spotterDataSources to a list of Model GUIDs in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterDataSources: ['model-guid-123', 'model-guid-456'], } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&spotterDataSources=${encodeURIComponent(JSON.stringify(['model-guid-123', 'model-guid-456']))}${defaultParamsPost}#/home`, ); }); }); test('should not add spotterDataSources to url when not provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expect(getIFrameSrc()).not.toContain('spotterDataSources'); }); }); test('should not add spotterDataSources to url when empty array', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterDataSources: [], } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expect(getIFrameSrc()).not.toContain('spotterDataSources'); }); }); test('should include spotterVizConfig in APP_INIT embedParams when spotterViz is provided', async () => { const spotterViz = { brandName: 'MyBrand', brandHeadline: "Hi, there! I'm", description: 'Ask questions about your data', inputChatPlaceholder: 'Ask a question...', hideStarterPrompts: false, customStarterPrompts: [ { id: '001', displayText: 'Show revenue by region', fullPrompt: 'Show revenue by region', }, { id: '002', displayText: 'Top customers', fullPrompt: 'Top customers by sales' }, ], }; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterViz, } as AppViewConfig); mockMessageChannel(); appEmbed.render(); const mockPort: any = { postMessage: jest.fn() }; await executeAfterWait(() => { postMessageToParent( getIFrameEl().contentWindow, { type: EmbedEvent.APP_INIT, data: {} }, mockPort, ); }); await executeAfterWait(() => { expect(mockPort.postMessage).toHaveBeenCalledWith({ type: EmbedEvent.APP_INIT, data: expect.objectContaining({ embedParams: expect.objectContaining({ spotterVizConfig: spotterViz, }), }), }); }); }); test('should include starterPrompts in APP_INIT embedParams', async () => { const starterPrompts = { enable: true, quick: { label: 'Quick', questions: [ { label: 'Q1', prompt: 'P1' }, { label: 'Q2', prompt: 'P2' }, ], }, research: { label: 'Research' }, }; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterChatConfig: { starterPrompts }, } as AppViewConfig); mockMessageChannel(); appEmbed.render(); const mockPort: any = { postMessage: jest.fn() }; await executeAfterWait(() => { postMessageToParent( getIFrameEl().contentWindow, { type: EmbedEvent.APP_INIT, data: {} }, mockPort, ); }); await executeAfterWait(() => { expect(mockPort.postMessage.mock.calls[0][0].data.embedParams.starterPrompts) .toEqual(starterPrompts); }); }); test('should pass brandHeadline through spotterVizConfig in APP_INIT', async () => { const spotterViz = { brandName: 'MyBrand', brandHeadline: "Hi, there! I'm" }; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterViz, } as AppViewConfig); mockMessageChannel(); appEmbed.render(); const mockPort: any = { postMessage: jest.fn() }; await executeAfterWait(() => { postMessageToParent( getIFrameEl().contentWindow, { type: EmbedEvent.APP_INIT, data: {} }, mockPort, ); }); await executeAfterWait(() => { expect(mockPort.postMessage).toHaveBeenCalledWith({ type: EmbedEvent.APP_INIT, data: expect.objectContaining({ embedParams: expect.objectContaining({ spotterVizConfig: expect.objectContaining({ brandHeadline: "Hi, there! I'm", }), }), }), }); }); }); test('should not include spotterVizConfig in APP_INIT when spotterViz is not provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); mockMessageChannel(); appEmbed.render(); const mockPort: any = { postMessage: jest.fn() }; await executeAfterWait(() => { postMessageToParent( getIFrameEl().contentWindow, { type: EmbedEvent.APP_INIT, data: {} }, mockPort, ); }); await executeAfterWait(() => { const callArgs = mockPort.postMessage.mock.calls[0][0]; expect(callArgs.type).toBe(EmbedEvent.APP_INIT); if (callArgs.data.embedParams) { expect(callArgs.data.embedParams.spotterVizConfig).toBeUndefined(); } }); }); test('should include spotterVizConfig alongside spotterSidebarConfig in APP_INIT', async () => { const spotterViz = { brandName: 'MyBrand' }; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterViz, spotterSidebarConfig: { enablePastConversationsSidebar: true }, } as AppViewConfig); mockMessageChannel(); appEmbed.render(); const mockPort: any = { postMessage: jest.fn() }; await executeAfterWait(() => { postMessageToParent( getIFrameEl().contentWindow, { type: EmbedEvent.APP_INIT, data: {} }, mockPort, ); }); await executeAfterWait(() => { expect(mockPort.postMessage).toHaveBeenCalledWith({ type: EmbedEvent.APP_INIT, data: expect.objectContaining({ embedParams: expect.objectContaining({ spotterVizConfig: spotterViz, spotterSidebarConfig: expect.objectContaining({ enablePastConversationsSidebar: true, }), }), }), }); }); }); test('should forward spotterChatPinConfig nested in spotterSidebarConfig in APP_INIT', async () => { const spotterChatPinConfig = { enabled: true, pinLabel: 'Pin to top', unpinLabel: 'Unpin', }; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterSidebarConfig: { enablePastConversationsSidebar: true, spotterChatPinConfig, }, } as AppViewConfig); mockMessageChannel(); appEmbed.render(); const mockPort: any = { postMessage: jest.fn() }; await executeAfterWait(() => { postMessageToParent( getIFrameEl().contentWindow, { type: EmbedEvent.APP_INIT, data: {} }, mockPort, ); }); await executeAfterWait(() => { expect(mockPort.postMessage).toHaveBeenCalledWith({ type: EmbedEvent.APP_INIT, data: expect.objectContaining({ embedParams: expect.objectContaining({ spotterSidebarConfig: expect.objectContaining({ spotterChatPinConfig, }), }), }), }); }); }); test('should include spotterShareConversationConfig in APP_INIT embedParams when provided', async () => { const spotterShareConversationConfig = { enableShareConversation: true, spotterShareLabel: 'Share', spotterShareModalTitle: 'Share conversation', }; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterShareConversationConfig, } as AppViewConfig); mockMessageChannel(); appEmbed.render(); const mockPort: any = { postMessage: jest.fn() }; await executeAfterWait(() => { postMessageToParent( getIFrameEl().contentWindow, { type: EmbedEvent.APP_INIT, data: {} }, mockPort, ); }); await executeAfterWait(() => { expect(mockPort.postMessage).toHaveBeenCalledWith({ type: EmbedEvent.APP_INIT, data: expect.objectContaining({ embedParams: expect.objectContaining({ spotterShareConversationConfig, }), }), }); }); }); test('should not include spotterShareConversationConfig in APP_INIT when not provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); mockMessageChannel(); appEmbed.render(); const mockPort: any = { postMessage: jest.fn() }; await executeAfterWait(() => { postMessageToParent( getIFrameEl().contentWindow, { type: EmbedEvent.APP_INIT, data: {} }, mockPort, ); }); await executeAfterWait(() => { const callArgs = mockPort.postMessage.mock.calls[0][0]; expect(callArgs.type).toBe(EmbedEvent.APP_INIT); if (callArgs.data.embedParams) { expect(callArgs.data.embedParams.spotterShareConversationConfig).toBeUndefined(); } }); }); test('should set toolResponseCardBrandingLabel in url via spotterChatConfig', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterChatConfig: { toolResponseCardBrandingLabel: 'MyBrand', }, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&toolResponseCardBrandingLabel=MyBrand&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set spotterFileUploadEnabled to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterChatConfig: { spotterFileUploadEnabled: true }, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&spotterFileUploadEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set enableStarterPrompts to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterChatConfig: { enableStarterPrompts: true }, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&enableStarterPrompts=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set openSpotterOnLiveboardByDefault to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterChatConfig: { openSpotterOnLiveboardByDefault: true }, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlToHaveParamsWithValues(getIFrameSrc(), { openSpotterOnLiveboardByDefault: 'true', }); }); }); test('should set openSpotterOnLiveboardByDefault to false when explicitly disabled', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterChatConfig: { openSpotterOnLiveboardByDefault: false }, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlToHaveParamsWithValues(getIFrameSrc(), { openSpotterOnLiveboardByDefault: 'false', }); }); }); test('should not set openSpotterOnLiveboardByDefault when it is not configured', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterChatConfig: { enableStarterPrompts: true }, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expect(getIFrameSrc()).not.toContain('openSpotterOnLiveboardByDefault'); }); }); test('should set spotterFileUploadFileTypes in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, spotterChatConfig: { spotterFileUploadFileTypes: { types: ['image/png', 'application/pdf'] } }, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlToHaveParamsWithValues(getIFrameSrc(), { spotterFileUploadFileTypes: JSON.stringify({ types: ['image/png', 'application/pdf'] }), }); }); }); test('should set isLiveboardXLSXCSVDownloadEnabled to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isLiveboardXLSXCSVDownloadEnabled: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isLiveboardXLSXCSVDownloadEnabled=false&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isGranularXLSXCSVSchedulesEnabled to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isGranularXLSXCSVSchedulesEnabled: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isGranularXLSXCSVSchedulesEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isGranularXLSXCSVSchedulesEnabled to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isGranularXLSXCSVSchedulesEnabled: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isGranularXLSXCSVSchedulesEnabled=false&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isCentralizedLiveboardFilterUXEnabled to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isCentralizedLiveboardFilterUXEnabled: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isCentralizedLiveboardFilterUXEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isCentralizedLiveboardFilterUXEnabled to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isCentralizedLiveboardFilterUXEnabled: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isCentralizedLiveboardFilterUXEnabled=false&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set isScopedLiveboardFilteringEnabled to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isScopedLiveboardFilteringEnabled: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isScopedLiveboardFilteringEnabled=true${defaultParamsPost}#/home`, ); }); }); test('should set isScopedLiveboardFilteringEnabled to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isScopedLiveboardFilteringEnabled: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&isScopedLiveboardFilteringEnabled=false${defaultParamsPost}#/home`, ); }); }); test('should not set isScopedLiveboardFilteringEnabled in url when undefined', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expect(getIFrameSrc()).not.toContain('isScopedLiveboardFilteringEnabled'); }); }); test('Should add the tag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, tag: 'Finance', } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}&tag=Finance${defaultParamsPost}#/home`, ); }); }); test('Should add the hideTagFilterChips true to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, hideTagFilterChips: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}&hideTagFilterChips=true${defaultParamsPost}#/home`, ); }); }); test('Should add the hideTagFilterChips false to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, hideTagFilterChips: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}&hideTagFilterChips=false${defaultParamsPost}#/home`, ); }); }); test('Should not add hideTagFilterChips if it is undefined', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add showMaskedFilterChip true to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, showMaskedFilterChip: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&showMaskedFilterChip=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add showMaskedFilterChip false to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, showMaskedFilterChip: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&showMaskedFilterChip=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add default showMaskedFilterChip false when not specified', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&showMaskedFilterChip=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add isLiveboardMasterpiecesEnabled true to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, isLiveboardMasterpiecesEnabled: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&isLiveboardMasterpiecesEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add isLiveboardMasterpiecesEnabled false to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, isLiveboardMasterpiecesEnabled: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&isLiveboardMasterpiecesEnabled=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add newChartsLibrary true to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, newChartsLibrary: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&muzeChartPhase1EnabledGA=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add newChartsLibrary false to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, newChartsLibrary: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&muzeChartPhase1EnabledGA=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should not add newChartsLibrary to the iframe src when not specified', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showPrimaryNavbar: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add enableSearchAssist flagto the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, enableSearchAssist: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&enableSearchAssist=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add enableDataPanelV2 flag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, dataPanelV2: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&enableDataPanelV2=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add isLiveboardHeaderSticky flag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isLiveboardHeaderSticky: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&isLiveboardHeaderSticky=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add isLiveboardCompactHeaderEnabled flag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isLiveboardCompactHeaderEnabled: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&isLiveboardHeaderV2Enabled=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add showLiveboardReverifyBanner flag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showLiveboardReverifyBanner: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&showLiveboardReverifyBanner=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add isUnifiedSearchExperienceEnabled flag to the iframe src', async () => { // Case 1: explicitly true const appEmbedTrue = new AppEmbed(getRootEl(), { ...defaultViewConfig, isUnifiedSearchExperienceEnabled: true, } as AppViewConfig); appEmbedTrue.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&isUnifiedSearchExperienceEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); appEmbedTrue.destroy(); // Case 2: explicitly false const appEmbedFalse = new AppEmbed(getRootEl(), { ...defaultViewConfig, isUnifiedSearchExperienceEnabled: false, } as AppViewConfig); appEmbedFalse.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&isUnifiedSearchExperienceEnabled=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); appEmbedFalse.destroy(); // Case 3: not provided — defaults to false const appEmbedDefault = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbedDefault.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&isUnifiedSearchExperienceEnabled=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should set newConnectionsExperience to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, newConnectionsExperience: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&newConnectionsExperience=true${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should set newConnectionsExperience to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, newConnectionsExperience: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&newConnectionsExperience=false${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should disable connection new experience by default in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add hideIrrelevantFiltersAtTabLevel flag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, hideIrrelevantChipsInLiveboardTabs: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&hideIrrelevantFiltersAtTabLevel=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add showLiveboardVerifiedBadge flag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, showLiveboardVerifiedBadge: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&showLiveboardVerifiedBadge=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add isLiveboardPermissionV2Enabled flag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, isEnhancedFilterInteractivityEnabled: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&isLiveboardPermissionV2Enabled=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add default values of flags to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&isLiveboardHeaderSticky=true&hideLiveboardHeader=false&showLiveboardDescription=true&showLiveboardTitle=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add modularHomeExperience flag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, modularHomeExperience: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&modularHomeExperience=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add hideHomepageLeftNav flag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, hideHomepageLeftNav: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&hideHomepageLeftNav=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add hideHamburger, hideObjectSearch, hideNotification flags to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, hideHamburger: true, hideObjectSearch: true, hideNotification: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&hideHamburger=true&hideObjectSearch=true&hideNotification=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should not add when hideHamburger, hideObjectSearch, hideNotification values are not true to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, hideHamburger: false, hideObjectSearch: undefined, hideNotification: null, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add enableAskSage flag to the iframe src', async () => { await testUrlParams( { ...defaultViewConfig, enableAskSage: true, } as AppViewConfig, `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&enableAskSage=true&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home` ); }); test('Should add listpageVersion=v3 when listPageVersion is ListWithUXChanges to the iframe src', async () => { await testUrlParams( { ...defaultViewConfig, discoveryExperience: { listPageVersion: ListPage.ListWithUXChanges, }, } as AppViewConfig, `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&listpageVersion=v3&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home` ); }); test('Should send navigationVersion=v3 & homepageVersion=v3 by default when no discoveryExperience is provided', async () => { await testUrlParams( { ...defaultViewConfig, } as AppViewConfig, `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home` ); }); test('Should not send modularHomeExperience param by default when not specified', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { const url = new URL(getIFrameSrc()); expect(url.searchParams.has('modularHomeExperience')).toBe(false); }); }); test('Should add listpageVersion=v3 combined with other discoveryExperience options to the iframe src', async () => { await testUrlParams( { ...defaultViewConfig, discoveryExperience: { listPageVersion: ListPage.ListWithUXChanges, }, } as AppViewConfig, `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3&listpageVersion=v3${defaultParams}${defaultParamsPost}#/home` ); }); test('Should add enablePendoHelp flag to the iframe src conditional on navbar', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?enablePendoHelp=true&embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); const noNavEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, enablePendoHelp: false, } as AppViewConfig); noNavEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?enablePendoHelp=false&embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should set enableLiveboardDataCache to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, enableLiveboardDataCache: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&enableLiveboardDataCache=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set enableLiveboardDataCache to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, enableLiveboardDataCache: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&enableLiveboardDataCache=false&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should not set enableLiveboardDataCache in url when not provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expect(getIFrameSrc()).not.toContain('enableLiveboardDataCache'); }); }); test('Should add HomePageSearchBarMode flag with object search to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, homePageSearchBarMode: HomePageSearchBarMode.OBJECT_SEARCH, } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&homePageSearchBarMode=objectSearch&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add HomePageSearchBarMode flag with ai answer to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, homePageSearchBarMode: HomePageSearchBarMode.AI_ANSWER, } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&homePageSearchBarMode=aiAnswer&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add HomePageSearchBarMode flag with none to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, homePageSearchBarMode: HomePageSearchBarMode.NONE, } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&homePageSearchBarMode=none&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('Should add dataPanelCustomGroupsAccordionInitialState flag to the iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, dataPanelCustomGroupsAccordionInitialState: DataPanelCustomColumnGroupsAccordionState.EXPAND_FIRST, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&dataPanelCustomGroupsAccordionInitialState=EXPAND_FIRST&navigationVersion=v3&homepageVersion=v3${defaultParams}${defaultParamsPost}#/home`, ); }); }); test('should register event handlers to adjust iframe height', async () => { let embedHeightCallback: any = () => { }; let embedIframeCenterCallback: any = () => { }; const onSpy = jest.spyOn(AppEmbed.prototype, 'on').mockImplementation((event, callback) => { if (event === EmbedEvent.RouteChange) { callback({ type: EmbedEvent.RouteChange, data: { currentPath: '/answers' } } as any, jest.fn()); } if (event === EmbedEvent.EmbedHeight) { embedHeightCallback = callback; } if (event === EmbedEvent.EmbedIframeCenter) { embedIframeCenterCallback = callback; } return null; }); jest.spyOn(TsEmbed.prototype as any, 'setIFrameHeight').mockReturnValue({}); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, lazyLoadingMargin: '10px', } as AppViewConfig); // Set the iframe before render (appEmbed as any).iFrame = document.createElement('iframe'); // Wait for render to complete await appEmbed.render(); embedHeightCallback({ data: '100%' }); // The app only asks for the iframe center once the iframe exists. const centerResponder = jest.fn(); embedIframeCenterCallback( { type: EmbedEvent.EmbedIframeCenter, data: {} } as any, centerResponder, ); // Verify event handlers were registered await executeAfterWait(() => { expect(onSpy).toHaveBeenCalledWith(EmbedEvent.EmbedHeight, expect.anything()); expect(onSpy).toHaveBeenCalledWith(EmbedEvent.RouteChange, expect.anything()); expect(onSpy).toHaveBeenCalledWith(EmbedEvent.EmbedIframeCenter, expect.anything()); expect(onSpy).toHaveBeenCalledWith(EmbedEvent.RequestVisibleEmbedCoordinates, expect.anything()); expect(centerResponder).toHaveBeenCalledWith( expect.objectContaining({ type: EmbedEvent.EmbedIframeCenter }), ); }, 100); // This test replaces AppEmbed.prototype.on; restore it so the mock does // not leak into the tests that follow. jest.restoreAllMocks(); }); describe('Navigate to Page API', () => { const path = 'pinboard/e0836cad-4fdf-42d4-bd97-567a6b2a6058'; beforeEach(() => { jest.spyOn(config, 'getThoughtSpotHost').mockImplementation(() => 'http://tshost'); }); test('when app is AppEmbed after navigateToPage function call, new path should be set to iframe', async () => { const appEmbed = new AppEmbed(getRootEl(), { frameParams: { width: '100%', height: '100%', }, }); await appEmbed.render(); appEmbed.navigateToPage(path); expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&navigationVersion=v3&homepageVersion=v3&${defaultParamsForPinboardEmbed}${defaultParamsPost}#/${path}`, ); }); test('navigateToPage with noReload should trigger the appropriate event', async () => { mockMessageChannel(); const appEmbed = new AppEmbed(getRootEl(), { frameParams: { width: '100%', height: '100%', }, }); await appEmbed.render(); const iframe = getIFrameEl(); iframe.contentWindow.postMessage = jest.fn(); appEmbed.navigateToPage(path, true); expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith( expect.objectContaining({ type: HostEvent.Navigate, data: path, }), `http://${thoughtSpotHost}`, expect.anything(), ); appEmbed.navigateToPage(-1, true); expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith( expect.objectContaining({ type: HostEvent.Navigate, data: -1, }), `http://${thoughtSpotHost}`, expect.anything(), ); }); test('Do not allow number path without noReload in navigateToPage', async () => { const appEmbed = new AppEmbed(getRootEl(), { frameParams: { width: '100%', height: '100%', }, }); await appEmbed.render(); jest.spyOn(logger, 'warn').mockImplementation(() => {}); appEmbed.navigateToPage(-1); expect(logger.warn).toHaveBeenCalledWith( 'Path can only by a string when triggered without noReload', ); }); test('navigateToPage function use before render', async () => { jest.spyOn(logger, 'log').mockImplementation(() => {}); const appEmbed = new AppEmbed(getRootEl(), { frameParams: { width: '100%', height: '100%', }, }); appEmbed.navigateToPage(path); await appEmbed.render(); expect(logger.log).toHaveBeenCalledWith( 'Please call render before invoking this method', ); }); describe('overrideHistoryState flag', () => { test('navigateToPage with overrideHistoryState=true should trigger HostEvent.Navigate', async () => { mockMessageChannel(); const appEmbed = new AppEmbed(getRootEl(), { frameParams: { width: '100%', height: '100%', }, overrideHistoryState: true, }); await appEmbed.render(); const iframe = getIFrameEl(); iframe.contentWindow.postMessage = jest.fn(); appEmbed.navigateToPage(path, false); expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith( expect.objectContaining({ type: HostEvent.Navigate, data: path, }), `http://${thoughtSpotHost}`, expect.anything(), ); }); test('navigateToPage with overrideHistoryState=true and path as number should trigger HostEvent.Navigate', async () => { mockMessageChannel(); const appEmbed = new AppEmbed(getRootEl(), { frameParams: { width: '100%', height: '100%', }, overrideHistoryState: true, }); await appEmbed.render(); const iframe = getIFrameEl(); iframe.contentWindow.postMessage = jest.fn(); appEmbed.navigateToPage(-1, false); expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith( expect.objectContaining({ type: HostEvent.Navigate, data: -1, }), `http://${thoughtSpotHost}`, expect.anything(), ); }); test('navigateToPage with overrideHistoryState=false and noReload=false should update iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { frameParams: { width: '100%', height: '100%', }, overrideHistoryState: false, }); await appEmbed.render(); appEmbed.navigateToPage(path, false); expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}${defaultParamsPost}#/${path}`, ); }); test('navigateToPage with overrideHistoryState=undefined and noReload=false should update iframe src', async () => { const appEmbed = new AppEmbed(getRootEl(), { frameParams: { width: '100%', height: '100%', }, }); await appEmbed.render(); appEmbed.navigateToPage(path, false); expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}${defaultParamsPost}#/${path}`, ); }); test('navigateToPage respects noReload priority over overrideHistoryState', async () => { mockMessageChannel(); const appEmbed = new AppEmbed(getRootEl(), { frameParams: { width: '100%', height: '100%', }, overrideHistoryState: false, }); await appEmbed.render(); const iframe = getIFrameEl(); iframe.contentWindow.postMessage = jest.fn(); appEmbed.navigateToPage(path, true); expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith( expect.objectContaining({ type: HostEvent.Navigate, data: path, }), `http://${thoughtSpotHost}`, expect.anything(), ); }); }); }); describe('LazyLoadingForFullHeight functionality', () => { let mockIFrame: HTMLIFrameElement; beforeEach(() => { mockIFrame = createMockIFrame(); setupIFrameCreationSpy(mockIFrame); }); afterEach(() => { jest.restoreAllMocks(); }); test('should set lazyLoadingMargin parameter when provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, lazyLoadingMargin: '100px 0px', } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { const iframeSrc = getIFrameSrc(); expect(iframeSrc).toContain('isLazyLoadingForEmbedEnabled=true'); expect(iframeSrc).toContain('isFullHeightPinboard=true'); expect(iframeSrc).toContain('rootMarginForLazyLoad=100px%200px'); }, 100); }); test('should set isLazyLoadingForEmbedEnabled=true when both fullHeight and lazyLoadingForFullHeight are enabled', async () => { // Mock the iframe element first mockIFrame.getBoundingClientRect = jest.fn().mockReturnValue({ top: 100, left: 150, bottom: 600, right: 800, width: 650, height: 500, }); Object.defineProperty(mockIFrame, 'scrollHeight', { value: 500 }); // Mock the event handlers const onSpy = jest.spyOn(AppEmbed.prototype, 'on').mockImplementation((event, callback) => { return null; }); jest.spyOn(TsEmbed.prototype as any, 'setIFrameHeight').mockReturnValue({}); // Create the AppEmbed instance const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, lazyLoadingMargin: '10px', } as AppViewConfig); // Set the iframe before render (appEmbed as any).iFrame = mockIFrame; // Add the iframe to the DOM const rootEl = getRootEl(); rootEl.appendChild(mockIFrame); // Wait for render to complete await appEmbed.render(); // Wait for iframe initialization and URL parameters to be set await executeAfterWait(() => { const iframeSrc = appEmbed.getIFrameSrc(); expect(iframeSrc).toContain('isLazyLoadingForEmbedEnabled=true'); expect(iframeSrc).toContain('isFullHeightPinboard=true'); }, 100); }); test('should default lazy loading flags to true when fullHeight is enabled', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { const iframeSrc = getIFrameSrc(); expect(iframeSrc).toContain('isLazyLoadingForEmbedEnabled=true'); expect(iframeSrc).toContain('isFullHeightPinboard=true'); expect(iframeSrc).toContain('rootMarginForLazyLoad=500px%200px'); }, 100); }); test('should not default lazy loading flags when fullHeight is not enabled', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { const iframeSrc = getIFrameSrc(); expect(iframeSrc).not.toContain('isFullHeightPinboard'); expect(iframeSrc).not.toContain('isLazyLoadingForEmbedEnabled'); expect(iframeSrc).not.toContain('rootMarginForLazyLoad'); }, 100); }); test('should not write the defaults back onto the caller view config', async () => { const callerViewConfig = { ...defaultViewConfig, fullHeight: true, } as AppViewConfig; new AppEmbed(getRootEl(), callerViewConfig); expect(callerViewConfig.lazyLoadingForFullHeight).toBeUndefined(); expect(callerViewConfig.enableScrollableContainerLazyLoading).toBeUndefined(); expect(callerViewConfig.lazyLoadingMargin).toBeUndefined(); }); test('should not default lazy loading flags when fullHeight is explicitly false', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: false, } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { const iframeSrc = getIFrameSrc(); expect(iframeSrc).not.toContain('isFullHeightPinboard'); expect(iframeSrc).not.toContain('isLazyLoadingForEmbedEnabled'); expect(iframeSrc).not.toContain('rootMarginForLazyLoad'); }, 100); }); test('should default lazyLoadingMargin when lazyLoadingForFullHeight is set explicitly', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { expect(getIFrameSrc()).toContain( `rootMarginForLazyLoad=${encodeURIComponent( config.DEFAULT_LAZY_LOADING_MARGIN, )}`, ); }, 100); }); test('should let an explicit lazyLoadingMargin win over the default', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingMargin: '250px', } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { const iframeSrc = getIFrameSrc(); expect(iframeSrc).toContain('rootMarginForLazyLoad=250px'); expect(iframeSrc).not.toContain('rootMarginForLazyLoad=500px%200px'); }, 100); }); test('should drop an invalid lazyLoadingMargin and log an error', async () => { const loggerErrorSpy = jest.spyOn(logger, 'error').mockImplementation(() => undefined); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingMargin: 'not-a-margin', } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { const iframeSrc = getIFrameSrc(); expect(iframeSrc).toContain('isLazyLoadingForEmbedEnabled=true'); expect(iframeSrc).not.toContain('rootMarginForLazyLoad'); expect(loggerErrorSpy).toHaveBeenCalledWith( 'Please provide a valid lazyLoadingMargin value (e.g., "10px")', ); }, 100); }); test('should track scrollable ancestors by default when only fullHeight is set', async () => { const scrollContainer = getRootEl(); scrollContainer.style.overflow = 'auto'; const scrollContainerSpy = jest.spyOn(scrollContainer, 'addEventListener'); const resizeObserveSpy = jest.fn(); const resizeDisconnectSpy = jest.fn(); (window as any).ResizeObserver = jest.fn().mockImplementation(() => ({ observe: resizeObserveSpy, disconnect: resizeDisconnectSpy, })); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { expect(scrollContainerSpy).toHaveBeenCalledWith('scroll', expect.any(Function)); expect(resizeObserveSpy).toHaveBeenCalledWith(scrollContainer); }, 100); appEmbed.destroy(); expect(resizeDisconnectSpy).toHaveBeenCalled(); scrollContainer.style.overflow = ''; (window as any).ResizeObserver = originalResizeObserver; }); test('should skip ancestor tracking when enableScrollableContainerLazyLoading is false', async () => { const scrollContainer = getRootEl(); scrollContainer.style.overflow = 'auto'; const scrollContainerSpy = jest.spyOn(scrollContainer, 'addEventListener'); const windowSpy = jest.spyOn(window, 'addEventListener'); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, enableScrollableContainerLazyLoading: false, } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { expect(windowSpy).toHaveBeenCalledWith('scroll', expect.anything(), true); expect(scrollContainerSpy).not.toHaveBeenCalledWith('scroll', expect.any(Function)); }, 100); appEmbed.destroy(); scrollContainer.style.overflow = ''; }); test('should wire the window scroll listener to the coordinates sender by default', async () => { const addEventListenerSpy = jest.spyOn(window, 'addEventListener'); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig); const mockTrigger = jest .spyOn(appEmbed, 'trigger') .mockImplementation(() => Promise.resolve(undefined as any)); await appEmbed.render(); await executeAfterWait(() => { const scrollCall = addEventListenerSpy.mock.calls.find( ([eventName, , capture]) => eventName === 'scroll' && capture === true, ); expect(scrollCall).toBeDefined(); // Call the handler the way a real scroll event would. (scrollCall as any)[1](); expect(mockTrigger).toHaveBeenCalledWith( HostEvent.VisibleEmbedCoordinates, expect.objectContaining({ top: expect.any(Number) }), ); }, 100); appEmbed.destroy(); addEventListenerSpy.mockRestore(); }); test('should remove listeners on destroy when the flags come from defaults', async () => { const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener'); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig); await appEmbed.render(); appEmbed.destroy(); expect(removeEventListenerSpy).toHaveBeenCalledWith('resize', expect.anything()); expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.anything(), true); removeEventListenerSpy.mockRestore(); }); test('should keep an explicit false for the lazy loading flags', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: false, enableScrollableContainerLazyLoading: false, lazyLoadingMargin: '0px', } as AppViewConfig); expect((appEmbed as any).viewConfig.lazyLoadingForFullHeight).toBe(false); expect((appEmbed as any).viewConfig.enableScrollableContainerLazyLoading).toBe(false); expect((appEmbed as any).viewConfig.lazyLoadingMargin).toBe('0px'); }); test('should not set lazyLoadingForEmbed when lazyLoadingForFullHeight is enabled but fullHeight is false', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: false, lazyLoadingForFullHeight: true, } as AppViewConfig); // Wait for render to complete await appEmbed.render(); // Wait for iframe initialization and URL parameters to be set await executeAfterWait(() => { const iframeSrc = getIFrameSrc(); expect(iframeSrc).not.toContain('isLazyLoadingForEmbedEnabled=true'); expect(iframeSrc).not.toContain('isFullHeightPinboard=true'); }, 100); // 100ms wait time to ensure iframe src is set }); test('should not set isLazyLoadingForEmbedEnabled when fullHeight is true but lazyLoadingForFullHeight is false', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: false, } as AppViewConfig); // Wait for render to complete await appEmbed.render(); // Wait for iframe initialization and URL parameters to be set await executeAfterWait(() => { const iframeSrc = getIFrameSrc(); expect(iframeSrc).not.toContain('isLazyLoadingForEmbedEnabled=true'); expect(iframeSrc).toContain('isFullHeightPinboard=true'); }, 100); // 100ms wait time to ensure iframe src is set }); test('should register RequestFullHeightLazyLoadData event handler when fullHeight is enabled', async () => { const onSpy = jest.spyOn(AppEmbed.prototype, 'on'); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig); await appEmbed.render(); expect(onSpy).toHaveBeenCalledWith(EmbedEvent.RequestVisibleEmbedCoordinates, expect.any(Function)); onSpy.mockRestore(); }); test('should send correct visible data when RequestFullHeightLazyLoadData is triggered', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, lazyLoadingMargin: '10px', } as AppViewConfig); const mockTrigger = jest.spyOn(appEmbed, 'trigger'); await appEmbed.render(); // Trigger the lazy load data calculation (appEmbed as any).fullHeightController.sendVisibleCoordinates(); expect(mockTrigger).toHaveBeenCalledWith(HostEvent.VisibleEmbedCoordinates, { top: 0, height: 500, left: 0, width: 650, }); }); test('should not send correct visible data when RequestFullHeightLazyLoadData is triggered if lazyLoadingForFullHeight is false', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: false, lazyLoadingMargin: '10px', } as AppViewConfig); const mockTrigger = jest.spyOn(appEmbed, 'trigger'); await appEmbed.render(); // Trigger the lazy load data calculation (appEmbed as any).fullHeightController.sendVisibleCoordinates(); expect(mockTrigger).not.toHaveBeenCalledWith(HostEvent.VisibleEmbedCoordinates, { top: 0, height: 500, left: 0, width: 650, }); }); test('should calculate correct visible data for partially visible full height element', async () => { // Mock iframe partially clipped from top and left mockIFrame.getBoundingClientRect = jest.fn().mockReturnValue({ top: -50, left: -30, bottom: 700, right: 1024, width: 1054, height: 750, }); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, lazyLoadingMargin: '10px', } as AppViewConfig); const mockTrigger = jest.spyOn(appEmbed, 'trigger'); await appEmbed.render(); // Trigger the lazy load data calculation (appEmbed as any).fullHeightController.sendVisibleCoordinates(); expect(mockTrigger).toHaveBeenCalledWith(HostEvent.VisibleEmbedCoordinates, { top: 50, // 50px clipped from top height: 700, // visible height (from 0 to 700) left: 30, // 30px clipped from left width: 1024, // visible width (from 0 to 1024) }); }); test('should add window event listeners for resize and scroll when fullHeight and lazyLoadingForFullHeight are enabled', async () => { const addEventListenerSpy = jest.spyOn(window, 'addEventListener'); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, lazyLoadingMargin: '10px', } as AppViewConfig); await appEmbed.render(); // Wait for the post-render events to be registered await executeAfterWait(() => { expect(addEventListenerSpy).toHaveBeenCalledWith('resize', expect.any(Function)); expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function), true); }, 100); addEventListenerSpy.mockRestore(); }); test('should listen to scroll and resize changes from scrollable iframe ancestors', async () => { const scrollContainer = getRootEl(); scrollContainer.style.overflow = 'auto'; const scrollContainerAddEventListenerSpy = jest.spyOn(scrollContainer, 'addEventListener'); const resizeObserveSpy = jest.fn(); const resizeDisconnectSpy = jest.fn(); (window as any).ResizeObserver = jest.fn().mockImplementation(() => ({ observe: resizeObserveSpy, disconnect: resizeDisconnectSpy, })); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, enableScrollableContainerLazyLoading: true, } as AppViewConfig); await appEmbed.render(); await executeAfterWait(() => { expect(scrollContainerAddEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function)); expect(resizeObserveSpy).toHaveBeenCalledWith(scrollContainer); }, 100); appEmbed.destroy(); expect(resizeDisconnectSpy).toHaveBeenCalled(); }); test('should remove window event listeners on destroy when fullHeight and lazyLoadingForFullHeight are enabled', async () => { const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener'); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, lazyLoadingMargin: '10px', } as AppViewConfig); await appEmbed.render(); appEmbed.destroy(); expect(removeEventListenerSpy).toHaveBeenCalledWith('resize', expect.any(Function)); expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function), true); removeEventListenerSpy.mockRestore(); }); test('should handle RequestVisibleEmbedCoordinates event and respond with correct data', async () => { // Mock the iframe element mockIFrame.getBoundingClientRect = jest.fn().mockReturnValue({ top: 100, left: 150, bottom: 600, right: 800, width: 650, height: 500, }); Object.defineProperty(mockIFrame, 'scrollHeight', { value: 500 }); const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, lazyLoadingMargin: '10px', } as AppViewConfig); // Set the iframe before render (appEmbed as any).iFrame = mockIFrame; await appEmbed.render(); // Create a mock responder function const mockResponder = jest.fn(); // Trigger the handler directly (appEmbed as any).fullHeightController.handleRequestVisibleCoordinates({}, mockResponder); // Verify the responder was called with the correct data expect(mockResponder).toHaveBeenCalledWith({ type: EmbedEvent.RequestVisibleEmbedCoordinates, data: { top: 0, height: 500, left: 0, width: 650, }, }); }); }); describe('IFrame height management', () => { let mockIFrame: HTMLIFrameElement; beforeEach(() => { mockIFrame = document.createElement('iframe'); mockIFrame.getBoundingClientRect = jest.fn().mockReturnValue({ top: 100, left: 150, bottom: 600, right: 800, width: 650, height: 500, }); Object.defineProperty(mockIFrame, 'scrollHeight', { value: 500 }); }); test('should not call setIFrameHeight if currentPath starts with "/embed/viz/"', () => { testSetIframeHeightBehavior('/embed/viz/', false); }); test('should not call setIFrameHeight if currentPath starts with "/embed/insights/viz/"', () => { testSetIframeHeightBehavior('/embed/insights/viz/', false); }); test('should call setIFrameHeight if currentPath starts with "/some/other/path/"', () => { testSetIframeHeightBehavior('/some/other/path/', true); }); test('should update iframe height correctly', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig) as any; // Set up the mock iframe appEmbed.iFrame = mockIFrame; document.body.appendChild(mockIFrame); await appEmbed.render(); const mockEvent = { data: 600, type: EmbedEvent.EmbedHeight, }; appEmbed.fullHeightController.handleEmbedHeight(mockEvent); // Check if the iframe style was updated expect(mockIFrame.style.height).toBe('600px'); }); test('should handle updateIFrameHeight with default height', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig) as any; // Set up the mock iframe appEmbed.iFrame = mockIFrame; document.body.appendChild(mockIFrame); await appEmbed.render(); const mockEvent = { data: 0, // This will make it use the default height type: EmbedEvent.EmbedHeight, }; appEmbed.fullHeightController.handleEmbedHeight(mockEvent); // Should use the default height expect(mockIFrame.style.height).toBe('500px'); }); }); }); describe('App Embed Default Height and Minimum Height Handling', () => { test('should set default height to 500 when neither default height nor minimum height is provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig); await appEmbed.render(); expect(appEmbed['fullHeightController'].minimumHeight).toBe(500); }); test('should set default height to 700 when default height is provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, minimumHeight: 700, } as AppViewConfig); await appEmbed.render(); expect(appEmbed['fullHeightController'].minimumHeight).toBe(700); }); }); describe('AppEmbed uncovered branch tests', () => { beforeEach(() => { document.body.innerHTML = getDocumentBody(); }); test('should set enableStopAnswerGenerationEmbed param when provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, enableStopAnswerGenerationEmbed: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlToHaveParamsWithValues(getIFrameSrc(), { enableStopAnswerGenerationEmbed: 'true', }); }); }); test('should set enableStopAnswerGenerationEmbed=false when set to false', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, enableStopAnswerGenerationEmbed: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlToHaveParamsWithValues(getIFrameSrc(), { enableStopAnswerGenerationEmbed: 'false', }); }); }); test('should set showAnswerEditPanel=false when hideAnswerEditPanel is true', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, hideAnswerEditPanel: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlToHaveParamsWithValues(getIFrameSrc(), { showAnswerEditPanel: 'false', }); }); }); test('should set showAnswerEditPanel=true when hideAnswerEditPanel is false', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, hideAnswerEditPanel: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlToHaveParamsWithValues(getIFrameSrc(), { showAnswerEditPanel: 'true', }); }); }); test('should not set showAnswerEditPanel param when hideAnswerEditPanel is not provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { const url = new URL(getIFrameSrc()); expect(url.searchParams.has('showAnswerEditPanel')).toBe(false); }); }); test('should set hideObjects param when non-empty array is provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, hideObjects: ['guid-123', 'guid-456'], } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlToHaveParamsWithValues(getIFrameSrc(), { hideObjects: JSON.stringify(['guid-123', 'guid-456']), }); }); }); test('should not set hideObjects param when array is empty', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, hideObjects: [], } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { const src = getIFrameSrc(); expect(src).not.toContain('hideObjects'); }); }); test('should set liveboardV2 param when provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, liveboardV2: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlToHaveParamsWithValues(getIFrameSrc(), { isPinboardV2Enabled: 'true', }); }); }); test('should strip leading slash from path in getIFrameSrc', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, path: '/pinboards', } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { const src = getIFrameSrc(); expect(src).toContain('#/pinboards'); expect(src).not.toContain('#//pinboards'); }); }); test('lazy load registration should return early when iFrame is not set', () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, lazyLoadingForFullHeight: true, } as AppViewConfig); // iFrame is not set (render not called), should not throw expect(() => { (appEmbed as any).fullHeightController.onRender(); }).not.toThrow(); }); }); describe('AppEmbed visualOverrides tests', () => { test('should include visualOverridesParams in APP_INIT when visualOverrides config is provided', async () => { const visualOverrides = { chart: { legend: { show: true, position: 'bottom' as const, }, }, }; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, visualOverrides, } as AppViewConfig); mockMessageChannel(); await testVisualOverridesInEmbed(appEmbed, visualOverrides); }); test('should not include visualOverridesParams when visualOverrides is not provided', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, } as AppViewConfig); const mockEmbedEventPayload = { type: EmbedEvent.APP_INIT, data: {}, }; mockMessageChannel(); appEmbed.render(); const mockPort: any = { postMessage: jest.fn(), }; await executeAfterWait(() => { const iframe = getIFrameEl(); postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort); }); await executeAfterWait(() => { const callArgs = mockPort.postMessage.mock.calls[0][0]; expect(callArgs.type).toBe(EmbedEvent.APP_INIT); if (callArgs.data.embedParams) { expect(callArgs.data.embedParams.visualOverridesParams).toBeUndefined(); } }); }); test('should pass visualOverrides with table config in AppEmbed', async () => { const visualOverrides = { table: { display: { tableTheme: 'ZEBRA', tableContentDensity: 'COMPACT', }, }, }; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, visualOverrides, } as AppViewConfig); mockMessageChannel(); await testVisualOverridesInEmbed(appEmbed, visualOverrides); }); test('should pass visualOverrides with both chart and table configs', async () => { const visualOverrides = { chart: { legend: { show: true, position: 'right' as const, }, }, table: { display: { tableTheme: 'STRIPED', }, }, }; const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, visualOverrides, } as AppViewConfig); mockMessageChannel(); await testVisualOverridesInEmbed(appEmbed, visualOverrides); }); }); describe('AppEmbed getEmbedParamsObject', () => { beforeEach(() => { cleanUp(); jest.spyOn(TsEmbed.prototype as any, 'getAppInitData').mockResolvedValue({}); }); afterEach(() => { jest.restoreAllMocks(); }); test('getEmbedParams should use getEmbedParamsObject internally', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig); const getEmbedParamsObjectSpy = jest.spyOn(appEmbed as any, 'getEmbedParamsObject'); await appEmbed.render(); expect(getEmbedParamsObjectSpy).toHaveBeenCalled(); }); test('getEmbedParamsObject should contain isFullHeightPinboard when fullHeight is true', () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig); const params = (appEmbed as any).getEmbedParamsObject(); expect(params.isFullHeightPinboard).toBe(true); expect(params.fullHeight).toBeUndefined(); }); test('getEmbedParamsObject should not contain isFullHeightPinboard when fullHeight is false', () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: false, } as AppViewConfig); const params = (appEmbed as any).getEmbedParamsObject(); expect(params.isFullHeightPinboard).toBeUndefined(); }); test('getUpdateEmbedParamsObject should contain isFullHeightPinboard when fullHeight is true', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: true, } as AppViewConfig); const params = await (appEmbed as any).getUpdateEmbedParamsObject(); expect(params.isFullHeightPinboard).toBe(true); }); test('getUpdateEmbedParamsObject should not contain isFullHeightPinboard when fullHeight is false', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, fullHeight: false, } as AppViewConfig); const params = await (appEmbed as any).getUpdateEmbedParamsObject(); expect(params.isFullHeightPinboard).toBeUndefined(); }); }); describe('AppEmbed updatedSpotterExperience tests', () => { beforeEach(() => { cleanUp(); }); test('should set updatedSpotterExperience to true in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, updatedSpotterExperience: true, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&updatedSpotterExperience=true&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); test('should set updatedSpotterExperience to false in url', async () => { const appEmbed = new AppEmbed(getRootEl(), { ...defaultViewConfig, updatedSpotterExperience: false, } as AppViewConfig); appEmbed.render(); await executeAfterWait(() => { expectUrlMatchesWithParams( getIFrameSrc(), `http://${thoughtSpotHost}/?embedApp=true&profileAndHelpInNavBarHidden=false&updatedSpotterExperience=false&navigationVersion=v3&homepageVersion=v3${defaultParamsPost}#/home`, ); }); }); });