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:
Vishwas Shashidhar 2018-08-14 10:28:43 +05:30
commit 6dcc8089c7
35 changed files with 2817 additions and 414 deletions

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 361 KiB

247
js/cryptoLib.js Normal file
View 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,
};

View File

@ -8,5 +8,5 @@ let keyMirror = require('keymirror');
*/
module.exports = keyMirror({
CUSTOM: null,
NATIVE_WITH_CUSTOM: null,
NATIVE: null,
});

View File

@ -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:

View File

@ -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,

View File

@ -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;

View File

@ -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();
}

View File

@ -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,194 +373,204 @@ 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);
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 = {};
}
Object.assign(newWinOptions.webPreferences, topWebContents);
let newWinParsedUrl = getParsedUrl(newWinUrl);
let mainWinParsedUrl = getParsedUrl(url);
let newWinHost = newWinParsedUrl && newWinParsedUrl.host;
let mainWinHost = mainWinParsedUrl && mainWinParsedUrl.host;
let emptyUrlString = 'about:blank';
let dispositionWhitelist = ['new-window', 'foreground-tab'];
// only allow window.open to succeed is if coming from same hsot,
// otherwise open in default browser.
if ((newWinHost === mainWinHost || newWinUrl === emptyUrlString) && dispositionWhitelist.includes(disposition)) {
// handle: window.open
if (!frameName) {
// abort - no frame name provided.
return;
}
log.send(logLevels.INFO, 'creating pop-out window url: ' + newWinParsedUrl);
let x = 0;
let y = 0;
let width = newWinOptions.width || DEFAULT_WIDTH;
let height = newWinOptions.height || DEFAULT_HEIGHT;
// try getting x and y position from query parameters
let query = newWinParsedUrl && querystring.parse(newWinParsedUrl.query);
if (query && query.x && query.y) {
let newX = Number.parseInt(query.x, 10);
let newY = Number.parseInt(query.y, 10);
let newWinRect = { x: newX, y: newY, width, height };
// only accept if both are successfully parsed.
if (Number.isInteger(newX) && Number.isInteger(newY) &&
isInDisplayBounds(newWinRect)) {
x = newX;
y = newY;
} else {
x = 0;
y = 0;
}
} else {
// create new window at slight offset from main window.
({ x, y } = getWindowSizeAndPosition(mainWindow));
x += 50;
y += 50;
}
/* eslint-disable no-param-reassign */
newWinOptions.x = x;
newWinOptions.y = y;
newWinOptions.width = Math.max(width, DEFAULT_WIDTH);
newWinOptions.height = Math.max(height, DEFAULT_HEIGHT);
newWinOptions.minWidth = MIN_WIDTH;
newWinOptions.minHeight = MIN_HEIGHT;
newWinOptions.alwaysOnTop = alwaysOnTop;
newWinOptions.frame = true;
let newWinKey = getGuid();
newWinOptions.winKey = newWinKey;
/* eslint-enable no-param-reassign */
let childWebContents = newWinOptions.webContents;
// Event needed to hide native menu bar
childWebContents.once('did-start-loading', () => {
let browserWin = BrowserWindow.fromWebContents(childWebContents);
if (isWindowsOS && browserWin && !browserWin.isDestroyed()) {
browserWin.setMenuBarVisibility(false);
}
});
childWebContents.once('did-finish-load', function () {
let browserWin = BrowserWindow.fromWebContents(childWebContents);
if (browserWin) {
log.send(logLevels.INFO, 'loaded pop-out window url: ' + newWinParsedUrl);
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());
initCrashReporterMain({ process: 'pop-out window' });
initCrashReporterRenderer(browserWin, { process: 'render | pop-out window' });
browserWin.winName = frameName;
browserWin.setAlwaysOnTop(alwaysOnTop);
let handleChildWindowCrashEvent = (e) => {
const options = {
type: 'error',
title: i18n.getMessageFor('Renderer Process Crashed'),
message: i18n.getMessageFor('Oops! Looks like we have had a crash. Please reload or close this window.'),
buttons: ['Reload', 'Close']
};
let childBrowserWindow = BrowserWindow.fromWebContents(e.sender);
if (childBrowserWindow && !childBrowserWindow.isDestroyed()) {
electron.dialog.showMessageBox(childBrowserWindow, options, function (index) {
if (index === 0) {
childBrowserWindow.reload();
} else {
childBrowserWindow.close();
}
});
}
};
browserWin.webContents.on('crashed', handleChildWindowCrashEvent);
// 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
if (browserWin.webContents) {
handleNewWindow(browserWin.webContents);
}
addWindowKey(newWinKey, browserWin);
// Method that sends bound changes as soon
// as a new window is created
// issue https://perzoinc.atlassian.net/browse/ELECTRON-172
sendChildWinBoundsChange(browserWin);
// throttle full screen
let throttledFullScreen = throttle(1000,
handleChildWindowFullScreen.bind(null, browserWin));
// throttle leave full screen
let throttledLeaveFullScreen = throttle(1000,
handleChildWindowLeaveFullScreen.bind(null, browserWin));
// throttle changes so we don't flood client.
let throttledBoundsChange = throttle(1000,
sendChildWinBoundsChange.bind(null, browserWin));
browserWin.on('move', throttledBoundsChange);
browserWin.on('resize', throttledBoundsChange);
browserWin.on('enter-full-screen', throttledFullScreen);
browserWin.on('leave-full-screen', throttledLeaveFullScreen);
let handleChildWindowClosed = () => {
removeWindowKey(newWinKey);
browserWin.removeListener('move', throttledBoundsChange);
browserWin.removeListener('resize', throttledBoundsChange);
browserWin.removeListener('enter-full-screen', throttledFullScreen);
browserWin.removeListener('leave-full-screen', throttledLeaveFullScreen);
};
browserWin.on('close', () => {
browserWin.webContents.removeListener('crashed', handleChildWindowCrashEvent);
});
browserWin.once('closed', () => {
handleChildWindowClosed();
});
handlePermissionRequests(browserWin.webContents);
if (!isDevEnv) {
browserWin.webContents.session.setCertificateVerifyProc(handleCertificateTransparencyChecks);
}
browserWin.webContents.on('devtools-opened', () => {
handleDevTools(browserWin);
});
}
});
} else {
event.preventDefault();
openUrlInDefaultBrowser(newWinUrl);
}
});
};
handleNewWindow(topWebContents);
};
if (!isDevEnv) {
mainWindow.webContents.session.setCertificateVerifyProc(handleCertificateTransparencyChecks);
}
function handleNewWindow(event, newWinUrl, frameName, disposition, newWinOptions) {
let newWinParsedUrl = getParsedUrl(newWinUrl);
let mainWinParsedUrl = getParsedUrl(url);
let newWinHost = newWinParsedUrl && newWinParsedUrl.host;
let mainWinHost = mainWinParsedUrl && mainWinParsedUrl.host;
let emptyUrlString = 'about:blank';
let dispositionWhitelist = ['new-window', 'foreground-tab'];
// only allow window.open to succeed is if coming from same hsot,
// otherwise open in default browser.
if ((newWinHost === mainWinHost || newWinUrl === emptyUrlString) && dispositionWhitelist.includes(disposition)) {
// handle: window.open
if (!frameName) {
// abort - no frame name provided.
return;
}
log.send(logLevels.INFO, 'creating pop-out window url: ' + newWinParsedUrl);
let x = 0;
let y = 0;
let width = newWinOptions.width || DEFAULT_WIDTH;
let height = newWinOptions.height || DEFAULT_HEIGHT;
// try getting x and y position from query parameters
let query = newWinParsedUrl && querystring.parse(newWinParsedUrl.query);
if (query && query.x && query.y) {
let newX = Number.parseInt(query.x, 10);
let newY = Number.parseInt(query.y, 10);
let newWinRect = { x: newX, y: newY, width, height };
// only accept if both are successfully parsed.
if (Number.isInteger(newX) && Number.isInteger(newY) &&
isInDisplayBounds(newWinRect)) {
x = newX;
y = newY;
} else {
x = 0;
y = 0;
}
} else {
// create new window at slight offset from main window.
({ x, y } = getWindowSizeAndPosition(mainWindow));
x += 50;
y += 50;
}
/* eslint-disable no-param-reassign */
newWinOptions.x = x;
newWinOptions.y = y;
newWinOptions.width = Math.max(width, DEFAULT_WIDTH);
newWinOptions.height = Math.max(height, DEFAULT_HEIGHT);
newWinOptions.minWidth = MIN_WIDTH;
newWinOptions.minHeight = MIN_HEIGHT;
newWinOptions.alwaysOnTop = alwaysOnTop;
newWinOptions.frame = true;
let newWinKey = getGuid();
newWinOptions.winKey = newWinKey;
/* eslint-enable no-param-reassign */
let webContents = newWinOptions.webContents;
// Event needed to hide native menu bar
webContents.once('did-start-loading', () => {
let browserWin = BrowserWindow.fromWebContents(webContents);
if (isWindowsOS && browserWin && !browserWin.isDestroyed()) {
browserWin.setMenuBarVisibility(false);
}
});
webContents.once('did-finish-load', function () {
let browserWin = BrowserWindow.fromWebContents(webContents);
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());
initCrashReporterMain({ process: 'pop-out window' });
initCrashReporterRenderer(browserWin, { process: 'render | pop-out window' });
browserWin.winName = frameName;
browserWin.setAlwaysOnTop(alwaysOnTop);
let handleChildWindowCrashEvent = (e) => {
const options = {
type: 'error',
title: i18n.getMessageFor('Renderer Process Crashed'),
message: i18n.getMessageFor('Oops! Looks like we have had a crash. Please reload or close this window.'),
buttons: ['Reload', 'Close']
};
let childBrowserWindow = BrowserWindow.fromWebContents(e.sender);
if (childBrowserWindow && !childBrowserWindow.isDestroyed()) {
electron.dialog.showMessageBox(childBrowserWindow, options, function (index) {
if (index === 0) {
childBrowserWindow.reload();
} else {
childBrowserWindow.close();
}
});
}
};
browserWin.webContents.on('crashed', handleChildWindowCrashEvent);
// 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));
addWindowKey(newWinKey, browserWin);
// Method that sends bound changes as soon
// as a new window is created
// issue https://perzoinc.atlassian.net/browse/ELECTRON-172
sendChildWinBoundsChange(browserWin);
// throttle full screen
let throttledFullScreen = throttle(1000,
handleChildWindowFullScreen.bind(null, browserWin));
// throttle leave full screen
let throttledLeaveFullScreen = throttle(1000,
handleChildWindowLeaveFullScreen.bind(null, browserWin));
// throttle changes so we don't flood client.
let throttledBoundsChange = throttle(1000,
sendChildWinBoundsChange.bind(null, browserWin));
browserWin.on('move', throttledBoundsChange);
browserWin.on('resize', throttledBoundsChange);
browserWin.on('enter-full-screen', throttledFullScreen);
browserWin.on('leave-full-screen', throttledLeaveFullScreen);
let handleChildWindowClosed = () => {
removeWindowKey(newWinKey);
browserWin.removeListener('move', throttledBoundsChange);
browserWin.removeListener('resize', throttledBoundsChange);
browserWin.removeListener('enter-full-screen', throttledFullScreen);
browserWin.removeListener('leave-full-screen', throttledLeaveFullScreen);
};
browserWin.on('close', () => {
browserWin.webContents.removeListener('new-window', handleNewWindow);
browserWin.webContents.removeListener('crashed', handleChildWindowCrashEvent);
});
browserWin.once('closed', () => {
handleChildWindowClosed();
});
handlePermissionRequests(browserWin.webContents);
if (!isDevEnv) {
browserWin.webContents.session.setCertificateVerifyProc(handleCertificateTransparencyChecks);
}
browserWin.webContents.on('devtools-opened', () => {
handleDevTools(browserWin);
});
}
});
} else {
event.preventDefault();
openUrlInDefaultBrowser(newWinUrl);
}
// 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);
menu = electron.Menu.buildFromTemplate(getTemplate(app));
electron.Menu.setApplicationMenu(menu);
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,
};

View File

@ -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');

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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": "ズームアウト"
}

View File

@ -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": "ズームアウト"
}

View File

@ -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"
}
}

View 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();

View 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}`));
}
});
})

View 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);
});
});
});

View 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();

View File

@ -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');

View 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();

View 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}`));
};
});
})

View File

@ -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) => {
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(() => {
afterAll(async (done) => {
try {
if (app && app.isRunning()) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
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});
done();
}).catch((err) => {
done.fail(new Error(`failed to minimize window to 300 px with error: ${err}`));
})
});
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) {
done.fail(new Error(`failed to minimize window to 300 px with error: ${err}`));
}
});
}) : describe.skip();
});

View 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();

View File

@ -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
};

View 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",
};

View File

@ -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) {
@ -116,7 +129,7 @@ class App {
});
});
}
}
module.exports = App;

View 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;

View 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;

View 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;

View 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;

View 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();

View 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}`));
};
});
})

View 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();

View File

@ -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']);