From a7e838fe0cb330c8550e0e539ff7455f27e1b3d1 Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Fri, 22 Dec 2017 15:46:10 +0530 Subject: [PATCH 01/21] SEARCH-538 - Working on electron unit tests --- tests/Search.test.js | 137 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/Search.test.js diff --git a/tests/Search.test.js b/tests/Search.test.js new file mode 100644 index 00000000..ce429efb --- /dev/null +++ b/tests/Search.test.js @@ -0,0 +1,137 @@ +const electron = require('./__mocks__/electron'); +const childProcess = require('child_process'); +const path = require('path'); +const fs = require('fs'); + +const { isMac } = require('../js/utils/misc.js'); + +let executionPath = null; +let userConfigDir = null; + +let searchConfig; +let SearchApi; + +jest.mock('electron', function() { + return { + app: { + getPath: mockedGetPath + } + } +}); + +function mockedGetPath(type) { + if (type === 'exe') { + return executionPath; + } + + if (type === 'userData') { + return userConfigDir + } + return ''; +} + +describe('Tests for Search', function() { + + let userId; + let key; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 900000; + + beforeAll(function (done) { + childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) { + userId = 12345678910112; + key = 'abcdefghijklmnopqrstuvwxyz123456789!@#$%^&*='; + executionPath = path.join(__dirname, 'library'); + userConfigDir = path.join(__dirname, '..'); + searchConfig = require('../js/search/searchConfig.js'); + const { Search } = require('../js/search/search.js'); + SearchApi = new Search(userId, key); + done(); + }); + }); + + describe('Search Initial checks', function() { + + it('Should be initialized', function () { + expect(SearchApi.isInitialized).toBe(true); + expect(SearchApi.indexFolderName).toBe(`${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME_PATH}_${userId}_${searchConfig.INDEX_VERSION}`); + expect(SearchApi.dataFolder).toBe(searchConfig.FOLDERS_CONSTANTS.INDEX_PATH); + expect(SearchApi.realTimeIndex).toBe(searchConfig.FOLDERS_CONSTANTS.TEMP_REAL_TIME_INDEX); + expect(SearchApi.batchIndex).toBe(searchConfig.FOLDERS_CONSTANTS.TEMP_BATCH_INDEX_FOLDER); + expect(SearchApi.messageData).toEqual([]); + expect(SearchApi.isRealTimeIndexing).toBe(false); + }); + + it('Should exist index folder', function() { + expect(fs.existsSync(path.join(userConfigDir, 'data', 'search_index_12345678910112_v1'))).toBe(true); + expect(fs.existsSync(path.join(userConfigDir, 'data', 'temp_realtime_index'))).toBe(true); + }); + + it('Should not exist index folder', function() { + expect(fs.existsSync(path.join(userConfigDir, 'data', 'temp_batch_indexes'))).toBe(false); + }); + + it('Should index in a batch', function () { + let messages = [ + { + messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", + threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", + ingestionDate: "1510684200000", + senderId: "71811853189212", + chatType: "CHATROOM", + isPublic: "false", + sendingApp: "lc", + text: "it works" + } + ]; + SearchApi.indexBatch(JSON.stringify(messages)).then(function () { + expect(fs.existsSync(path.join(userConfigDir, 'data', 'temp_batch_indexes'))).toBe(true); + }); + }); + + it('Should not batch index', function () { + + SearchApi.indexBatch().catch(function (err) { + expect(err).toThrow(err); + }); + + let message = { + messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", + threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", + ingestionDate: "1510684200000", + senderId: "71811853189212", + chatType: "CHATROOM", + isPublic: "false", + sendingApp: "lc", + text: "it fails" + }; + SearchApi.indexBatch(JSON.stringify(message)).catch(function (err) { + expect(err).toThrow(err); + }); + + SearchApi.isInitialized = false; + SearchApi.indexBatch(JSON.stringify(message)).catch(function (err) { + expect(err).toThrow(err); + }); + SearchApi.isInitialized = true; + }); + + it('Should match messages length after batch indexing', function () { + SearchApi.searchQuery('', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { + expect(res.messages.length).toEqual(0); + }); + }); + + it('Should merge batch index to user index', function () { + SearchApi.mergeIndexBatches().then(function () { + expect(fs.existsSync(path.join(userConfigDir, 'data', 'temp_batch_indexes'))).toBe(false); + }); + }); + + it('Should match messages length after batch indexing', function () { + SearchApi.searchQuery('', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { + expect(res.messages.length).toEqual(2); + }); + }); + + }); +}); \ No newline at end of file From 316744acafcd08c534148631061d6d14e1b3f37c Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Tue, 26 Dec 2017 17:34:18 +0530 Subject: [PATCH 02/21] Working on unit tests --- js/search/search.js | 8 +- tests/Search.test.js | 292 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 264 insertions(+), 36 deletions(-) diff --git a/js/search/search.js b/js/search/search.js index 6f0ad02b..79221e89 100644 --- a/js/search/search.js +++ b/js/search/search.js @@ -171,7 +171,7 @@ class Search { realTimeIndexing(message) { if (!message) { log.send(logLevels.ERROR, 'RealTime Indexing: Messages not provided'); - return new Error('RealTime Indexing: Messages is required'); + throw new Error('RealTime Indexing: Messages is required'); } try { @@ -182,12 +182,12 @@ class Search { } } catch(e) { log.send(logLevels.ERROR, 'RealTime Indexing: parse error -> ' + e); - return (new Error(e)); + throw (new Error(e)); } if (!this.isInitialized) { log.send(logLevels.ERROR, 'Library not initialized'); - return new Error('Library not initialized'); + throw new Error('Library not initialized'); } this.isRealTimeIndexing = true; @@ -195,7 +195,7 @@ class Search { this.isRealTimeIndexing = false; if (err) { log.send(logLevels.ERROR, 'RealTime Indexing: error -> ' + err); - return new Error(err); + throw new Error(err); } return result; }); diff --git a/tests/Search.test.js b/tests/Search.test.js index ce429efb..e8a743bb 100644 --- a/tests/Search.test.js +++ b/tests/Search.test.js @@ -34,10 +34,12 @@ describe('Tests for Search', function() { let userId; let key; + let realTimeIndexPath; + let tempBatchPath; jasmine.DEFAULT_TIMEOUT_INTERVAL = 900000; beforeAll(function (done) { - childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) { + /*childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) { userId = 12345678910112; key = 'abcdefghijklmnopqrstuvwxyz123456789!@#$%^&*='; executionPath = path.join(__dirname, 'library'); @@ -45,13 +47,51 @@ describe('Tests for Search', function() { searchConfig = require('../js/search/searchConfig.js'); const { Search } = require('../js/search/search.js'); SearchApi = new Search(userId, key); + realTimeIndexPath = path.join(userConfigDir, 'data', 'temp_realtime_index'); + tempBatchPath = path.join(userConfigDir, 'data', 'temp_batch_indexes'); done(); - }); + });*/ + userId = 12345678910112; + key = 'jjjehdnctsjyieoalskcjdhsnahsadndfnusdfsdfsd='; + executionPath = path.join(__dirname, 'library'); + userConfigDir = path.join(__dirname, '..'); + searchConfig = require('../js/search/searchConfig.js'); + const { Search } = require('../js/search/search.js'); + SearchApi = new Search(userId, key); + realTimeIndexPath = path.join(userConfigDir, 'data', 'temp_realtime_index'); + tempBatchPath = path.join(userConfigDir, 'data', 'temp_batch_indexes'); + done(); }); + afterAll(function (done) { + setTimeout(() => { + let dataPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data'); + deleteIndexFolders(dataPath); + let root = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', `${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME}_${userId}_${searchConfig.INDEX_VERSION}.enc`); + if (fs.existsSync(root)) { + fs.unlink(root); + } + done(); + }, 3000); + }); + + function deleteIndexFolders(location) { + if (fs.existsSync(location)) { + fs.readdirSync(location).forEach((file) => { + let curPath = location + "/" + file; + if (fs.lstatSync(curPath).isDirectory()) { + deleteIndexFolders(curPath); + } else { + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(location); + } + } + describe('Search Initial checks', function() { - it('Should be initialized', function () { + it('should be initialized', function () { expect(SearchApi.isInitialized).toBe(true); expect(SearchApi.indexFolderName).toBe(`${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME_PATH}_${userId}_${searchConfig.INDEX_VERSION}`); expect(SearchApi.dataFolder).toBe(searchConfig.FOLDERS_CONSTANTS.INDEX_PATH); @@ -61,16 +101,32 @@ describe('Tests for Search', function() { expect(SearchApi.isRealTimeIndexing).toBe(false); }); - it('Should exist index folder', function() { + it('should isLibInit to true', function () { + let init = SearchApi.isLibInit(); + expect(init).toEqual(true); + }); + + it('should isLibInit to true', function () { + SearchApi.isInitialized = false; + let init = SearchApi.isLibInit(); + expect(init).toEqual(false); + SearchApi.isInitialized = true; + }); + + it('should exist index folder', function() { expect(fs.existsSync(path.join(userConfigDir, 'data', 'search_index_12345678910112_v1'))).toBe(true); - expect(fs.existsSync(path.join(userConfigDir, 'data', 'temp_realtime_index'))).toBe(true); + expect(fs.existsSync(realTimeIndexPath)).toBe(true); }); - it('Should not exist index folder', function() { - expect(fs.existsSync(path.join(userConfigDir, 'data', 'temp_batch_indexes'))).toBe(false); + it('should not exist index folder', function() { + expect(fs.existsSync(tempBatchPath)).toBe(false); }); - it('Should index in a batch', function () { + }); + + describe('Batch indexing process tests', function () { + + it('should index in a batch', function (done) { let messages = [ { messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", @@ -84,16 +140,26 @@ describe('Tests for Search', function() { } ]; SearchApi.indexBatch(JSON.stringify(messages)).then(function () { - expect(fs.existsSync(path.join(userConfigDir, 'data', 'temp_batch_indexes'))).toBe(true); + expect(fs.existsSync(tempBatchPath)).toBe(true); + done(); }); }); - it('Should not batch index', function () { - + it('should not batch index', function (done) { SearchApi.indexBatch().catch(function (err) { - expect(err).toThrow(err); + expect(err).toBeTruthy(); + done(); }); + }); + it('should not batch index invalid object', function (done) { + SearchApi.indexBatch('message').catch(function (err) { + expect(err).toBeTruthy(); + done(); + }); + }); + + it('should not batch index parse error', function (done) { let message = { messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", @@ -102,36 +168,198 @@ describe('Tests for Search', function() { chatType: "CHATROOM", isPublic: "false", sendingApp: "lc", - text: "it fails" + text: "it works" }; SearchApi.indexBatch(JSON.stringify(message)).catch(function (err) { - expect(err).toThrow(err); + expect(err).toBeTruthy(); + done(); }); + }); + + it('should not batch index isInitialized', function (done) { + SearchApi.isInitialized = false; + let message = [{ + messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", + threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", + ingestionDate: "1510684200000", + senderId: "71811853189212", + chatType: "CHATROOM", + isPublic: "false", + sendingApp: "lc", + text: "it fails" + }]; + SearchApi.indexBatch(JSON.stringify(message)).catch(function (err) { + expect(err).toBeTruthy(); + SearchApi.isInitialized = true; + done(); + }); + }); + + it('should match messages length after batch indexing', function (done) { + SearchApi.searchQuery('it works', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { + expect(res.messages.length).toEqual(0); + done() + }); + }); + + it('should merge batch index to user index', function (done) { + SearchApi.mergeIndexBatches().then(function () { + expect(fs.existsSync(tempBatchPath)).toBe(false); + done(); + }); + }); + + it('should match messages length after batch indexing', function (done) { + SearchApi.searchQuery('it works', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { + expect(res.messages.length).toEqual(1); + done(); + }); + }); + }); + + describe('RealTime indexing process', function () { + + it('should index realTime message', function () { + let message = [{ + messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", + threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", + ingestionDate: "1510684200000", + senderId: "71811853189212", + chatType: "CHATROOM", + isPublic: "false", + sendingApp: "lc", + text: "realtime working" + }]; + const batchRealTimeIndexing = jest.spyOn(SearchApi, 'batchRealTimeIndexing'); + SearchApi.batchRealTimeIndexing(message); + expect(batchRealTimeIndexing).toHaveBeenCalled(); + }); + + it('should match message length', function (done) { + SearchApi.searchQuery('realtime working', ["71811853189212"], ["Au8O2xKHyX1LtE6zW019GX///rZYegAtdA=="], '', undefined, undefined, 25, 0, 0).then(function (res) { + expect(res.messages.length).toEqual(1); + expect(fs.existsSync(realTimeIndexPath)).toBe(true); + done(); + }) + }); + + it('should not index realTime message', function (done) { + let message = [{ + messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", + threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", + ingestionDate: "1510684200000", + senderId: "71811853189212", + chatType: "CHATROOM", + isPublic: "false", + sendingApp: "lc", + text: "isRealTimeIndexing" + }]; + const batchRealTimeIndexing = jest.spyOn(SearchApi, 'batchRealTimeIndexing'); + SearchApi.isRealTimeIndexing = false; + SearchApi.batchRealTimeIndexing(message); + expect(batchRealTimeIndexing).toHaveBeenCalled(); + SearchApi.searchQuery('isRealTimeIndexing', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { + expect(res.messages.length).toEqual(0); + expect(fs.existsSync(realTimeIndexPath)).toBe(true); + done(); + }); + }); + + it('should not realTime index invalid object', function () { + let message = [{ + messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", + threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", + ingestionDate: "1510684200000", + senderId: "71811853189212", + chatType: "CHATROOM", + isPublic: "false", + sendingApp: "lc", + text: "isRealTimeIndexing" + }]; + + expect(function () { + SearchApi.realTimeIndexing('message') + }).toThrow(); + + expect(function () { + SearchApi.realTimeIndexing() + }).toThrow(new Error('RealTime Indexing: Messages is required')); SearchApi.isInitialized = false; - SearchApi.indexBatch(JSON.stringify(message)).catch(function (err) { - expect(err).toThrow(err); - }); + expect(function () { + SearchApi.realTimeIndexing(JSON.stringify(message)) + }).toThrow(new Error('Library not initialized')); SearchApi.isInitialized = true; }); - it('Should match messages length after batch indexing', function () { - SearchApi.searchQuery('', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { - expect(res.messages.length).toEqual(0); - }); + it('should return realTime bool', function () { + SearchApi.isRealTimeIndexing = true; + expect(SearchApi.checkIsRealTimeIndexing()).toBe(true); + SearchApi.isRealTimeIndexing = false; + expect(SearchApi.checkIsRealTimeIndexing()).toBe(false); }); - it('Should merge batch index to user index', function () { - SearchApi.mergeIndexBatches().then(function () { - expect(fs.existsSync(path.join(userConfigDir, 'data', 'temp_batch_indexes'))).toBe(false); - }); + it('should delete realtime index', function () { + SearchApi.deleteRealTimeFolder(); + expect(fs.existsSync(realTimeIndexPath)).toBe(true); }); - - it('Should match messages length after batch indexing', function () { - SearchApi.searchQuery('', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { - expect(res.messages.length).toEqual(2); - }); - }); - }); + + + describe('Test for encryption of the index', function () { + + it('should encrypt the user index', function (done) { + SearchApi.encryptIndex(key); + done(); + }); + + it('should exist encrypted file', function (done) { + setTimeout(function () { + expect(fs.existsSync(path.join(userConfigDir, 'search_index_12345678910112_v1.enc'))).toBe(true); + expect(fs.existsSync(path.join(userConfigDir, 'search_index_12345678910112_v1.tar.lz4'))).toBe(false); + done(); + },1000); + }); + }); + + describe('Test for latest timestamp', function () { + + it('should get the latest timestamp', function (done) { + SearchApi.getLatestMessageTimestamp().then(function (res) { + expect(res).toEqual('1510684200000'); + done(); + }); + }); + + it('should not get the latest timestamp', function (done) { + SearchApi.isInitialized = false; + SearchApi.getLatestMessageTimestamp().catch(function (err) { + expect(err).toEqual('Not initialized'); + SearchApi.isInitialized = true; + done(); + }); + }); + }); + + describe('Test to decrypt the index', function () { + + it('should decrypt the index', function () { + let dataPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data'); + deleteIndexFolders(dataPath); + SearchApi.decryptAndInit(); + }); + + it('should get message from the decrypted index', function (done) { + setTimeout(function () { + SearchApi.searchQuery('it works', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { + expect(res.messages.length).toEqual(1); + done() + }); + }, 3000) + }); + }); + + describe('Test for search functions', function () { + + }) }); \ No newline at end of file From 52c2078e9658024c3432ecc453d77214afa5e494 Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Wed, 27 Dec 2017 11:57:49 +0530 Subject: [PATCH 03/21] Completed search.js tests --- js/search/queue.js | 2 +- js/search/search.js | 12 ++-- tests/Search.test.js | 144 ++++++++++++++++++++++++++++--------------- 3 files changed, 102 insertions(+), 56 deletions(-) diff --git a/js/search/queue.js b/js/search/queue.js index 65452808..7fb409f4 100644 --- a/js/search/queue.js +++ b/js/search/queue.js @@ -6,7 +6,7 @@ let makeBoundTimedCollector = function(isIndexing, timeout, callback) { return function (...args) { if (!timer){ timer = setTimeout(function(){ - if (!isIndexing) { + if (!isIndexing()) { flush(getQueue()); } }, timeout); diff --git a/js/search/search.js b/js/search/search.js index 79221e89..836c7a19 100644 --- a/js/search/search.js +++ b/js/search/search.js @@ -273,7 +273,7 @@ class Search { if (!fs.existsSync(this.indexFolderName) || !fs.existsSync(this.realTimeIndex)) { log.send(logLevels.ERROR, 'Index folder does not exist.'); - reject('Index folder does not exist.'); + reject(new Error('Index folder does not exist.')); return; } @@ -301,15 +301,15 @@ class Search { } } - if (!_limit && _limit === "" && typeof _limit !== 'number' && Math.round(_limit) !== _limit) { + if (!_limit || _limit === "" || typeof _limit !== 'number' || Math.round(_limit) !== _limit) { _limit = 25; } - if (!_offset && _offset === "" && typeof _offset !== 'number' && Math.round(_offset) !== _offset) { + if (!_offset || _offset === "" || typeof _offset !== 'number' || Math.round(_offset) !== _offset) { _offset = 0 } - if (!_sortOrder && _sortOrder === "" && typeof _sortOrder !== 'number' && Math.round(_sortOrder) !== _sortOrder) { + if (!_sortOrder || _sortOrder === "" || typeof _sortOrder !== 'number' || Math.round(_sortOrder) !== _sortOrder) { _sortOrder = searchConfig.SORT_BY_SCORE; } @@ -332,13 +332,13 @@ class Search { return new Promise((resolve, reject) => { if (!this.isInitialized) { log.send(logLevels.ERROR, 'Library not initialized'); - reject('Not initialized'); + reject(new Error('Not initialized')); return; } if (!fs.existsSync(this.indexFolderName)) { log.send(logLevels.ERROR, 'Index folder does not exist.'); - reject('Index folder does not exist.'); + reject(new Error('Index folder does not exist.')); return; } diff --git a/tests/Search.test.js b/tests/Search.test.js index e8a743bb..bcbdc2f0 100644 --- a/tests/Search.test.js +++ b/tests/Search.test.js @@ -1,10 +1,7 @@ -const electron = require('./__mocks__/electron'); const childProcess = require('child_process'); const path = require('path'); const fs = require('fs'); -const { isMac } = require('../js/utils/misc.js'); - let executionPath = null; let userConfigDir = null; @@ -34,14 +31,16 @@ describe('Tests for Search', function() { let userId; let key; + let dataFolderPath; let realTimeIndexPath; let tempBatchPath; - jasmine.DEFAULT_TIMEOUT_INTERVAL = 900000; + let currentDate = new Date().getTime(); + jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000; beforeAll(function (done) { - /*childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) { + childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) { userId = 12345678910112; - key = 'abcdefghijklmnopqrstuvwxyz123456789!@#$%^&*='; + key = 'jjjehdnctsjyieoalskcjdhsnahsadndfnusdfsdfsd='; executionPath = path.join(__dirname, 'library'); userConfigDir = path.join(__dirname, '..'); searchConfig = require('../js/search/searchConfig.js'); @@ -49,27 +48,17 @@ describe('Tests for Search', function() { SearchApi = new Search(userId, key); realTimeIndexPath = path.join(userConfigDir, 'data', 'temp_realtime_index'); tempBatchPath = path.join(userConfigDir, 'data', 'temp_batch_indexes'); + dataFolderPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data'); done(); - });*/ - userId = 12345678910112; - key = 'jjjehdnctsjyieoalskcjdhsnahsadndfnusdfsdfsd='; - executionPath = path.join(__dirname, 'library'); - userConfigDir = path.join(__dirname, '..'); - searchConfig = require('../js/search/searchConfig.js'); - const { Search } = require('../js/search/search.js'); - SearchApi = new Search(userId, key); - realTimeIndexPath = path.join(userConfigDir, 'data', 'temp_realtime_index'); - tempBatchPath = path.join(userConfigDir, 'data', 'temp_batch_indexes'); - done(); + }); }); afterAll(function (done) { setTimeout(() => { - let dataPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data'); - deleteIndexFolders(dataPath); + deleteIndexFolders(dataFolderPath); let root = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', `${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME}_${userId}_${searchConfig.INDEX_VERSION}.enc`); if (fs.existsSync(root)) { - fs.unlink(root); + fs.unlinkSync(root); } done(); }, 3000); @@ -91,14 +80,17 @@ describe('Tests for Search', function() { describe('Search Initial checks', function() { - it('should be initialized', function () { - expect(SearchApi.isInitialized).toBe(true); - expect(SearchApi.indexFolderName).toBe(`${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME_PATH}_${userId}_${searchConfig.INDEX_VERSION}`); - expect(SearchApi.dataFolder).toBe(searchConfig.FOLDERS_CONSTANTS.INDEX_PATH); - expect(SearchApi.realTimeIndex).toBe(searchConfig.FOLDERS_CONSTANTS.TEMP_REAL_TIME_INDEX); - expect(SearchApi.batchIndex).toBe(searchConfig.FOLDERS_CONSTANTS.TEMP_BATCH_INDEX_FOLDER); - expect(SearchApi.messageData).toEqual([]); - expect(SearchApi.isRealTimeIndexing).toBe(false); + it('should be initialized', function (done) { + setTimeout(function () { + expect(SearchApi.isInitialized).toBe(true); + expect(SearchApi.indexFolderName).toBe(`${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME_PATH}_${userId}_${searchConfig.INDEX_VERSION}`); + expect(SearchApi.dataFolder).toBe(searchConfig.FOLDERS_CONSTANTS.INDEX_PATH); + expect(SearchApi.realTimeIndex).toBe(searchConfig.FOLDERS_CONSTANTS.TEMP_REAL_TIME_INDEX); + expect(SearchApi.batchIndex).toBe(searchConfig.FOLDERS_CONSTANTS.TEMP_BATCH_INDEX_FOLDER); + expect(SearchApi.messageData).toEqual([]); + expect(SearchApi.isRealTimeIndexing).toBe(false); + done(); + }, 3000) }); it('should isLibInit to true', function () { @@ -121,7 +113,6 @@ describe('Tests for Search', function() { it('should not exist index folder', function() { expect(fs.existsSync(tempBatchPath)).toBe(false); }); - }); describe('Batch indexing process tests', function () { @@ -131,7 +122,7 @@ describe('Tests for Search', function() { { messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", - ingestionDate: "1510684200000", + ingestionDate: currentDate.toString(), senderId: "71811853189212", chatType: "CHATROOM", isPublic: "false", @@ -163,7 +154,7 @@ describe('Tests for Search', function() { let message = { messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", - ingestionDate: "1510684200000", + ingestionDate: currentDate.toString(), senderId: "71811853189212", chatType: "CHATROOM", isPublic: "false", @@ -181,7 +172,7 @@ describe('Tests for Search', function() { let message = [{ messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", - ingestionDate: "1510684200000", + ingestionDate: currentDate.toString(), senderId: "71811853189212", chatType: "CHATROOM", isPublic: "false", @@ -223,7 +214,7 @@ describe('Tests for Search', function() { let message = [{ messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", - ingestionDate: "1510684200000", + ingestionDate: currentDate.toString(), senderId: "71811853189212", chatType: "CHATROOM", isPublic: "false", @@ -247,7 +238,7 @@ describe('Tests for Search', function() { let message = [{ messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", - ingestionDate: "1510684200000", + ingestionDate: currentDate.toString(), senderId: "71811853189212", chatType: "CHATROOM", isPublic: "false", @@ -255,21 +246,23 @@ describe('Tests for Search', function() { text: "isRealTimeIndexing" }]; const batchRealTimeIndexing = jest.spyOn(SearchApi, 'batchRealTimeIndexing'); - SearchApi.isRealTimeIndexing = false; + SearchApi.isRealTimeIndexing = true; SearchApi.batchRealTimeIndexing(message); expect(batchRealTimeIndexing).toHaveBeenCalled(); - SearchApi.searchQuery('isRealTimeIndexing', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { - expect(res.messages.length).toEqual(0); - expect(fs.existsSync(realTimeIndexPath)).toBe(true); - done(); - }); + setTimeout(function () { + SearchApi.searchQuery('isRealTimeIndexing', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { + expect(res.messages.length).toEqual(0); + expect(fs.existsSync(realTimeIndexPath)).toBe(true); + done(); + }); + }, 6000) }); it('should not realTime index invalid object', function () { let message = [{ messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", - ingestionDate: "1510684200000", + ingestionDate: currentDate.toString(), senderId: "71811853189212", chatType: "CHATROOM", isPublic: "false", @@ -305,7 +298,6 @@ describe('Tests for Search', function() { }); }); - describe('Test for encryption of the index', function () { it('should encrypt the user index', function (done) { @@ -318,7 +310,7 @@ describe('Tests for Search', function() { expect(fs.existsSync(path.join(userConfigDir, 'search_index_12345678910112_v1.enc'))).toBe(true); expect(fs.existsSync(path.join(userConfigDir, 'search_index_12345678910112_v1.tar.lz4'))).toBe(false); done(); - },1000); + }, 3000); }); }); @@ -326,7 +318,7 @@ describe('Tests for Search', function() { it('should get the latest timestamp', function (done) { SearchApi.getLatestMessageTimestamp().then(function (res) { - expect(res).toEqual('1510684200000'); + expect(res).toEqual(currentDate.toString()); done(); }); }); @@ -334,24 +326,34 @@ describe('Tests for Search', function() { it('should not get the latest timestamp', function (done) { SearchApi.isInitialized = false; SearchApi.getLatestMessageTimestamp().catch(function (err) { - expect(err).toEqual('Not initialized'); + expect(err).toEqual(new Error('Not initialized')); SearchApi.isInitialized = true; done(); }); }); + + it('should not get the latest timestamp', function (done) { + SearchApi.indexFolderName = ''; + SearchApi.getLatestMessageTimestamp().catch(function (err) { + expect(err).toEqual(new Error('Index folder does not exist.')); + SearchApi.indexFolderName = `${dataFolderPath}/${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME}_${userId}_${searchConfig.INDEX_VERSION}`; + done(); + }); + }); }); describe('Test to decrypt the index', function () { it('should decrypt the index', function () { - let dataPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data'); - deleteIndexFolders(dataPath); + deleteIndexFolders(dataFolderPath); SearchApi.decryptAndInit(); }); it('should get message from the decrypted index', function (done) { setTimeout(function () { - SearchApi.searchQuery('it works', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { + let endTime = new Date().getTime(); + let startTime = new Date().getTime() - (4 * 31 * 24 * 60 * 60 * 1000); + SearchApi.searchQuery('it works', [], [], '', startTime.toString(), endTime.toString(), '0', 0.2, 0.1).then(function (res) { expect(res.messages.length).toEqual(1); done() }); @@ -361,5 +363,49 @@ describe('Tests for Search', function() { describe('Test for search functions', function () { - }) + it('should search fail isInitialized', function (done) { + SearchApi.isInitialized = false; + SearchApi.searchQuery('it works', [], [], '', '', '', 25, 0, 0).catch(function (err) { + expect(err).toEqual(new Error('Library not initialized')); + SearchApi.isInitialized = true; + done(); + }); + }); + + it('should search fails index folder not fund', function (done) { + deleteIndexFolders(dataFolderPath); + SearchApi.searchQuery('it works', [], [], '', '', '', 25, 0, 0).catch(function (err) { + expect(err).toEqual(new Error('Index folder does not exist.')); + SearchApi = undefined; + const { Search } = require('../js/search/search.js'); + SearchApi = new Search(userId, key); + done(); + }); + }); + + it('should search fails query is undefined', function (done) { + setTimeout(function () { + expect(SearchApi.isInitialized).toBe(true); + SearchApi.searchQuery(undefined, [], [], '', '', '', 25, 0, 0).catch(function (err) { + expect(err).toEqual(new Error('Search query error')); + done(); + }); + }, 3000); + }); + + it('should search for hashtag', function (done) { + SearchApi.searchQuery('#123 "testing"', [], [], 'attachment', '', '', 25, 0, 0).then(function (res) { + expect(res.messages.length).toEqual(0); + done(); + }); + }); + + it('should search for pdf', function (done) { + SearchApi.searchQuery('', [], [], 'pdf', '', '', 25, 0, 0).then(function (res) { + expect(res.messages.length).toEqual(0); + done(); + }); + }); + }); + }); \ No newline at end of file From d7b5c28e0f14e088c0847b6ecf21e11bf157874d Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Wed, 27 Dec 2017 14:04:04 +0530 Subject: [PATCH 04/21] SEARCH-538 - Completed Unit tests for search.js --- tests/Search.test.js | 110 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 13 deletions(-) diff --git a/tests/Search.test.js b/tests/Search.test.js index bcbdc2f0..916786bd 100644 --- a/tests/Search.test.js +++ b/tests/Search.test.js @@ -35,38 +35,46 @@ describe('Tests for Search', function() { let realTimeIndexPath; let tempBatchPath; let currentDate = new Date().getTime(); + jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000; beforeAll(function (done) { childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) { + userId = 12345678910112; key = 'jjjehdnctsjyieoalskcjdhsnahsadndfnusdfsdfsd='; + executionPath = path.join(__dirname, 'library'); userConfigDir = path.join(__dirname, '..'); + searchConfig = require('../js/search/searchConfig.js'); const { Search } = require('../js/search/search.js'); SearchApi = new Search(userId, key); + realTimeIndexPath = path.join(userConfigDir, 'data', 'temp_realtime_index'); tempBatchPath = path.join(userConfigDir, 'data', 'temp_batch_indexes'); dataFolderPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data'); + done(); }); }); afterAll(function (done) { - setTimeout(() => { + setTimeout(function () { + deleteIndexFolders(dataFolderPath); let root = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', `${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME}_${userId}_${searchConfig.INDEX_VERSION}.enc`); if (fs.existsSync(root)) { fs.unlinkSync(root); } + done(); }, 3000); }); function deleteIndexFolders(location) { if (fs.existsSync(location)) { - fs.readdirSync(location).forEach((file) => { + fs.readdirSync(location).forEach(function(file) { let curPath = location + "/" + file; if (fs.lstatSync(curPath).isDirectory()) { deleteIndexFolders(curPath); @@ -82,6 +90,7 @@ describe('Tests for Search', function() { it('should be initialized', function (done) { setTimeout(function () { + expect(SearchApi.isInitialized).toBe(true); expect(SearchApi.indexFolderName).toBe(`${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME_PATH}_${userId}_${searchConfig.INDEX_VERSION}`); expect(SearchApi.dataFolder).toBe(searchConfig.FOLDERS_CONSTANTS.INDEX_PATH); @@ -89,6 +98,7 @@ describe('Tests for Search', function() { expect(SearchApi.batchIndex).toBe(searchConfig.FOLDERS_CONSTANTS.TEMP_BATCH_INDEX_FOLDER); expect(SearchApi.messageData).toEqual([]); expect(SearchApi.isRealTimeIndexing).toBe(false); + done(); }, 3000) }); @@ -98,7 +108,7 @@ describe('Tests for Search', function() { expect(init).toEqual(true); }); - it('should isLibInit to true', function () { + it('should isLibInit to false', function () { SearchApi.isInitialized = false; let init = SearchApi.isLibInit(); expect(init).toEqual(false); @@ -118,8 +128,7 @@ describe('Tests for Search', function() { describe('Batch indexing process tests', function () { it('should index in a batch', function (done) { - let messages = [ - { + let messages = [ { messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", ingestionDate: currentDate.toString(), @@ -128,24 +137,29 @@ describe('Tests for Search', function() { isPublic: "false", sendingApp: "lc", text: "it works" - } - ]; + } ]; + const indexBatch = jest.spyOn(SearchApi, 'indexBatch'); SearchApi.indexBatch(JSON.stringify(messages)).then(function () { expect(fs.existsSync(tempBatchPath)).toBe(true); + expect(indexBatch).toHaveBeenCalledWith(JSON.stringify(messages)); done(); }); }); it('should not batch index', function (done) { + const indexBatch = jest.spyOn(SearchApi, 'indexBatch'); SearchApi.indexBatch().catch(function (err) { + expect(indexBatch).toHaveBeenCalled(); expect(err).toBeTruthy(); done(); }); }); it('should not batch index invalid object', function (done) { + const indexBatch = jest.spyOn(SearchApi, 'indexBatch'); SearchApi.indexBatch('message').catch(function (err) { expect(err).toBeTruthy(); + expect(indexBatch).toHaveBeenCalledWith('message'); done(); }); }); @@ -161,15 +175,17 @@ describe('Tests for Search', function() { sendingApp: "lc", text: "it works" }; + const indexBatch = jest.spyOn(SearchApi, 'indexBatch'); SearchApi.indexBatch(JSON.stringify(message)).catch(function (err) { expect(err).toBeTruthy(); + expect(indexBatch).toHaveBeenCalled(); done(); }); }); - it('should not batch index isInitialized', function (done) { + it('should not batch index isInitialized is false', function (done) { SearchApi.isInitialized = false; - let message = [{ + let message = [ { messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", ingestionDate: currentDate.toString(), @@ -178,31 +194,39 @@ describe('Tests for Search', function() { isPublic: "false", sendingApp: "lc", text: "it fails" - }]; + } ]; + const indexBatch = jest.spyOn(SearchApi, 'indexBatch'); SearchApi.indexBatch(JSON.stringify(message)).catch(function (err) { expect(err).toBeTruthy(); + expect(indexBatch).toHaveBeenCalledWith(JSON.stringify(message)); SearchApi.isInitialized = true; done(); }); }); it('should match messages length after batch indexing', function (done) { + const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); SearchApi.searchQuery('it works', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { expect(res.messages.length).toEqual(0); + expect(searchQuery).toHaveBeenCalled(); done() }); }); it('should merge batch index to user index', function (done) { + const mergeIndexBatches = jest.spyOn(SearchApi, 'mergeIndexBatches'); SearchApi.mergeIndexBatches().then(function () { expect(fs.existsSync(tempBatchPath)).toBe(false); + expect(mergeIndexBatches).toHaveBeenCalled(); done(); }); }); it('should match messages length after batch indexing', function (done) { + const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); SearchApi.searchQuery('it works', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { expect(res.messages.length).toEqual(1); + expect(searchQuery).toHaveBeenCalled(); done(); }); }); @@ -221,15 +245,18 @@ describe('Tests for Search', function() { sendingApp: "lc", text: "realtime working" }]; + const batchRealTimeIndexing = jest.spyOn(SearchApi, 'batchRealTimeIndexing'); SearchApi.batchRealTimeIndexing(message); expect(batchRealTimeIndexing).toHaveBeenCalled(); }); it('should match message length', function (done) { + const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); SearchApi.searchQuery('realtime working', ["71811853189212"], ["Au8O2xKHyX1LtE6zW019GX///rZYegAtdA=="], '', undefined, undefined, 25, 0, 0).then(function (res) { expect(res.messages.length).toEqual(1); expect(fs.existsSync(realTimeIndexPath)).toBe(true); + expect(searchQuery).toHaveBeenCalled(); done(); }) }); @@ -245,19 +272,42 @@ describe('Tests for Search', function() { sendingApp: "lc", text: "isRealTimeIndexing" }]; + const batchRealTimeIndexing = jest.spyOn(SearchApi, 'batchRealTimeIndexing'); SearchApi.isRealTimeIndexing = true; SearchApi.batchRealTimeIndexing(message); expect(batchRealTimeIndexing).toHaveBeenCalled(); setTimeout(function () { + SearchApi.searchQuery('isRealTimeIndexing', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { expect(res.messages.length).toEqual(0); expect(fs.existsSync(realTimeIndexPath)).toBe(true); + done(); }); }, 6000) }); + it('should not call the real-time index', function () { + let message = [{ + messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", + threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", + ingestionDate: currentDate.toString(), + senderId: "71811853189212", + chatType: "CHATROOM", + isPublic: "false", + sendingApp: "lc", + text: "isRealTimeIndexing" + }]; + + const batchRealTimeIndexing = jest.spyOn(SearchApi, 'batchRealTimeIndexing'); + const realTimeIndexing = jest.spyOn(SearchApi, 'realTimeIndexing'); + SearchApi.isRealTimeIndexing = true; + SearchApi.batchRealTimeIndexing(message); + expect(batchRealTimeIndexing).toHaveBeenCalled(); + expect(realTimeIndexing).not.toBeCalled(); + }); + it('should not realTime index invalid object', function () { let message = [{ messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", @@ -269,7 +319,7 @@ describe('Tests for Search', function() { sendingApp: "lc", text: "isRealTimeIndexing" }]; - + const realTimeIndexing = jest.spyOn(SearchApi, 'realTimeIndexing'); expect(function () { SearchApi.realTimeIndexing('message') }).toThrow(); @@ -283,32 +333,43 @@ describe('Tests for Search', function() { SearchApi.realTimeIndexing(JSON.stringify(message)) }).toThrow(new Error('Library not initialized')); SearchApi.isInitialized = true; + expect(realTimeIndexing).toHaveBeenCalled(); + expect(realTimeIndexing).toHaveBeenCalledTimes(3); }); it('should return realTime bool', function () { + const checkIsRealTimeIndexing = jest.spyOn(SearchApi, 'checkIsRealTimeIndexing'); SearchApi.isRealTimeIndexing = true; expect(SearchApi.checkIsRealTimeIndexing()).toBe(true); SearchApi.isRealTimeIndexing = false; expect(SearchApi.checkIsRealTimeIndexing()).toBe(false); + expect(checkIsRealTimeIndexing).toHaveBeenCalled(); + expect(checkIsRealTimeIndexing).toHaveBeenCalledTimes(2); }); it('should delete realtime index', function () { + const deleteRealTimeFolder = jest.spyOn(SearchApi, 'deleteRealTimeFolder'); SearchApi.deleteRealTimeFolder(); expect(fs.existsSync(realTimeIndexPath)).toBe(true); + expect(deleteRealTimeFolder).toHaveBeenCalled(); }); }); describe('Test for encryption of the index', function () { - it('should encrypt the user index', function (done) { + it('should encrypt user index', function (done) { + const encryptIndex = jest.spyOn(SearchApi, 'encryptIndex'); SearchApi.encryptIndex(key); + expect(encryptIndex).toHaveBeenCalled(); done(); }); it('should exist encrypted file', function (done) { setTimeout(function () { + expect(fs.existsSync(path.join(userConfigDir, 'search_index_12345678910112_v1.enc'))).toBe(true); expect(fs.existsSync(path.join(userConfigDir, 'search_index_12345678910112_v1.tar.lz4'))).toBe(false); + done(); }, 3000); }); @@ -317,16 +378,20 @@ describe('Tests for Search', function() { describe('Test for latest timestamp', function () { it('should get the latest timestamp', function (done) { + const getLatestMessageTimestamp = jest.spyOn(SearchApi, 'getLatestMessageTimestamp'); SearchApi.getLatestMessageTimestamp().then(function (res) { expect(res).toEqual(currentDate.toString()); + expect(getLatestMessageTimestamp).toHaveBeenCalled(); done(); }); }); it('should not get the latest timestamp', function (done) { + const getLatestMessageTimestamp = jest.spyOn(SearchApi, 'getLatestMessageTimestamp'); SearchApi.isInitialized = false; SearchApi.getLatestMessageTimestamp().catch(function (err) { expect(err).toEqual(new Error('Not initialized')); + expect(getLatestMessageTimestamp).toHaveBeenCalled(); SearchApi.isInitialized = true; done(); }); @@ -334,9 +399,12 @@ describe('Tests for Search', function() { it('should not get the latest timestamp', function (done) { SearchApi.indexFolderName = ''; + const getLatestMessageTimestamp = jest.spyOn(SearchApi, 'getLatestMessageTimestamp'); SearchApi.getLatestMessageTimestamp().catch(function (err) { expect(err).toEqual(new Error('Index folder does not exist.')); SearchApi.indexFolderName = `${dataFolderPath}/${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME}_${userId}_${searchConfig.INDEX_VERSION}`; + expect(getLatestMessageTimestamp).toHaveBeenCalled(); + expect(getLatestMessageTimestamp).toHaveBeenCalledTimes(3); done(); }); }); @@ -346,15 +414,19 @@ describe('Tests for Search', function() { it('should decrypt the index', function () { deleteIndexFolders(dataFolderPath); + const decryptAndInit = jest.spyOn(SearchApi, 'decryptAndInit'); SearchApi.decryptAndInit(); + expect(decryptAndInit).toHaveBeenCalled(); }); it('should get message from the decrypted index', function (done) { setTimeout(function () { + const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); let endTime = new Date().getTime(); let startTime = new Date().getTime() - (4 * 31 * 24 * 60 * 60 * 1000); SearchApi.searchQuery('it works', [], [], '', startTime.toString(), endTime.toString(), '0', 0.2, 0.1).then(function (res) { expect(res.messages.length).toEqual(1); + expect(searchQuery).toHaveBeenCalled(); done() }); }, 3000) @@ -363,19 +435,24 @@ describe('Tests for Search', function() { describe('Test for search functions', function () { - it('should search fail isInitialized', function (done) { + it('should search fail isInitialized is false', function (done) { + const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); SearchApi.isInitialized = false; SearchApi.searchQuery('it works', [], [], '', '', '', 25, 0, 0).catch(function (err) { expect(err).toEqual(new Error('Library not initialized')); + expect(searchQuery).toHaveBeenCalled(); SearchApi.isInitialized = true; done(); }); }); it('should search fails index folder not fund', function (done) { + const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); deleteIndexFolders(dataFolderPath); SearchApi.searchQuery('it works', [], [], '', '', '', 25, 0, 0).catch(function (err) { expect(err).toEqual(new Error('Index folder does not exist.')); + expect(searchQuery).toHaveBeenCalledTimes(7); + expect(searchQuery).toHaveBeenCalled(); SearchApi = undefined; const { Search } = require('../js/search/search.js'); SearchApi = new Search(userId, key); @@ -385,24 +462,31 @@ describe('Tests for Search', function() { it('should search fails query is undefined', function (done) { setTimeout(function () { + const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); expect(SearchApi.isInitialized).toBe(true); SearchApi.searchQuery(undefined, [], [], '', '', '', 25, 0, 0).catch(function (err) { expect(err).toEqual(new Error('Search query error')); + expect(searchQuery).toHaveBeenCalled(); done(); }); }, 3000); }); it('should search for hashtag', function (done) { + const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); SearchApi.searchQuery('#123 "testing"', [], [], 'attachment', '', '', 25, 0, 0).then(function (res) { expect(res.messages.length).toEqual(0); + expect(searchQuery).toHaveBeenCalled(); done(); }); }); it('should search for pdf', function (done) { + const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); SearchApi.searchQuery('', [], [], 'pdf', '', '', 25, 0, 0).then(function (res) { expect(res.messages.length).toEqual(0); + expect(searchQuery).toHaveBeenCalled(); + expect(searchQuery).toHaveBeenCalledTimes(3); done(); }); }); From 193a4c126dd02b2b495eed5c137736c6fc1875cc Mon Sep 17 00:00:00 2001 From: Kiran Niranjan Date: Wed, 27 Dec 2017 16:25:40 +0530 Subject: [PATCH 05/21] Electron-250 Updated the bundle file Added "http" prefix validation --- .../Contents/Info.plist | 12 ++++++------ .../Contents/MacOS/SymphonySettingsPlugin | Bin 68512 -> 68640 bytes .../Resources/Base.lproj/MyInstallerPane.nib | Bin 11948 -> 7351 bytes .../Contents/_CodeSignature/CodeResources | 6 +++--- .../project.pbxproj | 6 +++--- .../SymphonySettingsPlugin/MyInstallerPane.m | 7 ++++--- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/installer/mac/SymphonySettingsPlugin.bundle/Contents/Info.plist b/installer/mac/SymphonySettingsPlugin.bundle/Contents/Info.plist index 091a38ee..324dd548 100644 --- a/installer/mac/SymphonySettingsPlugin.bundle/Contents/Info.plist +++ b/installer/mac/SymphonySettingsPlugin.bundle/Contents/Info.plist @@ -3,7 +3,7 @@ BuildMachineOSBuild - 17B48 + 16G1036 CFBundleDevelopmentRegion en CFBundleExecutable @@ -27,17 +27,17 @@ DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild - 9B55 + 8E3004b DTPlatformVersion GM DTSDKBuild - 17B41 + 16E185 DTSDKName - macosx10.13 + macosx10.12 DTXcode - 0910 + 0833 DTXcodeBuild - 9B55 + 8E3004b InstallerSectionTitle Pod Settings NSHumanReadableCopyright diff --git a/installer/mac/SymphonySettingsPlugin.bundle/Contents/MacOS/SymphonySettingsPlugin b/installer/mac/SymphonySettingsPlugin.bundle/Contents/MacOS/SymphonySettingsPlugin index b5565571c3e35c418b1ad681ebf9067628a65f14..6111d2c15aaae84375e64b42d117dbf677e5b8f3 100755 GIT binary patch delta 22284 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 delta 20879 zcmb7s2Ut|c`u;h~E`ox%D1u6tYGDCE0YO;>U2svbU_}H(Q4vtUmgu60akHZ6*iEcS zY|&tiB~cV(jf&l9VlN9ATMTyh|9xj>METwOJokTc_sqQSSEkQ==Wwp8m&#W!k(bQ2 z(>Vyzwat^J5}yBUN2Px!N`#0s^`BqaS((w90tXpQGf1e|Sy`Z_NK=od1w;?*aScU3 zDrkC~`~hrUPl^`OM93q)vd&72UKD<7MpFwiS=QNZLvMzdE@CsXtw9T7l)2iKGU+=e zMJ$t~A!Z=$L+N-cnj)sQ6&4CoZ1$BHMC|0f#P%dz-cDRimdRU)8nR2?S{a#2Eecq} zBgr#)XWOzg%39DwC`%(<6rF63*P)N6kp+rQb`^~FVcLkL(ifP!+P$h{gP2x4-rSY= zD=aLZ@>0lj^Uk(UGpUyxR-vbvQRIHuzqyZ?=i>pE zQswK_7X<720*=%$_@PIs{D$jDwdEo+LAXZ>>$jI#eNXn+KkelTg`t`+{LmZ=$IA(R zh|BH>!Y!X2)5o2&A_Yz4I(1?njlt<|KS9tKf`{}K1g#nLPUdL|b)#b)veTx^JKO82x*# z!LtXri4z}b^$(4Ez&)+nZtw-ou%Rwktu})=Mt@9lu0V+)cyS*=hlRe>U?okUII}E& z5C~>Tm5I11@o$)eG2lR}A6pTl{|$PqyqjcWx)7tkiKTb^Sb72)BUvRGLa$tj(?8J~ z##U$y5i_yZ48g&)wFflV{$lGTP*6Y_D@QI>^ayA%tsA0FQV&%p zCzXHJyPCpB4l()!sYZY+N64M3g25wg}w>tBWeuJxb@( zBaQ+#*+n8SL=0r!_O?V9<=_Q++wUcfCnmdl$e9Jd$9C&q8vA`(NP$$yCeYwBekUR+*hHHLoBzfd>*?Ku6b zIDI8fJ{OFF}M+cn1KGE4d?TU)f zVTu|lSB`@OPGE|$;lggepfOCX(CDwitZ^8u=99*B3ug_i=Ic-cV<)M|V@Z_?$S}cS zteXCEQe|i@_X8>`sj@Dq@~YcM?P9KNRjWM?weNurv?{4`bFK0Ku6(nW8-mI|bLDBZ zg05V7do5=JoX+?)*G{OF)L@rkt&123TbBMU4B1~O5N{d-(8v(+m_c_6k1^Po!ZHTs z2+f@KBIG+-N$REeNz8qGv7GeSL zqSeHsI4;QUI5pyL+-~J}@2x1y;?1IxZg=bx|LE{!;5R zf%=?`KF?xaTGQC9+*)UC>Nu-0zLHe2%`<9!DX6b7^i}wYuMb9VKsr{lTTtK68~fu& zXPR1Pd!3;&2E~*}L3nP{Nbx*S5JuSe2@}*XP=B|^9$+OHf*;4BYQ((H92*lqdX-CF zb#S&GH7-N9g%O3|mQH|1AT+8dn}n=Q(R~g=mU{ z7Xb(J!{{f`3OC$Ti^}qxV4En$8Wr0`T4Q33ovYF7ZW6^`lTrps{e#pAO5v6ZT9rucmME$5VNj3LS`^u-Xi>?i2#xp}i!uc7p~B%*7)ymZ z<5*gepV3`Lt-CM5!i8ECQ42TbE}Xh^r^0!)Cql^kesAWV$ z(9soJ-=R|6Rq-IM{HuW=a3Gz^bn_{Uh=&O;p&P)X3U|1RFoWLqBm>kPI^Z63=mvGMTn>RrT&Y1my<_yn+(ZbP@)D#n4RL!6j-tjasZ4zkY7AjSP0XPO zaLyen%HKj`kP3{|%Wp>)5BCtY9n(w3 z8z8h`0-n@^Kez3JmNDTK#hx0ppr8c~*c12t_V@qt!qA zR9*^lDWow3--kd7ELPSsJ18@!mN`M0DLPISozH6s{tr_=4QN2W0Mh7Bg0tRC?`wqy zHiwyIHbwoRcVYP!d!mYBytJPv`XZ@UWKzUrBPg0rWy2_%PVIDqC|o10Lq%VT##3QW ziX0&v1g9E`(x|KpMYdEHPSHRrYfq6tW&RZPpt4pJ)j$9PK0S04s;SVKqDNHdKv4vh z*;4eD%B(2rL}lg_J)<&#qG&361G^FQ6RXS%isGp3ajcglWVJDZ@Tj5^?zqjlL%I(( zWFe`t6&d2~ZIy11bv~xmKQL}5^StdE!~;<+3%CwpFPW}trn?-Ye;K2H7guyIFCCst z@`mAaUjxai95yTC+@{i_Ms*8mdObF30K=@yv&BQGI{wud?Br1Z10;U(lH+gTV!WAxwZNa zTKyekA_@2P68|P?zFrQgtjOD1*p(_bNxR6@3gbqy#&@V}Wmh7$augkibE_!SZ{@CY z=qfFejlp!;Bvqz2{ty z-7#3LxH6B?m6_{?N(vFzaIqp=A)&Y3Bs7M~{-tP(g(Q1|$e?hQoGb9j5bOq0&FfUt z-BQxLL^TtsW-`^(b*HL_sH&E#?x(8v+DV2HRJ9G&Tu(J8Qq46~^DxzHK{f5DW---V zOEtfunj5I*YbulJsA?{Sr>W{#syd6RUZJuoSE(`sDSAaU`%q0S)!ap8=c#6AioEJe zcA-?W3Sw*MUqWR+yW#@TD?BMwQdNAv0bLqGosOfjRn%z%icGY41=ZY5o%RBBRPidB z?gVN`_ceeiomAQEUy}*9c?`L5(j9IR0@U_KgIaY)1$Y;-ja>5cZk_#q^bf+~{6zn1C#Ih)={x-&{ru1MXK{UN z;;r)bKZOA>tIr3iKCMAt8~RV415~C1Rp!C2$#XZErfL;BjvGCJy$N1^Y4m@X>~K3k zJBR^;uR-Vujre?fgm;AGwn`P=Lt6R4FT!ytN7(t}OO-2NL?okQ()+6D;~%{wt(I0g zYMhH8TK|?_a!E0|6dtyfm^CQeDFZKGJm*0cU%nNQMgEC)d$17N*M_9yQv1GDF9>iD zTaab}PU82ZLqNO*zOB{h7iExn0V6VMI$}H$;)k7l?0^qUIw;;~1?P|i@I*1eRE#m? zJ7D~)zXmURj4MD46V7JlThQ0)w+w^xLbJCX0HMHSg11{*!><^{#kie~aXW+6D<(;S zZFHy?V~F;bY$HTLh%)q)M;oH$QHBBXxT5PeGw(?i*d>L4A?gF50`Cje`(VR@a&G9a zYYM+~un_W^8MF?r(&O=kJB6=JYMZF@>d?zkHeH^JZ#AYf`IptE6VbkhqVy-C#AoW4 zH=^~=)NUtQ!7C<%C@S=i@pr3-li+qqy1h^; zcu^Nj6@*DjctrgdjwOx0XCwH9R5y+>#9^)xq#?)@G%V2QyZNiJJ>qeFH+i&KoJEwr zCq7JQ^^c9^X42qj4BrmG{(=>_O=2WrD>)Sc>mp@M+Ad&|Tub+bthy#zkV((ZCqkhr z#diCFgo^EICRrQr!sdcU`8?c?42n5qP>{P=Or{34Xf_xqSVuKoUB!HFbxk)&TtQ`b zC$cTby`?Ys!EwX0_JOg5gpR;WkWStz9P?|WCa9zPVQKd|X7qq`W?7!Oo-FHw!>$f{sZ|7|dBabMwk$cKO$<4)*}PmM0Ys57H4C8Od0 z!6>>lnq<8lA64RtI|oorHz-S^cD`XxuI>9qI92YS$ky=UEk} zNQQn>n4!sT666A4&x4nQ^PxPti;e+OawYMTNlXW4|8Brx{8MyKZK#*$hA&58&g*aX zFoeH@Zs`cdJ@oQ-rgB5{pP}XB?~}zH;+lqH$p)5@kpWCt;yZEze71 z4^b!1PM1*_WT!Or&IB5?)pWifYCb{8l#m;0+_-NZI*Z`#(-k>LX<$U54;jF`* zPufGjL(d%*9#FW=%q0L02r%sEWerMTi!Pdu8AI?gICrF$fY4-D?v8Cd8dp3*#2sD^ z{$Lv8GsH;H%?Diw-B*GVeK!v_aO}Y@0AW1zq3y322pWPDRd|*G6Rah_M>y*ASQHC~ zKfaPLH#z`q@*EA`OrDw2JxjXhNcZv5Jx{t%knWSD`xNQ^rF5Sr-3z4q4Cy{oy3dmC zv!(mj?ygwC90|;o?j_QFfplL8cOvh!(@IR1(<^;M4@sv z=JGK&4ReK<%ln{%>_W_rMsh9YhGFgzj7eo8=3Zk?i#cl<Ox!U%s)4v8tZOTg zZ0b_7BRhJ!nQo|$Pv@|Vyu6$+mCE-^md{1t>oalUL_goos4zIC?G$y}LH=z+lUn;B zNcy7PR7rjK7XfYBP4)Xv3D|`VZ|&O|JPlTTbl{iN%GX2a6Rqv$8mI2NV|>gO1zD7k zOe}kQcTWopiu4Z)iFA)2pPiSTmYwMy&|Zji@7bqM-uO{jBLSgUNNX1;sR0ZO64HYF zYf)soKw$z&h<7jpA#qMfNqDbF64A?&eBUdEn0mRBF1=^yo-T-lVGmtbgTm{)CS3xLs1$(G@ zzPn)7#zPR+dkEqc_{qr5J|31o9f8OOu3R#IPz&oQsQDYlbR^r7TUciTWBnV|8BE3v zXleZoFd;`N6G&bswzU2on6TrNX-01MX<_{WnAVJGA~BB8*Y=<2I9dT=@tH~{FfWdA zH*5O4Sl(xdy7&d;=RV4xf&B1iaw{0Ntsmv*`Z^-9>N6EwdIrV}BGVJ?iBF;{1lci( zDsdi}oaik+C2Qb*o194Wwmfx;Rz?r4^cFqHxc)88u^kQ1kl6lS@HNZ0{@!9B(u##Z zJ4y;zOY<|Ns=t?|`59{e1yK*OC;kI0#DOGqfXZ^lS<1J8{`3~7k!1r^;(ekX*ivx> z>UWkr19qE)o4~AE0Xc86h>U{!Y%(A2ld(qJpQ1X{YXOp779lQb`nTT5;kuf z+_#cta9=?VC3#yKm}VMz2HXMCY^X|RdyT{n^%8^0xS=X>I$1N++cNnYbyz~q4fPfe zlGh+PLVS}|@a@aMWN&dPnGW{}WFy?Ck#ot%Y$vE;+QD@=67PcWdn8#q%#S=Brg1wD z53P9p$x;iZYRgg>yA*p{FNXYC8m1;QQdX*?80^W?J&}?xVF1IaEX`yol?uXf=mflu zlX0mo;(0P3?ia|mR2PzyYSC;06@I*P?LI`A<|3w%0Ju*l32>i9#=(6KnGbg&<(H9j zX)d}IG1M3~1nIh(MN^+_n(~j0N*QjHL-`z*R2fb@*M3_p8I7V|;iW{uSfnKI5_cuC%Ovp9vte`Y*gI ziua15;iU5q>5AMcxQxuq6oF*j(Dp()}zw%FXGW6Q^q z4;@eEvYy~W!Ts=_<~Xkxyr;&CcOCCqUV(MY5l^uf@QNpHBkgr-SbyxQ)A5J?3xAXG z<#qIJ@W_PgVjW(?_`3d+DL-1cU&o+`8PwF_o5#}vc-oQSF)Urd(w!{*lcl`5rNk}6 ze$*-E-8X@Hq;jYed#$cz^r`m6tMrq~zF>rx}fO zgPC7Giu~)?6vnq^{+{E!g)4{g?HS(|UYp=mP)C1ubIQ+Uyqf8+aH9M-l-GTnmisX7 zgA2bw!z+dvTwr_!<9S6NGX8EIed()6uzyoWzX>bYqMPKN`G+OjmI<0O0WUz!c)!mK z1Yr>4yVcRpVm!Vh$BQ@ME5^G5r-0vTb|~ba9&Q_C%X<9LwRE=@AEMqW|C^I{YWh5c9 zgAV`Zf>$={pnus&;8d2u?;exqGCAtv^?~uz>hN5T^W2YPcWO81bA6pKlL?l5E)cjP z=lMB4OHCauX6as*o@MEEmOfyqiKRB|IQC#^h?K(I>cogBmiA+5HcPLumfm0~?-AYu zyk+Ec$mLjS5!N z%1(aO%r=s2)_ORW2;*)+J~=yebXsz5UiSF({^==W1fY#(#BFxdW>1+kJuNSJWO`oA zgp~0pePu@CXWNMdWW;QDcskS1_7qFVPqUlD)5*!%j-6M_!DnuICYtmcH5@HA$QuKd zH$H7_4#H9fGt;vW?qM)X5)&_dH(lvpB1C9c;SO)h{Bt{Amu;6fYr_8_Z_uScFla1*tZ{C1MD2qW{N$& zG%pc%$$mD=H}|J=;Wcc~pU$2Pb2u_SC8n6e=Za>c(5->>AT+^B`N55!<0I?Ali(*9 z7)m@=&FbC9P5uC#j}9Pzhi($N#Ha9?&E* zdo^{-uC)_y3 zw|TC>4TPU`#WI5933c%LI{2?T*row>$n672=v^71zPnCdw zj=*adV>!lxaT@DoD6Q?UPcc?Z>q+HCX0Ii;p{${dZ3nMJvARQvTZu|p#@JPiEt7Q5 zGgfR!b8M~OVcO~{;#{S0Ge@O~aDP3FB(%vxEmAEZb*#u}%F*>LNc|Uq*}Y_6p~XuS6@u8Qq)F;pFxrm2xs; z*D-c7aa*iXo?`4%#-2jf)=5Rn3xaJJ9XqLr`(i(3HlrsqI-A@E7v+pS!q{@+wnSyy z#-F-q1aIlkMH_#Ty2LM`UjU`YGMZ!1kosp3KAypa80XZd82jg^AW1BgGW(y%?IkK@ z&tS?L8QYV%EmbMkGS&$`7sM*8B`LtZWNatKzLZ#Pd+H&Lv9K^fcMW6bFm{c^)-bk| zu{9)R8PtKjm$}H;o@5QMX^b^7HjUg~rb_sNv90WB%{azZ<19&|mHF5UpJMEV8_a$% zv%f)7maCMhA=GREV^hf*V7D;#6l1rL+sjq9zHH(t;OR=5I-$gUgPEUB1MCG8oj+Lo%`ktmuxU=^k(tz} zJ>XC6`=T6b%U?9-A_gD+muB@%`Z(vJ~KnL!RA7}Us!xaoyGklfd1}?M$w-|2Eu#sV08N>0$u4s@q1Po6GQu)GA zM)1(SfMFh~uVa`;-bWbbf%FrGdBp7ON(WSGY=rzzG6JbZb@6nNm`4Ph7-z(bZH4D(=RF~dAexy&$+PwXKwLwg>X z#4^ldl8F+A@#o>k1*X8m3s)cNfCm<74D*O$C&N6HP(Ub$1@a)GH^V%9Sj;dF9IhkQ zVGUVu;03`MI^e;=c!qhHaF$^n57l5{2d9A9b$g+E@zk@ zu_qYjM`#tp{P2`Pq=xqVnDk_rACx^9<|kyPAB;a5@Uw9dD#!(azpveb*c|>{gPwGs zIEc7j?R(rSfSn;~=KmB$70Mgeo}P_hXc6<%Gmv3^dJfb9;S5m+e-r*S!~D(qZie}r z_=gPhSNBd3LZUr?)7}d)9KZZE;aI>8KRJ%)ApYby)~!MU4i9!73%@eVkK=a?^W!)o zfa>$(IEP_=9DmO+KaNi_%#Y*8471}{CpZOChx9xagzgOU<9H;){5YP;Fh7o$G0czS zBMkH7_yNQGIJRs@%cI9JjDH{_r1KbhZXm<_IG)WgKaLMF%#Y(&4D;jI8NzSuQGOgp zG0czS6o#ea7|)*>jKK34uk{S`;f zHZUjfn*eJp(3KITJCy6MnCZ+E(z6AsNmr9PxJ?}#UI%L>9Qn~cqmKNmI(QkyW~{j0 z!j{hop}da4**fwob@0tP*ho%nUhjsV{Mf*+5&V?!YYe|8@Uw$oQ}{WMZ+~n-e)%!V z+!m0%j9lKl{C_gCw{R7bdv%HJnoJV(4m&Ki-jMHjwOuCKP8gM&F)<}q1z)y}hL^*+ zs$Al?yC2Ee*}NVTk$F4ALsCbMojhSg#)OQl^z1RC(lb(rPs~W2oRyhBE_;NkOa7?L z;i{Ody!7#7(}#~r$xF}m%gP>=H9UP1iQj48&@WX|=$)RKo|2o66basS>pv8|!*BNx zGqJ-C!`|iAV!It)$2vK-d2_?644PUFJrA3nbio#le~}Vj(tprJc2Ii25)`TAo4f9^ zJ7uKoZjdgpYya4G9ilpP>mnp}^>O~?kmtI|>Z`FkU6#}+iWA1(Zgc`(^ew=%!OSMb(>vfUq3;untyO4S_b*ovJsygxL zy^-xI?rb}{DzxpDkP%b-4w>!$t!!)Z4@17mOEyejvgFUZjcP_#HSoW*_I1`St6ndc zpTBqN=aG%uM70*Z_iK86KlR0jSqbX&mch51bgp31= z8hUv7v8Fd--~Z4e{rgON3x|cu-P>|wH%8}o9Mkf&tp5*MpFr;=jV+GfRG#sw34M{$ zw!!w{@6POXTdJMk;pmV3J%h^1d{(V{ntpaiPp#fgdT9bAB*IQoct1J7KQ1NBH7b2VdS-S``uH%{QEZm^H~Y0t8D{=Vhe@86YO z>@>M`pAh}iGVi^*>{AbtV<+`ETp7H#YR2CKT4s-_8UJJI5Q~fNy8US7Uh<&xpF`SO zjJqLUURYE;pv~FWL$gob&+-@>8{`aAo-%K6+?}Ut~=bNf@kLvFnVrw!AH7$WmxvAbPHr-)M_J>RiH`b#Z{Pm2?)Jc$&rDy` zTwT*;;nJpNNexN|H}Ck>^z#<;UVqzm@hDyKpvs{gwU(a{tgT6=to(zQ{tUd8(^TGi&xSuQ`>SNHSG
    o_taep%CLv$qWN?Xsm)=!yJO zecvpclJ?B$!i2(*t_y1_w95nUG(F<4nC%lX@Rv(3#}C)_SlhsVX7(5Lr@l)W^taXO zuUqV!bE;J5*m1#`U$rgQd8UrhO)KuyW0ULh9U$OIvMcmXebD#9+RJC|A{DCa6=exDbH}(HM%%w83+B0#S zA^+&sGgf=5%Ga$uV|LbT$ga597T=6{WcuaLqHfbRChh5|OP?t3?)7D_SAGYRM*UL0 zzro|UL?5Ss-(w5zezUhj!}pi{6b(DQxzPY7scj0mVeBA&xua>N(V_|QpCSKZwV6zm z$t+^NUFY`m@pCtc?encV|KNbY0RN!&{y~H6tph?nf&SAM2Q>G0Ft2a9z)Me_w;3*q zR`mn5(fvaQw2K-L5)kU&!Q8B$+|26#d+rA`_E(~51FQPpwfk8>3;$-w*0-`vq`S9k zpNy2;j8VfgQpS_v|M=;S&UN{7<;B588$7CJ448k)W7>wWjsEh)raisur(D&ye?IJZ z;tRhC<{Lj4r~hI3^`!fkR<-uC^&ITn^vlK-!TyCya-S*wYW!lcPSyP%Md`BU>a&v` z9ZW23Skm0@yJn9w@9d74vivt?zMaE7m!xy!4)5#sUFwUJgIV3n&b^Ew8^&$Wz0s#t zT2{aH=pUP8y?f#Npp%#9yj*eV&~G+!`%P=d&WqS|z;=oM$-tY1uaYv`2L>NMJ*C*z z@3ilLhLI;*O`jV6ZtHooH@lzp-gCHhR?_=6K>eC^7V?~+%J zpHLpU?W*7A^3E$y=6*ebB>Q-x|LS9OfJ9 z(7ko}@DCSaXQhVBG<~l+F!g-f%fFs@AATu3eadL-sYPz<&ks>edp%nDW?2s(hi)b7 ze(hQ{`?S}Ut8u*^$|@c&E$-gzTc3gD)(5W~n!PA+{o9S{fiLXGx7c@Br(2RSBHwhm z-4mAy35xxu`M1U$eO_R?IP3VRCmS@npvoQ-k9Aw#ICae1?T#b%|5ZHoqR+w_2ls!c zrjL!>F@Mm(ljl|zd)_~A?4DC{df4H)E4!vl*A$(MI@+wJ+NNy7SLfQ!-(eN2e&X_n z=Rcc{?ak2t*t^w|CD;C0q-Gk!Q+6k@uP7WQ|Ga;hBrDeEkcuHPMC(Q9o|D2rc;EZ0G zqqE|oyT^p|>^gjObbj23fvwv`lYd`_>6+v&95%u;x+-<_tmxQhL*~v5P~X1ye0k!_ z6_Im$WMvfTe7oG=p45F|qSxsAo2wUI{APdKfl5=$OOt;;o9FiCq-V!b;f%n0pWw8e@?5h6D@vrSa&FZvkVY|ep zjjI<~SN*ldI%dFcnYt5uU-wbXy1O#ChjD*HyTC)6y$-spcD%js?X)-F>1%9P&6;<| z_pjzh67BQjR&7rn6yDoyNO|U@lF~(ucWU#E*PO3ED}Ph<>$YpMukP#!c>AOgd|VLp F{{SX0Z%qII diff --git a/installer/mac/SymphonySettingsPlugin.bundle/Contents/Resources/Base.lproj/MyInstallerPane.nib b/installer/mac/SymphonySettingsPlugin.bundle/Contents/Resources/Base.lproj/MyInstallerPane.nib index e0e1caf6d9e6f6b559d533ed0b0c2208854f2a26..04c2ab45791496570d9acdf62e8aba35a822c55c 100644 GIT binary patch delta 4062 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 PME&wx diff --git a/installer/mac/SymphonySettingsPlugin.bundle/Contents/_CodeSignature/CodeResources b/installer/mac/SymphonySettingsPlugin.bundle/Contents/_CodeSignature/CodeResources index 97c3e45b..c2d4bc58 100644 --- a/installer/mac/SymphonySettingsPlugin.bundle/Contents/_CodeSignature/CodeResources +++ b/installer/mac/SymphonySettingsPlugin.bundle/Contents/_CodeSignature/CodeResources @@ -6,7 +6,7 @@ Resources/Base.lproj/MyInstallerPane.nib - GZWN47BPj6b6mD6MnSVH/Qn0m3s= + 4+CUZ1pazjwZ3nhbwM+NddoYZ1k= Resources/InstallerSections.plist @@ -37,11 +37,11 @@ hash - GZWN47BPj6b6mD6MnSVH/Qn0m3s= + 4+CUZ1pazjwZ3nhbwM+NddoYZ1k= hash2 - cJ/kr1HEozYL0YeZriWDtnOL1NEd22vHzH5WGp1T3hk= + 1zIxJqDs3dAZH36I2+CHfZtMpD5NB6xVDsGcWEQ1P2A= Resources/InstallerSections.plist diff --git a/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin.xcodeproj/project.pbxproj b/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin.xcodeproj/project.pbxproj index 715569dd..1cadf319 100644 --- a/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin.xcodeproj/project.pbxproj +++ b/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin.xcodeproj/project.pbxproj @@ -97,7 +97,7 @@ TargetAttributes = { 3A10EBCE1ED4336D0083702F = { CreatedOnToolsVersion = 8.3.2; - DevelopmentTeam = 2CWJ37D7FB; + DevelopmentTeam = NG92SF5D2E; ProvisioningStyle = Automatic; }; }; @@ -279,7 +279,7 @@ buildSettings = { CODE_SIGN_IDENTITY = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 2CWJ37D7FB; + DEVELOPMENT_TEAM = NG92SF5D2E; INFOPLIST_FILE = SymphonySettingsPlugin/Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; MACOSX_DEPLOYMENT_TARGET = 10.7; @@ -295,7 +295,7 @@ buildSettings = { CODE_SIGN_IDENTITY = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 2CWJ37D7FB; + DEVELOPMENT_TEAM = NG92SF5D2E; INFOPLIST_FILE = SymphonySettingsPlugin/Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; MACOSX_DEPLOYMENT_TARGET = 10.7; diff --git a/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin/MyInstallerPane.m b/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin/MyInstallerPane.m index acb51799..3c6c99e4 100644 --- a/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin/MyInstallerPane.m +++ b/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin/MyInstallerPane.m @@ -24,9 +24,10 @@ NSString *podUrl = [_podUrlTextBox stringValue]; // Check if the url contains a protocol, if not, prepend https to it - NSString *prefix = @"https://"; - if (![podUrl hasPrefix:prefix]) { - podUrl = [prefix stringByAppendingString:podUrl]; + NSString *prefix1 = @"https://"; + NSString *prefix2 = @"http://"; + if (![podUrl hasPrefix:prefix1] && ![podUrl hasPrefix:prefix2]) { + podUrl = [prefix1 stringByAppendingString:podUrl]; [_podUrlTextBox setStringValue:podUrl]; } From fbf051b3a3423bc1181abb94d64bf673ce025be6 Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Thu, 28 Dec 2017 15:29:37 +0530 Subject: [PATCH 06/21] Fixed tests --- demo/search.html | 1 - tests/Search.test.js | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/demo/search.html b/demo/search.html index d0c34550..ce8084a0 100644 --- a/demo/search.html +++ b/demo/search.html @@ -220,7 +220,6 @@ }); sendMessage.addEventListener('click', function () { - search.deleteRealTimeFolder(); if (realTimeIndexing.value !== "") { let message = realTimeIndexing.value; search.batchRealTimeIndexing(JSON.parse(message)); diff --git a/tests/Search.test.js b/tests/Search.test.js index 916786bd..88ce0c96 100644 --- a/tests/Search.test.js +++ b/tests/Search.test.js @@ -274,9 +274,12 @@ describe('Tests for Search', function() { }]; const batchRealTimeIndexing = jest.spyOn(SearchApi, 'batchRealTimeIndexing'); + const realTimeIndexing = jest.spyOn(SearchApi, 'realTimeIndexing'); SearchApi.isRealTimeIndexing = true; + expect(SearchApi.checkIsRealTimeIndexing()).toBe(true); SearchApi.batchRealTimeIndexing(message); expect(batchRealTimeIndexing).toHaveBeenCalled(); + expect(realTimeIndexing).not.toBeCalled(); setTimeout(function () { SearchApi.searchQuery('isRealTimeIndexing', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { @@ -491,5 +494,4 @@ describe('Tests for Search', function() { }); }); }); - }); \ No newline at end of file From ee6cf8732b7398df2370cfdad2cd16734f65e049 Mon Sep 17 00:00:00 2001 From: Kiran Niranjan Date: Thu, 28 Dec 2017 17:18:00 +0530 Subject: [PATCH 07/21] Electron-253 Added type button to prevent form submit --- js/basicAuth/basic-auth.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/basicAuth/basic-auth.html b/js/basicAuth/basic-auth.html index 8eee4110..fd5ea733 100644 --- a/js/basicAuth/basic-auth.html +++ b/js/basicAuth/basic-auth.html @@ -87,7 +87,7 @@
    - +
    From c48bb2225e657f6f1c1b7b18ab837e910e4d53ec Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Fri, 29 Dec 2017 12:04:40 +0530 Subject: [PATCH 08/21] SEARCH-538 - Test for search limit --- demo/search.html | 2 +- tests/Search.test.js | 75 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/demo/search.html b/demo/search.html index ce8084a0..dc92aae1 100644 --- a/demo/search.html +++ b/demo/search.html @@ -182,7 +182,7 @@ threadIdObj = JSON.parse(threadIdEl.value); } let _has = has.value || null; - search.searchQuery(queryEl.value, senderIdObj, threadIdObj, _has, startDate, endDate, limitEl.value, offsetEl.value, 0).then(function (result) { + search.searchQuery(queryEl.value, senderIdObj, threadIdObj, _has, startDate, endDate, parseInt(limitEl.value, 10), parseInt(offsetEl.value, 10), 0).then(function (result) { if (result.messages.length < 1) { resultsEl.innerHTML = "No results found" } diff --git a/tests/Search.test.js b/tests/Search.test.js index 88ce0c96..1d271947 100644 --- a/tests/Search.test.js +++ b/tests/Search.test.js @@ -39,7 +39,7 @@ describe('Tests for Search', function() { jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000; beforeAll(function (done) { - childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) { + /*childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) { userId = 12345678910112; key = 'jjjehdnctsjyieoalskcjdhsnahsadndfnusdfsdfsd='; @@ -56,7 +56,22 @@ describe('Tests for Search', function() { dataFolderPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data'); done(); - }); + });*/ + userId = 12345678910112; + key = 'jjjehdnctsjyieoalskcjdhsnahsadndfnusdfsdfsd='; + + executionPath = path.join(__dirname, 'library'); + userConfigDir = path.join(__dirname, '..'); + + searchConfig = require('../js/search/searchConfig.js'); + const { Search } = require('../js/search/search.js'); + SearchApi = new Search(userId, key); + + realTimeIndexPath = path.join(userConfigDir, 'data', 'temp_realtime_index'); + tempBatchPath = path.join(userConfigDir, 'data', 'temp_batch_indexes'); + dataFolderPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data'); + + done(); }); afterAll(function (done) { @@ -128,16 +143,34 @@ describe('Tests for Search', function() { describe('Batch indexing process tests', function () { it('should index in a batch', function (done) { - let messages = [ { - messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", - threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", - ingestionDate: currentDate.toString(), - senderId: "71811853189212", - chatType: "CHATROOM", - isPublic: "false", - sendingApp: "lc", - text: "it works" - } ]; + let messages = [{ + messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", + threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", + ingestionDate: currentDate.toString(), + senderId: "71811853189212", + chatType: "CHATROOM", + isPublic: "false", + sendingApp: "lc", + text: "it works" + }, { + messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", + threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", + ingestionDate: currentDate.toString(), + senderId: "71811853189212", + chatType: "CHATROOM", + isPublic: "false", + sendingApp: "lc", + text: "it works" + }, { + messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==", + threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==", + ingestionDate: currentDate.toString(), + senderId: "71811853189212", + chatType: "CHATROOM", + isPublic: "false", + sendingApp: "lc", + text: "it works" + }]; const indexBatch = jest.spyOn(SearchApi, 'indexBatch'); SearchApi.indexBatch(JSON.stringify(messages)).then(function () { expect(fs.existsSync(tempBatchPath)).toBe(true); @@ -225,7 +258,7 @@ describe('Tests for Search', function() { it('should match messages length after batch indexing', function (done) { const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); SearchApi.searchQuery('it works', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) { - expect(res.messages.length).toEqual(1); + expect(res.messages.length).toEqual(3); expect(searchQuery).toHaveBeenCalled(); done(); }); @@ -254,7 +287,7 @@ describe('Tests for Search', function() { it('should match message length', function (done) { const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); SearchApi.searchQuery('realtime working', ["71811853189212"], ["Au8O2xKHyX1LtE6zW019GX///rZYegAtdA=="], '', undefined, undefined, 25, 0, 0).then(function (res) { - expect(res.messages.length).toEqual(1); + expect(res.messages.length).toEqual(3); expect(fs.existsSync(realTimeIndexPath)).toBe(true); expect(searchQuery).toHaveBeenCalled(); done(); @@ -428,7 +461,7 @@ describe('Tests for Search', function() { let endTime = new Date().getTime(); let startTime = new Date().getTime() - (4 * 31 * 24 * 60 * 60 * 1000); SearchApi.searchQuery('it works', [], [], '', startTime.toString(), endTime.toString(), '0', 0.2, 0.1).then(function (res) { - expect(res.messages.length).toEqual(1); + expect(res.messages.length).toEqual(3); expect(searchQuery).toHaveBeenCalled(); done() }); @@ -449,12 +482,22 @@ describe('Tests for Search', function() { }); }); + it('should filter search limit ', function (done) { + const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); + SearchApi.searchQuery('works', [], [], '', '', '', 2, 0, 0).then(function (res) { + expect(res.messages.length).toBe(2); + expect(searchQuery).toHaveBeenCalledTimes(7); + expect(searchQuery).toHaveBeenCalled(); + done(); + }); + }); + it('should search fails index folder not fund', function (done) { const searchQuery = jest.spyOn(SearchApi, 'searchQuery'); deleteIndexFolders(dataFolderPath); SearchApi.searchQuery('it works', [], [], '', '', '', 25, 0, 0).catch(function (err) { expect(err).toEqual(new Error('Index folder does not exist.')); - expect(searchQuery).toHaveBeenCalledTimes(7); + expect(searchQuery).toHaveBeenCalledTimes(8); expect(searchQuery).toHaveBeenCalled(); SearchApi = undefined; const { Search } = require('../js/search/search.js'); From d1e760489529bcfaff75ec153d45ce2e00a516e4 Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Fri, 29 Dec 2017 12:06:19 +0530 Subject: [PATCH 09/21] SEARCH-538 - Removed comment --- tests/Search.test.js | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/tests/Search.test.js b/tests/Search.test.js index 1d271947..637afd42 100644 --- a/tests/Search.test.js +++ b/tests/Search.test.js @@ -39,7 +39,7 @@ describe('Tests for Search', function() { jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000; beforeAll(function (done) { - /*childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) { + childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) { userId = 12345678910112; key = 'jjjehdnctsjyieoalskcjdhsnahsadndfnusdfsdfsd='; @@ -56,22 +56,7 @@ describe('Tests for Search', function() { dataFolderPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data'); done(); - });*/ - userId = 12345678910112; - key = 'jjjehdnctsjyieoalskcjdhsnahsadndfnusdfsdfsd='; - - executionPath = path.join(__dirname, 'library'); - userConfigDir = path.join(__dirname, '..'); - - searchConfig = require('../js/search/searchConfig.js'); - const { Search } = require('../js/search/search.js'); - SearchApi = new Search(userId, key); - - realTimeIndexPath = path.join(userConfigDir, 'data', 'temp_realtime_index'); - tempBatchPath = path.join(userConfigDir, 'data', 'temp_batch_indexes'); - dataFolderPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data'); - - done(); + }); }); afterAll(function (done) { From c13bfd5f14bbee538f03aca76606437c8cb3064b Mon Sep 17 00:00:00 2001 From: Vishwas Shashidhar Date: Fri, 29 Dec 2017 12:48:19 +0530 Subject: [PATCH 10/21] electron-205: fixes the already fixed issue post merge conflict regression --- js/downloadManager/index.js | 2 +- js/windowMgr.js | 132 +++++++++++++++++------------------- 2 files changed, 65 insertions(+), 69 deletions(-) diff --git a/js/downloadManager/index.js b/js/downloadManager/index.js index 5ef5ed9e..3f1fbba5 100644 --- a/js/downloadManager/index.js +++ b/js/downloadManager/index.js @@ -56,7 +56,7 @@ function showInFinder(id) { function createDOM(arg) { if (arg && arg._id) { - + let fileDisplayName = arg.fileName; let downloadItemKey = arg._id; diff --git a/js/windowMgr.js b/js/windowMgr.js index d756c179..ff1e144a 100644 --- a/js/windowMgr.js +++ b/js/windowMgr.js @@ -145,7 +145,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { } // will set the main window on top as per the user prefs - if (alwaysOnTop){ + if (alwaysOnTop) { newWinOpts.alwaysOnTop = alwaysOnTop; } @@ -179,7 +179,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { loadErrors.showNetworkConnectivityError(mainWindow, url, retry); } else { // updates the notify config with user preference - notify.updateConfig({position: position, display: display}); + notify.updateConfig({ position: position, display: display }); // removes all existing notifications when main window reloads notify.reset(); log.send(logLevels.INFO, 'loaded main window url: ' + url); @@ -188,7 +188,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { }); mainWindow.webContents.on('did-fail-load', function (event, errorCode, - errorDesc, validatedURL) { + errorDesc, validatedURL) { loadErrors.showLoadFailure(mainWindow, validatedURL, errorDesc, errorCode, retry, false); }); @@ -218,7 +218,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { const menu = electron.Menu.buildFromTemplate(getTemplate(app)); electron.Menu.setApplicationMenu(menu); - mainWindow.on('close', function(e) { + mainWindow.on('close', function (e) { if (willQuitApp) { destroyAllWindows(); return; @@ -259,7 +259,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { // When download is in progress, send necessary data to indicate the same webContents.send('downloadProgress'); - + // An extra check to see if the user created downloads directory has been deleted // This scenario can occur when user doesn't quit electron and continues using it // across days and then deletes the folder. @@ -267,21 +267,17 @@ function doCreateMainWindow(initialUrl, initialBounds) { downloadsDirectory = defaultDownloadsDirectory; updateConfigField("downloadsDirectory", downloadsDirectory); } - + // We check the downloads directory to see if a file with the similar name // already exists and get a unique filename if that's the case let newFileName = getUniqueFileName(item.getFilename()); - item.setSavePath(downloadsDirectory + "/" + newFileName); - - // Send file path to construct the DOM in the UI when the download is complete - - // if the user has set a custom downloads directory, save file to that directory - // if otherwise, we save it to the operating system's default downloads directory - if (downloadsDirectory) { - item.setSavePath(downloadsDirectory + "/" + item.getFilename()); + if (isMac) { + item.setSavePath(downloadsDirectory + "/" + newFileName); + } else { + item.setSavePath(downloadsDirectory + "\\" + newFileName); } - // Send file path when download is complete + // Send file path to construct the DOM in the UI when the download is complete item.once('done', (e, state) => { if (state === 'completed') { let data = { @@ -296,25 +292,25 @@ function doCreateMainWindow(initialUrl, initialBounds) { }); getConfigField('url') - .then(initializeCrashReporter) - .catch(app.quit); - - function initializeCrashReporter(podUrl) { + .then(initializeCrashReporter) + .catch(app.quit); + + function initializeCrashReporter(podUrl) { getConfigField('crashReporter') - .then((crashReporterConfig) => { - log.send(logLevels.INFO, 'Initializing crash reporter on the main window!'); - crashReporter.start({companyName: crashReporterConfig.companyName, submitURL: crashReporterConfig.submitURL, uploadToServer: crashReporterConfig.uploadToServer, extra: {'process': 'renderer / main window', podUrl: podUrl}}); - log.send(logLevels.INFO, 'initialized crash reporter on the main window!'); - mainWindow.webContents.send('register-crash-reporter', {companyName: crashReporterConfig.companyName, submitURL: crashReporterConfig.submitURL, uploadToServer: crashReporterConfig.uploadToServer, process: 'preload script / main window renderer'}); - }) - .catch((err) => { - log.send(logLevels.ERROR, 'Unable to initialize crash reporter in the main window. Error is -> ' + err); - }); - } + .then((crashReporterConfig) => { + log.send(logLevels.INFO, 'Initializing crash reporter on the main window!'); + crashReporter.start({ companyName: crashReporterConfig.companyName, submitURL: crashReporterConfig.submitURL, uploadToServer: crashReporterConfig.uploadToServer, extra: { 'process': 'renderer / main window', podUrl: podUrl } }); + log.send(logLevels.INFO, 'initialized crash reporter on the main window!'); + mainWindow.webContents.send('register-crash-reporter', { companyName: crashReporterConfig.companyName, submitURL: crashReporterConfig.submitURL, uploadToServer: crashReporterConfig.uploadToServer, process: 'preload script / main window renderer' }); + }) + .catch((err) => { + log.send(logLevels.ERROR, 'Unable to initialize crash reporter in the main window. Error is -> ' + err); + }); + } // open external links in default browser - a tag with href='_blank' or window.open mainWindow.webContents.on('new-window', function (event, newWinUrl, - frameName, disposition, newWinOptions) { + frameName, disposition, newWinOptions) { let newWinParsedUrl = getParsedUrl(newWinUrl); let mainWinParsedUrl = getParsedUrl(url); @@ -346,7 +342,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { let newX = Number.parseInt(query.x, 10); let newY = Number.parseInt(query.y, 10); - let newWinRect = {x: newX, y: newY, width, height}; + let newWinRect = { x: newX, y: newY, width, height }; // only accept if both are successfully parsed. if (Number.isInteger(newX) && Number.isInteger(newY) && @@ -359,7 +355,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { } } else { // create new window at slight offset from main window. - ({x, y} = getWindowSizeAndPosition(mainWindow)); + ({ x, y } = getWindowSizeAndPosition(mainWindow)); x += 50; y += 50; } @@ -393,18 +389,18 @@ function doCreateMainWindow(initialUrl, initialBounds) { } getConfigField('url') - .then((podUrl) => { - getConfigField('crashReporter') - .then((crashReporterConfig) => { - crashReporter.start({companyName: crashReporterConfig.companyName, submitURL: crashReporterConfig.submitURL, uploadToServer: crashReporterConfig.uploadToServer, extra: {'process': 'renderer / child window', podUrl: podUrl}}); - log.send(logLevels.INFO, 'initialized crash reporter on a child window!'); - browserWin.webContents.send('register-crash-reporter', {companyName: crashReporterConfig.companyName, submitURL: crashReporterConfig.submitURL, uploadToServer: crashReporterConfig.uploadToServer, process: 'preload script / child window renderer'}); + .then((podUrl) => { + getConfigField('crashReporter') + .then((crashReporterConfig) => { + crashReporter.start({ companyName: crashReporterConfig.companyName, submitURL: crashReporterConfig.submitURL, uploadToServer: crashReporterConfig.uploadToServer, extra: { 'process': 'renderer / child window', podUrl: podUrl } }); + log.send(logLevels.INFO, 'initialized crash reporter on a child window!'); + browserWin.webContents.send('register-crash-reporter', { companyName: crashReporterConfig.companyName, submitURL: crashReporterConfig.submitURL, uploadToServer: crashReporterConfig.uploadToServer, process: 'preload script / child window renderer' }); + }) + .catch((err) => { + log.send(logLevels.ERROR, 'Unable to initialize crash reporter in the child window. Error is -> ' + err); + }); }) - .catch((err) => { - log.send(logLevels.ERROR, 'Unable to initialize crash reporter in the child window. Error is -> ' + err); - }); - }) - .catch(app.quit); + .catch(app.quit); browserWin.winName = frameName; browserWin.setAlwaysOnTop(alwaysOnTop); @@ -412,7 +408,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { let handleChildWindowClosed = () => { removeWindowKey(newWinKey); browserWin.removeListener('move', throttledBoundsChange); - browserWin.removeListener('resize', throttledBoundsChange); + browserWin.removeListener('resize', throttledBoundsChange); }; browserWin.once('closed', () => { @@ -448,11 +444,11 @@ function doCreateMainWindow(initialUrl, initialBounds) { childEvent.preventDefault(); openUrlInDefaultBrowser(childWinUrl); }; - + // In case we navigate to an external link from inside a pop-out, // we open that link in an external browser rather than creating // a new window - browserWin.webContents.on('new-window', handleChildNewWindowEvent); + browserWin.webContents.on('new-window', handleChildNewWindowEvent); addWindowKey(newWinKey, browserWin); @@ -465,7 +461,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { let throttledBoundsChange = throttle(1000, sendChildWinBoundsChange.bind(null, browserWin)); browserWin.on('move', throttledBoundsChange); - browserWin.on('resize', throttledBoundsChange); + browserWin.on('resize', throttledBoundsChange); } }); } else { @@ -475,14 +471,14 @@ function doCreateMainWindow(initialUrl, initialBounds) { }); // whenever the main window is navigated for ex: window.location.href or url redirect - mainWindow.webContents.on('will-navigate', function(event, navigatedURL) { + mainWindow.webContents.on('will-navigate', function (event, navigatedURL) { deleteIndexFolder(); isWhitelisted(navigatedURL) .catch(() => { event.preventDefault(); electron.dialog.showMessageBox(mainWindow, { type: 'warning', - buttons: [ 'Ok' ], + buttons: ['Ok'], title: 'Not Allowed', message: `Sorry, you are not allowed to access this website (${navigatedURL}), please contact your administrator for more details`, }); @@ -628,7 +624,7 @@ function sendChildWinBoundsChange(window) { * @param urlToOpen */ function openUrlInDefaultBrowser(urlToOpen) { - if (urlToOpen) { + if (urlToOpen) { electron.shell.openExternal(urlToOpen); } } @@ -677,7 +673,7 @@ eventEmitter.on('notificationSettings', (notificationSettings) => { function verifyDisplays() { // This is only for Windows, macOS handles this by itself - if (!mainWindow || isMac){ + if (!mainWindow || isMac) { return; } @@ -687,14 +683,14 @@ function verifyDisplays() { let isYAxisValid = true; // checks to make sure the x,y are valid pairs - if ((bounds.x === undefined && (bounds.y || bounds.y === 0))){ + if ((bounds.x === undefined && (bounds.y || bounds.y === 0))) { isXAxisValid = false; } - if ((bounds.y === undefined && (bounds.x || bounds.x === 0))){ + if ((bounds.y === undefined && (bounds.x || bounds.x === 0))) { isYAxisValid = false; } - if (!isXAxisValid && !isYAxisValid){ + if (!isXAxisValid && !isYAxisValid) { return; } @@ -725,7 +721,7 @@ function checkExternalDisplay(appBounds) { // Loops through all the available displays and // verifies if the wrapper exists within the display bounds // returns false if not exists otherwise true - return !!screen.getAllDisplays().find(({bounds}) => { + return !!screen.getAllDisplays().find(({ bounds }) => { const leftMost = x + (width * factor); const topMost = y + (height * factor); @@ -748,7 +744,7 @@ function checkExternalDisplay(appBounds) { function repositionMainWindow() { const screen = electron.screen; - const {workArea} = screen.getPrimaryDisplay(); + const { workArea } = screen.getPrimaryDisplay(); const bounds = workArea; if (!bounds) { @@ -765,10 +761,10 @@ function repositionMainWindow() { const x = Math.round(centerX - (windowWidth / 2.0)); const y = Math.round(centerY - (windowHeight / 2.0)); - let rectangle = {x, y, width: windowWidth, height: windowHeight}; + let rectangle = { x, y, width: windowWidth, height: windowHeight }; // resetting the main window bounds - if (mainWindow){ + if (mainWindow) { if (!mainWindow.isVisible()) { mainWindow.show(); } @@ -790,33 +786,33 @@ function repositionMainWindow() { * @returns {String} the new filename */ function getUniqueFileName(filename) { - + // By default, we assume that the file exists const fileExists = true; - + // We break the file from it's extension to get the name const actualFilename = filename.substr(0, filename.lastIndexOf('.')) || filename; const fileType = filename.split('.').pop(); - + // We use this to set the new file name with an increment on the previous existing file let fileNumber = 0; let newPath; - + while (fileExists) { - + let fileNameString = fileNumber.toString(); - + // By default, we know if the file doesn't exist, // we can use the filename sent by the remote server let current = filename; - + // If the file already exists, we know that the // file number variable is increased, so, // we construct a new file name with the file number if (fileNumber > 0) { current = actualFilename + " (" + fileNameString + ")." + fileType; } - + // If the file exists, increment the file number and repeat the loop if (fs.existsSync(downloadsDirectory + "/" + current)) { fileNumber++; @@ -824,9 +820,9 @@ function getUniqueFileName(filename) { newPath = current; break; } - + } - + return newPath; } From c8101a9b1ee508abf5aaef02f3cb1544ec6d86ba Mon Sep 17 00:00:00 2001 From: Kiran Niranjan Date: Fri, 29 Dec 2017 15:59:19 +0530 Subject: [PATCH 11/21] Electron-222 Added a safety check to prevent from using an already destroyed notification window Added method to close all notification when notification config is changed --- js/notify/electron-notify.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/js/notify/electron-notify.js b/js/notify/electron-notify.js index 769b30e7..5ebc8c6a 100644 --- a/js/notify/electron-notify.js +++ b/js/notify/electron-notify.js @@ -165,6 +165,7 @@ function updateConfig(customConfig) { if (customConfig.display) { displayId = customConfig.display; } + closeAll(); } /** @@ -477,6 +478,13 @@ function setNotificationContents(notfWindow, notfObj) { */ function buildCloseNotification(notificationWindow, notificationObj, getTimeoutId) { return function(event) { + + // safety check to prevent from using an + // already destroyed notification window + if (notificationWindow.isDestroyed()) { + return new Promise(function(exitEarly) { exitEarly() }) + } + if (closedNotifications[notificationObj.id]) { delete closedNotifications[notificationObj.id]; return new Promise(function(exitEarly) { exitEarly() }); @@ -711,6 +719,34 @@ function cleanUpInactiveWindow() { inactiveWindows = []; } +/** + * Closes all the notifications and windows + */ +function closeAll() { + // Clear out animation Queue and close windows + animationQueue.clear(); + + activeNotifications.forEach(function(window) { + if (window.displayTimer) { + clearTimeout(window.displayTimer); + } + if (window.electronNotifyOnCloseFunc) { + // ToDo: fix this: shouldn't delete method on arg + /* eslint-disable */ + delete window.electronNotifyOnCloseFunc; + /* eslint-enable */ + } + window.close(); + }); + + cleanUpInactiveWindow(); + + // Reset certain vars + nextInsertPos = {}; + activeNotifications = []; +} + + module.exports.notify = notify; module.exports.updateConfig = updateConfig; module.exports.reset = setupConfig; From 988439b780a16a2bfebc41be6f4840af09a005b5 Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Fri, 29 Dec 2017 17:36:40 +0530 Subject: [PATCH 12/21] SEARCH-538 - User config unit tests --- js/search/searchUtils.js | 19 +++-- js/search/utils/checkDiskSpace.js | 21 ++--- tests/SearchUtils.test.js | 129 ++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 20 deletions(-) create mode 100644 tests/SearchUtils.test.js diff --git a/js/search/searchUtils.js b/js/search/searchUtils.js index 3ab3985f..4269f0f3 100644 --- a/js/search/searchUtils.js +++ b/js/search/searchUtils.js @@ -23,14 +23,7 @@ class SearchUtils { if (!isMac) { this.path = this.path.substring(0, 2); } - checkDiskSpace(this.path, function (error, res) { - - if (error) { - return reject(new Error(error)); - } - - return resolve(res >= searchConfig.MINIMUM_DISK_SPACE); - }); + checkDiskSpace(this.path, resolve, reject); }); } @@ -120,7 +113,13 @@ function createUser(userId, oldConfig) { function createUserConfigFile(userId, data) { let createStream = fs.createWriteStream(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE); if (data) { - createStream.write(`{"${userId}": ${JSON.stringify(data)}}`); + let jsonData; + try { + jsonData = JSON.stringify(data); + createStream.write(`{"${userId}": ${jsonData}}`); + } catch (e) { + createStream.write(`{"${userId}": {}}`); + } } else { createStream.write(`{"${userId}": {}}`); } @@ -149,7 +148,7 @@ function updateConfig(userId, data, resolve, reject) { oldConfig = JSON.parse(oldData); } catch (e) { createUserConfigFile(userId, data); - return reject('can not parse user config file data: ' + e); + return reject(new Error('can not parse user config file data: ' + e)); } let newConfig = Object.assign({}, oldConfig); diff --git a/js/search/utils/checkDiskSpace.js b/js/search/utils/checkDiskSpace.js index 08fd297e..f8d1c729 100644 --- a/js/search/utils/checkDiskSpace.js +++ b/js/search/utils/checkDiskSpace.js @@ -1,42 +1,43 @@ const { exec } = require('child_process'); const { isMac } = require('../../utils/misc'); +const searchConfig = require('../searchConfig.js'); -function checkDiskSpace(path, callback) { +function checkDiskSpace(path, resolve, reject) { if (!path) { - return "Please provide path" + reject(new Error("Please provide path")); + return; } if (isMac) { exec("df -k '" + path.replace(/'/g,"'\\''") + "'", (error, stdout, stderr) => { if (error) { if (stderr.indexOf("No such file or directory") !== -1) { - return callback("No such file or directory : " + error) + return reject(new Error("No such file or directory : " + error)) } - return callback("Error : " + error) + return reject(new Error("Error : " + error)); } let data = stdout.trim().split("\n"); let disk_info_str = data[data.length - 1].replace( /[\s\n\r]+/g,' '); let freeSpace = disk_info_str.split(' '); - return callback(null, freeSpace[3] * 1024); + let space = freeSpace[3] * 1024; + return resolve(space >= searchConfig.MINIMUM_DISK_SPACE); }); } else { exec(`fsutil volume diskfree ${path}`, (error, stdout, stderr) => { if (error) { if (stderr.indexOf("No such file or directory") !== -1) { - return callback("No such file or directory : " + error) + return reject(new Error("No such file or directory : " + error)); } - return callback("Error : " + error) + return reject(new Error("Error : " + error)); } let data = stdout.trim().split("\n"); let disk_info_str = data[data.length - 1].split(':'); - return callback(null, disk_info_str[1]); + return resolve(disk_info_str[1] >= searchConfig.MINIMUM_DISK_SPACE); }); } - - return null; } module.exports = { diff --git a/tests/SearchUtils.test.js b/tests/SearchUtils.test.js new file mode 100644 index 00000000..76172c7c --- /dev/null +++ b/tests/SearchUtils.test.js @@ -0,0 +1,129 @@ +const fs = require('fs'); +const path = require('path'); + +let executionPath = null; +let userConfigDir = null; + +let SearchUtilsAPI; +let searchConfig; + +jest.mock('electron', function() { + return { + app: { + getPath: mockedGetPath + } + } +}); + +function mockedGetPath(type) { + if (type === 'exe') { + return executionPath; + } + + if (type === 'userData') { + return userConfigDir + } + return ''; +} + +describe('Tests for Search Utils', function() { + + jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000; + + beforeAll(function (done) { + executionPath = path.join(__dirname, 'library'); + userConfigDir = path.join(__dirname, '..'); + searchConfig = require('../js/search/searchConfig.js'); + const { SearchUtils } = require('../js/search/searchUtils.js'); + SearchUtilsAPI = new SearchUtils(); + SearchUtilsAPI.path = userConfigDir; + done(); + }); + + afterAll(function (done) { + fs.unlinkSync(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE); + done(); + }); + + describe('Tests for checking disk space', function () { + + it('should return free sapce', function (done) { + const checkFreeSpace = jest.spyOn(SearchUtilsAPI, 'checkFreeSpace'); + SearchUtilsAPI.checkFreeSpace().then(function () { + expect(checkFreeSpace).toHaveBeenCalled(); + done(); + }); + }); + + it('should return error', function (done) { + const checkFreeSpace = jest.spyOn(SearchUtilsAPI, 'checkFreeSpace'); + SearchUtilsAPI.path = undefined; + SearchUtilsAPI.checkFreeSpace().catch(function (err) { + expect(err).toEqual(new Error("Please provide path")); + expect(checkFreeSpace).toHaveBeenCalled(); + done(); + }); + }); + + it('should return error invalid path', function (done) { + const checkFreeSpace = jest.spyOn(SearchUtilsAPI, 'checkFreeSpace'); + SearchUtilsAPI.path = './tp'; + SearchUtilsAPI.checkFreeSpace().catch(function (err) { + expect(checkFreeSpace).toHaveBeenCalled(); + expect(err).toEqual(err); + done(); + }); + }); + }); + + describe('Test for search users config', function () { + + it('should return null for new user config', function (done) { + SearchUtilsAPI.getSearchUserConfig(1234567891011).then(function (res) { + expect(res).toEqual(null); + done(); + }); + }); + + it('should exist users config file', function (done) { + setTimeout(function () { + expect(fs.existsSync(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE)).toEqual(true); + done(); + }, 2000) + }); + + it('should exist users config file', function (done) { + setTimeout(function () { + SearchUtilsAPI.getSearchUserConfig(1234567891011).then(function (res) { + expect(res).toEqual({}); + done(); + }); + }, 3000) + }); + + it('should update user config file', function (done) { + let data = { + rotationId: 0, + version: 1, + language: 'en' + }; + SearchUtilsAPI.updateUserConfig(1234567891011, data).then(function (res) { + expect(res).toEqual(data); + done(); + }) + }); + + it('should modify user config file', function (done) { + let data = { + rotationId: 1, + version: 1, + language: 'en' + }; + SearchUtilsAPI.updateUserConfig(1234567891011, data).then(function (res) { + expect(res.rotationId).toEqual(1); + done(); + }) + }); + }); + +}); \ No newline at end of file From 9010c341cbd523daaec1267801a7dad404fce4fd Mon Sep 17 00:00:00 2001 From: Kiran Niranjan Date: Fri, 29 Dec 2017 17:55:42 +0530 Subject: [PATCH 13/21] Electron-231 Fixes the installer config issue on Windows --- installer/win/Symphony-x64.aip | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/installer/win/Symphony-x64.aip b/installer/win/Symphony-x64.aip index 7ad2734c..7663bb00 100644 --- a/installer/win/Symphony-x64.aip +++ b/installer/win/Symphony-x64.aip @@ -24,7 +24,7 @@ - + @@ -642,7 +642,7 @@ - + From 2c3b35270bcb78cf0a30593d9310b96aea588045 Mon Sep 17 00:00:00 2001 From: Kiran Niranjan Date: Sat, 30 Dec 2017 12:49:39 +0530 Subject: [PATCH 14/21] Electron-156 Updated the code not to encode the URL and just validate the protocol --- js/main.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/js/main.js b/js/main.js index 8d3184de..8a93340a 100644 --- a/js/main.js +++ b/js/main.js @@ -291,17 +291,14 @@ function getUrlAndCreateMainWindow() { * @param urlFromConfig */ function createWin(urlFromConfig) { - let protocol = ''; // add https protocol if none found. let parsedUrl = nodeURL.parse(urlFromConfig); + if (!parsedUrl.protocol) { - protocol = 'https'; + parsedUrl.protocol = 'https:'; + parsedUrl.slashes = true } - let url = nodeURL.format({ - protocol: protocol, - slahes: true, - pathname: parsedUrl.href - }); + let url = nodeURL.format(parsedUrl); windowMgr.createMainWindow(url); } From 4322615e6b29e32dcf8dba975dca4071ced769ef Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Tue, 2 Jan 2018 10:12:14 +0530 Subject: [PATCH 15/21] SEARCH-538 - Included two new tests --- tests/SearchUtils.test.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/SearchUtils.test.js b/tests/SearchUtils.test.js index 76172c7c..d1b95d11 100644 --- a/tests/SearchUtils.test.js +++ b/tests/SearchUtils.test.js @@ -124,6 +124,26 @@ describe('Tests for Search Utils', function() { done(); }) }); + + it('should create user if not exist', function (done) { + SearchUtilsAPI.getSearchUserConfig(2234567891011).catch(function (err) { + expect(err).toEqual(null); + done(); + }) + }); + + it('should create file on update', function (done) { + fs.unlinkSync(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE); + let data = { + rotationId: 0, + version: 2, + language: 'en' + }; + SearchUtilsAPI.updateUserConfig(2234567891011, data).catch(function (err) { + expect(err).toEqual(null); + done(); + }) + }); }); }); \ No newline at end of file From 742dcf2517ab3561f0e8bb053614466cca183c48 Mon Sep 17 00:00:00 2001 From: Kiran Niranjan Date: Tue, 2 Jan 2018 13:00:28 +0530 Subject: [PATCH 16/21] Electron-250 Renamed the variables as per the review --- .../Contents/Info.plist | 12 ++++++------ .../Contents/MacOS/SymphonySettingsPlugin | Bin 68640 -> 68688 bytes .../Resources/Base.lproj/MyInstallerPane.nib | Bin 7351 -> 11948 bytes .../Contents/_CodeSignature/CodeResources | 6 +++--- .../SymphonySettingsPlugin/MyInstallerPane.m | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/installer/mac/SymphonySettingsPlugin.bundle/Contents/Info.plist b/installer/mac/SymphonySettingsPlugin.bundle/Contents/Info.plist index 324dd548..6c807c3a 100644 --- a/installer/mac/SymphonySettingsPlugin.bundle/Contents/Info.plist +++ b/installer/mac/SymphonySettingsPlugin.bundle/Contents/Info.plist @@ -3,7 +3,7 @@ BuildMachineOSBuild - 16G1036 + 17C88 CFBundleDevelopmentRegion en CFBundleExecutable @@ -27,17 +27,17 @@ DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild - 8E3004b + 9C40b DTPlatformVersion GM DTSDKBuild - 16E185 + 17C76 DTSDKName - macosx10.12 + macosx10.13 DTXcode - 0833 + 0920 DTXcodeBuild - 8E3004b + 9C40b InstallerSectionTitle Pod Settings NSHumanReadableCopyright diff --git a/installer/mac/SymphonySettingsPlugin.bundle/Contents/MacOS/SymphonySettingsPlugin b/installer/mac/SymphonySettingsPlugin.bundle/Contents/MacOS/SymphonySettingsPlugin index 6111d2c15aaae84375e64b42d117dbf677e5b8f3..bc7b725ad2d38b205fbb464dd952e1b8a43488c2 100755 GIT binary patch delta 16522 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 O5}yw*%}80h-v19q$wA5h delta 16554 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 BXhZ-2 diff --git a/installer/mac/SymphonySettingsPlugin.bundle/Contents/Resources/Base.lproj/MyInstallerPane.nib b/installer/mac/SymphonySettingsPlugin.bundle/Contents/Resources/Base.lproj/MyInstallerPane.nib index 04c2ab45791496570d9acdf62e8aba35a822c55c..e0e1caf6d9e6f6b559d533ed0b0c2208854f2a26 100644 GIT binary patch literal 11948 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 PME&wx delta 4062 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 Resources/Base.lproj/MyInstallerPane.nib - 4+CUZ1pazjwZ3nhbwM+NddoYZ1k= + GZWN47BPj6b6mD6MnSVH/Qn0m3s= Resources/InstallerSections.plist @@ -37,11 +37,11 @@ hash - 4+CUZ1pazjwZ3nhbwM+NddoYZ1k= + GZWN47BPj6b6mD6MnSVH/Qn0m3s= hash2 - 1zIxJqDs3dAZH36I2+CHfZtMpD5NB6xVDsGcWEQ1P2A= + cJ/kr1HEozYL0YeZriWDtnOL1NEd22vHzH5WGp1T3hk= Resources/InstallerSections.plist diff --git a/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin/MyInstallerPane.m b/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin/MyInstallerPane.m index 3c6c99e4..1ad998be 100644 --- a/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin/MyInstallerPane.m +++ b/installer/mac/SymphonySettingsPlugin/SymphonySettingsPlugin/MyInstallerPane.m @@ -24,10 +24,10 @@ NSString *podUrl = [_podUrlTextBox stringValue]; // Check if the url contains a protocol, if not, prepend https to it - NSString *prefix1 = @"https://"; - NSString *prefix2 = @"http://"; - if (![podUrl hasPrefix:prefix1] && ![podUrl hasPrefix:prefix2]) { - podUrl = [prefix1 stringByAppendingString:podUrl]; + NSString *securePrefix = @"https://"; + NSString *prefix = @"http://"; + if (![podUrl hasPrefix:securePrefix] && ![podUrl hasPrefix:prefix]) { + podUrl = [securePrefix stringByAppendingString:podUrl]; [_podUrlTextBox setStringValue:podUrl]; } From 22d913f6c1a5f82a24e9f9bd06f8b10984e914f1 Mon Sep 17 00:00:00 2001 From: Kiran Niranjan Date: Tue, 2 Jan 2018 14:32:40 +0530 Subject: [PATCH 17/21] Electron-222 - Removed the code which was deleting the method on args --- js/notify/electron-notify.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/js/notify/electron-notify.js b/js/notify/electron-notify.js index 5ebc8c6a..6bfb7cfb 100644 --- a/js/notify/electron-notify.js +++ b/js/notify/electron-notify.js @@ -730,12 +730,6 @@ function closeAll() { if (window.displayTimer) { clearTimeout(window.displayTimer); } - if (window.electronNotifyOnCloseFunc) { - // ToDo: fix this: shouldn't delete method on arg - /* eslint-disable */ - delete window.electronNotifyOnCloseFunc; - /* eslint-enable */ - } window.close(); }); From 0edc3d01e20bf66ecf80d3ad751b74f3c3c4b1d7 Mon Sep 17 00:00:00 2001 From: Vishwas Shashidhar Date: Tue, 2 Jan 2018 17:00:12 +0530 Subject: [PATCH 18/21] electron-258: fixes the pop out issue for meetings and supports undefined urls in case of new window events as per the html dom specs --- js/windowMgr.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/windowMgr.js b/js/windowMgr.js index ff1e144a..8f65c209 100644 --- a/js/windowMgr.js +++ b/js/windowMgr.js @@ -311,7 +311,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { // open external links in default browser - a tag with href='_blank' or window.open mainWindow.webContents.on('new-window', function (event, newWinUrl, frameName, disposition, newWinOptions) { - + let newWinParsedUrl = getParsedUrl(newWinUrl); let mainWinParsedUrl = getParsedUrl(url); @@ -320,7 +320,7 @@ function doCreateMainWindow(initialUrl, initialBounds) { // only allow window.open to succeed is if coming from same hsot, // otherwise open in default browser. - if (disposition === 'new-window' && newWinHost === mainWinHost) { + if (disposition === 'new-window' && ((newWinHost === mainWinHost) || newWinUrl === 'about:blank')) { // handle: window.open if (!frameName) { From f250185067fbca91d5d3702fa0382111231b9f1e Mon Sep 17 00:00:00 2001 From: Vishwas Shashidhar Date: Thu, 4 Jan 2018 23:00:08 +0530 Subject: [PATCH 19/21] electron-258: made changes as per the PR recommendation --- js/windowMgr.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/windowMgr.js b/js/windowMgr.js index 8f65c209..1bfb4431 100644 --- a/js/windowMgr.js +++ b/js/windowMgr.js @@ -318,9 +318,11 @@ function doCreateMainWindow(initialUrl, initialBounds) { let newWinHost = newWinParsedUrl && newWinParsedUrl.host; let mainWinHost = mainWinParsedUrl && mainWinParsedUrl.host; + let emptyUrlString = 'about:blank'; + // only allow window.open to succeed is if coming from same hsot, // otherwise open in default browser. - if (disposition === 'new-window' && ((newWinHost === mainWinHost) || newWinUrl === 'about:blank')) { + if (disposition === 'new-window' && ((newWinHost === mainWinHost) || newWinUrl === emptyUrlString)) { // handle: window.open if (!frameName) { From 252047891ed34f8aa85483a4f369ea0c094278a3 Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Mon, 8 Jan 2018 16:57:18 +0530 Subject: [PATCH 20/21] SEARCH-538 - Review comments --- js/search/search.js | 2 +- tests/SearchUtils.test.js | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/js/search/search.js b/js/search/search.js index 3f360a2b..45cefc3f 100644 --- a/js/search/search.js +++ b/js/search/search.js @@ -92,7 +92,7 @@ class Search { return new Promise((resolve, reject) => { if (!messages) { log.send(logLevels.ERROR, 'Batch Indexing: Messages not provided'); - reject(new Error('Batch Indexing: Messages is required')); + reject(new Error('Batch Indexing: Messages are required')); return; } diff --git a/tests/SearchUtils.test.js b/tests/SearchUtils.test.js index d1b95d11..fc3ccb0c 100644 --- a/tests/SearchUtils.test.js +++ b/tests/SearchUtils.test.js @@ -16,14 +16,14 @@ jest.mock('electron', function() { }); function mockedGetPath(type) { - if (type === 'exe') { - return executionPath; + switch (type) { + case 'exe': + return executionPath; + case 'userData': + return userConfigDir; + default: + return '' } - - if (type === 'userData') { - return userConfigDir - } - return ''; } describe('Tests for Search Utils', function() { @@ -37,6 +37,9 @@ describe('Tests for Search Utils', function() { const { SearchUtils } = require('../js/search/searchUtils.js'); SearchUtilsAPI = new SearchUtils(); SearchUtilsAPI.path = userConfigDir; + if (fs.existsSync(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE)) { + fs.unlinkSync(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE); + } done(); }); @@ -47,7 +50,7 @@ describe('Tests for Search Utils', function() { describe('Tests for checking disk space', function () { - it('should return free sapce', function (done) { + it('should return free space', function (done) { const checkFreeSpace = jest.spyOn(SearchUtilsAPI, 'checkFreeSpace'); SearchUtilsAPI.checkFreeSpace().then(function () { expect(checkFreeSpace).toHaveBeenCalled(); From 63b2fe25bd8d5a855e5941443ba1defc83a45405 Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Mon, 8 Jan 2018 17:38:58 +0530 Subject: [PATCH 21/21] SEARCH-538 - Review comments --- js/search/queue.js | 4 +++- js/search/search.js | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/js/search/queue.js b/js/search/queue.js index 7fb409f4..ab635167 100644 --- a/js/search/queue.js +++ b/js/search/queue.js @@ -24,7 +24,9 @@ let makeBoundTimedCollector = function(isIndexing, timeout, callback) { clearTimeout(timer); timer = null; resetQueue(); - callback(JSON.stringify(queue)); + if (queue) { + callback(JSON.stringify(queue)); + } } function getQueue(){ diff --git a/js/search/search.js b/js/search/search.js index 45cefc3f..d394990b 100644 --- a/js/search/search.js +++ b/js/search/search.js @@ -169,10 +169,6 @@ class Search { * @param message */ realTimeIndexing(message) { - if (!message) { - log.send(logLevels.ERROR, 'RealTime Indexing: Messages not provided'); - throw new Error('RealTime Indexing: Messages is required'); - } try { let msg = JSON.parse(message);