@thoughtspot/visual-embed-sdk
Version:
ThoughtSpot Embed SDK
449 lines • 18.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const auth = tslib_1.__importStar(require("../auth"));
const authService = tslib_1.__importStar(require("../utils/authService/authService"));
const tokenAuthServices = tslib_1.__importStar(require("../utils/authService/tokenizedAuthService"));
const authTokenService = tslib_1.__importStar(require("../authToken"));
const index = tslib_1.__importStar(require("../index"));
const base = tslib_1.__importStar(require("./base"));
const embedConfigInstance = tslib_1.__importStar(require("./embedConfig"));
const resetService = tslib_1.__importStar(require("../utils/resetServices"));
const test_utils_1 = require("../test/test-utils");
const tokenizedFetchInstance = tslib_1.__importStar(require("../tokenizedFetch"));
const logger_1 = require("../utils/logger");
const thoughtSpotHost = 'tshost';
let authEE;
describe('Base TS Embed', () => {
beforeAll(() => {
authEE = index.init({
thoughtSpotHost,
authType: index.AuthType.None,
});
jest.spyOn(auth, 'postLoginService').mockImplementation(() => Promise.resolve({}));
});
beforeEach(() => {
document.body.innerHTML = (0, test_utils_1.getDocumentBody)();
});
test('Should show an alert when third party cookie access is blocked', (done) => {
const tsEmbed = new index.SearchEmbed((0, test_utils_1.getRootEl)(), {});
const iFrame = document.createElement('div');
iFrame.contentWindow = null;
/* This will return a div instead of HTMLIframeElement in ts-embed.ts
* so that the promise doesn't fail on url assigment
*/
jest.spyOn(document, 'createElement').mockReturnValueOnce(iFrame);
tsEmbed.render();
window.postMessage({
__type: index.EmbedEvent.NoCookieAccess,
}, '*');
jest.spyOn(window, 'alert').mockReset();
jest.spyOn(window, 'alert').mockImplementation(() => undefined);
authEE.on(auth.AuthStatus.FAILURE, (reason) => {
expect(reason).toEqual(auth.AuthFailureType.NO_COOKIE_ACCESS);
expect(window.alert).toBeCalledWith('Third-party cookie access is blocked on this browser. Please allow third-party cookies for this to work properly. \nYou can use `suppressNoCookieAccessAlert` to suppress this message.');
done();
});
});
test('Should ignore cookie blocked alert if ignoreNoCookieAccess is true', async (done) => {
jest.spyOn(window, 'fetch').mockResolvedValue({
ok: true,
json: jest.fn().mockResolvedValue({}),
});
const authEE = index.init({
thoughtSpotHost,
authType: index.AuthType.None,
ignoreNoCookieAccess: true,
});
const tsEmbed = new index.SearchEmbed((0, test_utils_1.getRootEl)(), {});
const iFrame = document.createElement('div');
iFrame.contentWindow = null;
/* This will return a div instead of HTMLIframeElement in ts-embed.ts
* so that the promise doesn't fail on url assigment
*/
jest.spyOn(document, 'createElement').mockReturnValueOnce(iFrame);
tsEmbed.render();
window.postMessage({
__type: index.EmbedEvent.NoCookieAccess,
}, '*');
jest.spyOn(window, 'alert').mockReset();
jest.spyOn(window, 'alert').mockImplementation(() => undefined);
authEE.on(auth.AuthStatus.FAILURE, (reason) => {
expect(reason).toEqual(auth.AuthFailureType.NO_COOKIE_ACCESS);
expect(window.alert).not.toHaveBeenCalled();
done();
});
});
test('should call the executeTML API and import TML', async () => {
jest.spyOn(window, 'fetch').mockResolvedValue({
ok: true,
json: jest.fn().mockResolvedValue({}),
});
index.init({
thoughtSpotHost,
authType: index.AuthType.None,
autoLogin: true,
});
const data = {
metadata_tmls: ['{"liveboard":{"name":"Parameters Liveboard"}}'],
import_policy: 'PARTIAL',
create_new: false,
};
await index.executeTML(data);
expect(window.fetch).toHaveBeenCalledWith(`http://${thoughtSpotHost}${authService.EndPoints.EXECUTE_TML}`, {
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'x-requested-by': 'ThoughtSpot',
},
body: JSON.stringify(data),
method: 'POST',
});
});
test('should call the executeTML API and import TML for cookiless auth', async () => {
jest.spyOn(authTokenService, 'getAuthenticationToken').mockResolvedValue('mockAuthToken');
jest.spyOn(tokenizedFetchInstance, 'tokenizedFetch').mockResolvedValueOnce({
ok: true,
json: jest.fn().mockResolvedValue({}),
});
index.init({
thoughtSpotHost,
authType: index.AuthType.TrustedAuthTokenCookieless,
autoLogin: true,
});
const data = {
metadata_tmls: ['{"liveboard":{"name":"Parameters Liveboard"}}'],
import_policy: 'PARTIAL',
create_new: false,
};
await index.executeTML(data);
expect(tokenizedFetchInstance.tokenizedFetch).toHaveBeenCalledWith(`http://${thoughtSpotHost}${authService.EndPoints.EXECUTE_TML}`, {
credentials: 'include',
headers: expect.objectContaining({
'Content-Type': 'application/json',
'x-requested-by': 'ThoughtSpot',
}),
body: JSON.stringify(data),
method: 'POST',
});
});
test('should log an error when executing TML fails', async () => {
jest.spyOn(window, 'fetch').mockRejectedValue(new Error('Network error'));
index.init({
thoughtSpotHost,
authType: index.AuthType.None,
autoLogin: true,
});
const data = {
metadata_tmls: ['{"liveboard":{"name":"Parameters Liveboard"}}'],
import_policy: 'PARTIAL',
create_new: false,
};
try {
await index.executeTML(data);
}
catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error.message).toBe('Network error');
}
});
test('should reject with an error when sanity check fails', async () => {
const error = new Error('ThoughtSpot host not provided');
const data = {
metadata_tmls: ['{"liveboard":{"name":"Parameters Liveboard"}}'],
import_policy: 'PARTIAL',
create_new: false,
};
base.reset();
try {
await index.executeTML(data);
}
catch (err) {
expect(err).toEqual(error);
}
});
test('should call the exportTML API and export TML', async () => {
jest.spyOn(tokenizedFetchInstance, 'tokenizedFetch').mockResolvedValueOnce({
ok: true,
json: jest.fn().mockResolvedValue({}),
});
index.init({
thoughtSpotHost,
authType: index.AuthType.None,
autoLogin: true,
});
const data = {
metadata: [{ identifier: 'f5728369-cf02-4953-87ab-a6cac691e360' }],
export_associated: false,
export_fqn: false,
edoc_format: 'YAML',
};
await index.exportTML(data);
expect(tokenizedFetchInstance.tokenizedFetch).toHaveBeenCalledWith(`http://${thoughtSpotHost}${authService.EndPoints.EXPORT_TML}`, {
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'x-requested-by': 'ThoughtSpot',
},
body: JSON.stringify(data),
method: 'POST',
});
});
test('should log an error when exeporting TML fails', async () => {
jest.spyOn(window, 'fetch').mockRejectedValue(new Error('Network error'));
index.init({
thoughtSpotHost,
authType: index.AuthType.None,
autoLogin: true,
});
const data = {
metadata: [{ identifier: 'f5728369-cf02-4953-87ab-a6cac691e360' }],
export_associated: false,
export_fqn: false,
edoc_format: 'YAML',
};
try {
await index.exportTML(data);
}
catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error.message).toBe('Network error');
}
});
test('Should add the prefetch iframe when prefetch is called. Should remove it once init is called.', async () => {
const url = 'https://10.87.90.95/?embedApp=true';
index.init({
thoughtSpotHost: url,
authType: index.AuthType.None,
callPrefetch: true,
});
expect((0, test_utils_1.getAllIframeEl)().length).toBe(1);
const prefetchIframe = document.querySelectorAll('.prefetchIframe');
expect(prefetchIframe.length).toBe(1);
const firstIframe = prefetchIframe[0];
expect(firstIframe.src).toBe(url);
expect(firstIframe.style.width).toBe('0px');
expect(firstIframe.classList.contains('prefetchIframeNum-0')).toBe(true);
});
test('Should add the prefetch iframe when prefetch is called with multiple options', async () => {
const url = 'https://10.87.90.95/';
const searchUrl = `${url}v2/?embedApp=true#/embed/answer`;
const liveboardUrl = `${url}?embedApp=true`;
index.prefetch(url, [
index.PrefetchFeatures.SearchEmbed,
index.PrefetchFeatures.LiveboardEmbed,
]);
expect((0, test_utils_1.getAllIframeEl)().length).toBe(2);
const prefetchIframe = document.querySelectorAll('.prefetchIframe');
expect(prefetchIframe.length).toBe(2);
const firstIframe = prefetchIframe[0];
expect(firstIframe.src).toBe(searchUrl);
const secondIframe = prefetchIframe[1];
expect(secondIframe.src).toBe(liveboardUrl);
});
test('Should add the prefetch iframe with additionalFlags', async () => {
const url = 'https://10.87.90.95/';
const searchUrl = `${url}v2/?embedApp=true&flag2=bool&flag3=block&flag1=true#/embed/answer`;
const liveboardUrl = `${url}?embedApp=true&flag2=bool&flag3=block&flag1=true`;
base.init({
thoughtSpotHost: url,
authType: index.AuthType.None,
additionalFlags: {
flag2: 'bar',
flag3: 'block',
},
});
index.prefetch(url, [
index.PrefetchFeatures.SearchEmbed,
index.PrefetchFeatures.LiveboardEmbed,
], { flag1: true, flag2: 'bool' });
expect((0, test_utils_1.getAllIframeEl)().length).toBe(2);
const prefetchIframe = document.querySelectorAll('.prefetchIframe');
expect(prefetchIframe.length).toBe(2);
const firstIframe = prefetchIframe[0];
expect(firstIframe.src).toBe(searchUrl);
const secondIframe = prefetchIframe[1];
expect(secondIframe.src).toBe(liveboardUrl);
});
test('Should add the prefetch iframe with additionalFlags for prefetch from init', async () => {
const url = 'https://10.87.90.95/';
const prefetchUrl = `${url}?embedApp=true&flag2=bar&flag3=block`;
base.init({
thoughtSpotHost: url,
authType: index.AuthType.None,
additionalFlags: {
flag2: 'bar',
flag3: 'block',
},
callPrefetch: true,
});
expect((0, test_utils_1.getAllIframeEl)().length).toBe(1);
const prefetchIframe = document.querySelectorAll('.prefetchIframe');
expect(prefetchIframe.length).toBe(1);
const firstIframe = prefetchIframe[0];
expect(firstIframe.src).toBe(prefetchUrl);
});
test('Should not generate a prefetch iframe when url is empty string', async () => {
const url = '';
index.prefetch(url);
expect((0, test_utils_1.getAllIframeEl)().length).toBe(0);
const prefetchIframe = document.querySelectorAll('.prefetchIframe');
expect(prefetchIframe.length).toBe(0);
});
test('Should not call prefetch inside init when callPrefetch is set to false', async () => {
const prefetch = jest.spyOn(index, 'prefetch');
index.init({
thoughtSpotHost,
authType: index.AuthType.None,
callPrefetch: false,
});
expect(prefetch).toHaveBeenCalledTimes(0);
});
test('Sets the disableLoginRedirect param when autoLogin is true', async () => {
index.init({
thoughtSpotHost,
authType: index.AuthType.None,
autoLogin: true,
});
const tsEmbed = new index.AppEmbed((0, test_utils_1.getRootEl)(), {});
await tsEmbed.render();
await (0, test_utils_1.executeAfterWait)(() => {
expect((0, test_utils_1.getIFrameSrc)()).toContain('disableLoginRedirect=true');
});
});
test('handleAuth notifies for SDK auth failure', (done) => {
jest.spyOn(auth, 'authenticate').mockResolvedValue(false);
const authEmitter = index.init({
thoughtSpotHost,
authType: index.AuthType.Basic,
username: 'test',
password: 'test',
});
authEmitter.on(auth.AuthStatus.FAILURE, (reason) => {
expect(reason).toBe(auth.AuthFailureType.SDK);
done();
});
});
test('handleAuth notifies for SDK auth success', (done) => {
jest.spyOn(auth, 'authenticate').mockResolvedValue(true);
const failureCallback = jest.fn();
const authEmitter = index.init({
thoughtSpotHost,
authType: index.AuthType.Basic,
username: 'test',
password: 'test',
});
authEmitter.on(auth.AuthStatus.FAILURE, failureCallback);
authEmitter.on(auth.AuthStatus.SDK_SUCCESS, (...args) => {
expect(failureCallback).not.toBeCalled();
expect(args.length).toBe(0);
done();
});
});
test('Logout method should disable autoLogin', () => {
jest.spyOn(window, 'fetch').mockResolvedValueOnce({
type: 'opaque',
});
index.init({
thoughtSpotHost,
authType: index.AuthType.None,
autoLogin: true,
});
index.logout();
expect(window.fetch).toHaveBeenCalledWith(`http://${thoughtSpotHost}${authService.EndPoints.LOGOUT}`, {
credentials: 'include',
headers: {
'x-requested-by': 'ThoughtSpot',
},
method: 'POST',
});
expect(embedConfigInstance.getEmbedConfig().autoLogin).toBe(false);
});
test('Logout method should reset caches', async () => {
jest.spyOn(tokenAuthServices, 'fetchLogoutService').mockResolvedValueOnce({});
jest.spyOn(resetService, 'resetAllCachedServices');
index.init({
thoughtSpotHost,
authType: index.AuthType.None,
autoLogin: true,
});
expect(resetService.resetAllCachedServices).toHaveBeenCalledTimes(1);
await index.logout();
expect(resetService.resetAllCachedServices).toHaveBeenCalledTimes(2);
});
test('config sanity, no ts host', () => {
expect(() => {
index.init({
authType: index.AuthType.None,
});
}).toThrowError();
});
test('config sanity, no username in trusted auth', () => {
expect(() => {
index.init({
authType: index.AuthType.TrustedAuthToken,
thoughtSpotHost,
});
}).toThrowError();
});
test('config sanity, no authEndpoint and getAuthToken', () => {
expect(() => {
index.init({
authType: index.AuthType.TrustedAuthToken,
thoughtSpotHost,
username: 'test',
});
}).toThrowError();
});
test('config backward compat, should assign inPopup when noRedirect is set', () => {
index.init({
authType: index.AuthType.None,
thoughtSpotHost,
noRedirect: true,
});
expect(embedConfigInstance.getEmbedConfig().inPopup).toBe(true);
});
test('config backward compat, should not override inPopup with noRedirect', () => {
index.init({
authType: index.AuthType.None,
thoughtSpotHost,
noRedirect: true,
inPopup: false,
});
expect(embedConfigInstance.getEmbedConfig().inPopup).toBe(false);
});
test('@P0 @SCAL-226935 embedConfig should contain correct value of customCSSUrl when added in init ', async () => {
index.init({
thoughtSpotHost,
authType: index.AuthType.None,
customizations: {
style: {
customCSSUrl: 'test.com',
},
},
});
expect(embedConfigInstance.getEmbedConfig().customizations.style.customCSSUrl).toEqual('test.com');
});
});
describe('Base without init', () => {
test('notify should error when called without init', () => {
base.reset();
jest.spyOn(logger_1.logger, 'error').mockImplementation(() => undefined);
base.notifyAuthSuccess();
base.notifyAuthFailure(auth.AuthFailureType.SDK);
base.notifyLogout();
base.notifyAuthSDKSuccess();
expect(logger_1.logger.error).toHaveBeenCalledTimes(4);
});
});
describe('Init tests', () => {
test('clear caches on init', () => {
jest.spyOn(resetService, 'resetAllCachedServices');
base.init({
thoughtSpotHost,
authType: index.AuthType.None,
});
expect(resetService.resetAllCachedServices).toBeCalled();
});
});
//# sourceMappingURL=base.spec.js.map