@thoughtspot/visual-embed-sdk 1.49.2 → 1.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/package.json +4 -10
- package/cjs/src/authToken.spec.js +5 -0
- package/cjs/src/authToken.spec.js.map +1 -1
- package/cjs/src/embed/app.d.ts +7 -1
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +6 -0
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +11 -2
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/conversation.d.ts +12 -0
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/conversation.spec.js +30 -0
- package/cjs/src/embed/conversation.spec.js.map +1 -1
- package/cjs/src/embed/embedConfig.spec.d.ts +2 -0
- package/cjs/src/embed/embedConfig.spec.d.ts.map +1 -0
- package/cjs/src/embed/embedConfig.spec.js +74 -0
- package/cjs/src/embed/embedConfig.spec.js.map +1 -0
- package/cjs/src/embed/host-events.spec.d.ts +2 -0
- package/cjs/src/embed/host-events.spec.d.ts.map +1 -0
- package/cjs/src/embed/host-events.spec.js +1219 -0
- package/cjs/src/embed/host-events.spec.js.map +1 -0
- package/cjs/src/embed/liveboard.d.ts +6 -0
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/spotter-viz-utils.d.ts +29 -0
- package/cjs/src/embed/spotter-viz-utils.d.ts.map +1 -1
- package/cjs/src/embed/spotter-viz-utils.js.map +1 -1
- package/cjs/src/index.d.ts +2 -2
- package/cjs/src/index.d.ts.map +1 -1
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/mixpanel-service.spec.js +14 -0
- package/cjs/src/mixpanel-service.spec.js.map +1 -1
- package/cjs/src/types.d.ts +93 -6
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +88 -3
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/custom-actions.spec.js +26 -0
- package/cjs/src/utils/custom-actions.spec.js.map +1 -1
- package/cjs/src/utils/graphql/answerService/answerService.spec.js +15 -0
- package/cjs/src/utils/graphql/answerService/answerService.spec.js.map +1 -1
- package/cjs/src/utils/processData.spec.js +30 -0
- package/cjs/src/utils/processData.spec.js.map +1 -1
- package/cjs/src/utils/reporting.spec.js +21 -0
- package/cjs/src/utils/reporting.spec.js.map +1 -1
- package/cjs/src/utils/sessionInfoService.spec.d.ts +2 -0
- package/cjs/src/utils/sessionInfoService.spec.d.ts.map +1 -0
- package/cjs/src/utils/sessionInfoService.spec.js +130 -0
- package/cjs/src/utils/sessionInfoService.spec.js.map +1 -0
- package/cjs/src/utils.js +2 -3
- package/cjs/src/utils.js.map +1 -1
- package/cjs/src/utils.spec.js +158 -0
- package/cjs/src/utils.spec.js.map +1 -1
- package/dist/{index-CS7yJ2Jj.js → index-B5xoN6Cp.js} +303 -187
- package/dist/src/embed/app.d.ts +7 -1
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/conversation.d.ts +12 -0
- package/dist/src/embed/conversation.d.ts.map +1 -1
- package/dist/src/embed/embedConfig.spec.d.ts +2 -0
- package/dist/src/embed/embedConfig.spec.d.ts.map +1 -0
- package/dist/src/embed/host-events.spec.d.ts +2 -0
- package/dist/src/embed/host-events.spec.d.ts.map +1 -0
- package/dist/src/embed/liveboard.d.ts +6 -0
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/spotter-viz-utils.d.ts +29 -0
- package/dist/src/embed/spotter-viz-utils.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +93 -6
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/sessionInfoService.spec.d.ts +2 -0
- package/dist/src/utils/sessionInfoService.spec.d.ts.map +1 -0
- package/dist/tsembed-react.es.js +126 -18
- package/dist/tsembed-react.js +427 -203
- package/dist/tsembed.es.js +126 -18
- package/dist/tsembed.js +427 -203
- package/dist/visual-embed-sdk-react-full.d.ts +147 -7
- package/dist/visual-embed-sdk-react.d.ts +147 -7
- package/dist/visual-embed-sdk.d.ts +147 -7
- package/lib/package.json +4 -10
- package/lib/src/authToken.spec.js +5 -0
- package/lib/src/authToken.spec.js.map +1 -1
- package/lib/src/embed/app.d.ts +7 -1
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +6 -0
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +11 -2
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/conversation.d.ts +12 -0
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/conversation.spec.js +30 -0
- package/lib/src/embed/conversation.spec.js.map +1 -1
- package/lib/src/embed/embedConfig.spec.d.ts +2 -0
- package/lib/src/embed/embedConfig.spec.d.ts.map +1 -0
- package/lib/src/embed/embedConfig.spec.js +72 -0
- package/lib/src/embed/embedConfig.spec.js.map +1 -0
- package/lib/src/embed/host-events.spec.d.ts +2 -0
- package/lib/src/embed/host-events.spec.d.ts.map +1 -0
- package/lib/src/embed/host-events.spec.js +1216 -0
- package/lib/src/embed/host-events.spec.js.map +1 -0
- package/lib/src/embed/liveboard.d.ts +6 -0
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/spotter-viz-utils.d.ts +29 -0
- package/lib/src/embed/spotter-viz-utils.d.ts.map +1 -1
- package/lib/src/embed/spotter-viz-utils.js.map +1 -1
- package/lib/src/index.d.ts +2 -2
- package/lib/src/index.d.ts.map +1 -1
- package/lib/src/index.js.map +1 -1
- package/lib/src/mixpanel-service.spec.js +14 -0
- package/lib/src/mixpanel-service.spec.js.map +1 -1
- package/lib/src/types.d.ts +93 -6
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +88 -3
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/custom-actions.spec.js +26 -0
- package/lib/src/utils/custom-actions.spec.js.map +1 -1
- package/lib/src/utils/graphql/answerService/answerService.spec.js +15 -0
- package/lib/src/utils/graphql/answerService/answerService.spec.js.map +1 -1
- package/lib/src/utils/processData.spec.js +30 -0
- package/lib/src/utils/processData.spec.js.map +1 -1
- package/lib/src/utils/reporting.spec.js +21 -0
- package/lib/src/utils/reporting.spec.js.map +1 -1
- package/lib/src/utils/sessionInfoService.spec.d.ts +2 -0
- package/lib/src/utils/sessionInfoService.spec.d.ts.map +1 -0
- package/lib/src/utils/sessionInfoService.spec.js +127 -0
- package/lib/src/utils/sessionInfoService.spec.js.map +1 -0
- package/lib/src/utils.js +1 -1
- package/lib/src/utils.js.map +1 -1
- package/lib/src/utils.spec.js +159 -1
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +147 -7
- package/package.json +4 -10
- package/src/authToken.spec.ts +6 -0
- package/src/embed/app.spec.ts +15 -2
- package/src/embed/app.ts +13 -1
- package/src/embed/conversation.spec.ts +32 -0
- package/src/embed/conversation.ts +12 -0
- package/src/embed/embedConfig.spec.ts +79 -0
- package/src/embed/host-events.spec.ts +1635 -0
- package/src/embed/liveboard.ts +6 -0
- package/src/embed/spotter-viz-utils.ts +30 -0
- package/src/index.ts +2 -1
- package/src/mixpanel-service.spec.ts +15 -0
- package/src/types.ts +96 -6
- package/src/utils/custom-actions.spec.ts +28 -0
- package/src/utils/graphql/answerService/answerService.spec.ts +18 -0
- package/src/utils/processData.spec.ts +32 -0
- package/src/utils/reporting.spec.ts +27 -0
- package/src/utils/sessionInfoService.spec.ts +151 -0
- package/src/utils.spec.ts +195 -0
- package/src/utils.ts +1 -1
package/src/embed/liveboard.ts
CHANGED
|
@@ -574,6 +574,12 @@ export interface LiveboardViewConfig extends BaseViewConfig, LiveboardOtherViewC
|
|
|
574
574
|
* inputChatPlaceholder: 'Ask a question...',
|
|
575
575
|
* hideStarterPrompts: false,
|
|
576
576
|
* customStarterPrompts: [{ id: '1', displayText: 'Top products', fullPrompt: 'What are the top products by revenue?' }],
|
|
577
|
+
* // loaderHeadline and loaderTips require SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
578
|
+
* loaderHeadline: 'Crunching the numbers...',
|
|
579
|
+
* loaderTips: [
|
|
580
|
+
* { label: 'Tip', text: 'try asking about revenue by region' },
|
|
581
|
+
* { label: 'Tip', text: 'use natural language' },
|
|
582
|
+
* ],
|
|
577
583
|
* },
|
|
578
584
|
* })
|
|
579
585
|
* ```
|
|
@@ -32,6 +32,12 @@ export interface SpotterVizStarterPrompt {
|
|
|
32
32
|
* customStarterPrompts: [
|
|
33
33
|
* { id: '1', displayText: 'Top products', fullPrompt: 'What are the top products by revenue?' }
|
|
34
34
|
* ],
|
|
35
|
+
* // loaderHeadline and loaderTips require SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
36
|
+
* loaderHeadline: 'Crunching the numbers...',
|
|
37
|
+
* loaderTips: [
|
|
38
|
+
* { label: 'Tip', text: 'try asking about revenue by region' },
|
|
39
|
+
* { label: 'Tip', text: 'use natural language' },
|
|
40
|
+
* ],
|
|
35
41
|
* },
|
|
36
42
|
* })
|
|
37
43
|
* ```
|
|
@@ -76,6 +82,30 @@ export interface SpotterVizConfig {
|
|
|
76
82
|
* @version SDK: 1.50.0 | ThoughtSpot Cloud: 26.7.0.cl
|
|
77
83
|
*/
|
|
78
84
|
inputChatPlaceholder?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Custom headline text shown in the SpotterViz loading state.
|
|
87
|
+
* Replaces the default loading headline.
|
|
88
|
+
* @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
89
|
+
*/
|
|
90
|
+
loaderHeadline?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Custom tips shown in the SpotterViz loading state.
|
|
93
|
+
* Replaces the default loading tips with the provided list.
|
|
94
|
+
* @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
95
|
+
*/
|
|
96
|
+
loaderTips?: SpotterVizLoaderTip[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* A single tip shown in the SpotterViz loading state.
|
|
101
|
+
* @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
102
|
+
* @group Embed components
|
|
103
|
+
*/
|
|
104
|
+
export interface SpotterVizLoaderTip {
|
|
105
|
+
/** Short label rendered alongside the tip (e.g. "Tip"). */
|
|
106
|
+
label: string;
|
|
107
|
+
/** Tip body text. */
|
|
108
|
+
text: string;
|
|
79
109
|
}
|
|
80
110
|
|
|
81
111
|
export function buildSpotterVizAppInitData<T extends DefaultAppInitData>(
|
package/src/index.ts
CHANGED
|
@@ -33,7 +33,7 @@ import { SearchEmbed, SearchViewConfig } from './embed/search';
|
|
|
33
33
|
import { SearchBarEmbed, SearchBarViewConfig } from './embed/search-bar';
|
|
34
34
|
import { SpotterAgentEmbed, SpotterAgentEmbedViewConfig, BodylessConversation, BodylessConversationViewConfig} from './embed/bodyless-conversation';
|
|
35
35
|
import { SpotterEmbed, SpotterEmbedViewConfig, SpotterChatViewConfig, SpotterSidebarViewConfig, ConversationEmbed, ConversationViewConfig } from './embed/conversation';
|
|
36
|
-
import { SpotterVizConfig, SpotterVizStarterPrompt } from './embed/spotter-viz-utils';
|
|
36
|
+
import { SpotterVizConfig, SpotterVizStarterPrompt, SpotterVizLoaderTip } from './embed/spotter-viz-utils';
|
|
37
37
|
import {
|
|
38
38
|
AuthFailureType, AuthStatus, AuthEvent, AuthEventEmitter,
|
|
39
39
|
} from './auth';
|
|
@@ -143,6 +143,7 @@ export {
|
|
|
143
143
|
LiveboardViewConfig,
|
|
144
144
|
SpotterVizConfig,
|
|
145
145
|
SpotterVizStarterPrompt,
|
|
146
|
+
SpotterVizLoaderTip,
|
|
146
147
|
AppViewConfig,
|
|
147
148
|
PrefetchFeatures,
|
|
148
149
|
FrameParams,
|
|
@@ -89,4 +89,19 @@ describe('Unit test for mixpanel', () => {
|
|
|
89
89
|
expect(logger.error).toHaveBeenCalled();
|
|
90
90
|
expect(mixpanel.register_once).not.toHaveBeenCalled();
|
|
91
91
|
});
|
|
92
|
+
|
|
93
|
+
test('logs error when mixpanel.init throws (covers catch block)', () => {
|
|
94
|
+
testResetMixpanel();
|
|
95
|
+
jest.spyOn(logger, 'error').mockImplementation(() => {});
|
|
96
|
+
(mixpanel.init as jest.Mock).mockImplementationOnce(() => {
|
|
97
|
+
throw new Error('mixpanel init failed');
|
|
98
|
+
});
|
|
99
|
+
const sessionInfo = {
|
|
100
|
+
mixpanelToken: 'abc123',
|
|
101
|
+
userGUID: '12345',
|
|
102
|
+
isPublicUser: false,
|
|
103
|
+
} as SessionInfo;
|
|
104
|
+
initMixpanel(sessionInfo);
|
|
105
|
+
expect(logger.error).toHaveBeenCalledWith('Error initializing mixpanel', expect.any(Error));
|
|
106
|
+
});
|
|
92
107
|
});
|
package/src/types.ts
CHANGED
|
@@ -963,7 +963,8 @@ export interface BaseViewConfig extends ApiInterceptFlags {
|
|
|
963
963
|
* The list of actions to completely remove from the embedded view.
|
|
964
964
|
* Hidden actions are not visible to the user at all (fully removed from the UI).
|
|
965
965
|
* Use this when you want to remove an action entirely.
|
|
966
|
-
* To keep an action visible but non-interactive (grayed out), use {@link
|
|
966
|
+
* To keep an action visible but non-interactive (grayed out), use {@link
|
|
967
|
+
* disabledActions} instead.
|
|
967
968
|
*
|
|
968
969
|
* Supported embed types: `AppEmbed`, `LiveboardEmbed`, `SearchEmbed`, `SpotterAgentEmbed`, `SpotterEmbed`, `SearchBarEmbed`
|
|
969
970
|
* @version SDK: 1.6.0 | ThoughtSpot: ts8.nov.cl, 8.4.1.sw
|
|
@@ -983,7 +984,8 @@ export interface BaseViewConfig extends ApiInterceptFlags {
|
|
|
983
984
|
* (...), and the contextual menu. These will be only actions that
|
|
984
985
|
* are visible to the user.
|
|
985
986
|
* Use this as an allowlist — only the actions listed here will be shown.
|
|
986
|
-
* All other actions will be hidden. Use either this or {@link hiddenActions}, not
|
|
987
|
+
* All other actions will be hidden. Use either this or {@link hiddenActions}, not
|
|
988
|
+
* both.
|
|
987
989
|
*
|
|
988
990
|
* Supported embed types: `AppEmbed`, `LiveboardEmbed`, `SearchEmbed`, `SpotterAgentEmbed`, `SpotterEmbed`, `SearchBarEmbed`
|
|
989
991
|
* @version SDK: 1.6.0 | ThoughtSpot: ts8.nov.cl, 8.4.1.sw
|
|
@@ -3760,10 +3762,11 @@ export enum EmbedEvent {
|
|
|
3760
3762
|
EmbedPageContextChanged = 'EmbedPageContextChanged',
|
|
3761
3763
|
|
|
3762
3764
|
/**
|
|
3763
|
-
* Represents a special embed event that is triggered whenever any host event is
|
|
3765
|
+
* Represents a special embed event that is triggered whenever any host event is
|
|
3766
|
+
* subscribed.
|
|
3764
3767
|
*
|
|
3765
|
-
* You can listen to this event when you need to dispatch a host event during load or
|
|
3766
|
-
* particularly in situations where timing issues may occur.
|
|
3768
|
+
* You can listen to this event when you need to dispatch a host event during load or
|
|
3769
|
+
* render, particularly in situations where timing issues may occur.
|
|
3767
3770
|
*
|
|
3768
3771
|
* @example
|
|
3769
3772
|
* ```js
|
|
@@ -6016,6 +6019,27 @@ export enum HostEvent {
|
|
|
6016
6019
|
* ```
|
|
6017
6020
|
*/
|
|
6018
6021
|
InitSpotterVizConversation = 'InitSpotterVizConversation',
|
|
6022
|
+
|
|
6023
|
+
/**
|
|
6024
|
+
* Opens the SpotterViz panel.
|
|
6025
|
+
* @version SDK: 1.50.0 | ThoughtSpot Cloud: 26.7.0.cl
|
|
6026
|
+
* @example
|
|
6027
|
+
* ```js
|
|
6028
|
+
* liveboardEmbed.trigger(HostEvent.OpenSpotterVizPanel);
|
|
6029
|
+
* ```
|
|
6030
|
+
*/
|
|
6031
|
+
OpenSpotterVizPanel = 'OpenSpotterVizPanel',
|
|
6032
|
+
|
|
6033
|
+
/**
|
|
6034
|
+
* Closes the SpotterViz panel.
|
|
6035
|
+
* @version SDK: 1.50.0 | ThoughtSpot Cloud: 26.7.0.cl
|
|
6036
|
+
* @example
|
|
6037
|
+
* ```js
|
|
6038
|
+
* liveboardEmbed.trigger(HostEvent.CloseSpotterVizPanel);
|
|
6039
|
+
* ```
|
|
6040
|
+
*/
|
|
6041
|
+
CloseSpotterVizPanel = 'CloseSpotterVizPanel',
|
|
6042
|
+
|
|
6019
6043
|
/**
|
|
6020
6044
|
* Clears browser cache and fetches new data for liveboard ChartViz Containers.
|
|
6021
6045
|
* Requires `enableLiveboardDataCache` to be enabled.
|
|
@@ -7972,7 +7996,73 @@ export enum Action {
|
|
|
7972
7996
|
* ```
|
|
7973
7997
|
* @version SDK: 1.49.0 | ThoughtSpot Cloud: 26.6.0.cl
|
|
7974
7998
|
*/
|
|
7975
|
-
RefreshLiveboardBrowserCache = 'refreshLiveboardBrowserCache',
|
|
7999
|
+
RefreshLiveboardBrowserCache = 'refreshLiveboardBrowserCache',
|
|
8000
|
+
/**
|
|
8001
|
+
* Controls visibility and disable state of the share action
|
|
8002
|
+
* in the Spotter Analyst interface.
|
|
8003
|
+
* @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
8004
|
+
* @example
|
|
8005
|
+
* ```js
|
|
8006
|
+
* hiddenActions: [Action.SpotterAnalystShare]
|
|
8007
|
+
* disabledActions: [Action.SpotterAnalystShare]
|
|
8008
|
+
* ```
|
|
8009
|
+
*/
|
|
8010
|
+
SpotterAnalystShare = 'spotterAnalystShare',
|
|
8011
|
+
/**
|
|
8012
|
+
* Controls visibility and disable state of the edit action
|
|
8013
|
+
* in the Spotter Analyst interface.
|
|
8014
|
+
* @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
8015
|
+
* @example
|
|
8016
|
+
* ```js
|
|
8017
|
+
* hiddenActions: [Action.SpotterAnalystEdit]
|
|
8018
|
+
* disabledActions: [Action.SpotterAnalystEdit]
|
|
8019
|
+
* ```
|
|
8020
|
+
*/
|
|
8021
|
+
SpotterAnalystEdit = 'spotterAnalystEdit',
|
|
8022
|
+
/**
|
|
8023
|
+
* Controls visibility and disable state of the create action
|
|
8024
|
+
* in the Spotter Analyst interface.
|
|
8025
|
+
* @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
8026
|
+
* @example
|
|
8027
|
+
* ```js
|
|
8028
|
+
* hiddenActions: [Action.SpotterAnalystCreate]
|
|
8029
|
+
* disabledActions: [Action.SpotterAnalystCreate]
|
|
8030
|
+
* ```
|
|
8031
|
+
*/
|
|
8032
|
+
SpotterAnalystCreate = 'spotterAnalystCreate',
|
|
8033
|
+
/**
|
|
8034
|
+
* Controls visibility and disable state of the delete action
|
|
8035
|
+
* in the Spotter Analyst interface.
|
|
8036
|
+
* @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
8037
|
+
* @example
|
|
8038
|
+
* ```js
|
|
8039
|
+
* hiddenActions: [Action.SpotterAnalystDelete]
|
|
8040
|
+
* disabledActions: [Action.SpotterAnalystDelete]
|
|
8041
|
+
* ```
|
|
8042
|
+
*/
|
|
8043
|
+
SpotterAnalystDelete = 'spotterAnalystDelete',
|
|
8044
|
+
/**
|
|
8045
|
+
* Controls visibility and disable state of the make a copy action
|
|
8046
|
+
* in the Spotter Analyst interface.
|
|
8047
|
+
* @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
8048
|
+
* @example
|
|
8049
|
+
* ```js
|
|
8050
|
+
* hiddenActions: [Action.SpotterAnalystMakeACopy]
|
|
8051
|
+
* disabledActions: [Action.SpotterAnalystMakeACopy]
|
|
8052
|
+
* ```
|
|
8053
|
+
*/
|
|
8054
|
+
SpotterAnalystMakeACopy = 'spotterAnalystMakeACopy',
|
|
8055
|
+
/**
|
|
8056
|
+
* Controls visibility and disable state of the sidebar
|
|
8057
|
+
* in the Spotter Analyst interface.
|
|
8058
|
+
* @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
|
|
8059
|
+
* @example
|
|
8060
|
+
* ```js
|
|
8061
|
+
* hiddenActions: [Action.SpotterAnalystSidebar]
|
|
8062
|
+
* disabledActions: [Action.SpotterAnalystSidebar]
|
|
8063
|
+
* ```
|
|
8064
|
+
*/
|
|
8065
|
+
SpotterAnalystSidebar = 'spotterAnalystSidebar',
|
|
7976
8066
|
}
|
|
7977
8067
|
export interface AnswerServiceType {
|
|
7978
8068
|
getAnswer?: (offset: number, batchSize: number) => any;
|
|
@@ -429,6 +429,34 @@ describe('getCustomActions function', () => {
|
|
|
429
429
|
});
|
|
430
430
|
});
|
|
431
431
|
|
|
432
|
+
describe('Empty allowed IDs edge cases', () => {
|
|
433
|
+
test('should show "none" as supported metadata IDs when target has no allowed metadata IDs (SPOTTER)', () => {
|
|
434
|
+
const action = {
|
|
435
|
+
id: 'test-id',
|
|
436
|
+
name: 'Test Action',
|
|
437
|
+
target: CustomActionTarget.SPOTTER,
|
|
438
|
+
position: CustomActionsPosition.MENU,
|
|
439
|
+
metadataIds: { liveboardIds: ['lb-1'] },
|
|
440
|
+
} as any;
|
|
441
|
+
const result = getCustomActions([action]);
|
|
442
|
+
// Expect an error containing 'none' for unsupported metadataIds on SPOTTER
|
|
443
|
+
expect(result.errors.some((e) => e.includes('none'))).toBe(true);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test('should show "none" as supported data model IDs when target has no allowed data model IDs (LIVEBOARD)', () => {
|
|
447
|
+
const action = {
|
|
448
|
+
id: 'test-id',
|
|
449
|
+
name: 'Test Action',
|
|
450
|
+
target: CustomActionTarget.LIVEBOARD,
|
|
451
|
+
position: CustomActionsPosition.PRIMARY,
|
|
452
|
+
dataModelIds: { modelIds: ['model-1'] },
|
|
453
|
+
} as any;
|
|
454
|
+
const result = getCustomActions([action]);
|
|
455
|
+
// Expect an error containing 'none' for unsupported dataModelIds on LIVEBOARD
|
|
456
|
+
expect(result.errors.some((e) => e.includes('none'))).toBe(true);
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
|
|
432
460
|
describe('Warnings', () => {
|
|
433
461
|
test('should warn when action name length exceeds 30 characters', () => {
|
|
434
462
|
// Arrange
|
|
@@ -707,4 +707,22 @@ describe('Answer service tests', () => {
|
|
|
707
707
|
expect(answer.name).toBe('name');
|
|
708
708
|
expect(answer.id).toBe('bar');
|
|
709
709
|
});
|
|
710
|
+
|
|
711
|
+
test('getTML parses nested objects and arrays from the YAML edoc', async () => {
|
|
712
|
+
const edoc = [
|
|
713
|
+
'answer:',
|
|
714
|
+
' name: Revenue answer',
|
|
715
|
+
' visualizations:',
|
|
716
|
+
' - id: viz-1',
|
|
717
|
+
' type: TABLE',
|
|
718
|
+
].join('\n');
|
|
719
|
+
fetchMock.mockResponseOnce(JSON.stringify({
|
|
720
|
+
data: { UnsavedAnswer_getTML: { object: [{ edoc }] } },
|
|
721
|
+
}));
|
|
722
|
+
|
|
723
|
+
const { answer } = await createAnswerService().getTML();
|
|
724
|
+
|
|
725
|
+
expect(answer.name).toBe('Revenue answer');
|
|
726
|
+
expect(answer.visualizations).toEqual([{ id: 'viz-1', type: 'TABLE' }]);
|
|
727
|
+
});
|
|
710
728
|
});
|
|
@@ -279,6 +279,38 @@ describe('Unit test for process data', () => {
|
|
|
279
279
|
mockHandleExitPresentMode.mockReset();
|
|
280
280
|
});
|
|
281
281
|
|
|
282
|
+
test('AuthInit with payload.userGUID fallback when data.userGUID is absent', () => {
|
|
283
|
+
const e = {
|
|
284
|
+
type: EmbedEvent.AuthInit,
|
|
285
|
+
payload: { userGUID: 'payloadGUID' },
|
|
286
|
+
};
|
|
287
|
+
jest.spyOn(base, 'notifyAuthSuccess');
|
|
288
|
+
expect(processDataInstance.processEventData(e.type, e, '', null)).toEqual({
|
|
289
|
+
type: e.type,
|
|
290
|
+
payload: { userGUID: 'payloadGUID' },
|
|
291
|
+
data: { userGUID: 'payloadGUID' },
|
|
292
|
+
});
|
|
293
|
+
expect(base.notifyAuthSuccess).toHaveBeenCalled();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test('process authFailure EmbeddedSSO with UNAUTHENTICATED_FAILURE suppresses login page', () => {
|
|
297
|
+
const e = {
|
|
298
|
+
type: EmbedEvent.AuthFailure,
|
|
299
|
+
data: { type: auth.AuthFailureType.UNAUTHENTICATED_FAILURE },
|
|
300
|
+
};
|
|
301
|
+
jest.spyOn(base, 'notifyAuthFailure');
|
|
302
|
+
jest.spyOn(embedConfigInstance, 'getEmbedConfig').mockReturnValue({
|
|
303
|
+
loginFailedMessage: 'Hello',
|
|
304
|
+
authType: AuthType.EmbeddedSSO,
|
|
305
|
+
disableLoginFailurePage: false,
|
|
306
|
+
} as any);
|
|
307
|
+
const el: any = {};
|
|
308
|
+
processDataInstance.processEventData(e.type, e, '', el);
|
|
309
|
+
// isEmbeddedSSOInfoFailure=true so neither branch fires, innerHTML stays unset
|
|
310
|
+
expect(el.innerHTML).toBeUndefined();
|
|
311
|
+
expect(base.notifyAuthFailure).not.toHaveBeenCalled();
|
|
312
|
+
});
|
|
313
|
+
|
|
282
314
|
test('should handle ClearInfoCache', () => {
|
|
283
315
|
const mockResetCachedPreauthInfo = jest.spyOn(sessionInfoService, 'resetCachedPreauthInfo').mockImplementation(() => {});
|
|
284
316
|
const mockResetCachedSessionInfo = jest.spyOn(sessionInfoService, 'resetCachedSessionInfo').mockImplementation(() => {});
|
|
@@ -88,4 +88,31 @@ describe('ReportingObserver', () => {
|
|
|
88
88
|
|
|
89
89
|
(window as any).ReportingObserver = currentObserver;
|
|
90
90
|
});
|
|
91
|
+
|
|
92
|
+
test('should suppress alert but still log error when suppressErrorAlerts is true', () => {
|
|
93
|
+
let callBackPassed: any;
|
|
94
|
+
const NewMockRO = jest.fn().mockImplementation((callback: any) => {
|
|
95
|
+
callBackPassed = callback;
|
|
96
|
+
return ({ observe: jest.fn(), disconnect: jest.fn(), takeRecords: jest.fn() });
|
|
97
|
+
});
|
|
98
|
+
const currentObserver = (window as any).ReportingObserver;
|
|
99
|
+
(window as any).ReportingObserver = NewMockRO;
|
|
100
|
+
|
|
101
|
+
setEmbedConfig({
|
|
102
|
+
thoughtSpotHost: 'testHost',
|
|
103
|
+
authType: AuthType.None,
|
|
104
|
+
suppressErrorAlerts: true,
|
|
105
|
+
} as any);
|
|
106
|
+
|
|
107
|
+
const mockAlert = jest.fn();
|
|
108
|
+
window.alert = mockAlert;
|
|
109
|
+
|
|
110
|
+
registerReportingObserver(true);
|
|
111
|
+
callBackPassed([{ type: 'csp-violation', url: 'testHost', body: { effectiveDirective: 'frame-ancestors' } }]);
|
|
112
|
+
|
|
113
|
+
expect(mockAlert).not.toHaveBeenCalled();
|
|
114
|
+
expect(logger.error).toHaveBeenCalledWith(ERROR_MESSAGE.CSP_FRAME_HOST_VIOLATION_LOG_MESSAGE);
|
|
115
|
+
|
|
116
|
+
(window as any).ReportingObserver = currentObserver;
|
|
117
|
+
});
|
|
91
118
|
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as authServiceModule from './authService';
|
|
2
|
+
import * as embedConfigModule from '../embed/embedConfig';
|
|
3
|
+
import {
|
|
4
|
+
formatPreauthInfo,
|
|
5
|
+
getPreauthInfo,
|
|
6
|
+
getSessionInfo,
|
|
7
|
+
resetCachedPreauthInfo,
|
|
8
|
+
resetCachedSessionInfo,
|
|
9
|
+
getCachedSessionInfo,
|
|
10
|
+
getSessionDetails,
|
|
11
|
+
} from './sessionInfoService';
|
|
12
|
+
|
|
13
|
+
describe('sessionInfoService', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
resetCachedPreauthInfo();
|
|
16
|
+
resetCachedSessionInfo();
|
|
17
|
+
jest.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('formatPreauthInfo', () => {
|
|
21
|
+
it('parses headers and JSON body from a response', async () => {
|
|
22
|
+
const headers = new Headers({ 'content-type': 'application/json' });
|
|
23
|
+
const mockResp = {
|
|
24
|
+
headers,
|
|
25
|
+
json: jest.fn().mockResolvedValue({ info: { userGUID: 'u1' } }),
|
|
26
|
+
};
|
|
27
|
+
const result = await formatPreauthInfo(mockResp);
|
|
28
|
+
expect(result.info.userGUID).toBe('u1');
|
|
29
|
+
expect(result.status).toBe(200);
|
|
30
|
+
expect(result.headers['content-type']).toBe('application/json');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('returns null when response.json() throws (covers catch block)', async () => {
|
|
34
|
+
const mockResp = {
|
|
35
|
+
headers: null as any,
|
|
36
|
+
json: jest.fn().mockRejectedValue(new Error('parse error')),
|
|
37
|
+
};
|
|
38
|
+
const result = await formatPreauthInfo(mockResp);
|
|
39
|
+
expect(result).toBeNull();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('handles response where headers is null (covers headers?.forEach branch)', async () => {
|
|
43
|
+
const mockResp = {
|
|
44
|
+
headers: null as any,
|
|
45
|
+
json: jest.fn().mockResolvedValue({ info: {} }),
|
|
46
|
+
};
|
|
47
|
+
const result = await formatPreauthInfo(mockResp);
|
|
48
|
+
expect(result.headers).toEqual({});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('getPreauthInfo', () => {
|
|
53
|
+
it('fetches and caches preauthInfo on first call', async () => {
|
|
54
|
+
const mockResp = {
|
|
55
|
+
headers: new Headers({ 'x-caller': 'test' }),
|
|
56
|
+
json: jest.fn().mockResolvedValue({ info: { userGUID: 'u1' } }),
|
|
57
|
+
};
|
|
58
|
+
jest.spyOn(embedConfigModule, 'getEmbedConfig').mockReturnValue({ thoughtSpotHost: 'https://ts' } as any);
|
|
59
|
+
jest.spyOn(authServiceModule, 'fetchPreauthInfoService').mockResolvedValue(mockResp as any);
|
|
60
|
+
|
|
61
|
+
const result = await getPreauthInfo();
|
|
62
|
+
expect(result.info.userGUID).toBe('u1');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('returns cached preauthInfo on subsequent calls (covers allowCache=true && preauthInfo cached branch)', async () => {
|
|
66
|
+
const mockResp = {
|
|
67
|
+
headers: new Headers(),
|
|
68
|
+
json: jest.fn().mockResolvedValue({ info: { userGUID: 'cached-user' } }),
|
|
69
|
+
};
|
|
70
|
+
jest.spyOn(embedConfigModule, 'getEmbedConfig').mockReturnValue({ thoughtSpotHost: 'https://ts' } as any);
|
|
71
|
+
const fetchSpy = jest.spyOn(authServiceModule, 'fetchPreauthInfoService').mockResolvedValue(mockResp as any);
|
|
72
|
+
|
|
73
|
+
await getPreauthInfo(); // first call — fetches and caches
|
|
74
|
+
await getPreauthInfo(); // second call — should use cache
|
|
75
|
+
|
|
76
|
+
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('returns null when fetch throws (covers catch block)', async () => {
|
|
80
|
+
jest.spyOn(embedConfigModule, 'getEmbedConfig').mockReturnValue({ thoughtSpotHost: 'https://ts' } as any);
|
|
81
|
+
jest.spyOn(authServiceModule, 'fetchPreauthInfoService').mockRejectedValue(new Error('network error'));
|
|
82
|
+
|
|
83
|
+
const result = await getPreauthInfo();
|
|
84
|
+
expect(result).toBeNull();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('getSessionInfo', () => {
|
|
89
|
+
it('fetches and returns session info', async () => {
|
|
90
|
+
const sessionInfoResp = {
|
|
91
|
+
userGUID: 'u1',
|
|
92
|
+
releaseVersion: '1.0',
|
|
93
|
+
configInfo: {
|
|
94
|
+
mixpanelConfig: { devSdkKey: 'dev', prodSdkKey: 'prod', production: false },
|
|
95
|
+
isPublicUser: false,
|
|
96
|
+
selfClusterId: 'c1',
|
|
97
|
+
selfClusterName: 'cluster1',
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
jest.spyOn(embedConfigModule, 'getEmbedConfig').mockReturnValue({ thoughtSpotHost: 'https://ts' } as any);
|
|
101
|
+
jest.spyOn(authServiceModule, 'fetchSessionInfoService').mockResolvedValue(sessionInfoResp as any);
|
|
102
|
+
|
|
103
|
+
const result = await getSessionInfo();
|
|
104
|
+
expect(result.userGUID).toBe('u1');
|
|
105
|
+
expect(result.mixpanelToken).toBe('dev');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('returns cached session info on second call', async () => {
|
|
109
|
+
const sessionInfoResp = {
|
|
110
|
+
userGUID: 'u2',
|
|
111
|
+
releaseVersion: '2.0',
|
|
112
|
+
configInfo: {
|
|
113
|
+
mixpanelConfig: { devSdkKey: 'dev2', prodSdkKey: 'prod2', production: true },
|
|
114
|
+
isPublicUser: false,
|
|
115
|
+
selfClusterId: 'c2',
|
|
116
|
+
selfClusterName: 'cluster2',
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
jest.spyOn(embedConfigModule, 'getEmbedConfig').mockReturnValue({ thoughtSpotHost: 'https://ts' } as any);
|
|
120
|
+
const fetchSpy = jest.spyOn(authServiceModule, 'fetchSessionInfoService').mockResolvedValue(sessionInfoResp as any);
|
|
121
|
+
|
|
122
|
+
await getSessionInfo();
|
|
123
|
+
await getSessionInfo();
|
|
124
|
+
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('getCachedSessionInfo', () => {
|
|
129
|
+
it('returns null before any fetch', () => {
|
|
130
|
+
expect(getCachedSessionInfo()).toBeNull();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('getSessionDetails', () => {
|
|
135
|
+
it('uses prodSdkKey when production flag is true', () => {
|
|
136
|
+
const resp = {
|
|
137
|
+
userGUID: 'u3',
|
|
138
|
+
releaseVersion: '3.0',
|
|
139
|
+
configInfo: {
|
|
140
|
+
mixpanelConfig: { devSdkKey: 'dev', prodSdkKey: 'prod', production: true },
|
|
141
|
+
isPublicUser: true,
|
|
142
|
+
selfClusterId: 'c3',
|
|
143
|
+
selfClusterName: 'cluster3',
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
const result = getSessionDetails(resp);
|
|
147
|
+
expect(result.mixpanelToken).toBe('prod');
|
|
148
|
+
expect(result.isPublicUser).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|