@thoughtspot/visual-embed-sdk 1.49.3 → 1.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/package.json +1 -1
- package/cjs/src/authToken.spec.js +5 -0
- package/cjs/src/authToken.spec.js.map +1 -1
- package/cjs/src/css-variables.d.ts +59 -0
- package/cjs/src/css-variables.d.ts.map +1 -1
- package/cjs/src/embed/app.d.ts +31 -2
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +19 -2
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +112 -2
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/conversation.d.ts +38 -0
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js +12 -2
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/conversation.spec.js +53 -0
- package/cjs/src/embed/conversation.spec.js.map +1 -1
- package/cjs/src/embed/embedConfig.spec.d.ts +2 -0
- package/cjs/src/embed/embedConfig.spec.d.ts.map +1 -0
- package/cjs/src/embed/embedConfig.spec.js +74 -0
- package/cjs/src/embed/embedConfig.spec.js.map +1 -0
- package/cjs/src/embed/events.spec.js +593 -0
- package/cjs/src/embed/events.spec.js.map +1 -1
- package/cjs/src/embed/host-events.spec.d.ts +2 -0
- package/cjs/src/embed/host-events.spec.d.ts.map +1 -0
- package/cjs/src/embed/host-events.spec.js +1325 -0
- package/cjs/src/embed/host-events.spec.js.map +1 -0
- package/cjs/src/embed/hostEventClient/contracts.d.ts +36 -14
- package/cjs/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/contracts.js +10 -1
- package/cjs/src/embed/hostEventClient/contracts.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts +1 -0
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.js +8 -0
- package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.spec.js +209 -0
- package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
- package/cjs/src/embed/hostEventClient/utils.d.ts +2 -0
- package/cjs/src/embed/hostEventClient/utils.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/utils.js +32 -2
- package/cjs/src/embed/hostEventClient/utils.js.map +1 -1
- package/cjs/src/embed/hostEventClient/utils.spec.js +178 -0
- package/cjs/src/embed/hostEventClient/utils.spec.js.map +1 -1
- package/cjs/src/embed/liveboard.d.ts +6 -0
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/spotter-viz-utils.d.ts +34 -0
- package/cjs/src/embed/spotter-viz-utils.d.ts.map +1 -1
- package/cjs/src/embed/spotter-viz-utils.js.map +1 -1
- package/cjs/src/embed/spotter-viz-utils.spec.js +17 -0
- package/cjs/src/embed/spotter-viz-utils.spec.js.map +1 -1
- package/cjs/src/embed/ts-embed.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.js +14 -7
- package/cjs/src/embed/ts-embed.js.map +1 -1
- package/cjs/src/embed/ts-embed.spec.js +146 -0
- package/cjs/src/embed/ts-embed.spec.js.map +1 -1
- package/cjs/src/errors.d.ts +1 -0
- package/cjs/src/errors.d.ts.map +1 -1
- package/cjs/src/errors.js +1 -0
- package/cjs/src/errors.js.map +1 -1
- package/cjs/src/index.d.ts +2 -2
- package/cjs/src/index.d.ts.map +1 -1
- package/cjs/src/index.js +3 -2
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/mixpanel-service.spec.js +14 -0
- package/cjs/src/mixpanel-service.spec.js.map +1 -1
- package/cjs/src/react/util.spec.js +251 -0
- package/cjs/src/react/util.spec.js.map +1 -1
- package/cjs/src/types.d.ts +212 -8
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +185 -3
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/custom-actions.spec.js +26 -0
- package/cjs/src/utils/custom-actions.spec.js.map +1 -1
- package/cjs/src/utils/processData.spec.js +123 -0
- package/cjs/src/utils/processData.spec.js.map +1 -1
- package/cjs/src/utils/processTrigger.spec.js +61 -0
- package/cjs/src/utils/processTrigger.spec.js.map +1 -1
- package/cjs/src/utils/reporting.spec.js +21 -0
- package/cjs/src/utils/reporting.spec.js.map +1 -1
- package/cjs/src/utils/sessionInfoService.spec.d.ts +2 -0
- package/cjs/src/utils/sessionInfoService.spec.d.ts.map +1 -0
- package/cjs/src/utils/sessionInfoService.spec.js +130 -0
- package/cjs/src/utils/sessionInfoService.spec.js.map +1 -0
- package/cjs/src/utils.spec.js +158 -0
- package/cjs/src/utils.spec.js.map +1 -1
- package/dist/{index-DsSQndIB.js → index-fCne7kmU.js} +1 -1
- package/dist/src/css-variables.d.ts +59 -0
- package/dist/src/css-variables.d.ts.map +1 -1
- package/dist/src/embed/app.d.ts +31 -2
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/conversation.d.ts +38 -0
- package/dist/src/embed/conversation.d.ts.map +1 -1
- package/dist/src/embed/embedConfig.spec.d.ts +2 -0
- package/dist/src/embed/embedConfig.spec.d.ts.map +1 -0
- package/dist/src/embed/host-events.spec.d.ts +2 -0
- package/dist/src/embed/host-events.spec.d.ts.map +1 -0
- package/dist/src/embed/hostEventClient/contracts.d.ts +36 -14
- package/dist/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/host-event-client.d.ts +1 -0
- package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/utils.d.ts +2 -0
- package/dist/src/embed/hostEventClient/utils.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +6 -0
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/spotter-viz-utils.d.ts +34 -0
- package/dist/src/embed/spotter-viz-utils.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.d.ts.map +1 -1
- package/dist/src/errors.d.ts +1 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +212 -8
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/sessionInfoService.spec.d.ts +2 -0
- package/dist/src/utils/sessionInfoService.spec.d.ts.map +1 -0
- package/dist/tsembed-react.es.js +482 -86
- package/dist/tsembed-react.js +481 -85
- package/dist/tsembed.es.js +483 -87
- package/dist/tsembed.js +481 -85
- package/dist/visual-embed-sdk-react-full.d.ts +407 -23
- package/dist/visual-embed-sdk-react.d.ts +407 -23
- package/dist/visual-embed-sdk.d.ts +411 -23
- package/lib/package.json +1 -1
- package/lib/src/authToken.spec.js +5 -0
- package/lib/src/authToken.spec.js.map +1 -1
- package/lib/src/css-variables.d.ts +59 -0
- package/lib/src/css-variables.d.ts.map +1 -1
- package/lib/src/embed/app.d.ts +31 -2
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +19 -2
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +112 -2
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/conversation.d.ts +38 -0
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js +11 -1
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/conversation.spec.js +54 -1
- package/lib/src/embed/conversation.spec.js.map +1 -1
- package/lib/src/embed/embedConfig.spec.d.ts +2 -0
- package/lib/src/embed/embedConfig.spec.d.ts.map +1 -0
- package/lib/src/embed/embedConfig.spec.js +72 -0
- package/lib/src/embed/embedConfig.spec.js.map +1 -0
- package/lib/src/embed/events.spec.js +593 -0
- package/lib/src/embed/events.spec.js.map +1 -1
- package/lib/src/embed/host-events.spec.d.ts +2 -0
- package/lib/src/embed/host-events.spec.d.ts.map +1 -0
- package/lib/src/embed/host-events.spec.js +1322 -0
- package/lib/src/embed/host-events.spec.js.map +1 -0
- package/lib/src/embed/hostEventClient/contracts.d.ts +36 -14
- package/lib/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/contracts.js +9 -0
- package/lib/src/embed/hostEventClient/contracts.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.d.ts +1 -0
- package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.js +9 -1
- package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.spec.js +209 -0
- package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
- package/lib/src/embed/hostEventClient/utils.d.ts +2 -0
- package/lib/src/embed/hostEventClient/utils.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/utils.js +28 -1
- package/lib/src/embed/hostEventClient/utils.js.map +1 -1
- package/lib/src/embed/hostEventClient/utils.spec.js +179 -1
- package/lib/src/embed/hostEventClient/utils.spec.js.map +1 -1
- package/lib/src/embed/liveboard.d.ts +6 -0
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/spotter-viz-utils.d.ts +34 -0
- package/lib/src/embed/spotter-viz-utils.d.ts.map +1 -1
- package/lib/src/embed/spotter-viz-utils.js.map +1 -1
- package/lib/src/embed/spotter-viz-utils.spec.js +17 -0
- package/lib/src/embed/spotter-viz-utils.spec.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +14 -7
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +146 -0
- package/lib/src/embed/ts-embed.spec.js.map +1 -1
- package/lib/src/errors.d.ts +1 -0
- package/lib/src/errors.d.ts.map +1 -1
- package/lib/src/errors.js +1 -0
- package/lib/src/errors.js.map +1 -1
- package/lib/src/index.d.ts +2 -2
- package/lib/src/index.d.ts.map +1 -1
- package/lib/src/index.js +2 -2
- package/lib/src/index.js.map +1 -1
- package/lib/src/mixpanel-service.spec.js +14 -0
- package/lib/src/mixpanel-service.spec.js.map +1 -1
- package/lib/src/react/util.spec.js +251 -0
- package/lib/src/react/util.spec.js.map +1 -1
- package/lib/src/types.d.ts +212 -8
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +185 -3
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/custom-actions.spec.js +26 -0
- package/lib/src/utils/custom-actions.spec.js.map +1 -1
- package/lib/src/utils/processData.spec.js +123 -0
- package/lib/src/utils/processData.spec.js.map +1 -1
- package/lib/src/utils/processTrigger.spec.js +61 -0
- package/lib/src/utils/processTrigger.spec.js.map +1 -1
- package/lib/src/utils/reporting.spec.js +21 -0
- package/lib/src/utils/reporting.spec.js.map +1 -1
- package/lib/src/utils/sessionInfoService.spec.d.ts +2 -0
- package/lib/src/utils/sessionInfoService.spec.d.ts.map +1 -0
- package/lib/src/utils/sessionInfoService.spec.js +127 -0
- package/lib/src/utils/sessionInfoService.spec.js.map +1 -0
- package/lib/src/utils.spec.js +159 -1
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +411 -23
- package/package.json +1 -1
- package/src/authToken.spec.ts +6 -0
- package/src/css-variables.ts +73 -0
- package/src/embed/app.spec.ts +155 -2
- package/src/embed/app.ts +44 -2
- package/src/embed/conversation.spec.ts +64 -1
- package/src/embed/conversation.ts +41 -0
- package/src/embed/embedConfig.spec.ts +79 -0
- package/src/embed/events.spec.ts +705 -1
- package/src/embed/host-events.spec.ts +1759 -0
- package/src/embed/hostEventClient/contracts.ts +41 -14
- package/src/embed/hostEventClient/host-event-client.spec.ts +326 -0
- package/src/embed/hostEventClient/host-event-client.ts +15 -0
- package/src/embed/hostEventClient/utils.spec.ts +204 -0
- package/src/embed/hostEventClient/utils.ts +37 -10
- package/src/embed/liveboard.ts +6 -0
- package/src/embed/spotter-viz-utils.spec.ts +17 -0
- package/src/embed/spotter-viz-utils.ts +34 -0
- package/src/embed/ts-embed.spec.ts +186 -0
- package/src/embed/ts-embed.ts +12 -4
- package/src/errors.ts +1 -0
- package/src/index.ts +2 -1
- package/src/mixpanel-service.spec.ts +15 -0
- package/src/react/util.spec.tsx +255 -0
- package/src/types.ts +215 -6
- package/src/utils/custom-actions.spec.ts +28 -0
- package/src/utils/processData.spec.ts +129 -0
- package/src/utils/processTrigger.spec.ts +91 -0
- package/src/utils/reporting.spec.ts +27 -0
- package/src/utils/sessionInfoService.spec.ts +151 -0
- package/src/utils.spec.ts +195 -0
|
@@ -80,6 +80,11 @@ describe('Unit test for process data', () => {
|
|
|
80
80
|
);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
+
test('ProcessData QueryChanged default branch returns payload unchanged', () => {
|
|
84
|
+
const e = { type: EmbedEvent.QueryChanged, data: { query: 'revenue by region' } };
|
|
85
|
+
expect(processDataInstance.processEventData(EmbedEvent.QueryChanged, e, thoughtSpotHost, null)).toEqual(e);
|
|
86
|
+
});
|
|
87
|
+
|
|
83
88
|
test('ProcessData, when Action is non CustomAction', () => {
|
|
84
89
|
const processedData = { type: EmbedEvent.Data };
|
|
85
90
|
jest.spyOn(processDataInstance, 'processCustomAction').mockImplementation(async () => ({}));
|
|
@@ -279,6 +284,130 @@ describe('Unit test for process data', () => {
|
|
|
279
284
|
mockHandleExitPresentMode.mockReset();
|
|
280
285
|
});
|
|
281
286
|
|
|
287
|
+
test('AuthInit with payload.userGUID fallback when data.userGUID is absent', () => {
|
|
288
|
+
const e = {
|
|
289
|
+
type: EmbedEvent.AuthInit,
|
|
290
|
+
payload: { userGUID: 'payloadGUID' },
|
|
291
|
+
};
|
|
292
|
+
jest.spyOn(base, 'notifyAuthSuccess');
|
|
293
|
+
expect(processDataInstance.processEventData(e.type, e, '', null)).toEqual({
|
|
294
|
+
type: e.type,
|
|
295
|
+
payload: { userGUID: 'payloadGUID' },
|
|
296
|
+
data: { userGUID: 'payloadGUID' },
|
|
297
|
+
});
|
|
298
|
+
expect(base.notifyAuthSuccess).toHaveBeenCalled();
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test('process authFailure EmbeddedSSO with UNAUTHENTICATED_FAILURE suppresses login page', () => {
|
|
302
|
+
const e = {
|
|
303
|
+
type: EmbedEvent.AuthFailure,
|
|
304
|
+
data: { type: auth.AuthFailureType.UNAUTHENTICATED_FAILURE },
|
|
305
|
+
};
|
|
306
|
+
jest.spyOn(base, 'notifyAuthFailure');
|
|
307
|
+
jest.spyOn(embedConfigInstance, 'getEmbedConfig').mockReturnValue({
|
|
308
|
+
loginFailedMessage: 'Hello',
|
|
309
|
+
authType: AuthType.EmbeddedSSO,
|
|
310
|
+
disableLoginFailurePage: false,
|
|
311
|
+
} as any);
|
|
312
|
+
const el: any = {};
|
|
313
|
+
processDataInstance.processEventData(e.type, e, '', el);
|
|
314
|
+
// isEmbeddedSSOInfoFailure=true so neither branch fires, innerHTML stays unset
|
|
315
|
+
expect(el.innerHTML).toBeUndefined();
|
|
316
|
+
expect(base.notifyAuthFailure).not.toHaveBeenCalled();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test('processEventData default branch returns payload unchanged for non-special events', () => {
|
|
320
|
+
const events = [
|
|
321
|
+
EmbedEvent.Drilldown,
|
|
322
|
+
EmbedEvent.DataSourceSelected,
|
|
323
|
+
EmbedEvent.AddRemoveColumns,
|
|
324
|
+
EmbedEvent.VizPointDoubleClick,
|
|
325
|
+
EmbedEvent.VizPointClick,
|
|
326
|
+
EmbedEvent.Alert,
|
|
327
|
+
EmbedEvent.GetDataClick,
|
|
328
|
+
EmbedEvent.DialogClose,
|
|
329
|
+
EmbedEvent.Download,
|
|
330
|
+
EmbedEvent.DownloadAsPng,
|
|
331
|
+
EmbedEvent.DownloadAsPdf,
|
|
332
|
+
EmbedEvent.DownloadAsCsv,
|
|
333
|
+
EmbedEvent.DownloadAsXlsx,
|
|
334
|
+
EmbedEvent.DownloadLiveboardAsContinuousPDF,
|
|
335
|
+
EmbedEvent.AnswerDelete,
|
|
336
|
+
EmbedEvent.AIHighlights,
|
|
337
|
+
EmbedEvent.Pin,
|
|
338
|
+
EmbedEvent.SpotIQAnalyze,
|
|
339
|
+
EmbedEvent.Share,
|
|
340
|
+
EmbedEvent.DrillInclude,
|
|
341
|
+
EmbedEvent.DrillExclude,
|
|
342
|
+
EmbedEvent.CopyToClipboard,
|
|
343
|
+
EmbedEvent.UpdateTML,
|
|
344
|
+
EmbedEvent.EditTML,
|
|
345
|
+
EmbedEvent.ExportTML,
|
|
346
|
+
EmbedEvent.SaveAsView,
|
|
347
|
+
EmbedEvent.CopyAEdit,
|
|
348
|
+
EmbedEvent.ShowUnderlyingData,
|
|
349
|
+
EmbedEvent.AnswerChartSwitcher,
|
|
350
|
+
EmbedEvent.LiveboardInfo,
|
|
351
|
+
EmbedEvent.AddToFavorites,
|
|
352
|
+
EmbedEvent.Schedule,
|
|
353
|
+
EmbedEvent.Edit,
|
|
354
|
+
EmbedEvent.MakeACopy,
|
|
355
|
+
EmbedEvent.Present,
|
|
356
|
+
EmbedEvent.Delete,
|
|
357
|
+
EmbedEvent.SchedulesList,
|
|
358
|
+
EmbedEvent.Cancel,
|
|
359
|
+
EmbedEvent.Explore,
|
|
360
|
+
EmbedEvent.CopyLink,
|
|
361
|
+
EmbedEvent.CrossFilterChanged,
|
|
362
|
+
EmbedEvent.VizPointRightClick,
|
|
363
|
+
EmbedEvent.InsertIntoSlide,
|
|
364
|
+
EmbedEvent.FilterChanged,
|
|
365
|
+
EmbedEvent.UpdateConnection,
|
|
366
|
+
EmbedEvent.CreateConnection,
|
|
367
|
+
EmbedEvent.ResetLiveboard,
|
|
368
|
+
EmbedEvent.ChangePersonalizedView,
|
|
369
|
+
EmbedEvent.CreateWorksheet,
|
|
370
|
+
EmbedEvent.AskSageInit,
|
|
371
|
+
EmbedEvent.Rename,
|
|
372
|
+
EmbedEvent.ParameterChanged,
|
|
373
|
+
EmbedEvent.TableVizRendered,
|
|
374
|
+
EmbedEvent.CreateLiveboard,
|
|
375
|
+
EmbedEvent.CreateModel,
|
|
376
|
+
EmbedEvent.SpotterData,
|
|
377
|
+
EmbedEvent.PreviewSpotterData,
|
|
378
|
+
EmbedEvent.AddToCoaching,
|
|
379
|
+
EmbedEvent.DataModelInstructions,
|
|
380
|
+
EmbedEvent.SpotterQueryTriggered,
|
|
381
|
+
EmbedEvent.LastPromptEdited,
|
|
382
|
+
EmbedEvent.LastPromptDeleted,
|
|
383
|
+
EmbedEvent.ResetSpotterConversation,
|
|
384
|
+
EmbedEvent.SpotterInit,
|
|
385
|
+
EmbedEvent.SpotterLoadComplete,
|
|
386
|
+
EmbedEvent.OrgSwitched,
|
|
387
|
+
EmbedEvent.SpotterConversationRenamed,
|
|
388
|
+
EmbedEvent.SpotterConversationDeleted,
|
|
389
|
+
EmbedEvent.SpotterConversationSelected,
|
|
390
|
+
EmbedEvent.EmbedPageContextChanged,
|
|
391
|
+
EmbedEvent.Subscribed,
|
|
392
|
+
EmbedEvent.SendTestScheduleEmail,
|
|
393
|
+
EmbedEvent.SpotterVizInit,
|
|
394
|
+
EmbedEvent.SpotterVizQueryTriggered,
|
|
395
|
+
EmbedEvent.SpotterVizResponseComplete,
|
|
396
|
+
EmbedEvent.SpotterVizCheckpointCreated,
|
|
397
|
+
EmbedEvent.SpotterVizCheckpointRestored,
|
|
398
|
+
EmbedEvent.SpotterVizError,
|
|
399
|
+
EmbedEvent.SpotterVizClosed,
|
|
400
|
+
EmbedEvent.RefreshLiveboardBrowserCache,
|
|
401
|
+
EmbedEvent.V1Data,
|
|
402
|
+
];
|
|
403
|
+
events.forEach((eventType) => {
|
|
404
|
+
const e = { type: eventType, data: 'test-payload' };
|
|
405
|
+
expect(
|
|
406
|
+
processDataInstance.processEventData(eventType, e, thoughtSpotHost, null),
|
|
407
|
+
).toEqual(e);
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
282
411
|
test('should handle ClearInfoCache', () => {
|
|
283
412
|
const mockResetCachedPreauthInfo = jest.spyOn(sessionInfoService, 'resetCachedPreauthInfo').mockImplementation(() => {});
|
|
284
413
|
const mockResetCachedSessionInfo = jest.spyOn(sessionInfoService, 'resetCachedSessionInfo').mockImplementation(() => {});
|
|
@@ -216,5 +216,96 @@ describe('Unit test for processTrigger', () => {
|
|
|
216
216
|
'Fullscreen presentation mode is disabled. Set disableFullscreenPresentation: false to enable this feature.',
|
|
217
217
|
);
|
|
218
218
|
});
|
|
219
|
+
|
|
220
|
+
test('postMessage is called with type=present when disableFullscreenPresentation is false', () => {
|
|
221
|
+
mockGetEmbedConfig.mockReturnValue({ disableFullscreenPresentation: false });
|
|
222
|
+
const thoughtSpotHost = 'https://example.thoughtspot.com';
|
|
223
|
+
_processTriggerInstance.processTrigger(iFrame, HostEvent.Present, thoughtSpotHost, {});
|
|
224
|
+
expect(iFrame.contentWindow.postMessage).toHaveBeenCalledWith(
|
|
225
|
+
expect.objectContaining({ type: HostEvent.Present }),
|
|
226
|
+
thoughtSpotHost,
|
|
227
|
+
expect.anything(),
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('postMessage is still called with type=present when disableFullscreenPresentation is true', () => {
|
|
232
|
+
mockGetEmbedConfig.mockReturnValue({ disableFullscreenPresentation: true });
|
|
233
|
+
const thoughtSpotHost = 'https://example.thoughtspot.com';
|
|
234
|
+
_processTriggerInstance.processTrigger(iFrame, HostEvent.Present, thoughtSpotHost, {});
|
|
235
|
+
expect(iFrame.contentWindow.postMessage).toHaveBeenCalledWith(
|
|
236
|
+
expect.objectContaining({ type: HostEvent.Present }),
|
|
237
|
+
thoughtSpotHost,
|
|
238
|
+
expect.anything(),
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('handlePresentEvent receives the exact iFrame reference passed to processTrigger', () => {
|
|
243
|
+
mockGetEmbedConfig.mockReturnValue({ disableFullscreenPresentation: false });
|
|
244
|
+
_processTriggerInstance.processTrigger(
|
|
245
|
+
iFrame, HostEvent.Present, 'https://example.thoughtspot.com', {},
|
|
246
|
+
);
|
|
247
|
+
expect(mockHandlePresentEvent).toHaveBeenCalledWith(iFrame);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test('nested responseData.data.error causes promise rejection', async () => {
|
|
252
|
+
const messageType = HostEvent.Search;
|
|
253
|
+
const thoughtSpotHost = 'http://localhost:3000';
|
|
254
|
+
mockMessageChannel();
|
|
255
|
+
const triggerPromise = _processTriggerInstance.processTrigger(
|
|
256
|
+
iFrame, messageType, thoughtSpotHost, {},
|
|
257
|
+
);
|
|
258
|
+
messageChannelMock.port1.onmessage({ data: { data: { error: 'nested-error' } } });
|
|
259
|
+
await expect(triggerPromise).rejects.toEqual('nested-error');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test('context parameter is forwarded as context field in the postMessage payload', async () => {
|
|
263
|
+
const messageType = HostEvent.Search;
|
|
264
|
+
const thoughtSpotHost = 'http://localhost:3000';
|
|
265
|
+
const context = 'Liveboard' as any;
|
|
266
|
+
mockMessageChannel();
|
|
267
|
+
_processTriggerInstance.processTrigger(iFrame, messageType, thoughtSpotHost, {}, context);
|
|
268
|
+
expect(iFrame.contentWindow.postMessage).toHaveBeenCalledWith(
|
|
269
|
+
expect.objectContaining({ context }),
|
|
270
|
+
thoughtSpotHost,
|
|
271
|
+
expect.anything(),
|
|
272
|
+
);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe('Reload timing and side-effects', () => {
|
|
276
|
+
test('src is cleared immediately but not restored until exactly 100ms', () => {
|
|
277
|
+
jest.useFakeTimers();
|
|
278
|
+
const iFrameElement = document.createElement('iframe');
|
|
279
|
+
iFrameElement.src = 'http://localhost:3000';
|
|
280
|
+
const originalSrc = iFrameElement.src; // normalized by jsdom
|
|
281
|
+
|
|
282
|
+
_processTriggerInstance.reload(iFrameElement);
|
|
283
|
+
|
|
284
|
+
expect(iFrameElement.src).not.toBe(originalSrc); // cleared
|
|
285
|
+
jest.advanceTimersByTime(99);
|
|
286
|
+
expect(iFrameElement.src).not.toBe(originalSrc); // not yet restored
|
|
287
|
+
jest.advanceTimersByTime(1);
|
|
288
|
+
expect(iFrameElement.src).toBe(originalSrc); // restored at 100ms
|
|
289
|
+
jest.useRealTimers();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test('processTrigger(Reload) does not call iFrame.contentWindow.postMessage', () => {
|
|
293
|
+
jest.useFakeTimers();
|
|
294
|
+
// Use the outer iFrame mock which has a mockable contentWindow
|
|
295
|
+
jest.clearAllMocks();
|
|
296
|
+
_processTriggerInstance.processTrigger(
|
|
297
|
+
iFrame, HostEvent.Reload, 'http://localhost:3000', {},
|
|
298
|
+
);
|
|
299
|
+
jest.advanceTimersByTime(200);
|
|
300
|
+
expect(iFrame.contentWindow.postMessage).not.toHaveBeenCalled();
|
|
301
|
+
jest.useRealTimers();
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test('processTrigger(Reload) resolves with null', async () => {
|
|
305
|
+
const result = await _processTriggerInstance.processTrigger(
|
|
306
|
+
iFrame, HostEvent.Reload, 'http://localhost:3000', {},
|
|
307
|
+
);
|
|
308
|
+
expect(result).toBeNull();
|
|
309
|
+
});
|
|
219
310
|
});
|
|
220
311
|
});
|
|
@@ -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
|
+
});
|
package/src/utils.spec.ts
CHANGED
|
@@ -27,6 +27,13 @@ import {
|
|
|
27
27
|
validateHttpUrl,
|
|
28
28
|
setParamIfDefined,
|
|
29
29
|
querySelectorAcrossShadowRoot,
|
|
30
|
+
getSSOMarker,
|
|
31
|
+
deepMerge,
|
|
32
|
+
getHostEventsConfig,
|
|
33
|
+
isWindowUndefined,
|
|
34
|
+
getOffsetTop,
|
|
35
|
+
getDOMNode,
|
|
36
|
+
getOperationNameFromQuery,
|
|
30
37
|
} from './utils';
|
|
31
38
|
import { RuntimeFilterOp } from './types';
|
|
32
39
|
import { logger } from './utils/logger';
|
|
@@ -388,6 +395,18 @@ describe('Fullscreen Utility Functions', () => {
|
|
|
388
395
|
expect(logger.error).toHaveBeenCalledWith('Fullscreen API is not supported by this browser.');
|
|
389
396
|
});
|
|
390
397
|
|
|
398
|
+
it('should catch error and log warning when requestFullscreen throws (covers line 506)', async () => {
|
|
399
|
+
// Covers line 506: catch block inside fullscreen method attempt
|
|
400
|
+
(mockIframe.requestFullscreen as jest.Mock).mockRejectedValue(new Error('permission denied'));
|
|
401
|
+
|
|
402
|
+
await handlePresentEvent(mockIframe);
|
|
403
|
+
|
|
404
|
+
expect(logger.warn).toHaveBeenCalledWith(
|
|
405
|
+
expect.stringContaining('Failed to enter fullscreen'),
|
|
406
|
+
expect.any(Error),
|
|
407
|
+
);
|
|
408
|
+
});
|
|
409
|
+
|
|
391
410
|
it('should not attempt fullscreen when already in fullscreen mode', () => {
|
|
392
411
|
Object.defineProperty(document, 'fullscreenElement', {
|
|
393
412
|
writable: true,
|
|
@@ -1009,6 +1028,24 @@ describe('isValidCssMargin', () => {
|
|
|
1009
1028
|
expect(isValidCssMargin('invalid')).toBe(false);
|
|
1010
1029
|
expect(isValidCssMargin('10')).toBe(false); // missing unit
|
|
1011
1030
|
});
|
|
1031
|
+
|
|
1032
|
+
it('should return false and log error when value is not a string (non-string type)', () => {
|
|
1033
|
+
// Covers line 147-148: typeof value !== 'string' branch
|
|
1034
|
+
expect(isValidCssMargin(42 as any)).toBe(false);
|
|
1035
|
+
expect(logger.error).toHaveBeenCalledWith('Please provide a valid lazyLoadingMargin value (e.g., "10px")');
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
it('should return false when more than 4 space-separated parts are given', () => {
|
|
1039
|
+
// Covers line 157-158: parts.length > 4 branch
|
|
1040
|
+
expect(isValidCssMargin('10px 20px 30px 40px 50px')).toBe(false);
|
|
1041
|
+
expect(logger.error).toHaveBeenCalledWith('Please provide a valid lazyLoadingMargin value (e.g., "10px")');
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
it('should return true for valid multi-part margin (up to 4 parts)', () => {
|
|
1045
|
+
expect(isValidCssMargin('10px 20px')).toBe(true);
|
|
1046
|
+
expect(isValidCssMargin('10px 20px 30px 40px')).toBe(true);
|
|
1047
|
+
expect(isValidCssMargin('auto')).toBe(true);
|
|
1048
|
+
});
|
|
1012
1049
|
});
|
|
1013
1050
|
|
|
1014
1051
|
describe('getValueFromWindow and storeValueInWindow', () => {
|
|
@@ -1140,3 +1177,161 @@ describe('getValueFromWindow and storeValueInWindow', () => {
|
|
|
1140
1177
|
});
|
|
1141
1178
|
});
|
|
1142
1179
|
});
|
|
1180
|
+
|
|
1181
|
+
// ---------------------------------------------------------------------------
|
|
1182
|
+
// getSSOMarker
|
|
1183
|
+
// ---------------------------------------------------------------------------
|
|
1184
|
+
describe('getSSOMarker', () => {
|
|
1185
|
+
test('returns tsSSOMarker query string with encoded markerId', () => {
|
|
1186
|
+
expect(getSSOMarker('abc123')).toBe('tsSSOMarker=abc123');
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
test('URL-encodes special characters in markerId', () => {
|
|
1190
|
+
expect(getSSOMarker('hello world')).toBe('tsSSOMarker=hello%20world');
|
|
1191
|
+
expect(getSSOMarker('foo=bar&baz')).toBe('tsSSOMarker=foo%3Dbar%26baz');
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
test('handles empty string markerId', () => {
|
|
1195
|
+
expect(getSSOMarker('')).toBe('tsSSOMarker=');
|
|
1196
|
+
});
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
// ---------------------------------------------------------------------------
|
|
1200
|
+
// deepMerge
|
|
1201
|
+
// ---------------------------------------------------------------------------
|
|
1202
|
+
describe('deepMerge', () => {
|
|
1203
|
+
test('merges two flat objects', () => {
|
|
1204
|
+
const result = deepMerge({ a: 1 }, { b: 2 });
|
|
1205
|
+
expect(result).toEqual({ a: 1, b: 2 });
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
test('source overrides target for same keys', () => {
|
|
1209
|
+
const result = deepMerge({ a: 1, b: 2 }, { b: 99 });
|
|
1210
|
+
expect(result).toEqual({ a: 1, b: 99 });
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
test('deep merges nested objects', () => {
|
|
1214
|
+
const result = deepMerge({ a: { x: 1, y: 2 } }, { a: { y: 99, z: 3 } });
|
|
1215
|
+
expect(result).toEqual({ a: { x: 1, y: 99, z: 3 } });
|
|
1216
|
+
});
|
|
1217
|
+
|
|
1218
|
+
test('handles empty source', () => {
|
|
1219
|
+
const result = deepMerge({ a: 1 }, {});
|
|
1220
|
+
expect(result).toEqual({ a: 1 });
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
test('handles empty target', () => {
|
|
1224
|
+
const result = deepMerge({}, { b: 2 });
|
|
1225
|
+
expect(result).toEqual({ b: 2 });
|
|
1226
|
+
});
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
// ---------------------------------------------------------------------------
|
|
1230
|
+
// getHostEventsConfig
|
|
1231
|
+
// ---------------------------------------------------------------------------
|
|
1232
|
+
describe('getHostEventsConfig', () => {
|
|
1233
|
+
test('returns shouldBypassPayloadValidation and useHostEventsV2 from viewConfig', () => {
|
|
1234
|
+
const viewConfig: any = { shouldBypassPayloadValidation: true, useHostEventsV2: false };
|
|
1235
|
+
expect(getHostEventsConfig(viewConfig)).toEqual({
|
|
1236
|
+
shouldBypassPayloadValidation: true,
|
|
1237
|
+
useHostEventsV2: false,
|
|
1238
|
+
});
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
test('returns undefined for missing keys', () => {
|
|
1242
|
+
const result = getHostEventsConfig({} as any);
|
|
1243
|
+
expect(result.shouldBypassPayloadValidation).toBeUndefined();
|
|
1244
|
+
expect(result.useHostEventsV2).toBeUndefined();
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
test('ignores unrelated config properties', () => {
|
|
1248
|
+
const viewConfig: any = {
|
|
1249
|
+
shouldBypassPayloadValidation: false,
|
|
1250
|
+
useHostEventsV2: true,
|
|
1251
|
+
liveboardId: 'lb-1',
|
|
1252
|
+
};
|
|
1253
|
+
expect(getHostEventsConfig(viewConfig)).toEqual({
|
|
1254
|
+
shouldBypassPayloadValidation: false,
|
|
1255
|
+
useHostEventsV2: true,
|
|
1256
|
+
});
|
|
1257
|
+
});
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
// ---------------------------------------------------------------------------
|
|
1261
|
+
// isWindowUndefined
|
|
1262
|
+
// ---------------------------------------------------------------------------
|
|
1263
|
+
describe('isWindowUndefined', () => {
|
|
1264
|
+
test('returns false when window is defined (browser/jsdom env)', () => {
|
|
1265
|
+
// In jest/jsdom, window is always defined
|
|
1266
|
+
expect(isWindowUndefined()).toBe(false);
|
|
1267
|
+
});
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
// ---------------------------------------------------------------------------
|
|
1271
|
+
// getOffsetTop
|
|
1272
|
+
// ---------------------------------------------------------------------------
|
|
1273
|
+
describe('getOffsetTop', () => {
|
|
1274
|
+
test('returns rect.top + window.scrollY', () => {
|
|
1275
|
+
const mockElement = {
|
|
1276
|
+
getBoundingClientRect: () => ({ top: 50 }),
|
|
1277
|
+
};
|
|
1278
|
+
Object.defineProperty(window, 'scrollY', { value: 100, configurable: true });
|
|
1279
|
+
expect(getOffsetTop(mockElement)).toBe(150);
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
test('returns rect.top when scrollY is 0', () => {
|
|
1283
|
+
const mockElement = {
|
|
1284
|
+
getBoundingClientRect: () => ({ top: 30 }),
|
|
1285
|
+
};
|
|
1286
|
+
Object.defineProperty(window, 'scrollY', { value: 0, configurable: true });
|
|
1287
|
+
expect(getOffsetTop(mockElement)).toBe(30);
|
|
1288
|
+
});
|
|
1289
|
+
});
|
|
1290
|
+
|
|
1291
|
+
// ---------------------------------------------------------------------------
|
|
1292
|
+
// getDOMNode
|
|
1293
|
+
// ---------------------------------------------------------------------------
|
|
1294
|
+
describe('getDOMNode', () => {
|
|
1295
|
+
test('returns the element from document.querySelector when passed a string selector', () => {
|
|
1296
|
+
const div = document.createElement('div');
|
|
1297
|
+
div.id = 'test-dom-node';
|
|
1298
|
+
document.body.appendChild(div);
|
|
1299
|
+
expect(getDOMNode('#test-dom-node')).toBe(div);
|
|
1300
|
+
document.body.removeChild(div);
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
test('returns the element directly when passed an HTMLElement', () => {
|
|
1304
|
+
const div = document.createElement('div');
|
|
1305
|
+
expect(getDOMNode(div)).toBe(div);
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
test('returns null for a selector that matches nothing', () => {
|
|
1309
|
+
expect(getDOMNode('#nonexistent-element-xyz')).toBeNull();
|
|
1310
|
+
});
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
// ---------------------------------------------------------------------------
|
|
1314
|
+
// getOperationNameFromQuery
|
|
1315
|
+
// ---------------------------------------------------------------------------
|
|
1316
|
+
describe('getOperationNameFromQuery', () => {
|
|
1317
|
+
test('extracts operation name from a query string', () => {
|
|
1318
|
+
expect(getOperationNameFromQuery('query GetUser { user { id } }')).toBe('GetUser');
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
test('extracts operation name from a mutation string', () => {
|
|
1322
|
+
expect(getOperationNameFromQuery('mutation CreateUser { createUser { id } }')).toBe('CreateUser');
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
test('returns undefined when no operation name is present', () => {
|
|
1326
|
+
expect(getOperationNameFromQuery('{ user { id } }')).toBeUndefined();
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1329
|
+
test('handles multiline query strings', () => {
|
|
1330
|
+
const query = `
|
|
1331
|
+
query FetchData {
|
|
1332
|
+
data { id }
|
|
1333
|
+
}
|
|
1334
|
+
`;
|
|
1335
|
+
expect(getOperationNameFromQuery(query)).toBe('FetchData');
|
|
1336
|
+
});
|
|
1337
|
+
});
|