UNPKG

@thoughtspot/visual-embed-sdk

Version:
577 lines 23.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("./utils"); const types_1 = require("./types"); const logger_1 = require("./utils/logger"); // Mock logger jest.mock('./utils/logger', () => ({ logger: { warn: jest.fn(), error: jest.fn(), }, })); describe('unit test for utils', () => { test('getQueryParamString', () => { expect((0, utils_1.getQueryParamString)({ foo: 'bar', baz: '42', })).toBe('foo=bar&baz=42'); expect((0, utils_1.getQueryParamString)({})).toBe(null); // should not add undefined params expect((0, utils_1.getQueryParamString)({ foo: undefined, bar: 'baz', })).toBe('bar=baz'); }); test('getFilterQuery should encode URL params', () => { expect((0, utils_1.getFilterQuery)([])).toBe(null); expect((0, utils_1.getFilterQuery)([ { columnName: 'foo+foo', operator: types_1.RuntimeFilterOp.NE, values: ['bar+'], }, ])).toBe('col1=foo%2Bfoo&op1=NE&val1=bar%2B'); }); test('getFilterQuery', () => { expect((0, utils_1.getFilterQuery)([])).toBe(null); expect((0, utils_1.getFilterQuery)([ { columnName: 'foo', operator: types_1.RuntimeFilterOp.NE, values: ['bar'], }, ])).toBe('col1=foo&op1=NE&val1=bar'); const filters = [ { columnName: 'foo', operator: types_1.RuntimeFilterOp.EQ, values: [42], }, { columnName: 'bar', operator: types_1.RuntimeFilterOp.BW_INC, values: [1, 10], }, { columnName: 'baz', operator: types_1.RuntimeFilterOp.CONTAINS, values: ['abc'], }, ]; expect((0, utils_1.getFilterQuery)(filters)).toBe('col1=foo&op1=EQ&val1=42&col2=bar&op2=BW_INC&val2=1&val2=10&col3=baz&op3=CONTAINS&val3=abc'); }); test('getParameterOverride', () => { expect((0, utils_1.getRuntimeParameters)([])).toBe(null); expect((0, utils_1.getRuntimeParameters)([ { name: 'foo', value: 'bar', }, ])).toBe('param1=foo&paramVal1=bar'); const params = [ { name: 'foo', value: 42, }, { name: 'bar', value: 'abc', }, { name: 'baz', value: true, }, ]; expect((0, utils_1.getRuntimeParameters)(params)).toBe('param1=foo&paramVal1=42&param2=bar&paramVal2=abc&param3=baz&paramVal3=true'); }); test('getCssDimension', () => { expect((0, utils_1.getCssDimension)(100)).toBe('100px'); expect((0, utils_1.getCssDimension)('100%')).toBe('100%'); expect((0, utils_1.getCssDimension)('100px')).toBe('100px'); expect((0, utils_1.getCssDimension)(null)).toBe(null); }); test('appendToUrlHash', () => { expect((0, utils_1.appendToUrlHash)('http://myhost:3000', 'hashFrag')).toBe('http://myhost:3000#?tsSSOMarker=hashFrag'); expect((0, utils_1.appendToUrlHash)('http://xyz.com/#foo', 'bar')).toBe('http://xyz.com/#foo?tsSSOMarker=bar'); }); describe('getRedirectURL', () => { let windowSpy; beforeEach(() => { windowSpy = jest.spyOn(window, 'window', 'get'); }); afterEach(() => { windowSpy.mockRestore(); }); test('Should return correct value when path is undefined', () => { expect((0, utils_1.getRedirectUrl)('http://myhost:3000', 'hashFrag')).toBe('http://myhost:3000#?tsSSOMarker=hashFrag'); expect((0, utils_1.getRedirectUrl)('http://xyz.com/#foo', 'bar')).toBe('http://xyz.com/#foo?tsSSOMarker=bar'); }); test('Should return correct value when path is set', () => { windowSpy.mockImplementation(() => ({ location: { origin: 'http://myhost:3000', }, })); expect((0, utils_1.getRedirectUrl)('http://myhost:3000/', 'hashFrag', '/bar')).toBe('http://myhost:3000/bar#?tsSSOMarker=hashFrag'); expect((0, utils_1.getRedirectUrl)('http://myhost:3000/#/foo', 'hashFrag', '#/bar')).toBe('http://myhost:3000/#/bar?tsSSOMarker=hashFrag'); }); }); test('getEncodedQueryParamsString', () => { expect((0, utils_1.getEncodedQueryParamsString)('')).toBe(''); expect((0, utils_1.getEncodedQueryParamsString)('test')).toBe('dGVzdA'); }); test('when ReleaseVersion is empty ', () => { expect((0, utils_1.checkReleaseVersionInBeta)('', false)).toBe(false); }); test('when ReleaseVersion is 7.0.1.cl ', () => { expect((0, utils_1.checkReleaseVersionInBeta)('7.0.1.cl', false)).toBe(false); }); test('when cluster has dev version', () => { expect((0, utils_1.checkReleaseVersionInBeta)('dev', false)).toBe(false); }); test('when cluster is above 8.4.0.cl-11 software version', () => { expect((0, utils_1.checkReleaseVersionInBeta)('8.4.0.cl-117', false)).toBe(false); }); test('when cluster is bellow 8.0.0.sw software version', () => { expect((0, utils_1.checkReleaseVersionInBeta)('7.2.1.sw', false)).toBe(true); }); test('when suppressBetaWarning is true and ReleaseVersion is 7.0.1', () => { expect((0, utils_1.checkReleaseVersionInBeta)('7.0.1', true)).toBe(false); }); test('when suppressBetaWarning is false ReleaseVersion is 7.0.1', () => { expect((0, utils_1.checkReleaseVersionInBeta)('7.0.1', false)).toBe(true); }); test('removeTypename should removed __typename', () => { const input = { test: 'test', __typename: 'should be removed', obj: { test: 'test', __typename: 'should be removed', }, }; const result = (0, utils_1.removeTypename)(input); const expectedResult = { test: 'test', obj: { test: 'test', }, }; expect(result).toEqual(expectedResult); }); describe('validate removeStyleProperties', () => { it('should remove specified style properties from an HTML element', () => { const element = document.createElement('div'); element.style.backgroundColor = 'blue'; element.style.fontSize = '14px'; const propertiesToRemove = ['background-color', 'font-size']; (0, utils_1.removeStyleProperties)(element, propertiesToRemove); expect(element.style.backgroundColor).toBe(''); expect(element.style.fontSize).toBe(''); }); it('should handle undefined param', () => { expect(() => { (0, utils_1.removeStyleProperties)(undefined, []); }).not.toThrow(); }); it('should handle removing non-existent style properties', () => { const element = document.createElement('div'); element.style.backgroundColor = 'blue'; element.style.fontSize = '14px'; const propertiesToRemove = ['color', 'border']; (0, utils_1.removeStyleProperties)(element, propertiesToRemove); expect(element.style.backgroundColor).toBe('blue'); expect(element.style.fontSize).toBe('14px'); }); }); describe('validate setStyleProperties', () => { it('should set style properties on an HTML element', () => { const element = document.createElement('div'); const styles = { backgroundColor: 'red', fontSize: '16px', }; (0, utils_1.setStyleProperties)(element, styles); expect(element.style.backgroundColor).toBe('red'); expect(element.style.fontSize).toBe('16px'); }); it('should handle undefined param', () => { // should not throw an error expect(() => { (0, utils_1.setStyleProperties)(undefined, {}); }).not.toThrow(); }); }); test('isUndefined', () => { expect((0, utils_1.isUndefined)(undefined)).toBe(true); expect((0, utils_1.isUndefined)({})).toBe(false); expect((0, utils_1.isUndefined)(null)).toBe(false); expect((0, utils_1.isUndefined)('')).toBe(false); expect((0, utils_1.isUndefined)(0)).toBe(false); }); test('removeTypename should handle edge cases', () => { expect((0, utils_1.removeTypename)(null)).toBe(null); expect((0, utils_1.removeTypename)(undefined)).toBe(undefined); expect((0, utils_1.removeTypename)('string')).toBe('string'); expect((0, utils_1.removeTypename)(123)).toBe(123); }); test('getTypeFromValue should return correct types', () => { expect((0, utils_1.getTypeFromValue)('test')).toEqual(['char', 'string']); expect((0, utils_1.getTypeFromValue)(123)).toEqual(['double', 'double']); expect((0, utils_1.getTypeFromValue)(true)).toEqual(['boolean', 'boolean']); expect((0, utils_1.getTypeFromValue)(false)).toEqual(['boolean', 'boolean']); expect((0, utils_1.getTypeFromValue)(null)).toEqual(['', '']); expect((0, utils_1.getTypeFromValue)(undefined)).toEqual(['', '']); expect((0, utils_1.getTypeFromValue)({})).toEqual(['', '']); expect((0, utils_1.getTypeFromValue)([])).toEqual(['', '']); }); describe('getValueFromWindow and storeValueInWindow', () => { test('Store and retrieve', () => { (0, utils_1.storeValueInWindow)('test', 'testValue'); expect((0, utils_1.getValueFromWindow)('test')).toBe('testValue'); }); test('Object should be set if not', () => { window._tsEmbedSDK = null; (0, utils_1.storeValueInWindow)('test', 'testValue'); expect((0, utils_1.getValueFromWindow)('test')).toBe('testValue'); }); test('Return undefined if key is not found', () => { expect((0, utils_1.getValueFromWindow)('notFound')).toBe(undefined); }); test('Store with ignoreIfAlreadyExists option', () => { (0, utils_1.storeValueInWindow)('test2', 'firstValue'); const result = (0, utils_1.storeValueInWindow)('test2', 'secondValue', { ignoreIfAlreadyExists: true }); expect(result).toBe('firstValue'); expect((0, utils_1.getValueFromWindow)('test2')).toBe('firstValue'); }); }); }); describe('Fullscreen Utility Functions', () => { let originalExitFullscreen; let mockIframe; beforeEach(() => { jest.clearAllMocks(); // Store and mock exitFullscreen originalExitFullscreen = document.exitFullscreen; document.exitFullscreen = jest.fn(); // Mock iframe mockIframe = { requestFullscreen: jest.fn(), }; // Mock not in fullscreen initially Object.defineProperty(document, 'fullscreenElement', { writable: true, value: null, }); }); afterEach(() => { // Restore original method document.exitFullscreen = originalExitFullscreen; }); describe('handlePresentEvent', () => { it('should enter fullscreen when iframe is provided', () => { const mockPromise = Promise.resolve(); mockIframe.requestFullscreen.mockReturnValue(mockPromise); (0, utils_1.handlePresentEvent)(mockIframe); expect(mockIframe.requestFullscreen).toHaveBeenCalled(); expect(logger_1.logger.error).not.toHaveBeenCalled(); }); it('should log error when fullscreen API is not supported', () => { delete mockIframe.requestFullscreen; (0, utils_1.handlePresentEvent)(mockIframe); expect(logger_1.logger.error).toHaveBeenCalledWith('Fullscreen API is not supported by this browser.'); }); it('should not attempt fullscreen when already in fullscreen mode', () => { Object.defineProperty(document, 'fullscreenElement', { writable: true, value: mockIframe, }); (0, utils_1.handlePresentEvent)(mockIframe); expect(mockIframe.requestFullscreen).not.toHaveBeenCalled(); expect(logger_1.logger.error).not.toHaveBeenCalled(); }); }); describe('handleExitPresentMode', () => { beforeEach(() => { // Mock being in fullscreen Object.defineProperty(document, 'fullscreenElement', { writable: true, value: document.createElement('iframe'), }); }); it('should exit fullscreen when in fullscreen mode', () => { const mockPromise = Promise.resolve(); document.exitFullscreen.mockReturnValue(mockPromise); (0, utils_1.handleExitPresentMode)(); expect(document.exitFullscreen).toHaveBeenCalled(); expect(logger_1.logger.warn).not.toHaveBeenCalled(); }); it('should not attempt to exit when not in fullscreen mode', () => { Object.defineProperty(document, 'fullscreenElement', { writable: true, value: null, }); (0, utils_1.handleExitPresentMode)(); expect(document.exitFullscreen).not.toHaveBeenCalled(); expect(logger_1.logger.warn).not.toHaveBeenCalled(); }); it('should log warning when exit fullscreen API is not supported', () => { // Mock being in fullscreen but no exit methods available document.exitFullscreen = undefined; (0, utils_1.handleExitPresentMode)(); expect(logger_1.logger.warn).toHaveBeenCalledWith('Exit fullscreen API is not supported by this browser.'); }); }); describe('arrayIncludesString', () => { it('should return true when string is found in array', () => { const arr = ['test', 'example', 'value']; expect((0, utils_1.arrayIncludesString)(arr, 'test')).toBe(true); expect((0, utils_1.arrayIncludesString)(arr, 'example')).toBe(true); expect((0, utils_1.arrayIncludesString)(arr, 'value')).toBe(true); }); it('should return false when string is not found in array', () => { const arr = ['test', 'example', 'value']; expect((0, utils_1.arrayIncludesString)(arr, 'notfound')).toBe(false); expect((0, utils_1.arrayIncludesString)(arr, '')).toBe(false); }); it('should handle empty array', () => { const arr = []; expect((0, utils_1.arrayIncludesString)(arr, 'test')).toBe(false); }); it('should handle array with non-string values', () => { const arr = ['test', 123, true, 'value']; expect((0, utils_1.arrayIncludesString)(arr, 'test')).toBe(true); expect((0, utils_1.arrayIncludesString)(arr, 'value')).toBe(true); expect((0, utils_1.arrayIncludesString)(arr, '123')).toBe(false); // string '123' not found }); it('should be case sensitive', () => { const arr = ['Test', 'Example', 'Value']; expect((0, utils_1.arrayIncludesString)(arr, 'test')).toBe(false); expect((0, utils_1.arrayIncludesString)(arr, 'Test')).toBe(true); }); }); }); describe('calculateVisibleElementData', () => { let mockElement; let originalInnerHeight; let originalInnerWidth; beforeEach(() => { // Store original window dimensions originalInnerHeight = window.innerHeight; originalInnerWidth = window.innerWidth; // Mock window dimensions Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: 800, }); Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value: 1200, }); // Create mock element mockElement = document.createElement('div'); }); afterEach(() => { // Restore original window dimensions Object.defineProperty(window, 'innerHeight', { value: originalInnerHeight, }); Object.defineProperty(window, 'innerWidth', { value: originalInnerWidth, }); }); it('should calculate data for fully visible element', () => { // Mock getBoundingClientRect for element fully within viewport jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({ top: 100, left: 150, bottom: 300, right: 400, width: 250, height: 200, }); const result = (0, utils_1.calculateVisibleElementData)(mockElement); expect(result).toEqual({ top: 0, height: 200, left: 0, width: 250, // Full width visible }); }); it('should calculate data for element clipped from top', () => { // Mock getBoundingClientRect for element partially above viewport jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({ top: -50, left: 100, bottom: 150, right: 400, width: 300, height: 200, }); const result = (0, utils_1.calculateVisibleElementData)(mockElement); expect(result).toEqual({ top: 50, height: 150, left: 0, width: 300, // Full width visible }); }); it('should calculate data for element clipped from left', () => { // Mock getBoundingClientRect for element partially left of viewport jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({ top: 100, left: -80, bottom: 300, right: 200, width: 280, height: 200, }); const result = (0, utils_1.calculateVisibleElementData)(mockElement); expect(result).toEqual({ top: 0, height: 200, left: 80, width: 200, // 200px visible width (0 to 200) }); }); it('should calculate data for element clipped from bottom', () => { // Mock getBoundingClientRect for element extending below viewport jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({ top: 600, left: 100, bottom: 950, right: 400, width: 300, height: 350, }); const result = (0, utils_1.calculateVisibleElementData)(mockElement); expect(result).toEqual({ top: 0, height: 200, left: 0, width: 300, // Full width visible }); }); it('should calculate data for element clipped from right', () => { // Mock getBoundingClientRect for element extending beyond right edge jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({ top: 100, left: 1000, bottom: 300, right: 1400, width: 400, height: 200, }); const result = (0, utils_1.calculateVisibleElementData)(mockElement); expect(result).toEqual({ top: 0, height: 200, left: 0, width: 200, // Only 200px visible width (1000 to 1200) }); }); it('should calculate data for element clipped from multiple sides', () => { // Mock getBoundingClientRect for element clipped from top and left jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({ top: -100, left: -50, bottom: 200, right: 300, width: 350, height: 300, }); const result = (0, utils_1.calculateVisibleElementData)(mockElement); expect(result).toEqual({ top: 100, height: 200, left: 50, width: 300, // 300px visible width (0 to 300) }); }); it('should handle element completely outside viewport (above)', () => { // Mock getBoundingClientRect for element completely above viewport jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({ top: -300, left: 100, bottom: -100, right: 400, width: 300, height: 200, }); const result = (0, utils_1.calculateVisibleElementData)(mockElement); expect(result).toEqual({ top: 300, height: 0, left: 0, width: 300, // Full width would be visible if in viewport }); }); it('should handle element completely outside viewport (left)', () => { // Mock getBoundingClientRect for element completely left of viewport jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({ top: 100, left: -400, bottom: 300, right: -100, width: 300, height: 200, }); const result = (0, utils_1.calculateVisibleElementData)(mockElement); expect(result).toEqual({ top: 0, height: 200, left: 400, width: 0, // No visible width (min(1200, -100) - max(-400, 0) = -100 - 0 = -100, but clamped) }); }); it('should handle element larger than viewport', () => { // Mock getBoundingClientRect for element larger than viewport jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({ top: -200, left: -300, bottom: 1000, right: 1500, width: 1800, height: 1200, }); const result = (0, utils_1.calculateVisibleElementData)(mockElement); expect(result).toEqual({ top: 200, height: 800, left: 300, width: 1200, // Visible width equals window width }); }); it('should handle element exactly at viewport boundaries', () => { // Mock getBoundingClientRect for element at exact viewport boundaries jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({ top: 0, left: 0, bottom: 800, right: 1200, width: 1200, height: 800, }); const result = (0, utils_1.calculateVisibleElementData)(mockElement); expect(result).toEqual({ top: 0, height: 800, left: 0, width: 1200, // Full viewport width }); }); }); describe('formatTemplate', () => { it('should replace placeholders with provided values', () => { expect((0, utils_1.formatTemplate)('Hello {name}, you are {age} years old', { name: 'John', age: 30 })).toBe('Hello John, you are 30 years old'); expect((0, utils_1.formatTemplate)('Expected {type}, but received {actual}', { type: 'string', actual: 'number', })).toBe('Expected string, but received number'); expect((0, utils_1.formatTemplate)('Hello {name}, you are {age} years old', { name: 'John' })).toBe('Hello John, you are {age} years old'); }); }); //# sourceMappingURL=utils.spec.js.map