mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
Merge branch 'master' into rc-1.53.0
* master: SEARCH-905 - Electron "child_process" promisify issue (#462) AVT-1081 [Spectron][Windows] pop-out chat, inbox (#460) [AVT-1095] - [Spectron][MAC] Show 1 in tray icon when unread message = 1 (#458) Support on MAC (#456) bump up version number to 2.11.0 ELECTRON-600 (Implement Native Encryption for AESGCMEncrypt, AESGCMDecrypt & RSADecrypt) (#452) ELECTRON-670 - Remove setting menu to null for pop-out (#457) AVT-1031 AND AVT-1032: Verify toast notification for IMs, Signal,Room (#448) AVT-1025: Verify toast notification when Persist Notification is ON (#447) ELECTRON-607 (Optimization and bug fix) (#455) AVT-1024 Add test "Pop-up alert play depends on setting" (#454) ELECTRON-652: add new app icons for windows and macOS (#453) ELECTRON-607 (Change native menu bar style - Windows 10) (#451) ELECTRON-661 (Security fix for pop-outs RCE) (#450) AVT-1029 Add test "Verify the production logs exists when clicking on "Show logs in Explorer"" (#445) Add test (#443) ELECTRON-591 - Add logic to close up child windows whenever parent window is navigated or reloaded (#446) AVT-937,AVT-938,AVT-939 : [Spectron][Windows] Close window when 'Minimize on Close' is ON (#437) AVT-914 [Spectron][Windows] Add test "Keep size and position of the windows in previous session" (#430) Removes launchd plist files as there no longer needed (#442)
This commit is contained in:
commit
6dcc8089c7
BIN
build/icon.icns
BIN
build/icon.icns
Binary file not shown.
BIN
build/icon.ico
BIN
build/icon.ico
Binary file not shown.
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 361 KiB |
247
js/cryptoLib.js
Normal file
247
js/cryptoLib.js
Normal file
@ -0,0 +1,247 @@
|
||||
const electron = require('electron');
|
||||
const app = electron.app;
|
||||
const ffi = require('ffi');
|
||||
const ref = require('ref');
|
||||
const path = require('path');
|
||||
const execPath = path.dirname(app.getPath('exe'));
|
||||
|
||||
const log = require('./log.js');
|
||||
const logLevels = require('./enums/logLevels.js');
|
||||
const { isMac, isDevEnv } = require('../js/utils/misc');
|
||||
|
||||
const TAG_LENGTH = 16;
|
||||
const KEY_LENGTH = 32;
|
||||
const arch = process.arch === 'ia32';
|
||||
const winLibraryPath = isDevEnv ? path.join(__dirname, '..', 'library') : path.join(execPath, 'library');
|
||||
const macLibraryPath = isDevEnv ? path.join(__dirname, '..', 'library') : path.join(execPath, '..', 'library');
|
||||
|
||||
const cryptoLibPath = isMac ?
|
||||
path.join(macLibraryPath, 'cryptoLib.dylib') :
|
||||
(arch ? path.join(winLibraryPath, 'libsymphonysearch-x86.dll') : path.join(winLibraryPath, 'libsymphonysearch-x64.dll'));
|
||||
|
||||
const voidPtr = ref.refType(ref.types.void);
|
||||
const RSAKeyPair = exports.RSAKeyPair = voidPtr;
|
||||
const RSAKeyPairPtr = exports.RSAKeyPairPtr = ref.refType(RSAKeyPair);
|
||||
const RSAPubKey = exports.RSAPubKey = voidPtr;
|
||||
const RSAPubKeyPtr = exports.RSAPubKeyPtr = ref.refType(RSAPubKey);
|
||||
|
||||
const library = new ffi.Library((cryptoLibPath), {
|
||||
|
||||
AESEncryptGCM: [ref.types.int32, [
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
]],
|
||||
|
||||
AESDecryptGCM: [ref.types.int32, [
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
ref.refType(ref.types.uchar),
|
||||
]],
|
||||
|
||||
encryptRSA: [ref.types.uint32, [
|
||||
RSAPubKeyPtr,
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
]],
|
||||
|
||||
decryptRSA: [ref.types.uint32, [
|
||||
RSAPubKeyPtr,
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
]],
|
||||
|
||||
deserializeRSAPubKey: [RSAPubKey, [
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
]],
|
||||
deserializeRSAKeyPair: [RSAKeyPairPtr, [
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
]],
|
||||
|
||||
getRSAKeySize: [ref.types.uint32, [
|
||||
RSAKeyPairPtr
|
||||
]],
|
||||
|
||||
getVersion: [ref.types.CString, []],
|
||||
});
|
||||
|
||||
/**
|
||||
* Method to decrypt content
|
||||
* @param Base64IV
|
||||
* @param Base64AAD
|
||||
* @param Base64Key
|
||||
* @param Base64In
|
||||
* @return {*}
|
||||
* @constructor
|
||||
*/
|
||||
const AESGCMEncrypt = function(Base64IV, Base64AAD, Base64Key, Base64In) {
|
||||
return EncryptDecrypt('AESGCMEncrypt', Base64IV, Base64AAD, Base64Key, Base64In);
|
||||
};
|
||||
|
||||
/**
|
||||
* Method to decrypt content
|
||||
* @param Base64IV
|
||||
* @param Base64AAD
|
||||
* @param Base64Key
|
||||
* @param Base64In
|
||||
* @return {*}
|
||||
* @constructor
|
||||
*/
|
||||
const AESGCMDecrypt = function(Base64IV, Base64AAD, Base64Key, Base64In) {
|
||||
return EncryptDecrypt('AESGCMDecrypt', Base64IV, Base64AAD, Base64Key, Base64In);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt / Decrypt
|
||||
* @param name {String} - Method name
|
||||
* @param Base64IV {String} base64
|
||||
* @param Base64AAD {String} base64
|
||||
* @param Base64Key {String} base64
|
||||
* @param Base64In {String} base64
|
||||
* @return {*}
|
||||
* @constructor
|
||||
*/
|
||||
const EncryptDecrypt = function(name, Base64IV, Base64AAD, Base64Key, Base64In) {
|
||||
let base64In = Base64In;
|
||||
|
||||
if (!base64In) {
|
||||
base64In = "";
|
||||
}
|
||||
|
||||
const IV = Buffer.from(Base64IV, 'base64');
|
||||
const AAD = Buffer.from(Base64AAD, 'base64');
|
||||
const Key = Buffer.from(Base64Key, 'base64');
|
||||
const In = Buffer.from(base64In, 'base64');
|
||||
|
||||
if (name === 'AESGCMEncrypt') {
|
||||
const OutPtr = Buffer.alloc(In.length);
|
||||
const Tag = Buffer.alloc(TAG_LENGTH);
|
||||
|
||||
const resultCode = library.AESEncryptGCM(In, In.length, AAD, AAD.length, Key, IV, IV.length, OutPtr, Tag, TAG_LENGTH);
|
||||
|
||||
if (resultCode < 0) {
|
||||
log.send(logLevels.ERROR, `AESEncryptGCM, Failed to encrypt with exit code ${resultCode}`);
|
||||
}
|
||||
log.send(logLevels.INFO, `Output from AESEncryptGCM ${resultCode}`);
|
||||
const bufferArray = [OutPtr, Tag];
|
||||
return Buffer.concat(bufferArray).toString('base64');
|
||||
}
|
||||
|
||||
if (name === 'AESGCMDecrypt') {
|
||||
const CipherTextLen = In.length - TAG_LENGTH;
|
||||
const Tag = Buffer.from(In.slice(In.length - 16, In.length));
|
||||
const OutPtr = Buffer.alloc(In.length - TAG_LENGTH);
|
||||
|
||||
const resultCode = library.AESDecryptGCM(In, CipherTextLen, AAD, AAD.length, Tag, TAG_LENGTH, Key, IV, IV.length, OutPtr);
|
||||
|
||||
if (resultCode < 0) {
|
||||
log.send(logLevels.ERROR, `AESDecryptGCM, Failed to decrypt with exit code ${resultCode}`);
|
||||
}
|
||||
log.send(logLevels.INFO, `Output from AESDecryptGCM ${resultCode}`);
|
||||
return OutPtr.toString('base64');
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrypt RSA
|
||||
* @param pemKey
|
||||
* @param input
|
||||
* @return {*}
|
||||
* @constructor
|
||||
*/
|
||||
const RSADecrypt = function (pemKey, input) {
|
||||
return RSAEncryptDecrypt("RSADecrypt", pemKey, input);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt / Decrypt RSA
|
||||
* @param action
|
||||
* @param pemKey
|
||||
* @param inputStr
|
||||
* @return {String}
|
||||
* @constructor
|
||||
*/
|
||||
const RSAEncryptDecrypt = function (action, pemKey, inputStr) {
|
||||
|
||||
let rsaKey = getRSAKeyFromPEM(pemKey);
|
||||
|
||||
if (!rsaKey) {
|
||||
log.send(logLevels.ERROR, `Failed to parse formatted RSA PEM key`);
|
||||
}
|
||||
|
||||
let input = Buffer.from(inputStr, 'base64');
|
||||
let outLen = library.getRSAKeySize(rsaKey);
|
||||
|
||||
let outPtr = Buffer.alloc(KEY_LENGTH);
|
||||
|
||||
let ret = 0;
|
||||
|
||||
if (action === 'RSAEncrypt') {
|
||||
ret = library.encryptRSA(rsaKey, 0, input, input.length, outPtr, outLen);
|
||||
} else {
|
||||
outLen = library.decryptRSA(rsaKey, 0, input, input.length, outPtr, outLen);
|
||||
|
||||
if (outLen < 0) {
|
||||
ret = outLen;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret !== 0) {
|
||||
log.send(logLevels.ERROR, `${action} failed due to -> ${ret}`);
|
||||
}
|
||||
return Buffer.from(outPtr.toString('hex'), 'hex').toString('base64');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get RSA key from PEM key
|
||||
* @param pemKey
|
||||
* @return {*}
|
||||
*/
|
||||
const getRSAKeyFromPEM = function (pemKey) {
|
||||
|
||||
let pemKeyBytes = Buffer.from(pemKey, 'utf-8');
|
||||
|
||||
let rsaKey;
|
||||
|
||||
if (pemKey.startsWith("-----BEGIN PUBLIC KEY-----")) {
|
||||
rsaKey = library.deserializeRSAPubKey(pemKeyBytes, pemKey.length);
|
||||
} else {
|
||||
rsaKey = library.deserializeRSAKeyPair(pemKeyBytes, pemKey.length);
|
||||
}
|
||||
|
||||
if (rsaKey === 0) {
|
||||
log.send(logLevels.ERROR, 'RSAKey is 0!!');
|
||||
}
|
||||
return rsaKey;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
AESGCMEncrypt: AESGCMEncrypt,
|
||||
AESGCMDecrypt: AESGCMDecrypt,
|
||||
RSADecrypt: RSADecrypt,
|
||||
};
|
@ -8,5 +8,5 @@ let keyMirror = require('keymirror');
|
||||
*/
|
||||
module.exports = keyMirror({
|
||||
CUSTOM: null,
|
||||
NATIVE_WITH_CUSTOM: null,
|
||||
NATIVE: null,
|
||||
});
|
||||
|
@ -67,6 +67,8 @@ function sanitize(windowName) {
|
||||
if (!isMac) {
|
||||
eventEmitter.emit('killScreenSnippet');
|
||||
}
|
||||
// Closes all the child windows
|
||||
windowMgr.cleanUpChildWindows();
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,7 +163,8 @@ electron.ipcMain.on(apiName, (event, arg) => {
|
||||
break;
|
||||
case apiCmds.setLocale:
|
||||
if (typeof arg.locale === 'string') {
|
||||
eventEmitter.emit('language-changed', { language: arg.locale });
|
||||
let browserWin = electron.BrowserWindow.fromWebContents(event.sender);
|
||||
windowMgr.setLocale(browserWin, { language: arg.locale });
|
||||
}
|
||||
break;
|
||||
case apiCmds.keyPress:
|
||||
|
@ -399,23 +399,39 @@ function getTemplate(app) {
|
||||
label: i18n.getMessageFor('Title Bar Style'),
|
||||
submenu: [
|
||||
{
|
||||
label: i18n.getMessageFor('Native With Custom'),
|
||||
label: i18n.getMessageFor('Native'),
|
||||
type: 'checkbox',
|
||||
checked: titleBarStyle === titleBarStyles.NATIVE_WITH_CUSTOM,
|
||||
checked: titleBarStyle === titleBarStyles.NATIVE,
|
||||
enabled: titleBarStyle !== titleBarStyles.NATIVE,
|
||||
click: function (item) {
|
||||
item.menu.items[1].checked = false;
|
||||
titleBarStyle = titleBarStyles.NATIVE_WITH_CUSTOM;
|
||||
const isNativeStyle = titleBarStyle === titleBarStyles.NATIVE;
|
||||
item.menu.items[1].checked = isNativeStyle;
|
||||
|
||||
// Disable menu item accordingly
|
||||
item.menu.items[0].enabled = isNativeStyle;
|
||||
item.menu.items[1].enabled = !isNativeStyle;
|
||||
|
||||
titleBarStyle = titleBarStyles.NATIVE;
|
||||
updateConfigField('isCustomTitleBar', false);
|
||||
titleBarActions(app);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: i18n.getMessageFor('Custom'),
|
||||
type: 'checkbox',
|
||||
checked: titleBarStyle === titleBarStyles.CUSTOM,
|
||||
enabled: titleBarStyle !== titleBarStyles.CUSTOM,
|
||||
click: function (item) {
|
||||
item.menu.items[0].checked = false;
|
||||
const isCustomStyle = titleBarStyle === titleBarStyles.CUSTOM;
|
||||
item.menu.items[0].checked = isCustomStyle;
|
||||
|
||||
// Disable menu item accordingly
|
||||
item.menu.items[1].enabled = isCustomStyle;
|
||||
item.menu.items[0].enabled = !isCustomStyle;
|
||||
|
||||
titleBarStyle = titleBarStyles.CUSTOM;
|
||||
updateConfigField('isCustomTitleBar', true);
|
||||
titleBarActions(app);
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -479,7 +495,7 @@ function setCheckboxValues() {
|
||||
bringToFront = configData[key];
|
||||
break;
|
||||
case 'isCustomTitleBar':
|
||||
titleBarStyle = configData[key] ? titleBarStyles.CUSTOM : titleBarStyles.NATIVE_WITH_CUSTOM;
|
||||
titleBarStyle = configData[key] ? titleBarStyles.CUSTOM : titleBarStyles.NATIVE;
|
||||
break;
|
||||
case 'memoryRefresh':
|
||||
memoryRefresh = configData[key];
|
||||
@ -537,6 +553,28 @@ function getTitleBarStyle() {
|
||||
return titleBarStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an option to the user whether
|
||||
* to relaunch application
|
||||
*
|
||||
* @param app
|
||||
*/
|
||||
function titleBarActions(app) {
|
||||
const options = {
|
||||
type: 'question',
|
||||
title: i18n.getMessageFor('Relaunch Application'),
|
||||
message: i18n.getMessageFor('Updating Title bar style requires Symphony to relaunch'),
|
||||
buttons: ['Relaunch', 'Cancel']
|
||||
};
|
||||
|
||||
electron.dialog.showMessageBox(options, function (index) {
|
||||
if (index === 0) {
|
||||
app.relaunch();
|
||||
app.exit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getTemplate: getTemplate,
|
||||
getMinimizeOnClose: getMinimizeOnClose,
|
||||
|
@ -487,7 +487,7 @@ function buildCloseNotification(notificationWindow, notificationObj, getTimeoutI
|
||||
|
||||
// safety check to prevent from using an
|
||||
// already destroyed notification window
|
||||
if (notificationWindow.isDestroyed()) {
|
||||
if (notificationWindow && notificationWindow.isDestroyed()) {
|
||||
return new Promise(function(exitEarly) { exitEarly() })
|
||||
}
|
||||
|
||||
@ -699,6 +699,7 @@ function getWindow() {
|
||||
windowProperties.width = config.width;
|
||||
windowProperties.height = config.height;
|
||||
notificationWindow = new BrowserWindow(windowProperties);
|
||||
notificationWindow.winName = 'notification-window';
|
||||
notificationWindow.setVisibleOnAllWorkspaces(true);
|
||||
notificationWindow.loadURL(getTemplatePath());
|
||||
notificationWindow.webContents.on('did-finish-load', function() {
|
||||
@ -737,13 +738,39 @@ function cleanUpActiveNotification(event) {
|
||||
*/
|
||||
function cleanUpInactiveWindow() {
|
||||
inactiveWindows.forEach(function(window) {
|
||||
if (!window.isDestroyed()) {
|
||||
if (window && !window.isDestroyed()) {
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
inactiveWindows = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all the notification windows
|
||||
* and cleans up the reference variables
|
||||
*/
|
||||
function closeAll() {
|
||||
resetAnimationQueue();
|
||||
const notificationWin = Object.assign([], activeNotifications);
|
||||
for (let activeNotification of notificationWin) {
|
||||
if (activeNotification && !activeNotification.isDestroyed()) {
|
||||
activeNotification.close();
|
||||
}
|
||||
}
|
||||
|
||||
nextInsertPos = {};
|
||||
activeNotifications = [];
|
||||
notificationQueue = [];
|
||||
cleanUpInactiveWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the animation queue instance
|
||||
*/
|
||||
function resetAnimationQueue() {
|
||||
animationQueue = new AnimationQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new timer to close the notification
|
||||
* @param event
|
||||
@ -784,3 +811,5 @@ ipc.on('electron-notify-mouseover', onMouseOver);
|
||||
module.exports.notify = notify;
|
||||
module.exports.updateConfig = updateConfig;
|
||||
module.exports.reset = setupConfig;
|
||||
module.exports.closeAll = closeAll;
|
||||
module.exports.resetAnimationQueue = resetAnimationQueue;
|
||||
|
@ -26,10 +26,14 @@ const memoryMonitorInterval = 1000 * 60 * 60;
|
||||
const SnackBar = require('../snackBar').SnackBar;
|
||||
const KeyCodes = {
|
||||
Esc: 27,
|
||||
Alt: 18,
|
||||
};
|
||||
|
||||
let Search;
|
||||
let SearchUtils;
|
||||
let CryptoLib;
|
||||
let isAltKey = false;
|
||||
let isMenuOpen = false;
|
||||
|
||||
try {
|
||||
Search = remote.require('swift-search').Search;
|
||||
@ -45,6 +49,14 @@ try {
|
||||
console.warn("Failed to initialize swift search (Utils). You'll need to include the search dependency. Contact the developers for more details");
|
||||
}
|
||||
|
||||
try {
|
||||
CryptoLib = remote.require('./cryptoLib.js');
|
||||
} catch (e) {
|
||||
CryptoLib = null;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn("Failed to initialize Crypto Lib. You'll need to include the Crypto library. Contact the developers for more details");
|
||||
}
|
||||
|
||||
require('../downloadManager');
|
||||
let snackBar;
|
||||
|
||||
@ -199,6 +211,11 @@ function createAPI() {
|
||||
*/
|
||||
SearchUtils: SearchUtils || null,
|
||||
|
||||
/**
|
||||
* Native encryption and decryption.
|
||||
*/
|
||||
CryptoLib: CryptoLib,
|
||||
|
||||
/**
|
||||
* Brings window forward and gives focus.
|
||||
* @param {String} windowName Name of window. Note: main window name is 'main'
|
||||
@ -470,7 +487,6 @@ function createAPI() {
|
||||
* the window enters full screen
|
||||
*/
|
||||
local.ipcRenderer.on('window-enter-full-screen', (event, arg) => {
|
||||
window.addEventListener('keydown', throttledKeyDown, true);
|
||||
if (snackBar && typeof arg === 'object' && arg.snackBar) {
|
||||
setTimeout(() => snackBar.showSnackBar(arg.snackBar), 500);
|
||||
}
|
||||
@ -481,7 +497,6 @@ function createAPI() {
|
||||
* the window leave full screen
|
||||
*/
|
||||
local.ipcRenderer.on('window-leave-full-screen', () => {
|
||||
window.removeEventListener('keydown', throttledKeyDown, true);
|
||||
if (snackBar) {
|
||||
snackBar.removeSnackBar();
|
||||
}
|
||||
@ -498,22 +513,46 @@ function createAPI() {
|
||||
function sanitize() {
|
||||
local.ipcRenderer.send(apiName, {
|
||||
cmd: apiCmds.sanitize,
|
||||
windowName: window.name
|
||||
windowName: window.name || 'main'
|
||||
});
|
||||
}
|
||||
|
||||
const throttledKeyDown = throttle(1000, (event) => {
|
||||
// Handle key down events
|
||||
const throttledKeyDown = throttle(500, (event) => {
|
||||
isAltKey = event.keyCode === KeyCodes.Alt;
|
||||
if (event.keyCode === KeyCodes.Esc) {
|
||||
local.ipcRenderer.send(apiName, {
|
||||
cmd: apiCmds.keyPress,
|
||||
keyCode: KeyCodes.Esc
|
||||
keyCode: event.keyCode
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle key up events
|
||||
const throttledKeyUp = throttle(500, (event) => {
|
||||
if (isAltKey && (event.keyCode === KeyCodes.Alt || KeyCodes.Esc)) {
|
||||
isMenuOpen = !isMenuOpen;
|
||||
}
|
||||
if (isAltKey && isMenuOpen && event.keyCode === KeyCodes.Alt) {
|
||||
local.ipcRenderer.send(apiName, {
|
||||
cmd: apiCmds.keyPress,
|
||||
keyCode: event.keyCode
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const throttleMouseDown = throttle(500, () => {
|
||||
if (isAltKey && isMenuOpen) {
|
||||
isMenuOpen = !isMenuOpen;
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('offline', updateOnlineStatus, false);
|
||||
window.addEventListener('online', updateOnlineStatus, false);
|
||||
window.addEventListener('beforeunload', sanitize, false);
|
||||
window.addEventListener('keyup', throttledKeyUp, true);
|
||||
window.addEventListener('keydown', throttledKeyDown, true);
|
||||
window.addEventListener('mousedown', throttleMouseDown, { capture: true });
|
||||
|
||||
updateOnlineStatus();
|
||||
}
|
||||
|
123
js/windowMgr.js
123
js/windowMgr.js
@ -44,9 +44,11 @@ let display;
|
||||
let sandboxed = false;
|
||||
let isAutoReload = false;
|
||||
let devToolsEnabled = true;
|
||||
let isCustomTitleBarEnabled = true;
|
||||
|
||||
const KeyCodes = {
|
||||
Esc: 27,
|
||||
Alt: 18,
|
||||
};
|
||||
|
||||
// Application menu
|
||||
@ -132,7 +134,7 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
|
||||
const config = readConfigFileSync();
|
||||
|
||||
// condition whether to enable custom Windows 10 title bar
|
||||
const isCustomTitleBarEnabled = typeof isCustomTitleBar === 'boolean'
|
||||
isCustomTitleBarEnabled = typeof isCustomTitleBar === 'boolean'
|
||||
&& isCustomTitleBar
|
||||
&& isWindows10();
|
||||
log.send(logLevels.INFO, `we are configuring a custom title bar for windows -> ${isCustomTitleBarEnabled}`);
|
||||
@ -215,7 +217,7 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
|
||||
mainWindow.webContents.send('window-leave-full-screen');
|
||||
});
|
||||
|
||||
if (initialBounds && !isNodeEnv) {
|
||||
if (initialBounds) {
|
||||
// maximizes the application if previously maximized
|
||||
if (initialBounds.isMaximized) {
|
||||
mainWindow.maximize();
|
||||
@ -240,7 +242,7 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
|
||||
|
||||
// Event needed to hide native menu bar on Windows 10 as we use custom menu bar
|
||||
mainWindow.webContents.once('did-start-loading', () => {
|
||||
if (isWindows10() && mainWindow && !mainWindow.isDestroyed()) {
|
||||
if ((isCustomTitleBarEnabled || isWindows10()) && mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
}
|
||||
});
|
||||
@ -256,7 +258,7 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
|
||||
mainWindow.webContents.send('on-page-load');
|
||||
// initializes and applies styles required for snack bar
|
||||
mainWindow.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '/snackBar/style.css'), 'utf8').toString());
|
||||
if (isCustomTitleBarEnabled || isWindows10()) {
|
||||
if (isCustomTitleBarEnabled) {
|
||||
mainWindow.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '/windowsTitleBar/style.css'), 'utf8').toString());
|
||||
// This is required to initiate Windows title bar only after insertCSS
|
||||
const titleBarStyle = getTitleBarStyle();
|
||||
@ -320,7 +322,7 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
|
||||
addWindowKey(key, mainWindow);
|
||||
mainWindow.loadURL(url);
|
||||
|
||||
rebuildMenu(lang);
|
||||
setLocale(mainWindow, { language: lang });
|
||||
|
||||
mainWindow.on('close', function (e) {
|
||||
if (willQuitApp) {
|
||||
@ -371,13 +373,15 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
|
||||
});
|
||||
|
||||
// open external links in default browser - a tag with href='_blank' or window.open
|
||||
mainWindow.webContents.on('new-window', handleNewWindow);
|
||||
|
||||
if (!isDevEnv) {
|
||||
mainWindow.webContents.session.setCertificateVerifyProc(handleCertificateTransparencyChecks);
|
||||
const enforceInheritance = (topWebContents) => {
|
||||
const handleNewWindow = (webContents) => {
|
||||
webContents.on('new-window', (event, newWinUrl, frameName, disposition, newWinOptions) => {
|
||||
if (!newWinOptions.webPreferences) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
newWinOptions.webPreferences = {};
|
||||
}
|
||||
|
||||
function handleNewWindow(event, newWinUrl, frameName, disposition, newWinOptions) {
|
||||
Object.assign(newWinOptions.webPreferences, topWebContents);
|
||||
|
||||
let newWinParsedUrl = getParsedUrl(newWinUrl);
|
||||
let mainWinParsedUrl = getParsedUrl(url);
|
||||
@ -445,27 +449,22 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
|
||||
newWinOptions.winKey = newWinKey;
|
||||
/* eslint-enable no-param-reassign */
|
||||
|
||||
let webContents = newWinOptions.webContents;
|
||||
let childWebContents = newWinOptions.webContents;
|
||||
|
||||
// Event needed to hide native menu bar
|
||||
webContents.once('did-start-loading', () => {
|
||||
let browserWin = BrowserWindow.fromWebContents(webContents);
|
||||
childWebContents.once('did-start-loading', () => {
|
||||
let browserWin = BrowserWindow.fromWebContents(childWebContents);
|
||||
if (isWindowsOS && browserWin && !browserWin.isDestroyed()) {
|
||||
browserWin.setMenuBarVisibility(false);
|
||||
}
|
||||
});
|
||||
|
||||
webContents.once('did-finish-load', function () {
|
||||
let browserWin = BrowserWindow.fromWebContents(webContents);
|
||||
childWebContents.once('did-finish-load', function () {
|
||||
let browserWin = BrowserWindow.fromWebContents(childWebContents);
|
||||
|
||||
if (browserWin) {
|
||||
log.send(logLevels.INFO, 'loaded pop-out window url: ' + newWinParsedUrl);
|
||||
|
||||
if (!isMac) {
|
||||
// Removes the menu bar from the pop-out window
|
||||
// setMenu is currently only supported on Windows and Linux
|
||||
browserWin.setMenu(null);
|
||||
}
|
||||
browserWin.webContents.send('on-page-load');
|
||||
// applies styles required for snack bar
|
||||
browserWin.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '/snackBar/style.css'), 'utf8').toString());
|
||||
@ -501,7 +500,9 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
|
||||
// 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', handleNewWindow.bind(this));
|
||||
if (browserWin.webContents) {
|
||||
handleNewWindow(browserWin.webContents);
|
||||
}
|
||||
|
||||
addWindowKey(newWinKey, browserWin);
|
||||
|
||||
@ -536,7 +537,6 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
|
||||
};
|
||||
|
||||
browserWin.on('close', () => {
|
||||
browserWin.webContents.removeListener('new-window', handleNewWindow);
|
||||
browserWin.webContents.removeListener('crashed', handleChildWindowCrashEvent);
|
||||
});
|
||||
|
||||
@ -559,6 +559,18 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
|
||||
event.preventDefault();
|
||||
openUrlInDefaultBrowser(newWinUrl);
|
||||
}
|
||||
});
|
||||
};
|
||||
handleNewWindow(topWebContents);
|
||||
};
|
||||
|
||||
if (!isDevEnv) {
|
||||
mainWindow.webContents.session.setCertificateVerifyProc(handleCertificateTransparencyChecks);
|
||||
}
|
||||
|
||||
// enforce main window's webPreferences to child windows
|
||||
if (mainWindow.webContents) {
|
||||
enforceInheritance(mainWindow.webContents);
|
||||
}
|
||||
|
||||
// whenever the main window is navigated for ex: window.location.href or url redirect
|
||||
@ -955,19 +967,34 @@ eventEmitter.on('notificationSettings', (notificationSettings) => {
|
||||
display = notificationSettings.display;
|
||||
});
|
||||
|
||||
eventEmitter.on('language-changed', (opts) => {
|
||||
/**
|
||||
* Sets the locale settings
|
||||
*
|
||||
* @param browserWindow {Electron.BrowserWindow}
|
||||
* @param opts {Object}
|
||||
* @param opts.language {String} - locale string ex: en-US
|
||||
*/
|
||||
function setLocale(browserWindow, opts) {
|
||||
const language = opts && opts.language || app.getLocale();
|
||||
log.send(logLevels.INFO, `language changed to ${language}. Updating menu and user config`);
|
||||
rebuildMenu(language);
|
||||
updateConfigField('locale', language);
|
||||
});
|
||||
|
||||
function rebuildMenu(language) {
|
||||
setLanguage(language);
|
||||
if (browserWindow && isMainWindow(browserWindow)) {
|
||||
menu = electron.Menu.buildFromTemplate(getTemplate(app));
|
||||
electron.Menu.setApplicationMenu(menu);
|
||||
|
||||
if (isWindows10()) {
|
||||
browserWindow.setMenuBarVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
updateConfigField('locale', language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets language for i18n
|
||||
* @param language {String} - locale string ex: en-US
|
||||
*/
|
||||
function setLanguage(language) {
|
||||
i18n.setLanguage(language);
|
||||
}
|
||||
@ -1097,11 +1124,49 @@ function handleKeyPress(keyCode) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyCodes.Alt:
|
||||
if (isWindows10() && !isCustomTitleBarEnabled) {
|
||||
popupMenu();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all the child window and closes it
|
||||
*/
|
||||
function cleanUpChildWindows() {
|
||||
const browserWindows = BrowserWindow.getAllWindows();
|
||||
notify.resetAnimationQueue();
|
||||
if (browserWindows && browserWindows.length) {
|
||||
browserWindows.forEach(browserWindow => {
|
||||
// Closes only child windows
|
||||
if (browserWindow && !browserWindow.isDestroyed() && browserWindow.winName !== 'main') {
|
||||
// clean up notification windows
|
||||
if (browserWindow.winName === 'notification-window') {
|
||||
notify.closeAll();
|
||||
} else {
|
||||
browserWindow.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that popup the menu on top of the native title bar
|
||||
* whenever Alt key is pressed
|
||||
*/
|
||||
function popupMenu() {
|
||||
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||||
if (mainWindow && !mainWindow.isDestroyed() && isMainWindow(focusedWindow)) {
|
||||
const popupOpts = { browserWin: mainWindow, x: 10, y: -20 };
|
||||
getMenu().popup(popupOpts);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
createMainWindow: createMainWindow,
|
||||
@ -1115,5 +1180,7 @@ module.exports = {
|
||||
verifyDisplays: verifyDisplays,
|
||||
getMenu: getMenu,
|
||||
setIsAutoReload: setIsAutoReload,
|
||||
handleKeyPress: handleKeyPress
|
||||
handleKeyPress: handleKeyPress,
|
||||
cleanUpChildWindows: cleanUpChildWindows,
|
||||
setLocale: setLocale,
|
||||
};
|
||||
|
@ -54,7 +54,7 @@ class TitleBar {
|
||||
TitleBar.setTitleBarTitle();
|
||||
TitleBar.addWindowBorders();
|
||||
break;
|
||||
case titleBarStyles.NATIVE_WITH_CUSTOM:
|
||||
case titleBarStyles.NATIVE:
|
||||
TitleBar.hideTitleContainer();
|
||||
break;
|
||||
default:
|
||||
@ -104,7 +104,7 @@ class TitleBar {
|
||||
|
||||
/**
|
||||
* Method that hides the title container
|
||||
* if the title bar style is NATIVE_WITH_CUSTOM
|
||||
* if the title bar style is NATIVE
|
||||
*/
|
||||
static hideTitleContainer() {
|
||||
const titleContainer = document.getElementById('title-container');
|
||||
|
@ -15,21 +15,32 @@
|
||||
},
|
||||
"Bring All to Front": "Bring All to Front",
|
||||
"Bring to Front on Notifications": "Bring to Front on Notifications",
|
||||
"Flash Notification in Taskbar": "Flash Notification in Taskbar",
|
||||
"Title Bar Style": "Title Bar Style",
|
||||
"Native With Custom": "Native With Custom",
|
||||
"Custom": "Custom",
|
||||
"Certificate Error": "Certificate Error",
|
||||
"Close": "Close",
|
||||
"Copy": "Copy",
|
||||
"Custom": "Custom",
|
||||
"Cut": "Cut",
|
||||
"Delete": "Delete",
|
||||
"Dev Tools disabled": "Dev Tools disabled",
|
||||
"Dev Tools has been disabled. Please contact your system administrator": "Dev Tools has been disabled. Please contact your system administrator",
|
||||
"Edit": "Edit",
|
||||
"Error loading configuration": "Error loading configuration",
|
||||
"Error loading URL": "Error loading URL",
|
||||
"Error loading window": "Error loading window",
|
||||
"Error setting AutoLaunch configuration": "Error setting AutoLaunch configuration",
|
||||
"Failed!": "Failed!",
|
||||
"Flash Notification in Taskbar": "Flash Notification in Taskbar",
|
||||
"Help": "Help",
|
||||
"Hide Others": "Hide Others",
|
||||
"Hide Symphony": "Hide Symphony",
|
||||
"Learn More": "Learn More",
|
||||
"Loading Error": "Loading Error",
|
||||
"Minimize": "Minimize",
|
||||
"Minimize on Close": "Minimize on Close",
|
||||
"Native": "Native",
|
||||
"No crashes available to share": "No crashes available to share",
|
||||
"No logs are available to share": "No logs are available to share",
|
||||
"Not Allowed": "Not Allowed",
|
||||
"NotificationSettings": {
|
||||
"Bottom Left": "Bottom Left",
|
||||
"Bottom Right": "Bottom Right",
|
||||
@ -43,12 +54,19 @@
|
||||
"Top Left": "Top Left",
|
||||
"Top Right": "Top Right"
|
||||
},
|
||||
"Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.",
|
||||
"Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.",
|
||||
"Paste": "Paste",
|
||||
"Paste and Match Style": "Paste and Match Style",
|
||||
"Permission Denied": "Permission Denied",
|
||||
"Please contact your admin for help": "Please contact your admin for help",
|
||||
"please contact your administrator for more details": "please contact your administrator for more details",
|
||||
"Quit Symphony": "Quit Symphony",
|
||||
"Redo": "Redo",
|
||||
"Refresh app when idle": "Refresh app when idle",
|
||||
"Relaunch Application": "Relaunch Application",
|
||||
"Reload": "Reload",
|
||||
"Renderer Process Crashed": "Renderer Process Crashed",
|
||||
"ScreenPicker": {
|
||||
"Applications": "Applications",
|
||||
"Cancel": "Cancel",
|
||||
@ -61,56 +79,40 @@
|
||||
"Share": "Share"
|
||||
},
|
||||
"ScreenSnippet": {
|
||||
"Pen": "Pen",
|
||||
"Done": "Done",
|
||||
"Snipping Tool": "Snipping Tool",
|
||||
"Erase": "Erase",
|
||||
"Highlight": "Highlight"
|
||||
"Highlight": "Highlight",
|
||||
"Pen": "Pen",
|
||||
"Snipping Tool": "Snipping Tool"
|
||||
},
|
||||
"Select All": "Select All",
|
||||
"Services": "Services",
|
||||
"Show All": "Show All",
|
||||
"Show crash dump in Explorer": "Show crash dump in Explorer",
|
||||
"Show crash dump in Finder": "Show crash dump in Finder",
|
||||
"Show Logs in Finder": "Show Logs in Finder",
|
||||
"Show Logs in Explorer": "Show Logs in Explorer",
|
||||
"Show Logs in Finder": "Show Logs in Finder",
|
||||
"SnackBar": {
|
||||
"Press ": "Press ",
|
||||
" to exit full screen": " to exit full screen",
|
||||
"esc": "esc",
|
||||
" to exit full screen": " to exit full screen"
|
||||
"Press ": "Press "
|
||||
},
|
||||
"Sorry, you are not allowed to access this website": "Sorry, you are not allowed to access this website",
|
||||
"Speech": "Speech",
|
||||
"Start Speaking": "Start Speaking",
|
||||
"Stop Speaking": "Stop Speaking",
|
||||
"Symphony Help": "Symphony Help",
|
||||
"Title Bar Style": "Title Bar Style",
|
||||
"Toggle Full Screen": "Toggle Full Screen",
|
||||
"Troubleshooting": "Troubleshooting",
|
||||
"Unable to generate crash reports due to ": "Unable to generate crash reports due to ",
|
||||
"Unable to generate logs due to ": "Unable to generate logs due to ",
|
||||
"Undo": "Undo",
|
||||
"Updating Title bar style requires Symphony to relaunch": "Updating Title bar style requires Symphony to relaunch",
|
||||
"View": "View",
|
||||
"Window": "Window",
|
||||
"Your administrator has disabled": "Your administrator has disabled",
|
||||
"Zoom": "Zoom",
|
||||
"Zoom In": "Zoom In",
|
||||
"Zoom Out": "Zoom Out",
|
||||
"Renderer Process Crashed": "Renderer Process Crashed",
|
||||
"Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.",
|
||||
"Failed!": "Failed!",
|
||||
"No logs are available to share": "No logs are available to share",
|
||||
"Unable to generate logs due to ": "Unable to generate logs due to ",
|
||||
"Show crash dump in Explorer": "Show crash dump in Explorer",
|
||||
"No crashes available to share": "No crashes available to share",
|
||||
"Unable to generate crash reports due to ": "Unable to generate crash reports due to ",
|
||||
"Error setting AutoLaunch configuration": "Error setting AutoLaunch configuration",
|
||||
"Error loading configuration": "Error loading configuration",
|
||||
"Certificate Error": "Certificate Error",
|
||||
"Error loading URL": "Error loading URL",
|
||||
"Error loading window": "Error loading window",
|
||||
"Loading Error": "Loading Error",
|
||||
"Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.",
|
||||
"Not Allowed": "Not Allowed",
|
||||
"Sorry, you are not allowed to access this website": "Sorry, you are not allowed to access this website",
|
||||
"please contact your administrator for more details": "please contact your administrator for more details",
|
||||
"Your administrator has disabled": "Your administrator has disabled",
|
||||
"Please contact your admin for help": "Please contact your admin for help",
|
||||
"Permission Denied": "Permission Denied",
|
||||
"Dev Tools disabled": "Dev Tools disabled",
|
||||
"Dev Tools has been disabled. Please contact your system administrator": "Dev Tools has been disabled. Please contact your system administrator"
|
||||
"Zoom Out": "Zoom Out"
|
||||
}
|
@ -15,21 +15,30 @@
|
||||
},
|
||||
"Bring All to Front": "Bring All to Front",
|
||||
"Bring to Front on Notifications": "Bring to Front on Notifications",
|
||||
"Flash Notification in Taskbar": "Flash Notification in Taskbar",
|
||||
"Title Bar Style": "Title Bar Style",
|
||||
"Native With Custom": "Native With Custom",
|
||||
"Custom": "Custom",
|
||||
"Certificate Error": "Certificate Error",
|
||||
"Close": "Close",
|
||||
"Copy": "Copy",
|
||||
"Custom": "Custom",
|
||||
"Cut": "Cut",
|
||||
"Delete": "Delete",
|
||||
"Edit": "Edit",
|
||||
"Error loading configuration": "Error loading configuration",
|
||||
"Error loading URL": "Error loading URL",
|
||||
"Error loading window": "Error loading window",
|
||||
"Error setting AutoLaunch configuration": "Error setting AutoLaunch configuration",
|
||||
"Failed!": "Failed!",
|
||||
"Flash Notification in Taskbar": "Flash Notification in Taskbar",
|
||||
"Help": "Help",
|
||||
"Hide Others": "Hide Others",
|
||||
"Hide Symphony": "Hide Symphony",
|
||||
"Learn More": "Learn More",
|
||||
"Loading Error": "Loading Error",
|
||||
"Minimize": "Minimize",
|
||||
"Minimize on Close": "Minimize on Close",
|
||||
"Native": "Native",
|
||||
"No crashes available to share": "No crashes available to share",
|
||||
"No logs are available to share": "No logs are available to share",
|
||||
"Not Allowed": "Not Allowed",
|
||||
"NotificationSettings": {
|
||||
"Bottom Left": "Bottom Left",
|
||||
"Bottom Right": "Bottom Right",
|
||||
@ -43,12 +52,19 @@
|
||||
"Top Left": "Top Left",
|
||||
"Top Right": "Top Right"
|
||||
},
|
||||
"Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.",
|
||||
"Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.",
|
||||
"Paste": "Paste",
|
||||
"Paste and Match Style": "Paste and Match Style",
|
||||
"Permission Denied": "Permission Denied",
|
||||
"Please contact your admin for help": "Please contact your admin for help",
|
||||
"please contact your administrator for more details": "please contact your administrator for more details",
|
||||
"Quit Symphony": "Quit Symphony",
|
||||
"Redo": "Redo",
|
||||
"Refresh app when idle": "Refresh app when idle",
|
||||
"Relaunch Application": "Relaunch Application",
|
||||
"Reload": "Reload",
|
||||
"Renderer Process Crashed": "Renderer Process Crashed",
|
||||
"ScreenPicker": {
|
||||
"Applications": "Applications",
|
||||
"Cancel": "Cancel",
|
||||
@ -61,54 +77,40 @@
|
||||
"Share": "Share"
|
||||
},
|
||||
"ScreenSnippet": {
|
||||
"Pen": "Pen",
|
||||
"Done": "Done",
|
||||
"Snipping Tool": "Snipping Tool",
|
||||
"Erase": "Erase",
|
||||
"Highlight": "Highlight"
|
||||
"Highlight": "Highlight",
|
||||
"Pen": "Pen",
|
||||
"Snipping Tool": "Snipping Tool"
|
||||
},
|
||||
"Select All": "Select All",
|
||||
"Services": "Services",
|
||||
"Show All": "Show All",
|
||||
"Show crash dump in Explorer": "Show crash dump in Explorer",
|
||||
"Show crash dump in Finder": "Show crash dump in Finder",
|
||||
"Show Logs in Finder": "Show Logs in Finder",
|
||||
"Show Logs in Explorer": "Show Logs in Explorer",
|
||||
"Show Logs in Finder": "Show Logs in Finder",
|
||||
"SnackBar": {
|
||||
"Press ": "Press ",
|
||||
" to exit full screen": " to exit full screen",
|
||||
"esc": "esc",
|
||||
" to exit full screen": " to exit full screen"
|
||||
"Press ": "Press "
|
||||
},
|
||||
"Sorry, you are not allowed to access this website": "Sorry, you are not allowed to access this website",
|
||||
"Speech": "Speech",
|
||||
"Start Speaking": "Start Speaking",
|
||||
"Stop Speaking": "Stop Speaking",
|
||||
"Symphony Help": "Symphony Help",
|
||||
"Title Bar Style": "Title Bar Style",
|
||||
"Toggle Full Screen": "Toggle Full Screen",
|
||||
"Troubleshooting": "Troubleshooting",
|
||||
"Unable to generate crash reports due to ": "Unable to generate crash reports due to ",
|
||||
"Unable to generate logs due to ": "Unable to generate logs due to ",
|
||||
"Undo": "Undo",
|
||||
"Updating Title bar style requires Symphony to relaunch": "Updating Title bar style requires Symphony to relaunch",
|
||||
"View": "View",
|
||||
"Window": "Window",
|
||||
"Your administrator has disabled": "Your administrator has disabled",
|
||||
"Zoom": "Zoom",
|
||||
"Zoom In": "Zoom In",
|
||||
"Zoom Out": "Zoom Out",
|
||||
"Renderer Process Crashed": "Renderer Process Crashed",
|
||||
"Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.",
|
||||
"Failed!": "Failed!",
|
||||
"No logs are available to share": "No logs are available to share",
|
||||
"Unable to generate logs due to ": "Unable to generate logs due to ",
|
||||
"Show crash dump in Explorer": "Show crash dump in Explorer",
|
||||
"No crashes available to share": "No crashes available to share",
|
||||
"Unable to generate crash reports due to ": "Unable to generate crash reports due to ",
|
||||
"Error setting AutoLaunch configuration": "Error setting AutoLaunch configuration",
|
||||
"Error loading configuration": "Error loading configuration",
|
||||
"Certificate Error": "Certificate Error",
|
||||
"Error loading URL": "Error loading URL",
|
||||
"Error loading window": "Error loading window",
|
||||
"Loading Error": "Loading Error",
|
||||
"Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.",
|
||||
"Not Allowed": "Not Allowed",
|
||||
"Sorry, you are not allowed to access this website": "Sorry, you are not allowed to access this website",
|
||||
"please contact your administrator for more details": "please contact your administrator for more details",
|
||||
"Your administrator has disabled": "Your administrator has disabled",
|
||||
"Please contact your admin for help": "Please contact your admin for help",
|
||||
"Permission Denied": "Permission Denied"
|
||||
"Zoom Out": "Zoom Out"
|
||||
}
|
@ -15,21 +15,32 @@
|
||||
},
|
||||
"Bring All to Front": "すべて前面に表示",
|
||||
"Bring to Front on Notifications": "通知時に前面に表示",
|
||||
"Flash Notification in Taskbar": "タスクバーの通知を点滅",
|
||||
"Title Bar Style": "タイトルバーのスタイル",
|
||||
"Native With Custom": "Nativeでカスタムを使用",
|
||||
"Custom": "カスタム",
|
||||
"Certificate Error": "証明書のエラー",
|
||||
"Close": "閉じる",
|
||||
"Copy": "コピー",
|
||||
"Custom": "カスタム",
|
||||
"Cut": "切り取り",
|
||||
"Delete": "削除",
|
||||
"Dev Tools disabled": "開発ツールを無効にする",
|
||||
"Dev Tools has been disabled. Please contact your system administrator": "Dev Toolsが無効になっています。システム管理者に連絡してください",
|
||||
"Edit": "編集",
|
||||
"Error loading configuration": "構成の読み込みエラー",
|
||||
"Error loading URL": "URLの読み込みエラー",
|
||||
"Error loading window": "ウィンドウを読み込みエラー",
|
||||
"Error setting AutoLaunch configuration": "自動起動の構成の設定エラー",
|
||||
"Failed!": "問題が起きました!",
|
||||
"Flash Notification in Taskbar": "タスクバーの通知を点滅",
|
||||
"Help": "ヘルプ",
|
||||
"Hide Others": "他を隠す",
|
||||
"Hide Symphony": "Symphonyを隠す",
|
||||
"Learn More": "詳細",
|
||||
"Loading Error": "読み込みエラー",
|
||||
"Minimize": "最小化",
|
||||
"Minimize on Close": "閉じるで最小化",
|
||||
"Native": "Native",
|
||||
"No crashes available to share": "共有できるクラッシュはありません",
|
||||
"No logs are available to share": "共有できるログはありません",
|
||||
"Not Allowed": "許可されていませ。",
|
||||
"NotificationSettings": {
|
||||
"Bottom Left": "左下",
|
||||
"Bottom Right": "右下",
|
||||
@ -43,12 +54,19 @@
|
||||
"Top Left": "左上",
|
||||
"Top Right": "右上"
|
||||
},
|
||||
"Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。",
|
||||
"Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。",
|
||||
"Paste": "貼り付け",
|
||||
"Paste and Match Style": "貼り付けでスタイルを合わせる",
|
||||
"Permission Denied": "アクセス許可が拒否されています",
|
||||
"Please contact your admin for help": "不明な点がある場合は、管理者にお問い合わせください",
|
||||
"please contact your administrator for more details": "詳細は、管理者にお問い合わせください",
|
||||
"Quit Symphony": "Symphonyを終了",
|
||||
"Redo": "やり直し",
|
||||
"Refresh app when idle": "アイドル時にアプリを再表示",
|
||||
"Relaunch Application": "アプリケーションの再起動",
|
||||
"Reload": "再読み込み",
|
||||
"Renderer Process Crashed": "レンダラープロセスがクラッシュしました",
|
||||
"ScreenPicker": {
|
||||
"Applications": "アプリケーション",
|
||||
"Cancel": "キャンセル",
|
||||
@ -61,56 +79,40 @@
|
||||
"Share": "共有"
|
||||
},
|
||||
"ScreenSnippet": {
|
||||
"Pen": "ペン",
|
||||
"Done": "完了",
|
||||
"Snipping Tool": "切り取りツール",
|
||||
"Erase": "消去",
|
||||
"Highlight": "強調"
|
||||
"Highlight": "強調",
|
||||
"Pen": "ペン",
|
||||
"Snipping Tool": "切り取りツール"
|
||||
},
|
||||
"Select All": "すべてを選択",
|
||||
"Services": "サービス",
|
||||
"Show All": "すべてを表示",
|
||||
"Show crash dump in Explorer": "Explorerにクラッシュダンプを表示",
|
||||
"Show crash dump in Finder": "ファインダーにクラッシュダンプを表示",
|
||||
"Show Logs in Finder": "ファインダーにログを表示",
|
||||
"Show Logs in Explorer": "Explorerにログを表示",
|
||||
"Show Logs in Finder": "ファインダーにログを表示",
|
||||
"SnackBar": {
|
||||
"Press ": "全画面表示を終了するには ",
|
||||
" to exit full screen": " を押します",
|
||||
"esc": "esc",
|
||||
" to exit full screen": " を押します"
|
||||
"Press ": "全画面表示を終了するには "
|
||||
},
|
||||
"Sorry, you are not allowed to access this website": "申し訳ありませんが、このウェブサイトへのアクセスは許可されていません",
|
||||
"Speech": "スピーチ",
|
||||
"Start Speaking": "スピーチを開始",
|
||||
"Stop Speaking": "スピーチを終了",
|
||||
"Symphony Help": "Symphonyのヘルプ",
|
||||
"Title Bar Style": "タイトルバーのスタイル",
|
||||
"Toggle Full Screen": "画面表示を切り替え",
|
||||
"Troubleshooting": "トラブルシューティング",
|
||||
"Unable to generate crash reports due to ": "クラッシュレポートを生成できません。理由: ",
|
||||
"Unable to generate logs due to ": "ログを生成できません。理由:",
|
||||
"Undo": "元に戻す",
|
||||
"Updating Title bar style requires Symphony to relaunch": "タイトルバーのスタイルを更新するには、Symphonyが再起動する必要があります",
|
||||
"View": "ビュー",
|
||||
"Window": "ウインドウ",
|
||||
"Your administrator has disabled": "管理者によて無効にされています",
|
||||
"Zoom": "ズーム",
|
||||
"Zoom In": "ズームイン",
|
||||
"Zoom Out": "ズームアウト",
|
||||
"Renderer Process Crashed": "レンダラープロセスがクラッシュしました",
|
||||
"Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。",
|
||||
"Failed!": "問題が起きました!",
|
||||
"No logs are available to share": "共有できるログはありません",
|
||||
"Unable to generate logs due to ": "ログを生成できません。理由:",
|
||||
"Show crash dump in Explorer": "Explorerにクラッシュダンプを表示",
|
||||
"No crashes available to share": "共有できるクラッシュはありません",
|
||||
"Unable to generate crash reports due to ": "クラッシュレポートを生成できません。理由: ",
|
||||
"Error setting AutoLaunch configuration": "自動起動の構成の設定エラー",
|
||||
"Error loading configuration": "構成の読み込みエラー",
|
||||
"Certificate Error": "証明書のエラー",
|
||||
"Error loading URL": "URLの読み込みエラー",
|
||||
"Error loading window": "ウィンドウを読み込みエラー",
|
||||
"Loading Error": "読み込みエラー",
|
||||
"Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。",
|
||||
"Not Allowed": "許可されていませ。",
|
||||
"Sorry, you are not allowed to access this website": "申し訳ありませんが、このウェブサイトへのアクセスは許可されていません",
|
||||
"please contact your administrator for more details": "詳細は、管理者にお問い合わせください",
|
||||
"Your administrator has disabled": "管理者によて無効にされています",
|
||||
"Please contact your admin for help": "不明な点がある場合は、管理者にお問い合わせください",
|
||||
"Permission Denied": "アクセス許可が拒否されています",
|
||||
"Dev Tools disabled": "開発ツールを無効にする",
|
||||
"Dev Tools has been disabled. Please contact your system administrator": "Dev Toolsが無効になっています。システム管理者に連絡してください"
|
||||
"Zoom Out": "ズームアウト"
|
||||
}
|
@ -15,21 +15,30 @@
|
||||
},
|
||||
"Bring All to Front": "すべて前面に表示",
|
||||
"Bring to Front on Notifications": "通知時に前面に表示",
|
||||
"Flash Notification in Taskbar": "タスクバーの通知を点滅",
|
||||
"Title Bar Style": "タイトルバーのスタイル",
|
||||
"Native With Custom": "Nativeでカスタムを使用",
|
||||
"Custom": "カスタム",
|
||||
"Certificate Error": "証明書のエラー",
|
||||
"Close": "閉じる",
|
||||
"Copy": "コピー",
|
||||
"Custom": "カスタム",
|
||||
"Cut": "切り取り",
|
||||
"Delete": "削除",
|
||||
"Edit": "編集",
|
||||
"Error loading configuration": "構成の読み込みエラー",
|
||||
"Error loading URL": "URLの読み込みエラー",
|
||||
"Error loading window": "ウィンドウを読み込みエラー",
|
||||
"Error setting AutoLaunch configuration": "自動起動の構成の設定エラー",
|
||||
"Failed!": "問題が起きました!",
|
||||
"Flash Notification in Taskbar": "タスクバーの通知を点滅",
|
||||
"Help": "ヘルプ",
|
||||
"Hide Others": "他を隠す",
|
||||
"Hide Symphony": "Symphonyを隠す",
|
||||
"Learn More": "詳細",
|
||||
"Loading Error": "読み込みエラー",
|
||||
"Minimize": "最小化",
|
||||
"Minimize on Close": "閉じるで最小化",
|
||||
"Native": "Native",
|
||||
"No crashes available to share": "共有できるクラッシュはありません",
|
||||
"No logs are available to share": "共有できるログはありません",
|
||||
"Not Allowed": "許可されていませ。",
|
||||
"NotificationSettings": {
|
||||
"Bottom Left": "左下",
|
||||
"Bottom Right": "右下",
|
||||
@ -43,12 +52,19 @@
|
||||
"Top Left": "左上",
|
||||
"Top Right": "右上"
|
||||
},
|
||||
"Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。",
|
||||
"Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。",
|
||||
"Paste": "貼り付け",
|
||||
"Paste and Match Style": "貼り付けでスタイルを合わせる",
|
||||
"Permission Denied": "アクセス許可が拒否されています",
|
||||
"Please contact your admin for help": "不明な点がある場合は、管理者にお問い合わせください",
|
||||
"please contact your administrator for more details": "詳細は、管理者にお問い合わせください",
|
||||
"Quit Symphony": "Symphonyを終了",
|
||||
"Redo": "やり直し",
|
||||
"Refresh app when idle": "アイドル時にアプリを再表示",
|
||||
"Relaunch Application": "アプリケーションの再起動",
|
||||
"Reload": "再読み込み",
|
||||
"Renderer Process Crashed": "レンダラープロセスがクラッシュしました",
|
||||
"ScreenPicker": {
|
||||
"Applications": "アプリケーション",
|
||||
"Cancel": "キャンセル",
|
||||
@ -61,54 +77,40 @@
|
||||
"Share": "共有"
|
||||
},
|
||||
"ScreenSnippet": {
|
||||
"Pen": "ペン",
|
||||
"Done": "完了",
|
||||
"Snipping Tool": "切り取りツール",
|
||||
"Erase": "消去",
|
||||
"Highlight": "強調"
|
||||
"Highlight": "強調",
|
||||
"Pen": "ペン",
|
||||
"Snipping Tool": "切り取りツール"
|
||||
},
|
||||
"Select All": "すべてを選択",
|
||||
"Services": "サービス",
|
||||
"Show All": "すべてを表示",
|
||||
"Show crash dump in Explorer": "Explorerにクラッシュダンプを表示",
|
||||
"Show crash dump in Finder": "ファインダーにクラッシュダンプを表示",
|
||||
"Show Logs in Finder": "ファインダーにログを表示",
|
||||
"Show Logs in Explorer": "Explorerにログを表示",
|
||||
"Show Logs in Finder": "ファインダーにログを表示",
|
||||
"SnackBar": {
|
||||
"Press ": "全画面表示を終了するには ",
|
||||
" to exit full screen": " を押します",
|
||||
"esc": "esc",
|
||||
" to exit full screen": " を押します"
|
||||
"Press ": "全画面表示を終了するには "
|
||||
},
|
||||
"Sorry, you are not allowed to access this website": "申し訳ありませんが、このウェブサイトへのアクセスは許可されていません",
|
||||
"Speech": "スピーチ",
|
||||
"Start Speaking": "スピーチを開始",
|
||||
"Stop Speaking": "スピーチを終了",
|
||||
"Symphony Help": "Symphonyのヘルプ",
|
||||
"Title Bar Style": "タイトルバーのスタイル",
|
||||
"Toggle Full Screen": "画面表示を切り替え",
|
||||
"Troubleshooting": "トラブルシューティング",
|
||||
"Unable to generate crash reports due to ": "クラッシュレポートを生成できません。理由: ",
|
||||
"Unable to generate logs due to ": "ログを生成できません。理由:",
|
||||
"Undo": "元に戻す",
|
||||
"Updating Title bar style requires Symphony to relaunch": "タイトルバーのスタイルを更新するには、Symphonyが再起動する必要があります",
|
||||
"View": "ビュー",
|
||||
"Window": "ウインドウ",
|
||||
"Your administrator has disabled": "管理者によて無効にされています",
|
||||
"Zoom": "ズーム",
|
||||
"Zoom In": "ズームイン",
|
||||
"Zoom Out": "ズームアウト",
|
||||
"Renderer Process Crashed": "レンダラープロセスがクラッシュしました",
|
||||
"Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。",
|
||||
"Failed!": "問題が起きました!",
|
||||
"No logs are available to share": "共有できるログはありません",
|
||||
"Unable to generate logs due to ": "ログを生成できません。理由:",
|
||||
"Show crash dump in Explorer": "Explorerにクラッシュダンプを表示",
|
||||
"No crashes available to share": "共有できるクラッシュはありません",
|
||||
"Unable to generate crash reports due to ": "クラッシュレポートを生成できません。理由: ",
|
||||
"Error setting AutoLaunch configuration": "自動起動の構成の設定エラー",
|
||||
"Error loading configuration": "構成の読み込みエラー",
|
||||
"Certificate Error": "証明書のエラー",
|
||||
"Error loading URL": "URLの読み込みエラー",
|
||||
"Error loading window": "ウィンドウを読み込みエラー",
|
||||
"Loading Error": "読み込みエラー",
|
||||
"Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。",
|
||||
"Not Allowed": "許可されていませ。",
|
||||
"Sorry, you are not allowed to access this website": "申し訳ありませんが、このウェブサイトへのアクセスは許可されていません",
|
||||
"please contact your administrator for more details": "詳細は、管理者にお問い合わせください",
|
||||
"Your administrator has disabled": "管理者によて無効にされています",
|
||||
"Please contact your admin for help": "不明な点がある場合は、管理者にお問い合わせください",
|
||||
"Permission Denied": "アクセス許可が拒否されています"
|
||||
"Zoom Out": "ズームアウト"
|
||||
}
|
16
package.json
16
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Symphony",
|
||||
"productName": "Symphony",
|
||||
"version": "2.10.0",
|
||||
"version": "2.11.0",
|
||||
"buildNumber": "0",
|
||||
"description": "Symphony desktop app (Foundation ODP)",
|
||||
"author": "Symphony",
|
||||
@ -48,6 +48,7 @@
|
||||
"extraFiles": [
|
||||
"config/Symphony.config",
|
||||
"library/libsymphonysearch.dylib",
|
||||
"library/cryptoLib.dylib",
|
||||
"library/lz4.exec"
|
||||
],
|
||||
"appId": "com.symphony.electron-desktop",
|
||||
@ -88,8 +89,9 @@
|
||||
"devDependencies": {
|
||||
"bluebird": "3.5.1",
|
||||
"browserify": "16.2.2",
|
||||
"chromedriver": "2.40.0",
|
||||
"cross-env": "5.2.0",
|
||||
"electron": "2.0.4",
|
||||
"electron": "2.0.7",
|
||||
"electron-builder": "20.16.4",
|
||||
"electron-builder-squirrel-windows": "12.3.0",
|
||||
"electron-chromedriver": "2.0.0",
|
||||
@ -105,7 +107,9 @@
|
||||
"jest-html-reporter": "2.4.1",
|
||||
"ncp": "2.0.0",
|
||||
"robotjs": "0.5.1",
|
||||
"spectron": "3.8.0"
|
||||
"selenium-webdriver": "3.6.0",
|
||||
"spectron": "3.8.0",
|
||||
"wdio-selenium-standalone-service": "0.0.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paulcbetts/system-idle-time": "1.0.4",
|
||||
@ -124,10 +128,12 @@
|
||||
"lodash.omit": "4.5.0",
|
||||
"lodash.pick": "4.4.0",
|
||||
"shell-path": "2.1.0",
|
||||
"winreg": "1.2.4"
|
||||
"winreg": "1.2.4",
|
||||
"ffi": "2.2.0",
|
||||
"ref": "1.3.5"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"screen-snippet": "git+https://github.com/symphonyoss/ScreenSnippet.git#v1.0.2",
|
||||
"swift-search": "1.0.4"
|
||||
"swift-search": "1.0.5"
|
||||
}
|
||||
}
|
||||
|
69
tests/spectron/alwaysOnTopWithMultApps.spectron.js
Normal file
69
tests/spectron/alwaysOnTopWithMultApps.spectron.js
Normal file
@ -0,0 +1,69 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const WindowsActions = require('./spectronWindowsActions');
|
||||
const WebActions = require('./spectronWebActions');
|
||||
const Utils = require('./spectronUtils');
|
||||
const {isMac} = require('../../js/utils/misc');
|
||||
|
||||
let app;
|
||||
let windowActions;
|
||||
let webActions;
|
||||
|
||||
!isMac ? describe('Tests for always on top with mult-apps are opened', () => {
|
||||
|
||||
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
|
||||
|
||||
beforeAll(async (done) => {
|
||||
try {
|
||||
app = await new Application({}).startApplication({alwaysOnTop: false});
|
||||
windowActions = await new WindowsActions(app);
|
||||
webActions = await new WebActions(app);
|
||||
done();
|
||||
} catch(err) {
|
||||
done.fail(new Error(`Unable to start application error: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(async (done) => {
|
||||
try {
|
||||
await Utils.killProcess("notepad.exe");
|
||||
await Utils.killProcess("mspaint.exe");
|
||||
await windowActions.openMenu(["Window","Always on Top"]);
|
||||
if (app && app.isRunning()) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
await app.stop();
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Failed at post-condition: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify Always on Top options when multiple applications are opened
|
||||
* TC-ID: 2898431
|
||||
* Cover scenarios in AVT-990
|
||||
*/
|
||||
it('Verify Always on Top options when multiple applications are opened', async (done) => {
|
||||
try {
|
||||
await windowActions.openMenu(["Window","Always on Top"]);
|
||||
await webActions.minimizeWindows();
|
||||
await Utils.openAppInMaximize("C:\\Windows\\notepad.exe");
|
||||
await Utils.openAppInMaximize("C:\\Windows\\system32\\mspaint.exe");
|
||||
await windowActions.showWindow();
|
||||
await windowActions.clickOutsideWindow();
|
||||
await windowActions.verifyWindowsOnTop();
|
||||
|
||||
//Close and open app again, make sure it's always on top
|
||||
await app.stop();
|
||||
app = await new Application({}).startApplication();
|
||||
windowActions = await new WindowsActions(app);
|
||||
webActions = await new WebActions(app);
|
||||
await windowActions.clickOutsideWindow();
|
||||
await windowActions.verifyWindowsOnTop();
|
||||
done();
|
||||
} catch(err) {
|
||||
done.fail(new Error(`Fail to keep Always on Top options when multiple applications are opened with error: ${err}`));
|
||||
};
|
||||
});
|
||||
}): describe.skip();
|
69
tests/spectron/badgeCount.spectron.js
Normal file
69
tests/spectron/badgeCount.spectron.js
Normal file
@ -0,0 +1,69 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const WebDriver = require('./spectronWebDriver');
|
||||
const { isMac } = require('../../js/utils/misc.js');
|
||||
const Utils = require('./spectronUtils');
|
||||
let app = new Application({
|
||||
startTimeout: Application.getTimeOut(),
|
||||
waitTimeout: Application.getTimeOut()
|
||||
});
|
||||
let webdriver = new WebDriver({ browser: 'chrome' });
|
||||
const WindowsAction = require('./spectronWindowsActions');
|
||||
const WebActions = require('./spectronWebActions');
|
||||
const specconst = require('./spectronConstants.js');
|
||||
|
||||
let webActions, windowAction;
|
||||
|
||||
describe('Test for Badge Count on MAC', () => {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
|
||||
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||
beforeAll(async (done) => {
|
||||
try {
|
||||
app = await new Application({}).startApplication({ testedHost: specconst.TESTED_HOST, alwaysOnTop: true });
|
||||
windowAction = await new WindowsAction(app);
|
||||
webActions = await new WebActions(app);
|
||||
done();
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Unable to start application error: ${err}`));
|
||||
};
|
||||
});
|
||||
afterAll(async (done) => {
|
||||
try {
|
||||
if (app && app.isRunning()) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
await app.stop();
|
||||
await webdriver.quit();
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Failed at post-condition: ${err}`));
|
||||
};
|
||||
});
|
||||
/**
|
||||
* Show 1 in tray icon when unread message = 1
|
||||
* TC-ID: 2906586
|
||||
* Cover scenarios in AVT-1095
|
||||
*/
|
||||
it('Show 1 in tray icon when unread message = 1', async (done) => {
|
||||
try {
|
||||
if (isMac) {
|
||||
let message = await Utils.randomString();
|
||||
await webdriver.startDriver();
|
||||
await webdriver.login(specconst.USER_A);
|
||||
await webdriver.createIM(specconst.USER_B.username);
|
||||
await webActions.login(specconst.USER_B);
|
||||
await webActions.clickLeftNavItem(specconst.USER_A.name);
|
||||
await webActions.openAlertsSettings();
|
||||
let currentBadgeCount = await windowAction.getBadgeCount();
|
||||
await webdriver.sendMessage(message);
|
||||
await windowAction.verifyCurrentBadgeCount(currentBadgeCount + 1);
|
||||
done();
|
||||
}
|
||||
else {
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Show 1 in tray icon with error: ${err}`));
|
||||
}
|
||||
});
|
||||
|
||||
})
|
90
tests/spectron/closePopOutsOnReload.spectron.js
Normal file
90
tests/spectron/closePopOutsOnReload.spectron.js
Normal file
@ -0,0 +1,90 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const path = require('path');
|
||||
|
||||
let app = new Application({});
|
||||
|
||||
describe('Tests for pop outs reload scenario', () => {
|
||||
|
||||
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
|
||||
|
||||
beforeAll((done) => {
|
||||
return app.startApplication().then((startedApp) => {
|
||||
app = startedApp;
|
||||
done();
|
||||
}).catch((err) => {
|
||||
console.error(`Unable to start application: ${err}`);
|
||||
expect(err).toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterAll((done) => {
|
||||
if (app && app.isRunning()) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
app.stop().then(() => {
|
||||
done();
|
||||
}).catch((err) => {
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should launch the app', (done) => {
|
||||
return app.client.waitUntilWindowLoaded().then(() => {
|
||||
return app.client.getWindowCount().then((count) => {
|
||||
expect(count === 1).toBeTruthy();
|
||||
done();
|
||||
}).catch((err) => {
|
||||
expect(err).toBeNull();
|
||||
});
|
||||
}).catch((err) => {
|
||||
expect(err).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load the demo page', () => {
|
||||
return app.client.url('file:///' + path.join(__dirname, '..', '..', 'demo/index.html'));
|
||||
});
|
||||
|
||||
it('should open a new window and verify', function (done) {
|
||||
app.client.waitForExist('#open-win', 2000);
|
||||
app.client.moveToObject('#open-win', 10, 10);
|
||||
app.client.leftClick('#open-win', 10, 10);
|
||||
|
||||
setTimeout(() => {
|
||||
app.client.getWindowCount().then((count) => {
|
||||
expect(count === 2).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
it('should open a child window from pop-out and verify', function (done) {
|
||||
return app.client.windowByIndex(1).then(() => {
|
||||
app.client.waitForExist('#open-win', 2000);
|
||||
app.client.moveToObject('#open-win', 10, 10);
|
||||
app.client.leftClick('#open-win', 10, 10);
|
||||
|
||||
setTimeout(() => {
|
||||
app.client.getWindowCount().then((count) => {
|
||||
expect(count === 3).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
it('should close pop-out window when main window is reloaded', function (done) {
|
||||
return app.client.windowByIndex(0).then(() => {
|
||||
app.browserWindow.reload();
|
||||
|
||||
setTimeout(() => {
|
||||
app.client.getWindowCount().then((count) => {
|
||||
expect(count === 1).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
});
|
52
tests/spectron/electronProductionLogging.spectron.js
Normal file
52
tests/spectron/electronProductionLogging.spectron.js
Normal file
@ -0,0 +1,52 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const WindowsActions = require('./spectronWindowsActions');
|
||||
const { isMac } = require('../../js/utils/misc.js');
|
||||
const Utils = require('./spectronUtils');
|
||||
|
||||
let app;
|
||||
let windowActions;
|
||||
|
||||
!isMac ? describe('Tests for Electron Production Logging', () => {
|
||||
|
||||
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
|
||||
|
||||
beforeAll(async (done) => {
|
||||
try {
|
||||
app = await new Application({}).startApplication();
|
||||
windowActions = await new WindowsActions(app);
|
||||
await windowActions.deleteAllLogFiles();
|
||||
done();
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Unable to start application error: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(async (done) => {
|
||||
try {
|
||||
if (app && app.isRunning()) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
await app.stop();
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Failed at post-condition: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify the production logs exists when clicking on "Show logs in Explorer"
|
||||
* TC-ID: 3935260
|
||||
* Cover scenarios in AVT-1029
|
||||
*/
|
||||
it('Verify the production logs exists when clicking on Show logs in Explorer', async (done) => {
|
||||
try {
|
||||
await windowActions.openMenu(["Help", "Troubleshooting", "Show Logs in Explorer"]);
|
||||
Utils.sleep(2000) //sleep for creating log
|
||||
await windowActions.verifyLogExported();
|
||||
done();
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Fail to export production logs with error: ${err}`));
|
||||
};
|
||||
});
|
||||
}) : describe.skip();
|
@ -123,8 +123,8 @@ describe('Tests for Full screen', () => {
|
||||
robot.setMouseDelay(100);
|
||||
let x = bounds.x + 200;
|
||||
let y = bounds.y + 200;
|
||||
robot.moveMouseSmooth(x, y);
|
||||
robot.mouseClick();
|
||||
robot.moveMouse(x, y);
|
||||
robot.mouseClick("left");
|
||||
|
||||
robot.keyTap('f11');
|
||||
|
||||
|
154
tests/spectron/minimize-on-close-icon.spectron.js
Normal file
154
tests/spectron/minimize-on-close-icon.spectron.js
Normal file
@ -0,0 +1,154 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const { isMac } = require('../../js/utils/misc');
|
||||
const WindowsAction = require('./spectronWindowsActions');
|
||||
const WebAction = require('./spectronWebActions');
|
||||
var app = new Application({
|
||||
startTimeout: Application.getTimeOut(),
|
||||
waitTimeout: Application.getTimeOut()
|
||||
});
|
||||
let wActions;
|
||||
let webActions;
|
||||
|
||||
!isMac ?describe('Add Test To Verify Minimize on Close', () => {
|
||||
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
|
||||
beforeAll(async (done) => {
|
||||
await app.startApplication().then(async(startedApp) => {
|
||||
app.app = await startedApp;
|
||||
wActions = await new WindowsAction(app.app);
|
||||
webActions = await new WebAction(app.app);
|
||||
}).then((async() =>{
|
||||
await getConfigPath(app.app).then((config) => {
|
||||
app.pathApp = config;
|
||||
}).catch((err) => {
|
||||
done.fail(new Error(`Unable to start application error: ${err}`));
|
||||
});
|
||||
done();
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
function getConfigPath(app) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
app.client.addCommand('getUserDataPath', function () {
|
||||
return app.client.execute(function () {
|
||||
return require('electron').remote.app.getPath('userData');
|
||||
})
|
||||
});
|
||||
app.client.getUserDataPath().then((userConfigPath) => {
|
||||
resolve(userConfigPath.value)
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
afterAll((done) => {
|
||||
if (app && app.isRunning()) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
app.client.getWindowCount().then((count) => {
|
||||
if (count > 0) {
|
||||
app.stop().then(() => {
|
||||
done();
|
||||
}).catch((err) => {
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
})
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify Minimize on Close option once the application is installed
|
||||
* TC-ID: 3084609
|
||||
* Cover scenarios in AVT-939
|
||||
*/
|
||||
it('Verify Minimize on Close option once the application is installed', async(done) => {
|
||||
await Application.readConfig(app.pathApp).then(async (userConfig) => {
|
||||
|
||||
//When app un-ticked on Minimize On Close Menu Item
|
||||
//Select 1 times to perform for ticking Menu
|
||||
await wActions.openMenu(["Window","Minimize on Close"]);
|
||||
|
||||
if (userConfig.minimizeOnClose != false) {
|
||||
//When app ticked on Minimize On Close Menu Item
|
||||
//Select 2 times to perform for ticking Menu
|
||||
await wActions.openMenu(["Window","Minimize on Close"]);
|
||||
|
||||
}
|
||||
await wActions.openMenu(["Window","Close"]);
|
||||
await wActions.verifyMinimizeWindows();
|
||||
done();
|
||||
|
||||
}).catch((err) => {
|
||||
done.fail(new Error(`minimize-on-close failed in readConfig with error: ${err}`));
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* Close window when 'Minimize on Close' is ON
|
||||
* TC-ID: 2911252
|
||||
* Cover scenarios in AVT-937
|
||||
*/
|
||||
it('Close window when "Minimize on Close" is ON', async (done) => {
|
||||
|
||||
Application.readConfig(app.pathApp).then(async (userConfig) => {
|
||||
|
||||
//When app un-ticked on Minimize On Close Menu Item
|
||||
//Select 1 times to perform for ticking Menu
|
||||
await wActions.focusWindow();
|
||||
await wActions.openMenu(["Window","Minimize on Close"]);
|
||||
if (userConfig.minimizeOnClose != false) {
|
||||
await wActions.openMenu(["Window","Minimize on Close"]);
|
||||
}
|
||||
//When app ticked on Minimize On Close Menu Item
|
||||
//Select 2 times to perform for ticking Menu
|
||||
|
||||
await wActions.openMenu(["Window","Close"])
|
||||
await wActions.verifyMinimizeWindows();
|
||||
|
||||
await wActions.focusWindow();
|
||||
await wActions.pressCtrlW();
|
||||
await wActions.verifyMinimizeWindows();
|
||||
|
||||
await wActions.focusWindow();
|
||||
await wActions.openMenu(["Window","Close"])
|
||||
await wActions.verifyMinimizeWindows();
|
||||
done();
|
||||
|
||||
}).catch((err) => {
|
||||
done.fail(new Error(`minimize-on-close failed in readConfig with error: ${err}`));
|
||||
})
|
||||
});
|
||||
/**
|
||||
* Verify by deselecting Minimize on Close option once the application is launched
|
||||
* TC-ID: 3084612
|
||||
* Cover scenarios in AVT-938
|
||||
*/
|
||||
it('Verify by deselecting Minimize on Close option once the application is launched', async (done) => {
|
||||
await Application.readConfig(app.pathApp).then(async (userConfig) => {
|
||||
|
||||
await wActions.focusWindow();
|
||||
await wActions.openMenu(["Window","Minimize on Close"]).then(async ()=>
|
||||
{
|
||||
if (userConfig.minimizeOnClose == false) {
|
||||
//When app does not tick on Minimize On Close Menu Item
|
||||
//Select 2 times to perform for un-ticking Menu
|
||||
await wActions.openMenu(["Window","Minimize on Close"]);
|
||||
|
||||
}
|
||||
await wActions.openMenu(["Window","Close"])
|
||||
await wActions.verifyMinimizeWindows();
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
}).catch((err) => {
|
||||
done.fail(new Error(`minimize-on-close failed in readConfig with error: ${err}`));
|
||||
})
|
||||
});
|
||||
}) : describe.skip();
|
84
tests/spectron/pop-Outs.spectron.js
Normal file
84
tests/spectron/pop-Outs.spectron.js
Normal file
@ -0,0 +1,84 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const WebActions = require('./spectronWebActions');
|
||||
const WindowsActions = require('./spectronWindowsActions');
|
||||
const { isMac } = require('../../js/utils/misc.js');
|
||||
const constants = require('./spectronConstants.js');
|
||||
const Utils = require('./spectronUtils');
|
||||
|
||||
let app, webActions, windowsActions;
|
||||
|
||||
describe('Tests for Pop-Outs', () => {
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = constants.TIMEOUT_TEST_SUITE;
|
||||
|
||||
beforeAll(async (done) => {
|
||||
try {
|
||||
app = await new Application({}).startApplication({testedHost: constants.TESTED_HOST});
|
||||
webActions = await new WebActions(app);
|
||||
windowsActions = await new WindowsActions(app);
|
||||
done();
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Unable to start application error: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(async (done) => {
|
||||
try {
|
||||
await windowsActions.closeAllPopOutWindow();
|
||||
if (app && app.isRunning()) {
|
||||
await app.stop();
|
||||
await webDriver.quit();
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Failed at post-condition: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify pop-out chat, inbox
|
||||
* TC-ID: 2897209
|
||||
* Cover scenarios in AVT-1081
|
||||
*/
|
||||
it('Verify pop-out chat, inbox', async (done) => {
|
||||
try {
|
||||
if (isMac) {
|
||||
done();
|
||||
} else {
|
||||
await webActions.login(constants.USER_A);
|
||||
await windowsActions.closeAllPopOutWindow();
|
||||
await windowsActions.bringToFront("Symphony");
|
||||
|
||||
await webActions.createIM(constants.USER_B.name);
|
||||
await webActions.clickPopOutIcon();
|
||||
await windowsActions.verifyPopOutWindowAppear(constants.USER_B.name);
|
||||
await webActions.verifyPopInIconDisplay(constants.USER_B.name);
|
||||
|
||||
await webActions.clickInboxIcon();
|
||||
await webActions.clickInboxPopOutIcon();
|
||||
await windowsActions.verifyPopOutWindowAppear("Inbox");
|
||||
await webActions.verifyPopInIconDisplay("Inbox");
|
||||
|
||||
await windowsActions.bringToFront("Symphony");
|
||||
await webActions.clickInboxIcon();
|
||||
await windowsActions.verifyWindowFocus("Inbox");
|
||||
|
||||
await windowsActions.bringToFront("Symphony");
|
||||
await webActions.clickLeftNavItem(constants.USER_B.name);
|
||||
await Utils.sleep(1); //wait for popout overlaying completely
|
||||
await windowsActions.verifyWindowFocus(constants.USER_B.name);
|
||||
|
||||
await windowsActions.bringToFront("Symphony");
|
||||
await webActions.logout();
|
||||
await webActions.login(constants.USER_A);
|
||||
await windowsActions.verifyPopOutWindowAppear(constants.USER_B.name);
|
||||
await windowsActions.verifyPopOutWindowAppear("Inbox");
|
||||
|
||||
await windowsActions.closeAllPopOutWindow();
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Fail to verify pop-out chat, inbox: ${err}`));
|
||||
};
|
||||
});
|
||||
})
|
@ -1,44 +1,35 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const robot = require('robotjs');
|
||||
const {isMac} = require('../../js/utils/misc');
|
||||
const WindowsActions = require('./spectronWindowsActions');
|
||||
|
||||
let app = new Application({});
|
||||
let defaultWidth;
|
||||
let defaultHeight;
|
||||
let windowActions;
|
||||
|
||||
!isMac ? describe('Tests for Resizing windows', () => {
|
||||
describe('Tests for Resizing windows', () => {
|
||||
|
||||
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
|
||||
|
||||
beforeAll((done) => {
|
||||
return app.startApplication().then((startedApp) => {
|
||||
app = startedApp;
|
||||
beforeAll(async (done) => {
|
||||
try {
|
||||
app = await new Application({}).startApplication();
|
||||
windowActions = await new WindowsActions(app);
|
||||
done();
|
||||
}).catch((err) => {
|
||||
} catch(err) {
|
||||
done.fail(new Error(`Unable to start application error: ${err}`));
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
afterAll((done) => {
|
||||
afterAll(async (done) => {
|
||||
try {
|
||||
if (app && app.isRunning()) {
|
||||
// resize to default size
|
||||
app.browserWindow.getBounds().then((bounds) => {
|
||||
let x = bounds.x - (defaultWidth - bounds.width);
|
||||
let y = bounds.y - (defaultHeight - bounds.height);
|
||||
robot.moveMouse(bounds.x, bounds.y);
|
||||
robot.mouseToggle("down");
|
||||
robot.dragMouse(x, y);
|
||||
robot.mouseToggle("up");
|
||||
})
|
||||
//close app
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
app.stop().then(() => {
|
||||
await app.stop();
|
||||
done();
|
||||
}).catch((err) => {
|
||||
done();
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Failed at post-condition: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
@ -46,24 +37,13 @@ let defaultHeight;
|
||||
* TC-ID: 3028239
|
||||
* Cover scenarios in AVT-768
|
||||
*/
|
||||
it('should be minimized up to 300px', (done) => {
|
||||
app.browserWindow.getBounds().then((bounds) => {
|
||||
defaultHeight = bounds.height;
|
||||
defaultWidth = bounds.width;
|
||||
let x = bounds.x + bounds.width;
|
||||
let y = bounds.y + bounds.height;
|
||||
robot.setMouseDelay(500);
|
||||
robot.moveMouse(bounds.x, bounds.y);
|
||||
robot.mouseToggle("down");
|
||||
robot.dragMouse(x, y);
|
||||
robot.mouseToggle("up");
|
||||
return app.browserWindow.getBounds().then((bounds) => {
|
||||
const data = {x: bounds.width, y: bounds.height};
|
||||
expect(data).toEqual({x: 300, y: 300});
|
||||
it('Should be minimized up to 300px', async (done) => {
|
||||
try {
|
||||
await windowActions.resizeWindows(0, 0);
|
||||
expect([ 300, 300 ]).toEqual(await windowActions.getCurrentSize());
|
||||
done();
|
||||
}).catch((err) => {
|
||||
} catch (err) {
|
||||
done.fail(new Error(`failed to minimize window to 300 px with error: ${err}`));
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
}) : describe.skip();
|
75
tests/spectron/saveLayout.spectron.js
Normal file
75
tests/spectron/saveLayout.spectron.js
Normal file
@ -0,0 +1,75 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const WindowsActions = require('./spectronWindowsActions');
|
||||
const WebActions = require('./spectronWebActions');
|
||||
const { isMac } = require('../../js/utils/misc');
|
||||
const Utils = require('./spectronUtils');
|
||||
|
||||
let app;
|
||||
let windowActions;
|
||||
let webActions;
|
||||
|
||||
!isMac ? describe('Tests for saved layout', () => {
|
||||
|
||||
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
|
||||
|
||||
beforeAll(async (done) => {
|
||||
try {
|
||||
app = await new Application({}).startApplication();
|
||||
windowActions = await new WindowsActions(app);
|
||||
done();
|
||||
} catch(err) {
|
||||
done.fail(new Error(`Unable to start application error: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(async (done) => {
|
||||
try {
|
||||
if (app && app.isRunning()) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
await app.stop();
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Failed at post-condition: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Keep size and position of the windows in previous session
|
||||
* TC-ID: 2915948
|
||||
* Cover scenarios in AVT-914
|
||||
*/
|
||||
it('Keep size and position of the windows in previous session', async (done) => {
|
||||
try {
|
||||
var defaultPosition = await windowActions.getCurrentPosition();
|
||||
var defaultSize = await windowActions.getCurrentSize();
|
||||
|
||||
// Size and position of previos session keep after resizing and dragging
|
||||
await windowActions.setPosition(defaultPosition[0], 20);
|
||||
await windowActions.setSize(defaultSize[0] - 100, defaultSize[0] - 100);
|
||||
await Utils.sleep(1); // Sleep 1s after resizing
|
||||
var previousPosition = await windowActions.getCurrentPosition();
|
||||
var previousSize = await windowActions.getCurrentSize();
|
||||
await app.stop();
|
||||
app = await new Application({}).startApplication({defaultSize: false, defaultPosition: false});
|
||||
windowActions = await new WindowsActions(app);
|
||||
webActions = await new WebActions(app);
|
||||
expect(previousPosition).toEqual(await windowActions.getCurrentPosition());
|
||||
expect(previousSize).toEqual(await windowActions.getCurrentSize());
|
||||
|
||||
// Size and position of previous session keep after maximizing
|
||||
await webActions.maximizeWindows();
|
||||
await Utils.sleep(1); // Sleep 1s after resizing
|
||||
previousSize = await windowActions.getCurrentSize();
|
||||
await app.stop();
|
||||
app = await new Application({}).startApplication({defaultSize: false, defaultPosition: false});
|
||||
windowActions = await new WindowsActions(app);
|
||||
webActions = await new WebActions(app);
|
||||
expect(previousSize).toEqual(await windowActions.getCurrentSize());
|
||||
done();
|
||||
} catch(err) {
|
||||
done.fail(new Error(`Fail to keep size and position of the windows in previous session with error: ${err}`));
|
||||
};
|
||||
});
|
||||
}) : describe.skip();
|
@ -7,4 +7,24 @@ module.exports = {
|
||||
|
||||
SEARCH_LIBRARY_PATH_MAC: "node_modules/electron/dist/Electron.app/Contents/library",
|
||||
SEARCH_LIBRARY_PATH_WIN: "node_modules/electron/dist/library",
|
||||
MENU: {
|
||||
"root": {
|
||||
name: "menu", step: 0, items: [
|
||||
{ name: "Edit", step: 0, items: [{ name: "Undo", step: 0 }, { name: "Redo", step: 1 }, { name: "Cut", step: 2 }, { name: "Copy", step: 3 }, { name: "Paste", step: 4 }, { name: "Paste and Match Style", step: 5 }, { name: "Delete", step: 6 }, { name: "Select All", step: 7 }] },
|
||||
{ name: "View", step: 1, items: [{ name: "Reload", step: 0 }, { name: "Actual Size", step: 1 }, { name: "Zoom In", step: 2 }, { name: "Zoom Out", step: 3 }, { name: "Toogle Full Screen", step: 4 }] },
|
||||
{ name: "Window", step: 2, items: [{ name: "Minimize", step: 0 }, { name: "Close", step: 1 }, { name: "Auto Launch On Startup", step: 2 }, { name: "Always on Top", step: 3 }, { name: "Minimize on Close", step: 4 }] },
|
||||
{ name: "Help", step: 3, items: [{ name: "Symphony Help", step: 0 }, { name: "Learn More", step: 1 }, { name: "Troubleshooting", step: 2, items: [{ name: "Show Logs in Explorer", step: 0 }] }, { name: "About Symphony", step: 3 }] }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
LOG_FILENAME_PREFIX: "logs_symphony_",
|
||||
USER_A: { username: process.env.USER_A, password: process.env.PASSWORD, name: process.env.USER_A_NAME },
|
||||
USER_B: { username: process.env.USER_B, password: process.env.PASSWORD, name: process.env.USER_B_NAME },
|
||||
USER_C: { username: process.env.USER_C, password: process.env.PASSWORD, name: process.env.USER_C_NAME },
|
||||
TESTED_HOST: process.env.TESTED_HOST,
|
||||
TYPE_ROOM: { private: "PRIVATE", public: "PUBLIC" },
|
||||
TIMEOUT_TEST_SUITE: 600000,
|
||||
TIMEOUT_PAGE_LOAD: 120000,
|
||||
TIMEOUT_WAIT_ELEMENT: 10000
|
||||
};
|
69
tests/spectron/spectronInterfaces.js
Normal file
69
tests/spectron/spectronInterfaces.js
Normal file
@ -0,0 +1,69 @@
|
||||
module.exports= {
|
||||
// Title bar
|
||||
TITLE_BAR: "#title-bar",
|
||||
MAXIMIZE_BTN: "#title-bar-maximize-button",
|
||||
CLOSE_BUTTON: "button#title-bar-close-button",
|
||||
MAIN_MENU_ITEM: "#hamburger-menu-button",
|
||||
SYM_LOGO: "#logo",
|
||||
|
||||
//Sign In
|
||||
SIGN_IN_BUTTON: "//button[@name='signin-submit']",
|
||||
SIGN_IN_EMAIL: "//input[@name='signin-email']",
|
||||
SIGN_IN_PASSWORD: "//input[@name='signin-password']",
|
||||
NAV_PROFILE: "//div[@id='nav-profile']",
|
||||
PLUS_BTN: "//div[@class='nav-profile__plus-btn-icon']",
|
||||
IM_TAB: "//div[contains(@class,'modal-box modal-box--nfs')]//li[contains(text(),'Direct Chat')]",
|
||||
CHATROOM_TAB: "//div[contains(@class, 'modal-box modal-box--nfs')]//li[contains(text(),'Chat Room')]",
|
||||
CREATE_IM: "//form[@class='create-im']",
|
||||
CREATE_BUTTON: "//button[text()='Create']",
|
||||
ADD_PARTICIPANT_TEXT: "//div[@id='react-modal']//input[contains(@class,'react-autosuggest__input')]",
|
||||
USERS_SUGGESTION_LIST: "//li[@id='react-autowhatever-1-section-0-item-0']",
|
||||
CHATROOM_NAME_TEXT: "//form[@class='create-chatroom']//input[@name='name']",
|
||||
CHATROOM_DESCR_TEXT: "//form[@class='create-chatroom']//input[@name='description']",
|
||||
PRIVATE_ROOM_RADIO_BTN: "//form[@class='create-chatroom']//input[@value='PRIVATE']",
|
||||
PUBLIC_ROOM_RADIO_BTN: "//form[@class='create-chatroom']//input[@value='PUBLIC']",
|
||||
CREATE_IM_DONE_BTN: "//button[contains(@class,tempo-btn--good) and text()='Create']",
|
||||
START_CHAT: "//*[contains(@class, 'sym-menu-tooltip__option')]/*[text()='Start a Chat']",
|
||||
SIGNAL_OPTION: "//div[@class='sym-menu-tooltip__option']/*[text()='Create a Signal']",
|
||||
LEFT_NAV_SINGLE_ITEM: "//div[contains(@class, 'navigation-item-title')]//span[@class='navigation-item-name' and normalize-space()='$$']",
|
||||
CHAT_INPUT_TYPING: "//div[contains(@class,'public-DraftEditor-content')]",
|
||||
SETTTING_BUTTON: "//div[@class='toolbar-settings-text-container']",
|
||||
PERSIS_NOTIFICATION_INPUT_ROOM: "//div[@class='alerts-settings__notification-category']//h5[text()='Rooms:']/..//input[@class='persistent-notification']",
|
||||
PERSIS_NOTIFICATION_INPUT_IM: "//div[@class='alerts-settings__notification-category']//h5[text()='IMs:']/..//input[@class='persistent-notification']",
|
||||
PERSIS_NOTIFICATION_INPUT_SIGNAL: "//div[@class='alerts-settings__notification-category']//h5[text()='Signals:']/..//input[@class='persistent-notification']",
|
||||
ALERT_TAB: "//*[contains(@class,'tempo-tabs__tab tabs-tab') and @data-tab='alerts']",
|
||||
ALERT_OPTION: "//span[@class='sym-menu-tooltip__option-label' and contains(.,'Alerts')]",
|
||||
NAV_ALIAS: "//div[@class='nav-profile__alias']",
|
||||
SIGNAL_HEADER: "//span[@class='navigation-category-name' and contains(.,'Signals')]",
|
||||
WARNING_CLOSE_ICON: "//div[@id='sysMsg']//span[@class='close-icon']",
|
||||
SCROLL_TAB_ACTIVE: "//div[@class='active-tab-container']",
|
||||
SIGNAL_NAME: "//input[@class='react-signal__name']",
|
||||
HASHTAG_NAME: "//div[@class='react-signal__rule-name']//input",
|
||||
LAST_RULE_ROW: "//div[@class='react-signal__rules'][last()]",
|
||||
ENTER_KEYWORD_IN_LAST_INPUT: "//input",
|
||||
HEADER_MODULE: "//header[contains(@class,'module-header gs-draggable')]",
|
||||
MENTION_USER_SUGGESTION: "//span[@class='draftJs__suggestionsEntryText' and text()='$$']",
|
||||
SUGGESTED_ENTITY_DROPDOWN: "//span[@class='draftJs__suggestionsEntryText']",
|
||||
CONFIRM_CREATE_ROOM_BUTTON: "//div[@class='modal-box__footer-buttons']//button[text()='Yes']",
|
||||
MODULE_ON_GRID: "#simple_grid",
|
||||
SPINNER: ".spinner",
|
||||
SIGNOUT: ".sign-out",
|
||||
SIGNOUT_MODAL_BUTTON: "//div[@class='modal-content-buttons buttons']//button[contains(text(), 'Sign Out')]",
|
||||
|
||||
//Popin Popout
|
||||
POPOUT_BUTTON: ".enhanced-pop-out",
|
||||
POPOUT_INBOX_BUTTON: ".add-margin.popout",
|
||||
POPIN_BUTTON: "//*[contains(@class, 'enhanced-pop-in') or contains(@class, 'add-margin popin')]",
|
||||
PIN_CHAT_MOD: ".chat-module .pin-view",
|
||||
|
||||
//Alert Settings
|
||||
MUTE_POPUP_ALERTS_CKB: ".field.field-notifications-on input",
|
||||
|
||||
//Toast Message
|
||||
TOAST_MESSAGE_CONTENT: "#message",
|
||||
|
||||
//Inbox
|
||||
INBOX_BUTTON: ".toolbar-btn-inbox",
|
||||
INBOX_HEADER: ".inbox-header",
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ const fs = require('fs');
|
||||
const { isMac, isWindowsOS } = require('../../js/utils/misc');
|
||||
const ncp = require('ncp').ncp;
|
||||
const constants = require('./spectronConstants.js');
|
||||
const ui = require('./spectronInterfaces.js');
|
||||
|
||||
class App {
|
||||
|
||||
@ -26,22 +27,34 @@ class App {
|
||||
App.copyLibraries(constants.SEARCH_LIBRARY_PATH_WIN);
|
||||
}
|
||||
|
||||
|
||||
this.app = new Application(this.options);
|
||||
}
|
||||
|
||||
startApplication(configurations) {
|
||||
return this.app.start().then((app) => {
|
||||
if (configurations)
|
||||
{
|
||||
if (configurations.alwaysOnTop) {
|
||||
app.browserWindow.setAlwaysOnTop(true);
|
||||
async startApplication(configurations) {
|
||||
try {
|
||||
this.app = await this.app.start();
|
||||
await this.app.client.waitForVisible(ui.SYM_LOGO, constants.TIMEOUT_PAGE_LOAD);
|
||||
if (configurations) {
|
||||
if (typeof configurations.alwaysOnTop !== "undefined") {
|
||||
await this.app.browserWindow.setAlwaysOnTop(configurations.alwaysOnTop);
|
||||
}
|
||||
if (configurations.testedHost) {
|
||||
await this.app.client.waitUntilWindowLoaded().url(configurations.testedHost);
|
||||
}
|
||||
}
|
||||
return app;
|
||||
}).catch((err) => {
|
||||
|
||||
if ((typeof configurations === "undefined") || (typeof configurations.defaultSize === "undefined") || (configurations.defaultSize === true)) {
|
||||
await this.app.browserWindow.setSize(900, 900);
|
||||
}
|
||||
if ((typeof configurations === "undefined") || (typeof configurations.defaultPosition === "undefined") || (configurations.defaultPosition === true)) {
|
||||
await this.app.browserWindow.center();
|
||||
}
|
||||
await this.app.browserWindow.minimize();
|
||||
await this.app.browserWindow.restore();
|
||||
return this.app;
|
||||
} catch (err) {
|
||||
throw new Error("Unable to start application " + err);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
static getAppPath() {
|
||||
@ -53,7 +66,7 @@ class App {
|
||||
}
|
||||
|
||||
static getTimeOut() {
|
||||
return 90000
|
||||
return 90000;
|
||||
}
|
||||
|
||||
static readConfig(configPath) {
|
||||
|
44
tests/spectron/spectronUtils.js
Normal file
44
tests/spectron/spectronUtils.js
Normal file
@ -0,0 +1,44 @@
|
||||
const childProcess = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
class Utils {
|
||||
static async openAppInMaximize(appPath) {
|
||||
await childProcess.exec('start /MAX ' + appPath);
|
||||
}
|
||||
|
||||
static async killProcess(processName) {
|
||||
await childProcess.exec('taskkill /f /t /im ' + processName);
|
||||
}
|
||||
|
||||
static async sleep(second) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, this.toMs(second));
|
||||
})
|
||||
}
|
||||
|
||||
static getFolderPath(folderName) {
|
||||
return path.join(require('os').homedir(), folderName);
|
||||
}
|
||||
|
||||
static getFiles(path) {
|
||||
return fs.readdirSync(path);
|
||||
}
|
||||
|
||||
static toMs(second) {
|
||||
return second * 1000;
|
||||
}
|
||||
|
||||
static async randomString() {
|
||||
var chars = await "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
||||
var string_length = await 8;
|
||||
var randomstring = await '';
|
||||
for (var i = 0; i < string_length; i++) {
|
||||
var rnum = await Math.floor(Math.random() * chars.length);
|
||||
randomstring += await chars.substring(rnum, rnum + 1);
|
||||
}
|
||||
return randomstring;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Utils;
|
279
tests/spectron/spectronWebActions.js
Normal file
279
tests/spectron/spectronWebActions.js
Normal file
@ -0,0 +1,279 @@
|
||||
const ui = require('./spectronInterfaces.js');
|
||||
const constants = require('./spectronConstants.js');
|
||||
const Utils = require('./spectronUtils');
|
||||
const WindowsActions = require('./spectronWindowsActions');
|
||||
|
||||
class WebActions {
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
async maximizeButtonByClick() {
|
||||
await this.app.client.waitForVisible(ui.MAXIMIZE_BTN, constants.TIMEOUT_WAIT_ELEMENT).click(ui.MAXIMIZE_BTN);
|
||||
}
|
||||
|
||||
async maximizeWindows() {
|
||||
await this.maximizeButtonByClick();
|
||||
await this.app.browserWindow.isMaximized().then(function (maximized) {
|
||||
expect(maximized).toBeTruthy();
|
||||
})
|
||||
}
|
||||
|
||||
async clickMinimizeButton() {
|
||||
await this.app.client.waitForVisible(ui.MINIMIZE_BTN, 10000).click(ui.MINIMIZE_BTN);
|
||||
}
|
||||
|
||||
async minimizeWindows() {
|
||||
await this.clickMinimizeButton();
|
||||
await this.app.browserWindow.isMinimized().then(function (isMinimized) {
|
||||
expect(isMinimized).toBeTruthy();
|
||||
})
|
||||
}
|
||||
|
||||
async minimizeWindowByClick() {
|
||||
await this.app.client.click(ui.MINIMIZED_BUTTON);
|
||||
}
|
||||
|
||||
async closeWindowByClick() {
|
||||
await this.app.client.click(ui.CLOSE_BUTTON);
|
||||
|
||||
}
|
||||
|
||||
async openApplicationMenuByClick() {
|
||||
await this.app.client.click(ui.MAIN_MENU_ITEM);
|
||||
}
|
||||
|
||||
async getElementByXPath(xpath) {
|
||||
await this.app.client.waitForVisible(xpath, constants.TIMEOUT_WAIT_ELEMENT);
|
||||
var elem = this.app.client.element(xpath);
|
||||
if (elem.isVisible()) {
|
||||
return elem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async inputText(el, data) {
|
||||
var obj = await this.getElementByXPath(el);
|
||||
if (obj != null)
|
||||
await this.app.client.setValue(el, data);
|
||||
}
|
||||
async clickAndWaitElementVisible(xpath, elementToVisible, timeOut = constants.TIMEOUT_WAIT_ELEMENT) {
|
||||
await this.app.client.click(xpath).then(async () => {
|
||||
await this.app.client.waitForVisible(elementToVisible, timeOut);
|
||||
});
|
||||
}
|
||||
|
||||
async scrollAndClick(selector, findElement) {
|
||||
var i = 0;
|
||||
var y = 0;
|
||||
await this.app.client.scroll(selector, 0, y);
|
||||
var size = 0;
|
||||
while (i < 10) {
|
||||
size = this.app.client.getElementSize(findElement);
|
||||
if (findElement != null && size == 0) {
|
||||
y += 50;
|
||||
await this.app.client.scroll(selector, 0, y);
|
||||
}
|
||||
else {
|
||||
await this.app.client.click(findElement);
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
async promiseTimeout(ms, promiseFunc) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
// create a timeout to reject promise if not resolved
|
||||
var timer = setTimeout(function () {
|
||||
reject(new Error("promise timeout"));
|
||||
}, ms);
|
||||
promiseFunc
|
||||
.then(function (res) {
|
||||
clearTimeout(timer);
|
||||
resolve(res);
|
||||
})
|
||||
.catch(function (err) {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
async clickIfElementVisible(selector, timeOut = constants.TIMEOUT_WAIT_ELEMENT) {
|
||||
await this.app.client.waitForVisible(selector, timeOut)
|
||||
.click(selector)
|
||||
}
|
||||
|
||||
async openAlertsSettings() {
|
||||
await this.clickAndWaitElementVisible(ui.SETTTING_BUTTON, ui.ALERT_OPTION);
|
||||
await this.clickAndWaitElementVisible(ui.ALERT_OPTION, ui.ALERT_TAB);
|
||||
}
|
||||
|
||||
async verifyToastNotificationShow(message) {
|
||||
let show = false;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
let winCount = await this.app.client.getWindowCount();
|
||||
if (winCount > 1) {
|
||||
for (let j = 1; j < winCount; j++) {
|
||||
await this.app.client.windowByIndex(j);
|
||||
if (await this.app.client.getText(ui.TOAST_MESSAGE_CONTENT) === message) {
|
||||
show = true;
|
||||
}
|
||||
}
|
||||
if (show) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
await Utils.sleep(1);
|
||||
}
|
||||
await expect(show).toBeTruthy();
|
||||
await this.app.client.windowByIndex(0);
|
||||
}
|
||||
|
||||
async verifyNoToastNotificationShow(message) {
|
||||
let noShow;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
let winCount = await this.app.client.getWindowCount();
|
||||
if (winCount > 1) {
|
||||
for (let j = 1; j < winCount; j++) {
|
||||
await this.app.client.windowByIndex(j);
|
||||
if (await this.app.client.getText(ui.TOAST_MESSAGE_CONTENT) !== message) {
|
||||
noShow = true;
|
||||
}
|
||||
else {
|
||||
noShow = false;
|
||||
}
|
||||
}
|
||||
if (noShow === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
await Utils.sleep(1);
|
||||
}
|
||||
await expect(noShow).toBeTruthy();
|
||||
await this.app.client.windowByIndex(0);
|
||||
}
|
||||
|
||||
async getElementByXPath(xpath) {
|
||||
let elem = this.app.client.element(xpath);
|
||||
if (elem.isVisible()) {
|
||||
return elem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async inputText(el, data) {
|
||||
let obj = await this.getElementByXPath(el);
|
||||
if (obj != null)
|
||||
await this.app.client.setValue(el, data);
|
||||
}
|
||||
|
||||
async clickAndWaitElementVisible(xpath, elementToVisible, timeOut = constants.TIMEOUT_WAIT_ELEMENT) {
|
||||
await this.app.client.click(xpath);
|
||||
await this.app.client.waitForVisible(elementToVisible, timeOut);
|
||||
}
|
||||
|
||||
async clickIfElementVisible(xpath, timeOut = constants.TIMEOUT_WAIT_ELEMENT) {
|
||||
await this.app.client.waitForVisible(xpath, timeOut)
|
||||
.click(xpath)
|
||||
}
|
||||
|
||||
async login(user) {
|
||||
await this.inputText(ui.SIGN_IN_EMAIL, user.username);
|
||||
await this.inputText(ui.SIGN_IN_PASSWORD, user.password);
|
||||
await this.clickAndWaitElementVisible(ui.SIGN_IN_BUTTON, ui.SETTTING_BUTTON, constants.TIMEOUT_PAGE_LOAD);
|
||||
await this.waitElementNotVisible(ui.SPINNER);
|
||||
}
|
||||
|
||||
async persistToastIM() {
|
||||
await this.clickAndWaitElementVisible(ui.SETTTING_BUTTON, ui.ALERT_OPTION);
|
||||
await this.clickAndWaitElementVisible(ui.ALERT_OPTION, ui.ALERT_TAB);
|
||||
await this.clickAndWaitElementVisible(ui.PERSIS_NOTIFICATION_INPUT_IM, ui.PERSIS_NOTIFICATION_INPUT_IM);
|
||||
}
|
||||
|
||||
async clickPlusButton() {
|
||||
await this.clickIfElementVisible(ui.PLUS_BTN);
|
||||
}
|
||||
|
||||
async clickStartChat() {
|
||||
await this.clickIfElementVisible(ui.START_CHAT);
|
||||
}
|
||||
|
||||
async selectIMTab() {
|
||||
await this.clickIfElementVisible(ui.IM_TAB);
|
||||
}
|
||||
|
||||
async addParticipant(username) {
|
||||
await this.inputText(ui.ADD_PARTICIPANT_TEXT, username);
|
||||
await this.clickIfElementVisible(ui.USERS_SUGGESTION_LIST, 20000);
|
||||
}
|
||||
|
||||
async clickDoneButton() {
|
||||
await this.clickIfElementVisible(ui.CREATE_IM_DONE_BTN);
|
||||
await this.waitElementVisible(ui.HEADER_MODULE);
|
||||
}
|
||||
|
||||
async waitElementNotVisible(locator, timeOut = constants.TIMEOUT_WAIT_ELEMENT) {
|
||||
return await this.app.client.waitForVisible(locator, timeOut, true);
|
||||
}
|
||||
|
||||
async waitElementVisible(locator, timeOut = constants.TIMEOUT_WAIT_ELEMENT) {
|
||||
return await this.app.client.waitForVisible(locator, timeOut);
|
||||
}
|
||||
|
||||
async mouseOver(locator) {
|
||||
await this.app.client.moveToObject(locator);
|
||||
}
|
||||
|
||||
async createIM(username) {
|
||||
await this.clickPlusButton();
|
||||
await this.clickStartChat();
|
||||
await this.selectIMTab();
|
||||
await this.addParticipant(username);
|
||||
await this.clickDoneButton();
|
||||
}
|
||||
|
||||
async clickPopOutIcon() {
|
||||
let windowsActions = await new WindowsActions(this.app);
|
||||
await this.mouseOver(ui.PIN_CHAT_MOD);
|
||||
await Utils.sleep(2); //wait popout button clickable
|
||||
await this.clickIfElementVisible(ui.POPOUT_BUTTON);
|
||||
let index = await windowsActions.getWindowCount() - 1;
|
||||
await windowsActions.windowByIndex(index);
|
||||
await this.waitElementNotVisible(ui.SPINNER, constants.TIMEOUT_PAGE_LOAD);
|
||||
}
|
||||
|
||||
async clickInboxPopOutIcon() {
|
||||
let windowsActions = await new WindowsActions(this.app);
|
||||
await this.clickIfElementVisible(ui.POPOUT_INBOX_BUTTON);
|
||||
let index = await windowsActions.getWindowCount() - 1;
|
||||
await windowsActions.windowByIndex(index);
|
||||
await this.waitElementNotVisible(ui.SPINNER, constants.TIMEOUT_PAGE_LOAD);
|
||||
}
|
||||
|
||||
async verifyPopInIconDisplay(windowTitle){
|
||||
let windowsActions = await new WindowsActions(this.app);
|
||||
let index = await windowsActions.getWindowIndexFromTitle(windowTitle);
|
||||
await windowsActions.windowByIndex(index);
|
||||
await this.waitElementVisible(ui.POPIN_BUTTON, constants.TIMEOUT_WAIT_ELEMENT);
|
||||
await windowsActions.windowByIndex(0);
|
||||
}
|
||||
|
||||
async clickInboxIcon() {
|
||||
await this.clickIfElementVisible(ui.INBOX_BUTTON);
|
||||
}
|
||||
|
||||
async clickLeftNavItem(item){
|
||||
let singleItemLocator = ui.LEFT_NAV_SINGLE_ITEM.replace("$$",item);
|
||||
await this.clickIfElementVisible(singleItemLocator);
|
||||
}
|
||||
|
||||
async logout(){
|
||||
await this.openAlertsSettings();
|
||||
await this.clickAndWaitElementVisible(ui.SIGNOUT, ui.SIGNOUT_MODAL_BUTTON);
|
||||
await this.clickAndWaitElementVisible(ui.SIGNOUT_MODAL_BUTTON, ui.SIGN_IN_BUTTON, constants.TIMEOUT_PAGE_LOAD);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebActions;
|
279
tests/spectron/spectronWebDriver.js
Normal file
279
tests/spectron/spectronWebDriver.js
Normal file
@ -0,0 +1,279 @@
|
||||
const { Builder, By, Key, until } = require('selenium-webdriver')
|
||||
require('selenium-webdriver/chrome');
|
||||
require('chromedriver');
|
||||
var assert = require('assert');
|
||||
const ui = require('./spectronInterfaces.js');
|
||||
const specconst = require('./spectronConstants.js');
|
||||
|
||||
class WebDriver {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
this.d = new Builder().forBrowser(options.browser).build();
|
||||
this.initDriver();
|
||||
}
|
||||
|
||||
async waitElelmentIsNotVisible(xpath) {
|
||||
let result = false;
|
||||
try {
|
||||
const el = await this.driver.wait(
|
||||
until.elementLocated(By.xpath(xpath)),
|
||||
specconst.TIMEOUT_WAIT_ELEMENT
|
||||
)
|
||||
await this.driver.wait(until.elementIsNotVisible(el), specconst.TIMEOUT_WAIT_ELEMENT);
|
||||
if (this.driver.findElements(By.xpath(xpath)).length > 0) {
|
||||
result = true;
|
||||
}
|
||||
else {
|
||||
result = false;
|
||||
}
|
||||
return await assert.equal(result, false);
|
||||
}
|
||||
catch (err) {
|
||||
await assert.equal(result, false);
|
||||
}
|
||||
}
|
||||
|
||||
async waitElelmentIsVisible(xpath,timeout) {
|
||||
try {
|
||||
const el = await this.driver.wait(
|
||||
until.elementLocated(By.xpath(xpath)),
|
||||
specconst.TIMEOUT_WAIT_ELEMENT
|
||||
)
|
||||
await this.driver.wait(until.elementIsVisible(el), timeout);
|
||||
}
|
||||
catch (err) {
|
||||
console.log("Error:"+err.messages);
|
||||
}
|
||||
}
|
||||
|
||||
async waitElementVisibleAndGet(xpath) {
|
||||
const el = await this.driver.wait(
|
||||
until.elementLocated(By.xpath(xpath)),
|
||||
specconst.TIMEOUT_WAIT_ELEMENT
|
||||
)
|
||||
return await this.driver.wait(until.elementIsVisible(el), specconst.TIMEOUT_WAIT_ELEMENT)
|
||||
}
|
||||
|
||||
async getElementById(id) {
|
||||
const el = await this.driver.wait(until.elementLocated(By.id(id)), specconst.TIMEOUT_WAIT_ELEMENT)
|
||||
return await this.driver.wait(until.elementIsVisible(el), specconst.TIMEOUT_WAIT_ELEMENT)
|
||||
}
|
||||
|
||||
async getElementByXPath(xpath) {
|
||||
const el = await this.driver.wait(
|
||||
until.elementLocated(By.xpath(xpath)),
|
||||
specconst.TIMEOUT_WAIT_ELEMENT
|
||||
)
|
||||
return await this.driver.wait(until.elementIsVisible(el), specconst.TIMEOUT_WAIT_ELEMENT)
|
||||
}
|
||||
|
||||
async inputText(el, data) {
|
||||
var obj = await this.getElementByXPath(el);
|
||||
await obj.sendKeys(data);
|
||||
}
|
||||
|
||||
async sendEnter(el) {
|
||||
var obj = await this.getElementByXPath(el);
|
||||
await obj.sendKeys(Key.ENTER);
|
||||
}
|
||||
|
||||
async sendMessage(message) {
|
||||
await this.inputText(ui.CHAT_INPUT_TYPING, message);
|
||||
await this.sendEnter(ui.CHAT_INPUT_TYPING);
|
||||
}
|
||||
|
||||
async sendMessages(messages) {
|
||||
for (var i = 0; i < messages.length; i++) {
|
||||
await this.sendMessage(messages[i]);
|
||||
await this.sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
async login(user) {
|
||||
await this.inputText(ui.SIGN_IN_EMAIL, user.username);
|
||||
await this.inputText(ui.SIGN_IN_PASSWORD, user.password);
|
||||
var singin = await this.getElementByXPath(ui.SIGN_IN_BUTTON);
|
||||
await singin.click();
|
||||
await this.waitElelmentIsVisible(ui.SETTTING_BUTTON,specconst.TIMEOUT_PAGE_LOAD);
|
||||
}
|
||||
|
||||
async mentionUserOnChat(user)
|
||||
{
|
||||
await this.inputText(ui.CHAT_INPUT_TYPING, "@"+user.name);
|
||||
var suggestion = ui.MENTION_USER_SUGGESTION.replace("$$",user.name);
|
||||
var el = await this.getElementByXPath(suggestion);
|
||||
await el.click();
|
||||
await this.sendEnter(ui.CHAT_INPUT_TYPING);
|
||||
}
|
||||
|
||||
async waitSuggestionShowOnlyOneItem(xpath)
|
||||
{
|
||||
if (this.driver.findElements(By.xpath(xpath)).length==1) {
|
||||
return result = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async clickShowConversationCreationModal() {
|
||||
var plusButton = await this.getElementByXPath(ui.PLUS_BTN);
|
||||
await plusButton.click();
|
||||
}
|
||||
|
||||
async selectIMTab() {
|
||||
var imTab = await this.getElementByXPath(ui.IM_TAB);
|
||||
await imTab.click();
|
||||
}
|
||||
|
||||
async selectRoomTab() {
|
||||
var roomTab = await this.getElementByXPath(ui.CHATROOM_TAB);
|
||||
await roomTab.click();
|
||||
}
|
||||
|
||||
async addParticipant(username) {
|
||||
await this.inputText(ui.ADD_PARTICIPANT_TEXT, username);
|
||||
await this.sleep(5);
|
||||
var el = await this.waitElementVisibleAndGet(ui.USERS_SUGGESTION_LIST);
|
||||
await el.click();
|
||||
}
|
||||
|
||||
async clickDoneButton() {
|
||||
var el = await this.getElementByXPath(ui.CREATE_IM_DONE_BTN);
|
||||
await el.click();
|
||||
}
|
||||
|
||||
async clickDoneButton() {
|
||||
var el = await this.getElementByXPath(ui.CREATE_IM_DONE_BTN);
|
||||
await el.click();
|
||||
await this.waitElelmentIsNotVisible(ui.CREATE_IM_DONE_BTN);
|
||||
}
|
||||
|
||||
async clickConfirmCreateRoom() {
|
||||
var el = await this.getElementByXPath(ui.CONFIRM_CREATE_ROOM_BUTTON);
|
||||
await el.click();
|
||||
await this.waitElelmentIsNotVisible(ui.CONFIRM_CREATE_ROOM_BUTTON);
|
||||
}
|
||||
|
||||
async clickStartChat() {
|
||||
var el = await this.getElementByXPath(ui.START_CHAT);
|
||||
await el.click();
|
||||
}
|
||||
|
||||
async createIM(username) {
|
||||
await this.clickShowConversationCreationModal();
|
||||
await this.clickStartChat();
|
||||
await this.selectIMTab();
|
||||
await this.addParticipant(username);
|
||||
await this.clickDoneButton();
|
||||
}
|
||||
|
||||
async createMIM(usernames) {
|
||||
await this.clickShowConversationCreationModal();
|
||||
await this.clickStartChat();
|
||||
await this.selectIMTab();
|
||||
for (var i = 0; i < usernames.length; i++) {
|
||||
await this.addParticipant(usernames[i]);
|
||||
}
|
||||
await this.clickDoneButton();
|
||||
}
|
||||
|
||||
async clickCreateSignal() {
|
||||
var el = await this.getElementByXPath(ui.SIGNAL_OPTION);
|
||||
await el.click();
|
||||
}
|
||||
|
||||
async selectPublicRadioButton() {
|
||||
var el = await this.waitElementVisibleAndGet(ui.PUBLIC_ROOM_RADIO_BTN);
|
||||
await el.click();
|
||||
}
|
||||
|
||||
async selectPrivateRadioButton() {
|
||||
var el = await this.waitElementVisibleAndGet(ui.PRIVATE_ROOM_RADIO_BTN);
|
||||
await el.click();
|
||||
}
|
||||
|
||||
async clickLeftNavItem(name) {
|
||||
var xpath = await ui.LEFT_NAV_SINGLE_ITEM.replace("$$", name);
|
||||
var el = await this.getElementByXPath(xpath);
|
||||
await el.click();
|
||||
var eheader = await this.getElementByXPath(ui.HEADER_MODULE);
|
||||
await this.driver.wait(until.elementIsVisible(eheader), specconst.TIMEOUT_WAIT_ELEMENT)
|
||||
}
|
||||
|
||||
async createRoom(usernames, name, description, type) {
|
||||
await this.clickShowConversationCreationModal();
|
||||
await this.clickStartChat();
|
||||
await this.selectRoomTab();
|
||||
await this.inputText(ui.CHATROOM_NAME_TEXT, name);
|
||||
await this.inputText(ui.CHATROOM_DESCR_TEXT, description);
|
||||
if (type === specconst.TYPE_ROOM.private) {
|
||||
await this.selectPrivateRadioButton();
|
||||
}
|
||||
if (type === specconst.TYPE_ROOM.public) {
|
||||
await this.selectPublicRadioButton();
|
||||
}
|
||||
for (var i = 0; i < usernames.length; i++) {
|
||||
await this.addParticipant(usernames[i]);
|
||||
}
|
||||
await this.clickDoneButton();
|
||||
// await this.clickConfirmCreateRoom();
|
||||
}
|
||||
|
||||
async createSignal(signalName, hashTag)
|
||||
{
|
||||
await this.clickShowConversationCreationModal();
|
||||
await this.clickCreateSignal();
|
||||
await this.inputText(ui.SIGNAL_NAME,signalName);
|
||||
await this.inputText(ui.LAST_RULE_ROW+ui.ENTER_KEYWORD_IN_LAST_INPUT,hashTag);
|
||||
await this.clickDoneButton();
|
||||
}
|
||||
|
||||
async initDriver() {
|
||||
return this.d.then(_d => {
|
||||
this.driver = _d
|
||||
})
|
||||
}
|
||||
|
||||
async startDriver() {
|
||||
await this.driver
|
||||
.manage()
|
||||
.window()
|
||||
.setPosition(0, 0);
|
||||
var size = await await this.driver
|
||||
.manage()
|
||||
.window().getSize();
|
||||
await this.driver.get(specconst.TESTED_HOST);
|
||||
}
|
||||
|
||||
async focusCurrentBrowser() {
|
||||
this.driver.switchTo().window(this.driver.getAllWindowHandles()[0]);
|
||||
}
|
||||
|
||||
async sleep(secondSleep) {
|
||||
await this.driver.sleep(secondSleep * 1000);
|
||||
}
|
||||
|
||||
async timeOut(secondSleep) {
|
||||
return secondSleep * 1000;
|
||||
}
|
||||
|
||||
async waitElelmentIsVisible(xpath,timeout) {
|
||||
try {
|
||||
const el = await this.driver.wait(
|
||||
until.elementLocated(By.xpath(xpath)),
|
||||
specconst.TIMEOUT_WAIT_ELEMENT
|
||||
)
|
||||
await this.driver.wait(until.elementIsVisible(el), timeout);
|
||||
}
|
||||
catch (err) {
|
||||
console.log("Error:"+err.messages);
|
||||
}
|
||||
}
|
||||
|
||||
async quit() {
|
||||
await this.driver.quit();
|
||||
}
|
||||
async close() {
|
||||
await this.driver.close();
|
||||
}
|
||||
}
|
||||
module.exports = WebDriver;
|
428
tests/spectron/spectronWindowsActions.js
Normal file
428
tests/spectron/spectronWindowsActions.js
Normal file
@ -0,0 +1,428 @@
|
||||
const robot = require('robotjs');
|
||||
const constants = require('./spectronConstants.js');
|
||||
const Utils = require('./spectronUtils.js');
|
||||
const fs = require('fs');
|
||||
const WebActions = require('./spectronWebActions.js')
|
||||
|
||||
class WindowsActions {
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
async getCurrentSize() {
|
||||
return this.app.browserWindow.getSize();
|
||||
}
|
||||
|
||||
async setSize(width, height) {
|
||||
await this.app.browserWindow.setSize(width, height);
|
||||
}
|
||||
|
||||
async resizeWindows(width, height) {
|
||||
await this.app.browserWindow.getBounds().then((bounds) => {
|
||||
let x = bounds.x + (bounds.width - width);
|
||||
let y = bounds.y + (bounds.height - height);
|
||||
robot.setMouseDelay(500);
|
||||
// Plus 2 pixels to make sure this function works well on MAC
|
||||
robot.moveMouse(bounds.x + 2, bounds.y + 2);
|
||||
robot.mouseToggle("down");
|
||||
robot.dragMouse(x, y);
|
||||
robot.mouseToggle("up");
|
||||
})
|
||||
}
|
||||
|
||||
async getCurrentPosition() {
|
||||
return this.app.browserWindow.getPosition();
|
||||
}
|
||||
|
||||
async setPosition(x, y) {
|
||||
await this.app.browserWindow.setPosition(x, y);
|
||||
}
|
||||
|
||||
async dragWindows(x, y) {
|
||||
await this.app.browserWindow.getBounds().then((bounds) => {
|
||||
robot.setMouseDelay(500);
|
||||
robot.moveMouse(bounds.x + 200, bounds.y + 10);
|
||||
robot.mouseToggle("down");
|
||||
robot.moveMouse(bounds.x + 205, bounds.y + 10); // Workaround to make this keyword works properly, refer: https://github.com/octalmage/robotjs/issues/389
|
||||
robot.dragMouse(x + 205, y + 10);
|
||||
robot.mouseToggle("up");
|
||||
})
|
||||
}
|
||||
|
||||
async showWindow() {
|
||||
await this.app.browserWindow.restore();
|
||||
await this.app.browserWindow.setAlwaysOnTop(true);
|
||||
}
|
||||
|
||||
async clickOutsideWindow() {
|
||||
await this.setPosition(0, 0);
|
||||
let currentSize = await this.getCurrentSize();
|
||||
await robot.moveMouse(currentSize[0] + 20, currentSize[1] + 20);
|
||||
await robot.mouseClick();
|
||||
}
|
||||
|
||||
async verifyWindowsOnTop() {
|
||||
await this.app.browserWindow.isAlwaysOnTop().then(function (isAlwaysOnTop) {
|
||||
expect(isAlwaysOnTop).toBeTruthy();
|
||||
})
|
||||
}
|
||||
|
||||
async menuSearch(element, namevalue) {
|
||||
if (element.name == namevalue) {
|
||||
return await element;
|
||||
}
|
||||
else if (element.items !== undefined) {
|
||||
var result;
|
||||
for (var i = 0; result == null && i < element.items.length; i++) {
|
||||
result = await this.menuSearch(element.items[i], namevalue);
|
||||
result;
|
||||
}
|
||||
return await result;
|
||||
}
|
||||
return await null;
|
||||
}
|
||||
|
||||
async openMenu(arrMenu) {
|
||||
var arrStep = [];
|
||||
for (var i = 0; i < arrMenu.length; i++) {
|
||||
var item = await this.menuSearch(constants.MENU.root, arrMenu[i]);
|
||||
await arrStep.push(item);
|
||||
}
|
||||
await this.actionForMenus(arrStep);
|
||||
return arrStep;
|
||||
}
|
||||
|
||||
async actionForMenus(arrMenu) {
|
||||
let webAction = await new WebActions(this.app);
|
||||
await this.app.browserWindow.getBounds().then(async (bounds) => {
|
||||
await robot.setMouseDelay(100);
|
||||
let x = bounds.x + 95;
|
||||
let y = bounds.y + 35;
|
||||
await robot.moveMouseSmooth(x, y);
|
||||
await robot.moveMouse(x, y);
|
||||
await robot.mouseClick();
|
||||
await webAction.openApplicationMenuByClick();
|
||||
await robot.setKeyboardDelay(200);
|
||||
await robot.keyTap('enter');
|
||||
for (var i = 0; i < arrMenu.length; i++) {
|
||||
for (var s = 0; s < arrMenu[i].step; s++) {
|
||||
await robot.keyTap('down');
|
||||
}
|
||||
if (arrMenu.length > 1 && i != arrMenu.length - 1) {
|
||||
//handle right keygen
|
||||
await robot.keyTap('right');
|
||||
}
|
||||
}
|
||||
await robot.keyTap('enter');
|
||||
});
|
||||
}
|
||||
|
||||
async verifyLogExported() {
|
||||
let expected = false;
|
||||
let path = await Utils.getFolderPath('Downloads');
|
||||
let listFiles = Utils.getFiles(path);
|
||||
listFiles.forEach(function (fileName) {
|
||||
if (fileName.indexOf(constants.LOG_FILENAME_PREFIX) > -1) {
|
||||
expected = true;
|
||||
}
|
||||
})
|
||||
await expect(expected).toBeTruthy();
|
||||
}
|
||||
|
||||
async deleteAllLogFiles() {
|
||||
let path = await Utils.getFolderPath('Downloads');
|
||||
let listFiles = Utils.getFiles(path);
|
||||
await listFiles.forEach(function (fileName) {
|
||||
if (fileName.indexOf(constants.LOG_FILENAME_PREFIX) > -1) {
|
||||
fs.unlinkSync(path.concat("\\", fileName));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async verifyMinimizeWindows() {
|
||||
await this.app.browserWindow.isMinimized().then(async function (minimized) {
|
||||
await expect(minimized).toBeTruthy();
|
||||
}).catch((err) => {
|
||||
console.log(err.name);
|
||||
});;
|
||||
}
|
||||
|
||||
async isMinimizedWindows() {
|
||||
let rminimized = -1;
|
||||
|
||||
await this.app.browserWindow.isMinimized().then(async function (minimized) {
|
||||
rminimized = constants.MINIMIZED;
|
||||
}).catch((err) => {
|
||||
rminimized = constants.QUIT;
|
||||
return rminimized;
|
||||
});
|
||||
|
||||
return rminimized;
|
||||
}
|
||||
|
||||
async selectMinimizeOnClose() {
|
||||
let webAction = await new WebActions(this.app);
|
||||
await this.app.browserWindow.getBounds().then(async (bounds) => {
|
||||
await robot.setMouseDelay(100);
|
||||
let x = bounds.x + 95;
|
||||
let y = bounds.y + 35;
|
||||
await robot.moveMouseSmooth(x, y);
|
||||
await robot.moveMouse(x, y);
|
||||
await robot.mouseClick();
|
||||
await webAction.openApplicationMenuByClick();
|
||||
await robot.setKeyboardDelay(1000);
|
||||
await robot.keyTap('enter');
|
||||
await robot.keyTap('down');
|
||||
await robot.keyTap('down');
|
||||
await robot.keyTap('right');
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await robot.keyTap('down');
|
||||
}
|
||||
await robot.keyTap('enter');
|
||||
});
|
||||
}
|
||||
|
||||
async quitApp() {
|
||||
let webAction = await new WebActions(this.app);
|
||||
await this.app.browserWindow.getBounds().then(async (bounds) => {
|
||||
await robot.setMouseDelay(100);
|
||||
let x = bounds.x + 95;
|
||||
let y = bounds.y + 35;
|
||||
await robot.moveMouseSmooth(x, y);
|
||||
await robot.moveMouse(x, y);
|
||||
await robot.mouseClick();
|
||||
await webAction.openApplicationMenuByClick();
|
||||
await robot.setKeyboardDelay(1000);
|
||||
await robot.keyTap('enter');
|
||||
await robot.keyTap('down');
|
||||
await robot.keyTap('down');
|
||||
await robot.keyTap('right');
|
||||
for (let i = 0; i < 6; i++) {
|
||||
await robot.keyTap('down');
|
||||
}
|
||||
await robot.keyTap('enter');
|
||||
});
|
||||
}
|
||||
|
||||
async pressCtrlW() {
|
||||
await robot.keyToggle('w', 'down', ['control']);
|
||||
await robot.keyToggle('w', 'up', ['control']);
|
||||
}
|
||||
|
||||
async verifyMinimizeWindows() {
|
||||
await this.app.browserWindow.isMinimized().then(async function (minimized) {
|
||||
await expect(minimized).toBeTruthy();
|
||||
}).catch((err) => {
|
||||
console.log("error:" + err.name);
|
||||
});;
|
||||
}
|
||||
|
||||
async isMinimizedWindows() {
|
||||
let rminimized = -1;
|
||||
|
||||
await this.app.browserWindow.isMinimized().then(async function (minimized) {
|
||||
rminimized = constants.MINIMIZED;
|
||||
}).catch((err) => {
|
||||
rminimized = constants.QUIT;
|
||||
return rminimized;
|
||||
});
|
||||
|
||||
return rminimized;
|
||||
}
|
||||
|
||||
async pressCtrlM() {
|
||||
await robot.keyToggle('m', 'down', ['control']);
|
||||
await robot.keyToggle('m', 'up', ['control']);
|
||||
}
|
||||
|
||||
async pressCtrlR() {
|
||||
await robot.keyToggle('r', 'down', ['control']);
|
||||
await robot.keyToggle('r', 'up', ['control']);
|
||||
}
|
||||
|
||||
async focusWindow() {
|
||||
this.app.browserWindow.focus();
|
||||
this.app.browserWindow.setAlwaysOnTop(true);
|
||||
}
|
||||
|
||||
async reload() {
|
||||
await this.app.browserWindow.getBounds().then(async (bounds) => {
|
||||
await robot.setMouseDelay(100);
|
||||
let x = bounds.x + 95;
|
||||
let y = bounds.y + 200;
|
||||
await robot.moveMouseSmooth(x, y);
|
||||
await robot.moveMouse(x, y);
|
||||
await robot.mouseClick('right');
|
||||
await robot.setKeyboardDelay(2000);
|
||||
await robot.keyTap('right');
|
||||
await robot.keyTap('down');
|
||||
await robot.keyTap('enter');
|
||||
}).catch((err1) => {
|
||||
console.log("Message:" + err1);
|
||||
});
|
||||
}
|
||||
|
||||
async clickNotification() {
|
||||
let screen = await this.app.electron.screen.getAllDisplays();
|
||||
await this.app.browserWindow.getBounds().then(async (bounds) => {
|
||||
await robot.setMouseDelay(50);
|
||||
let x = screen[0].bounds.width - 50;
|
||||
let y = screen[0].bounds.height - 100;
|
||||
await robot.moveMouseSmooth(x, y);
|
||||
await robot.moveMouse(x, y);
|
||||
await robot.mouseClick();
|
||||
});
|
||||
}
|
||||
|
||||
async mouseMoveNotification() {
|
||||
let screen = await this.app.electron.screen.getAllDisplays();
|
||||
await this.app.browserWindow.getBounds().then(async (bounds) => {
|
||||
await robot.setMouseDelay(50);
|
||||
let x = screen[0].bounds.width - 50;
|
||||
let y = screen[0].bounds.height - 100;
|
||||
await robot.moveMouseSmooth(x, y);
|
||||
await robot.moveMouse(x, y);
|
||||
});
|
||||
}
|
||||
|
||||
async mouseMoveCenter() {
|
||||
let screen = await this.app.electron.screen.getAllDisplays();
|
||||
await this.app.browserWindow.getBounds().then(async (bounds) => {
|
||||
await robot.setMouseDelay(50);
|
||||
let x = screen[0].bounds.width - 500;
|
||||
let y = screen[0].bounds.height - 100;
|
||||
await robot.moveMouseSmooth(x, y);
|
||||
await robot.moveMouse(x, y);
|
||||
});
|
||||
}
|
||||
|
||||
async veriryPersistToastNotification(message) {
|
||||
let i = 0;
|
||||
while (i < 6) {
|
||||
await Utils.sleep(1);
|
||||
await i++;
|
||||
}
|
||||
await this.webAction.verifyToastNotificationShow(message);
|
||||
await this.clickNotification();
|
||||
await this.mouseMoveCenter();
|
||||
}
|
||||
|
||||
async verifyNotPersistToastNotification(message) {
|
||||
let i = 0;
|
||||
let count = 0;
|
||||
|
||||
while (i < 11) {
|
||||
await Utils.sleep(1);
|
||||
await i++;
|
||||
}
|
||||
await this.webAction.verifyNoToastNotificationShow(message);
|
||||
await this.mouseMoveCenter();
|
||||
}
|
||||
|
||||
async verifyNotCloseToastWhenMouseOver(message) {
|
||||
await this.mouseMoveNotification();
|
||||
let i = 0;
|
||||
while (i < 8) {
|
||||
await Utils.sleep(1);
|
||||
await i++;
|
||||
}
|
||||
await this.webAction.verifyToastNotificationShow(message);
|
||||
await this.mouseMoveCenter();
|
||||
}
|
||||
|
||||
async getBadgeCount() {
|
||||
let count = await this.app.electron.remote.app.getBadgeCount();
|
||||
return count;
|
||||
}
|
||||
|
||||
async resetBadgeCount() {
|
||||
await this.app.electron.remote.app.setBadgeCount(0);
|
||||
}
|
||||
|
||||
async getBadgeCount() {
|
||||
let count = await this.app.electron.remote.app.getBadgeCount();
|
||||
return count;
|
||||
}
|
||||
|
||||
async verifyCurrentBadgeCount(number) {
|
||||
let expected = false;
|
||||
let i = 0;
|
||||
let count = await this.getBadgeCount();
|
||||
while (i < 5) {
|
||||
if (count == number) {
|
||||
expected = true;
|
||||
break;
|
||||
}
|
||||
await Utils.sleep(1);
|
||||
count = await this.getBadgeCount();
|
||||
await i++;
|
||||
}
|
||||
await expect(expected).toBeTruthy();
|
||||
}
|
||||
|
||||
async getWindowIndexFromTitle(windowTitle) {
|
||||
let winCount = await this.getWindowCount();
|
||||
if (winCount > 1) {
|
||||
for (let j = 1; j < winCount; j++) {
|
||||
await this.windowByIndex(j);
|
||||
|
||||
//wait 120s for title loading
|
||||
let title = await this.app.browserWindow.getTitle();
|
||||
for (let i = 1; i <= 120; i++) {
|
||||
if (title != "Symphony") {
|
||||
break;
|
||||
}
|
||||
await Utils.sleep(1);
|
||||
title = await this.app.browserWindow.getTitle();;
|
||||
}
|
||||
|
||||
if (title === windowTitle) {
|
||||
await this.windowByIndex(0);
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
await this.windowByIndex(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
async windowByIndex(index) {
|
||||
await this.app.client.windowByIndex(index);
|
||||
}
|
||||
|
||||
async getWindowCount() {
|
||||
return await this.app.client.getWindowCount();
|
||||
}
|
||||
|
||||
async bringToFront(windowTitle) {
|
||||
let index = await this.getWindowIndexFromTitle(windowTitle);
|
||||
await this.windowByIndex(index);
|
||||
await this.app.browserWindow.minimize();
|
||||
await this.app.browserWindow.restore();
|
||||
}
|
||||
|
||||
async verifyWindowFocus(windowTitle) {
|
||||
let index = await this.getWindowIndexFromTitle(windowTitle);
|
||||
await this.windowByIndex(index);
|
||||
expect(await this.app.browserWindow.isFocused()).toBeTruthy();
|
||||
await this.windowByIndex(0);
|
||||
}
|
||||
|
||||
async verifyPopOutWindowAppear(windowTitle) {
|
||||
let index = await this.getWindowIndexFromTitle(windowTitle);
|
||||
expect(index).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
async closeAllPopOutWindow() {
|
||||
let winCount = await this.getWindowCount();
|
||||
while (winCount > 1) {
|
||||
await this.windowByIndex(winCount - 1);
|
||||
await this.app.browserWindow.close();
|
||||
await Utils.sleep(2);
|
||||
winCount = await this.getWindowCount();
|
||||
}
|
||||
await this.windowByIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WindowsActions;
|
85
tests/spectron/toastMouseHoveringForIM.spectron.js
Normal file
85
tests/spectron/toastMouseHoveringForIM.spectron.js
Normal file
@ -0,0 +1,85 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const WebDriver = require('./spectronWebDriver');
|
||||
const { isMac } = require('../../js/utils/misc.js');
|
||||
var app = new Application({
|
||||
startTimeout: Application.getTimeOut(),
|
||||
waitTimeout: Application.getTimeOut()
|
||||
});
|
||||
var webdriver = new WebDriver({ browser: 'chrome' });
|
||||
const WindowsAction = require('./spectronWindowsActions');
|
||||
const WebActions = require('./spectronWebActions');
|
||||
const specconst = require('./spectronConstants.js');
|
||||
const Utils = require('./spectronUtils');
|
||||
const ifc = require('./spectronInterfaces.js');
|
||||
let webActions, windowAction;
|
||||
|
||||
!isMac? describe('Verify toast notification for IMs', () => {
|
||||
let originalTimeout = specconst.DEFAULT_TIMEOUT_INTERVAL;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = specconst.TIMEOUT_TEST_SUITE;
|
||||
|
||||
beforeAll(async(done) => {
|
||||
try
|
||||
{
|
||||
app = await new Application({}).startApplication({testedHost:specconst.TESTED_HOST, alwaysOnTop: true});
|
||||
windowAction = await new WindowsAction(app);
|
||||
webActions = await new WebActions(app);
|
||||
done();
|
||||
} catch(err) {
|
||||
done.fail(new Error(`Unable to start application error: ${err}`));
|
||||
};
|
||||
});
|
||||
afterAll((done) => {
|
||||
if (app && app.isRunning()) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
app.stop().then(() => {
|
||||
webdriver.close();
|
||||
webdriver.quit();
|
||||
done();
|
||||
}).catch((err) => {
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Verify toast notification for IMs
|
||||
* TC-ID: 3395297
|
||||
* Cover scenarios in AVT-1031
|
||||
*/
|
||||
it('Toast notification should not be closed', async () => {
|
||||
|
||||
await webdriver.startDriver();
|
||||
await webdriver.login(specconst.USER_A);
|
||||
await webdriver.createIM(specconst.USER_B.username);
|
||||
await webActions.login(specconst.USER_B);
|
||||
await windowAction.reload();
|
||||
await app.client.waitForVisible(ifc.SETTTING_BUTTON, Utils.toMs(50));
|
||||
await webActions.clickIfElementVisible(ifc.SETTTING_BUTTON);
|
||||
await windowAction.pressCtrlM();
|
||||
await webdriver.clickLeftNavItem(specconst.USER_B.name);
|
||||
var message = await Utils.randomString();
|
||||
await webdriver.sendMessages([message]);
|
||||
await windowAction.verifyNotCloseToastWhenMouseOver(message);
|
||||
|
||||
});
|
||||
/**
|
||||
* Verify toast notification for signals, mentions and keywords
|
||||
* TC-ID: 3395306
|
||||
* Cover scenarios in AVT-1032
|
||||
*/
|
||||
it('Verify toast notification for signals, mentions and keywords', async () => {
|
||||
var nameSignal = await Utils.randomString();
|
||||
var nameHashTag = await Utils.randomString();
|
||||
var roomName = await Utils.randomString();
|
||||
var description =await Utils.randomString();
|
||||
|
||||
await webdriver.createSignal(nameSignal,nameHashTag);
|
||||
await webdriver.createRoom([specconst.USER_B.username],roomName,description,specconst.TYPE_ROOM.public)
|
||||
await webdriver.clickLeftNavItem(roomName);
|
||||
|
||||
await webdriver.sendMessages(["#"+nameHashTag]);
|
||||
await windowAction.verifyNotCloseToastWhenMouseOver(specconst.USER_A.name+": #"+nameHashTag);
|
||||
await webdriver.mentionUserOnChat(specconst.USER_B);
|
||||
await windowAction.verifyNotCloseToastWhenMouseOver(specconst.USER_A.name+": @"+specconst.USER_B.name);
|
||||
});
|
||||
|
||||
}):describe.skip();
|
77
tests/spectron/toastNotification.spectron.js
Normal file
77
tests/spectron/toastNotification.spectron.js
Normal file
@ -0,0 +1,77 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const WebActions = require('./spectronWebActions');
|
||||
const WindowsActions = require('./spectronWindowsActions');
|
||||
const WebDriver = require('./spectronWebDriver');
|
||||
const { isMac } = require('../../js/utils/misc.js');
|
||||
const constants = require('./spectronConstants.js');
|
||||
const ui = require('./spectronInterfaces.js');
|
||||
const Utils = require('./spectronUtils');
|
||||
|
||||
let app, webDriver, webActions, windowsActions;
|
||||
|
||||
describe('Tests for Toast Notification ', () => {
|
||||
|
||||
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = constants.TIMEOUT_PAGE_LOAD;
|
||||
|
||||
beforeAll(async (done) => {
|
||||
try {
|
||||
webDriver = await new WebDriver({ browser: 'chrome' });
|
||||
app = await new Application({}).startApplication({ testedHost: constants.TESTED_HOST, alwaysOnTop: true });
|
||||
webActions = await new WebActions(app);
|
||||
windowsActions = await new WindowsActions(app);
|
||||
done();
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Unable to start application error: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(async (done) => {
|
||||
try {
|
||||
if (app && app.isRunning()) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
await app.stop();
|
||||
await webDriver.quit();
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Failed at post-condition: ${err}`));
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify Pop-up alert play depends on setting
|
||||
* TC-ID: 2916217
|
||||
* Cover scenarios in AVT-1024
|
||||
*/
|
||||
it('Pop-up alert play depends on setting', async (done) => {
|
||||
try {
|
||||
if (isMac) {
|
||||
done();
|
||||
} else {
|
||||
let message1 = await Utils.randomString();
|
||||
let message2 = await Utils.randomString();
|
||||
|
||||
//"Mute pop-up alerts on my desktop"=OFF
|
||||
await webDriver.startDriver();
|
||||
await webActions.login(constants.USER_A);
|
||||
await windowsActions.reload(); //workaround to show topbar menu
|
||||
await webDriver.login(constants.USER_B);
|
||||
await webActions.openAlertsSettings();
|
||||
await webActions.checkBox(ui.MUTE_POPUP_ALERTS_CKB, false);
|
||||
await webDriver.createIM(constants.USER_A);
|
||||
await webDriver.sendMessages([message1]);
|
||||
await webActions.verifyToastNotificationShow(message1);
|
||||
await Utils.sleep(5); //waitting for toast message disappears
|
||||
|
||||
//"Mute pop-up alerts on my desktop"=ON
|
||||
await webActions.checkBox(ui.MUTE_POPUP_ALERTS_CKB, true);
|
||||
await webDriver.sendMessages([message2]);
|
||||
await webActions.verifyNoToastNotificationShow(message2);
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Fail to verify pop-up alert play depends on setting: ${err}`));
|
||||
};
|
||||
});
|
||||
})
|
89
tests/spectron/turnONPersistentToast.spectron.js
Normal file
89
tests/spectron/turnONPersistentToast.spectron.js
Normal file
@ -0,0 +1,89 @@
|
||||
const Application = require('./spectronSetup');
|
||||
const WebDriver = require('./spectronWebDriver');
|
||||
const { isMac } = require('../../js/utils/misc.js');
|
||||
const Utils = require('./spectronUtils');
|
||||
var app = new Application({
|
||||
startTimeout: Application.getTimeOut(),
|
||||
waitTimeout: Application.getTimeOut()
|
||||
});
|
||||
var webdriver = new WebDriver({ browser: 'chrome' });
|
||||
const WindowsAction = require('./spectronWindowsActions');
|
||||
const WebActions = require('./spectronWebActions');
|
||||
const ifc = require('./spectronInterfaces.js');
|
||||
const specconst = require('./spectronConstants.js');
|
||||
let webActions, windowAction;
|
||||
|
||||
!isMac ? describe('Verify toast notification when Persist Notification is ON', () => {
|
||||
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = specconst.TIMEOUT_TEST_SUITE;
|
||||
|
||||
beforeAll(async(done) => {
|
||||
try
|
||||
{
|
||||
app = await new Application({}).startApplication({testedHost:specconst.TESTED_HOST, alwaysOnTop: true});
|
||||
windowAction = await new WindowsAction(app);
|
||||
webActions = await new WebActions(app);
|
||||
done();
|
||||
} catch(err) {
|
||||
done.fail(new Error(`Unable to start application error: ${err}`));
|
||||
};
|
||||
});
|
||||
afterAll(async (done) => {
|
||||
try {
|
||||
if (app && app.isRunning()) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
await app.stop();
|
||||
await webdriver.quit();
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
done.fail(new Error(`Failed at post-condition: ${err}`));
|
||||
};
|
||||
});
|
||||
/**
|
||||
* Verify toast notification when Persist Notification is ON
|
||||
* TC-ID: 3308790
|
||||
* Cover scenarios in AVT-1025
|
||||
*/
|
||||
it('Toast notification appears on screen and should stay on the screen IM', async () => {
|
||||
|
||||
await webdriver.startDriver();
|
||||
await webdriver.login(specconst.USER_A);
|
||||
await webdriver.createIM(specconst.USER_B.username);
|
||||
await webActions.login(specconst.USER_B);
|
||||
|
||||
await windowAction.reload();
|
||||
await app.client.waitForVisible(ifc.SETTTING_BUTTON, Utils.toMs(50));
|
||||
await webActions.persistToastIM();
|
||||
|
||||
await windowAction.pressCtrlM();
|
||||
var message = await Utils.randomString();
|
||||
await webdriver.sendMessages([message]);
|
||||
await windowAction.veriryPersistToastNotification(message);
|
||||
await webdriver.startDriver();
|
||||
await webdriver.createMIM([specconst.USER_B.username, specconst.USER_C.username]);
|
||||
await webdriver.sendMessages([message]);
|
||||
await windowAction.veriryPersistToastNotification(message);
|
||||
|
||||
})
|
||||
/**
|
||||
* Verify toast notification when Persist Notification is OFF
|
||||
* TC-ID: 46602241
|
||||
* Cover scenarios in AVT-1027
|
||||
*/
|
||||
it('Toast notification appears on screen and should disappear in few seconds IM', async () => {
|
||||
|
||||
await windowAction.showWindow();
|
||||
await app.client.waitForVisible(ifc.SETTTING_BUTTON, Utils.toMs(50));
|
||||
await webActions.persistToastIM();
|
||||
await webdriver.clickLeftNavItem(specconst.USER_B.name);
|
||||
var message = await Utils.randomString();
|
||||
await webdriver.sendMessages([message]);
|
||||
await windowAction.verifyNotPersistToastNotification("Electron");
|
||||
await webdriver.createMIM([specconst.USER_B.username, specconst.USER_C.username]);
|
||||
await webdriver.sendMessages([message]);
|
||||
await windowAction.verifyNotPersistToastNotification("Electron");
|
||||
|
||||
})
|
||||
|
||||
}) : describe.skip();
|
@ -41,6 +41,16 @@ describe('Tests for Zoom in and Zoom out', () => {
|
||||
}
|
||||
|
||||
afterAll((done) => {
|
||||
// Get it back normal size
|
||||
if (!isMac) {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
robot.keyToggle('+', 'down', ['control', 'shift']);
|
||||
}
|
||||
robot.keyToggle('+', 'up');
|
||||
robot.keyToggle('control', 'up');
|
||||
robot.keyToggle('shift', 'up');
|
||||
}
|
||||
|
||||
if (app && app.isRunning()) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
app.client.getWindowCount().then((count) => {
|
||||
@ -124,7 +134,7 @@ describe('Tests for Zoom in and Zoom out', () => {
|
||||
robot.setMouseDelay(100);
|
||||
let x = bounds.x + 200;
|
||||
let y = bounds.y + 200;
|
||||
robot.moveMouseSmooth(x, y);
|
||||
robot.moveMouse(x, y);
|
||||
robot.mouseClick();
|
||||
|
||||
robot.keyToggle('0', 'down', ['control']);
|
||||
@ -174,7 +184,7 @@ describe('Tests for Zoom in and Zoom out', () => {
|
||||
robot.setMouseDelay(100);
|
||||
let x = bounds.x + 200;
|
||||
let y = bounds.y + 200;
|
||||
robot.moveMouseSmooth(x, y);
|
||||
robot.moveMouse(x, y);
|
||||
robot.mouseClick();
|
||||
|
||||
robot.keyToggle('0', 'down', ['control']);
|
||||
|
Loading…
Reference in New Issue
Block a user