[MM-40330] Unit tests for main/windows (#1885)

* [MM-40330] Unit tests for main/windows

* Merge'd
This commit is contained in:
Devin Binnie
2021-12-06 11:08:39 -05:00
committed by GitHub
parent 72f7cb4f08
commit 4a98dce51e
21 changed files with 1698 additions and 535 deletions

View File

@@ -6,7 +6,7 @@ import fs from 'fs';
import {shell, dialog} from 'electron';
import {getMainWindow} from './windows/windowManager';
import WindowManager from './windows/windowManager';
import {AllowProtocolDialog} from './allowProtocolDialog';
@@ -120,7 +120,7 @@ describe('main/allowProtocolDialog', () => {
});
it('should not open message box if main window is missing', () => {
getMainWindow.mockImplementation(() => null);
WindowManager.getMainWindow.mockImplementation(() => null);
allowProtocolDialog.handleDialogEvent('mattermost:', 'mattermost://community.mattermost.com');
expect(shell.openExternal).not.toBeCalled();
expect(dialog.showMessageBox).not.toBeCalled();
@@ -128,7 +128,7 @@ describe('main/allowProtocolDialog', () => {
describe('main window not null', () => {
beforeEach(() => {
getMainWindow.mockImplementation(() => ({}));
WindowManager.getMainWindow.mockImplementation(() => ({}));
});
it('should open the window but not save when clicking Yes', async () => {

View File

@@ -13,7 +13,7 @@ import log from 'electron-log';
import {protocols} from '../../electron-builder.json';
import * as Validator from './Validator';
import {getMainWindow} from './windows/windowManager';
import WindowManager from './windows/windowManager';
const allowedProtocolFile = path.resolve(app.getPath('userData'), 'allowedProtocols.json');
@@ -52,7 +52,7 @@ export class AllowProtocolDialog {
shell.openExternal(URL);
return;
}
const mainWindow = getMainWindow();
const mainWindow = WindowManager.getMainWindow();
if (!mainWindow) {
return;
}

View File

@@ -6,7 +6,7 @@ import {ipcMain} from 'electron';
import {UPDATE_MENTIONS, UPDATE_TRAY, UPDATE_BADGE, SESSION_EXPIRED, UPDATE_DROPDOWN_MENTIONS} from 'common/communication';
import * as WindowManager from './windows/windowManager';
import WindowManager from './windows/windowManager';
const status = {
unreads: new Map<string, boolean>(),

View File

@@ -10,12 +10,10 @@ import {LoginModalData} from 'types/auth';
import {BASIC_AUTH_PERMISSION} from 'common/permissions';
import urlUtils from 'common/utils/url';
import * as WindowManager from 'main/windows/windowManager';
import modalManager from './views/modalManager';
import {getLocalURLString, getLocalPreload} from './utils';
import TrustedOriginsStore from './trustedOrigins';
import modalManager from 'main/views/modalManager';
import TrustedOriginsStore from 'main/trustedOrigins';
import {getLocalURLString, getLocalPreload} from 'main/utils';
import WindowManager from 'main/windows/windowManager';
const modalPreload = getLocalPreload('modalPreload.js');
const loginModalHtml = getLocalURLString('loginModal.html');

View File

@@ -6,7 +6,7 @@ import {app} from 'electron';
import * as Badge from './badge';
import {setOverlayIcon} from './windows/windowManager';
import WindowManager from './windows/windowManager';
jest.mock('electron', () => ({
app: {
@@ -28,28 +28,28 @@ describe('main/badge', () => {
describe('showBadgeWindows', () => {
it('should show dot when session expired', () => {
Badge.showBadgeWindows(true, 7, false);
expect(setOverlayIcon).toBeCalledWith('•', expect.any(String), expect.any(Boolean));
expect(WindowManager.setOverlayIcon).toBeCalledWith('•', expect.any(String), expect.any(Boolean));
});
it('should show mention count when has mention count', () => {
Badge.showBadgeWindows(false, 50, false);
expect(setOverlayIcon).toBeCalledWith('50', expect.any(String), false);
expect(WindowManager.setOverlayIcon).toBeCalledWith('50', expect.any(String), false);
});
it('should show 99+ when has mention count over 99', () => {
Badge.showBadgeWindows(false, 200, false);
expect(setOverlayIcon).toBeCalledWith('99+', expect.any(String), true);
expect(WindowManager.setOverlayIcon).toBeCalledWith('99+', expect.any(String), true);
});
it('should not show dot when has unreads but setting is off', () => {
Badge.showBadgeWindows(false, 0, true);
expect(setOverlayIcon).not.toBeCalledWith('•', expect.any(String), expect.any(Boolean));
expect(WindowManager.setOverlayIcon).not.toBeCalledWith('•', expect.any(String), expect.any(Boolean));
});
it('should show dot when has unreads', () => {
Badge.setUnreadBadgeSetting(true);
Badge.showBadgeWindows(false, 0, true);
expect(setOverlayIcon).toBeCalledWith('•', expect.any(String), expect.any(Boolean));
expect(WindowManager.setOverlayIcon).toBeCalledWith('•', expect.any(String), expect.any(Boolean));
Badge.setUnreadBadgeSetting(false);
});
});

View File

@@ -6,7 +6,7 @@ import {app} from 'electron';
import {UPDATE_BADGE} from 'common/communication';
import * as WindowManager from './windows/windowManager';
import WindowManager from './windows/windowManager';
import * as AppState from './appState';
const MAX_WIN_COUNT = 99;

View File

@@ -5,7 +5,7 @@ import {Certificate, WebContents} from 'electron';
import {CertificateModalData} from 'types/certificate';
import * as WindowManager from './windows/windowManager';
import WindowManager from './windows/windowManager';
import modalManager from './views/modalManager';
import {getLocalURLString, getLocalPreload} from './utils';

View File

@@ -63,7 +63,7 @@ import allowProtocolDialog from './allowProtocolDialog';
import AppVersionManager from './AppVersionManager';
import initCookieManager from './cookieManager';
import UserActivityMonitor from './UserActivityMonitor';
import * as WindowManager from './windows/windowManager';
import WindowManager from './windows/windowManager';
import {displayMention, displayDownloadCompleted} from './notifications';
import parseArgs from './ParseArgs';

View File

@@ -3,7 +3,7 @@
'use strict';
import * as WindowManager from 'main/windows/windowManager';
import WindowManager from 'main/windows/windowManager';
import {createTemplate} from './app';

View File

@@ -9,7 +9,7 @@ import {SHOW_NEW_SERVER_MODAL} from 'common/communication';
import Config from 'common/config';
import {TabType, getTabDisplayName} from 'common/tabs/TabView';
import * as WindowManager from 'main/windows/windowManager';
import WindowManager from 'main/windows/windowManager';
export function createTemplate(config: Config) {
const separatorItem: MenuItemConstructorOptions = {

View File

@@ -6,7 +6,7 @@
import {Menu, MenuItem, MenuItemConstructorOptions} from 'electron';
import {CombinedConfig} from 'types/config';
import * as WindowManager from '../windows/windowManager';
import WindowManager from '../windows/windowManager';
export function createTemplate(config: CombinedConfig) {
const teams = config.teams;

View File

@@ -8,7 +8,7 @@ import {Notification, shell} from 'electron';
import {PLAY_SOUND} from 'common/communication';
import {TAB_MESSAGING} from 'common/tabs/TabView';
import * as WindowManager from '../windows/windowManager';
import WindowManager from '../windows/windowManager';
import {displayMention, displayDownloadCompleted, currentNotifications} from './index';

View File

@@ -9,7 +9,7 @@ import {MentionData} from 'types/notification';
import {PLAY_SOUND} from 'common/communication';
import {TAB_MESSAGING} from 'common/tabs/TabView';
import * as windowManager from '../windows/windowManager';
import WindowManager from '../windows/windowManager';
import {Mention} from './Mention';
import {DownloadNotification} from './Download';
@@ -21,7 +21,7 @@ export function displayMention(title: string, body: string, channel: {id: string
log.error('notification not supported');
return;
}
const serverName = windowManager.getServerNameByWebContentsId(webcontents.id);
const serverName = WindowManager.getServerNameByWebContentsId(webcontents.id);
const options = {
title: `${serverName}: ${title}`,
@@ -45,15 +45,15 @@ export function displayMention(title: string, body: string, channel: {id: string
}
const notificationSound = mention.getNotificationSound();
if (notificationSound) {
windowManager.sendToRenderer(PLAY_SOUND, notificationSound);
WindowManager.sendToRenderer(PLAY_SOUND, notificationSound);
}
windowManager.flashFrame(true);
WindowManager.flashFrame(true);
});
mention.on('click', () => {
log.info('notification click', serverName, mention);
if (serverName) {
windowManager.switchTab(serverName, TAB_MESSAGING);
WindowManager.switchTab(serverName, TAB_MESSAGING);
webcontents.send('notification-clicked', {channel, teamId, url});
}
});
@@ -68,7 +68,7 @@ export function displayDownloadCompleted(fileName: string, path: string, serverN
const download = new DownloadNotification(fileName, serverName);
download.on('show', () => {
windowManager.flashFrame(true);
WindowManager.flashFrame(true);
});
download.on('click', () => {

View File

@@ -6,7 +6,7 @@ import {app, nativeImage, Tray, systemPreferences, nativeTheme} from 'electron';
import {UPDATE_TRAY} from 'common/communication';
import * as WindowManager from '../windows/windowManager';
import WindowManager from '../windows/windowManager';
import * as AppState from '../appState';
const assetsDir = path.resolve(app.getAppPath(), 'assets');

View File

@@ -27,7 +27,7 @@ import {TabView} from 'common/tabs/TabView';
import {ServerInfo} from 'main/server/serverInfo';
import ContextMenu from '../contextMenu';
import {getWindowBoundaries, getLocalPreload, composeUserAgent} from '../utils';
import * as WindowManager from '../windows/windowManager';
import WindowManager from '../windows/windowManager';
import * as appState from '../appState';
import WebContentsEventManager from './webContentEvents';

View File

@@ -17,7 +17,7 @@ import {
GET_MODAL_UNCLOSEABLE,
} from 'common/communication';
import * as WindowManager from '../windows/windowManager';
import WindowManager from '../windows/windowManager';
import {ModalView} from './modalView';

View File

@@ -17,7 +17,7 @@ import {
import * as AppState from '../appState';
import {TAB_BAR_HEIGHT, THREE_DOT_MENU_WIDTH, THREE_DOT_MENU_WIDTH_MAC, MENU_SHADOW_WIDTH} from 'common/utils/constants';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import * as WindowManager from '../windows/windowManager';
import WindowManager from '../windows/windowManager';
export default class TeamDropdownView {
view: BrowserView;

View File

@@ -10,7 +10,7 @@ import urlUtils from 'common/utils/url';
import ContextMenu from 'main/contextMenu';
import * as WindowManager from '../windows/windowManager';
import WindowManager from '../windows/windowManager';
import {protocols} from '../../../electron-builder.json';

View File

@@ -0,0 +1,341 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import fs from 'fs';
import path from 'path';
import {BrowserWindow, screen, app, globalShortcut} from 'electron';
import {SELECT_NEXT_TAB, SELECT_PREVIOUS_TAB} from 'common/communication';
import {DEFAULT_WINDOW_HEIGHT, DEFAULT_WINDOW_WIDTH} from 'common/utils/constants';
import ContextMenu from '../contextMenu';
import * as Validator from '../Validator';
import createMainWindow from './mainWindow';
jest.mock('path', () => ({
join: jest.fn(),
}));
jest.mock('electron', () => ({
app: {
getPath: jest.fn(),
hide: jest.fn(),
},
BrowserWindow: jest.fn(),
ipcMain: {
handle: jest.fn(),
},
screen: {
getDisplayMatching: jest.fn(),
},
globalShortcut: {
register: jest.fn(),
registerAll: jest.fn(),
},
}));
jest.mock('electron-log', () => ({}));
jest.mock('global', () => ({
willAppQuit: false,
}));
jest.mock('fs', () => ({
readFileSync: jest.fn(),
writeFileSync: jest.fn(),
}));
jest.mock('../Validator', () => ({
validateBoundsInfo: jest.fn(),
}));
jest.mock('../contextMenu', () => jest.fn());
jest.mock('../utils', () => ({
getLocalPreload: jest.fn(),
getLocalURLString: jest.fn(),
}));
'use strict';
describe('main/windows/mainWindow', () => {
describe('createMainWindow', () => {
const baseWindow = {
setMenuBarVisibility: jest.fn(),
loadURL: jest.fn(),
once: jest.fn(),
on: jest.fn(),
maximize: jest.fn(),
show: jest.fn(),
hide: jest.fn(),
blur: jest.fn(),
minimize: jest.fn(),
webContents: {
on: jest.fn(),
send: jest.fn(),
},
isMaximized: jest.fn(),
isFullScreen: jest.fn(),
getBounds: jest.fn(),
};
beforeEach(() => {
baseWindow.loadURL.mockImplementation(() => ({
catch: jest.fn(),
}));
BrowserWindow.mockImplementation(() => baseWindow);
fs.readFileSync.mockImplementation(() => '{"x":400,"y":300,"width":1280,"height":700,"maximized":false,"fullscreen":false}');
path.join.mockImplementation(() => 'anyfile.txt');
screen.getDisplayMatching.mockImplementation(() => ({bounds: {x: 0, y: 0, width: 1920, height: 1080}}));
Validator.validateBoundsInfo.mockImplementation((data) => data);
ContextMenu.mockImplementation(() => ({
reload: jest.fn(),
}));
});
afterEach(() => {
jest.resetAllMocks();
});
it('should set window size using bounds read from file', () => {
createMainWindow({}, {});
expect(BrowserWindow).toHaveBeenCalledWith(expect.objectContaining({
x: 400,
y: 300,
width: 1280,
height: 700,
maximized: false,
fullscreen: false,
}));
});
it('should set default window size when failing to read bounds from file', () => {
fs.readFileSync.mockImplementation(() => 'just a bunch of garbage');
createMainWindow({}, {});
expect(BrowserWindow).toHaveBeenCalledWith(expect.objectContaining({
width: DEFAULT_WINDOW_WIDTH,
height: DEFAULT_WINDOW_HEIGHT,
}));
});
it('should set default window size when bounds are outside the normal screen', () => {
fs.readFileSync.mockImplementation(() => '{"x":-400,"y":-300,"width":1280,"height":700,"maximized":false,"fullscreen":false}');
screen.getDisplayMatching.mockImplementation(() => ({bounds: {x: 0, y: 0, width: 1920, height: 1080}}));
createMainWindow({}, {});
expect(BrowserWindow).toHaveBeenCalledWith(expect.objectContaining({
width: DEFAULT_WINDOW_WIDTH,
height: DEFAULT_WINDOW_HEIGHT,
}));
});
it('should set linux app icon', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'linux',
});
createMainWindow({}, {linuxAppIcon: 'linux-icon.png'});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(BrowserWindow).toHaveBeenCalledWith(expect.objectContaining({
icon: 'linux-icon.png',
}));
});
it('should reset zoom level and maximize if applicable on ready-to-show', () => {
const window = {
...baseWindow,
once: jest.fn().mockImplementation((event, cb) => {
if (event === 'ready-to-show') {
cb();
}
}),
};
BrowserWindow.mockImplementation(() => window);
fs.readFileSync.mockImplementation(() => '{"x":400,"y":300,"width":1280,"height":700,"maximized":true,"fullscreen":false}');
createMainWindow({}, {});
expect(window.webContents.zoomLevel).toStrictEqual(0);
expect(window.maximize).toBeCalled();
});
it('should save window state on close if the app will quit', () => {
global.willAppQuit = true;
const window = {
...baseWindow,
on: jest.fn().mockImplementation((event, cb) => {
if (event === 'close') {
cb({preventDefault: jest.fn()});
}
}),
};
BrowserWindow.mockImplementation(() => window);
createMainWindow({}, {});
global.willAppQuit = false;
expect(fs.writeFileSync).toHaveBeenCalled();
});
it('should hide window on close for Windows if app wont quit', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'win32',
});
const window = {
...baseWindow,
on: jest.fn().mockImplementation((event, cb) => {
if (event === 'close') {
cb({preventDefault: jest.fn()});
}
}),
};
BrowserWindow.mockImplementation(() => window);
createMainWindow({}, {});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(window.hide).toHaveBeenCalled();
});
it('should hide window on close for Linux if app wont quit and config item is set', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'linux',
});
const window = {
...baseWindow,
on: jest.fn().mockImplementation((event, cb) => {
if (event === 'close') {
cb({preventDefault: jest.fn()});
}
}),
};
BrowserWindow.mockImplementation(() => window);
createMainWindow({minimizeToTray: true}, {});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(window.hide).toHaveBeenCalled();
});
it('should minimize window on close for Linux if app wont quit and config item is not set', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'linux',
});
const window = {
...baseWindow,
on: jest.fn().mockImplementation((event, cb) => {
if (event === 'close') {
cb({preventDefault: jest.fn()});
}
}),
};
BrowserWindow.mockImplementation(() => window);
createMainWindow({}, {});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(window.minimize).toHaveBeenCalled();
});
it('should hide window on close for Mac if app wont quit and window is not full screen', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'darwin',
});
const window = {
...baseWindow,
on: jest.fn().mockImplementation((event, cb) => {
if (event === 'close') {
cb({preventDefault: jest.fn()});
}
}),
};
BrowserWindow.mockImplementation(() => window);
createMainWindow({}, {});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(app.hide).toHaveBeenCalled();
});
it('should leave full screen and then hide window on close for Mac if app wont quit and window is full screen', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'darwin',
});
const window = {
...baseWindow,
isFullScreen: jest.fn().mockImplementation(() => true),
setFullScreen: jest.fn(),
once: jest.fn().mockImplementation((event, cb) => {
if (event === 'leave-full-screen') {
cb();
}
}),
on: jest.fn().mockImplementation((event, cb) => {
if (event === 'close') {
cb({preventDefault: jest.fn()});
}
}),
};
BrowserWindow.mockImplementation(() => window);
createMainWindow({}, {});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(window.once).toHaveBeenCalledWith('leave-full-screen', expect.any(Function));
expect(app.hide).toHaveBeenCalled();
expect(window.setFullScreen).toHaveBeenCalledWith(false);
});
it('should select tabs using alt+cmd+arrow keys on Mac', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'darwin',
});
const window = {
...baseWindow,
webContents: {
...baseWindow.webContents,
on: jest.fn().mockImplementation((event, cb) => {
if (event === 'before-input-event') {
cb(null, {alt: true, meta: true, key: 'ArrowRight'});
cb(null, {alt: true, meta: true, key: 'ArrowLeft'});
}
}),
},
};
BrowserWindow.mockImplementation(() => window);
createMainWindow({}, {});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(window.webContents.send).toHaveBeenCalledWith(SELECT_NEXT_TAB);
expect(window.webContents.send).toHaveBeenCalledWith(SELECT_PREVIOUS_TAB);
});
it('should add override shortcuts for the top menu on Linux to stop it showing up', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'linux',
});
const window = {
...baseWindow,
on: jest.fn().mockImplementation((event, cb) => {
if (event === 'focus') {
cb();
}
}),
};
BrowserWindow.mockImplementation(() => window);
createMainWindow({}, {});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(globalShortcut.registerAll).toHaveBeenCalledWith(['Alt+F', 'Alt+E', 'Alt+V', 'Alt+H', 'Alt+W', 'Alt+P'], expect.any(Function));
});
});
});

View File

@@ -0,0 +1,815 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable max-lines */
'use strict';
import {app, systemPreferences} from 'electron';
import {getTabViewName, TAB_MESSAGING} from 'common/tabs/TabView';
import urlUtils from 'common/utils/url';
import {getAdjustedWindowBoundaries} from 'main/utils';
import {WindowManager} from './windowManager';
import createMainWindow from './mainWindow';
import {createSettingsWindow} from './settingsWindow';
jest.mock('path', () => ({
resolve: jest.fn(),
join: jest.fn(),
}));
jest.mock('electron', () => ({
ipcMain: {
handle: jest.fn(),
on: jest.fn(),
emit: jest.fn(),
},
app: {
getAppPath: jest.fn(),
quit: jest.fn(),
dock: {
show: jest.fn(),
bounce: jest.fn(),
},
},
systemPreferences: {
getUserDefault: jest.fn(),
},
}));
jest.mock('electron-log', () => ({
error: jest.fn(),
info: jest.fn(),
}));
jest.mock('common/utils/url', () => ({
isTeamUrl: jest.fn(),
isAdminUrl: jest.fn(),
getView: jest.fn(),
}));
jest.mock('common/tabs/TabView', () => ({
getTabViewName: jest.fn(),
TAB_MESSAGING: 'tab-messaging',
}));
jest.mock('../utils', () => ({
getAdjustedWindowBoundaries: jest.fn(),
}));
jest.mock('../views/viewManager', () => ({
ViewManager: jest.fn(),
}));
jest.mock('../CriticalErrorHandler', () => jest.fn());
jest.mock('../views/teamDropdownView', () => jest.fn());
jest.mock('./settingsWindow', () => ({
createSettingsWindow: jest.fn(),
}));
jest.mock('./mainWindow', () => jest.fn());
describe('main/windows/windowManager', () => {
describe('setConfig', () => {
const windowManager = new WindowManager();
beforeEach(() => {
windowManager.viewManager = {
reloadConfiguration: jest.fn(),
};
});
it('should reload config on set', () => {
windowManager.setConfig({some: 'config item'});
expect(windowManager.viewManager.reloadConfiguration).toHaveBeenCalled();
});
});
describe('showSettingsWindow', () => {
const windowManager = new WindowManager();
windowManager.config = {};
windowManager.showMainWindow = jest.fn();
afterEach(() => {
jest.resetAllMocks();
delete windowManager.settingsWindow;
delete windowManager.mainWindow;
});
it('should show settings window if it exists', () => {
const settingsWindow = {show: jest.fn()};
windowManager.settingsWindow = settingsWindow;
windowManager.showSettingsWindow();
expect(settingsWindow.show).toHaveBeenCalled();
});
it('should create windows if they dont exist and delete the settings window when it is closed', () => {
let callback;
createSettingsWindow.mockReturnValue({on: (event, cb) => {
if (event === 'closed') {
callback = cb;
}
}});
windowManager.showSettingsWindow();
expect(windowManager.showMainWindow).toHaveBeenCalled();
expect(createSettingsWindow).toHaveBeenCalled();
expect(windowManager.settingsWindow).toBeDefined();
callback();
expect(windowManager.settingsWindow).toBeUndefined();
});
});
describe('showMainWindow', () => {
const windowManager = new WindowManager();
windowManager.config = {};
windowManager.viewManager = {
handleDeepLink: jest.fn(),
updateMainWindow: jest.fn(),
};
windowManager.initializeViewManager = jest.fn();
afterEach(() => {
delete windowManager.mainWindow;
});
it('should show main window if it exists and focus it if it is already visible', () => {
windowManager.mainWindow = {
visible: false,
isVisible: () => windowManager.mainWindow.visible,
show: jest.fn().mockImplementation(() => {
windowManager.mainWindow.visible = true;
}),
focus: jest.fn(),
};
windowManager.showMainWindow();
expect(windowManager.mainWindow.show).toHaveBeenCalled();
windowManager.showMainWindow();
expect(windowManager.mainWindow.focus).toHaveBeenCalled();
});
it('should quit the app when the main window fails to create', () => {
windowManager.showMainWindow();
expect(app.quit).toHaveBeenCalled();
});
it('should create the main window and add listeners', () => {
const window = {
on: jest.fn(),
};
createMainWindow.mockReturnValue(window);
windowManager.showMainWindow();
expect(windowManager.mainWindow).toBe(window);
expect(window.on).toHaveBeenCalled();
});
it('should open deep link when provided', () => {
const window = {
on: jest.fn(),
};
createMainWindow.mockReturnValue(window);
windowManager.showMainWindow('mattermost://server-1.com/subpath');
expect(windowManager.viewManager.handleDeepLink).toHaveBeenCalledWith('mattermost://server-1.com/subpath');
});
});
describe('handleResizeMainWindow', () => {
const windowManager = new WindowManager();
const view = {
setBounds: jest.fn(),
tab: {
url: 'http://server-1.com',
},
view: {
webContents: {
getURL: jest.fn(),
},
},
};
windowManager.viewManager = {
getCurrentView: () => view,
setLoadingScreenBounds: jest.fn(),
};
windowManager.mainWindow = {
getContentBounds: () => ({width: 800, height: 600}),
getSize: () => [1000, 900],
};
windowManager.teamDropdown = {
updateWindowBounds: jest.fn(),
};
beforeEach(() => {
jest.useFakeTimers();
getAdjustedWindowBoundaries.mockImplementation((width, height) => ({width, height}));
});
afterEach(() => {
jest.resetAllMocks();
});
it('should update loading screen and team dropdown bounds', () => {
windowManager.handleResizeMainWindow();
expect(windowManager.viewManager.setLoadingScreenBounds).toHaveBeenCalled();
expect(windowManager.teamDropdown.updateWindowBounds).toHaveBeenCalled();
});
it('should use getContentBounds when the platform is not linux', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'win32',
});
windowManager.handleResizeMainWindow();
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(view.setBounds).toHaveBeenCalledWith({width: 800, height: 600});
});
it('should use getSize when the platform is linux', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'linux',
});
windowManager.handleResizeMainWindow();
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(view.setBounds).not.toHaveBeenCalled();
jest.runAllTimers();
expect(view.setBounds).toHaveBeenCalledWith({width: 1000, height: 900});
});
});
describe('restoreMain', () => {
const windowManager = new WindowManager();
windowManager.mainWindow = {
isVisible: jest.fn(),
isMinimized: jest.fn(),
restore: jest.fn(),
show: jest.fn(),
focus: jest.fn(),
};
afterEach(() => {
jest.resetAllMocks();
delete windowManager.settingsWindow;
});
it('should restore main window if minimized', () => {
windowManager.mainWindow.isMinimized.mockReturnValue(true);
windowManager.restoreMain();
expect(windowManager.mainWindow.restore).toHaveBeenCalled();
});
it('should show main window if not visible or minimized', () => {
windowManager.mainWindow.isVisible.mockReturnValue(false);
windowManager.mainWindow.isMinimized.mockReturnValue(false);
windowManager.restoreMain();
expect(windowManager.mainWindow.show).toHaveBeenCalled();
});
it('should focus main window if visible and not minimized', () => {
windowManager.mainWindow.isVisible.mockReturnValue(true);
windowManager.mainWindow.isMinimized.mockReturnValue(false);
windowManager.restoreMain();
expect(windowManager.mainWindow.focus).toHaveBeenCalled();
});
it('should focus settings window regardless of main window state if it exists', () => {
windowManager.settingsWindow = {
focus: jest.fn(),
};
windowManager.mainWindow.isVisible.mockReturnValue(false);
windowManager.mainWindow.isMinimized.mockReturnValue(false);
windowManager.restoreMain();
expect(windowManager.settingsWindow.focus).toHaveBeenCalled();
windowManager.settingsWindow.focus.mockClear();
windowManager.mainWindow.isVisible.mockReturnValue(true);
windowManager.mainWindow.isMinimized.mockReturnValue(false);
windowManager.restoreMain();
expect(windowManager.settingsWindow.focus).toHaveBeenCalled();
windowManager.settingsWindow.focus.mockClear();
windowManager.mainWindow.isVisible.mockReturnValue(false);
windowManager.mainWindow.isMinimized.mockReturnValue(true);
windowManager.restoreMain();
expect(windowManager.settingsWindow.focus).toHaveBeenCalled();
windowManager.settingsWindow.focus.mockClear();
windowManager.mainWindow.isVisible.mockReturnValue(true);
windowManager.mainWindow.isMinimized.mockReturnValue(true);
windowManager.restoreMain();
expect(windowManager.settingsWindow.focus).toHaveBeenCalled();
windowManager.settingsWindow.focus.mockClear();
});
it('should call macOS show on macOS', () => {
windowManager.mainWindow.isVisible.mockReturnValue(false);
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'darwin',
});
windowManager.restoreMain();
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(app.dock.show).toHaveBeenCalled();
});
});
describe('flashFrame', () => {
const windowManager = new WindowManager();
windowManager.mainWindow = {
flashFrame: jest.fn(),
};
windowManager.settingsWindow = {
flashFrame: jest.fn(),
};
afterEach(() => {
jest.resetAllMocks();
delete windowManager.config;
});
it('linux/windows - should not flash frame when config item is not set', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'linux',
});
windowManager.flashFrame(true);
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(windowManager.mainWindow.flashFrame).not.toBeCalled();
expect(windowManager.settingsWindow.flashFrame).not.toBeCalled();
});
it('linux/windows - should flash frame when config item is set', () => {
windowManager.config = {
notifications: {
flashWindow: true,
},
};
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'linux',
});
windowManager.flashFrame(true);
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(windowManager.mainWindow.flashFrame).toBeCalledWith(true);
expect(windowManager.settingsWindow.flashFrame).toBeCalledWith(true);
});
it('mac - should not bounce icon when config item is not set', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'darwin',
});
windowManager.flashFrame(true);
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(app.dock.bounce).not.toBeCalled();
});
it('mac - should bounce icon when config item is set', () => {
windowManager.config = {
notifications: {
bounceIcon: true,
bounceIconType: 'critical',
},
};
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'darwin',
});
windowManager.flashFrame(true);
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(app.dock.bounce).toHaveBeenCalledWith('critical');
});
});
describe('handleDoubleClick', () => {
const windowManager = new WindowManager();
const mainWindow = {
isMinimized: jest.fn(),
restore: jest.fn(),
minimize: jest.fn(),
isMaximized: jest.fn(),
unmaximize: jest.fn(),
maximize: jest.fn(),
};
const settingsWindow = {
isMinimized: jest.fn(),
restore: jest.fn(),
minimize: jest.fn(),
isMaximized: jest.fn(),
unmaximize: jest.fn(),
maximize: jest.fn(),
};
beforeEach(() => {
systemPreferences.getUserDefault.mockReturnValue('Maximize');
});
afterEach(() => {
jest.resetAllMocks();
delete windowManager.mainWindow;
delete windowManager.settingsWindow;
});
it('should do nothing when the windows arent set', () => {
windowManager.handleDoubleClick(null, 'settings');
expect(settingsWindow.isMaximized).not.toHaveBeenCalled();
windowManager.handleDoubleClick();
expect(mainWindow.isMaximized).not.toHaveBeenCalled();
});
it('should maximize when not maximized and vice versa', () => {
windowManager.mainWindow = mainWindow;
windowManager.mainWindow.isMaximized.mockReturnValue(false);
windowManager.handleDoubleClick();
expect(mainWindow.maximize).toHaveBeenCalled();
windowManager.mainWindow.isMaximized.mockReturnValue(true);
windowManager.handleDoubleClick();
expect(mainWindow.unmaximize).toHaveBeenCalled();
});
it('mac - should minimize when not minimized and vice versa when setting is set', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'darwin',
});
windowManager.flashFrame(true);
systemPreferences.getUserDefault.mockReturnValue('Minimize');
windowManager.settingsWindow = settingsWindow;
windowManager.settingsWindow.isMinimized.mockReturnValue(false);
windowManager.handleDoubleClick(null, 'settings');
expect(settingsWindow.minimize).toHaveBeenCalled();
windowManager.settingsWindow.isMinimized.mockReturnValue(true);
windowManager.handleDoubleClick(null, 'settings');
expect(settingsWindow.restore).toHaveBeenCalled();
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
});
});
describe('switchServer', () => {
const windowManager = new WindowManager();
windowManager.config = {
teams: [
{
name: 'server-1',
order: 1,
tabs: [
{
name: 'tab-1',
order: 0,
isOpen: false,
},
{
name: 'tab-2',
order: 2,
isOpen: true,
},
{
name: 'tab-3',
order: 1,
isOpen: true,
},
],
}, {
name: 'server-2',
order: 0,
tabs: [
{
name: 'tab-1',
order: 0,
isOpen: false,
},
{
name: 'tab-2',
order: 2,
isOpen: true,
},
{
name: 'tab-3',
order: 1,
isOpen: true,
},
],
lastActiveTab: 2,
},
],
};
windowManager.viewManager = {
showByName: jest.fn(),
};
const map = windowManager.config.teams.reduce((arr, item) => {
item.tabs.forEach((tab) => {
arr.push([`${item.name}_${tab.name}`, {}]);
});
return arr;
}, []);
windowManager.viewManager.views = new Map(map);
beforeEach(() => {
jest.useFakeTimers();
getTabViewName.mockImplementation((server, tab) => `${server}_${tab}`);
});
afterEach(() => {
jest.resetAllMocks();
});
it('should do nothing if cannot find the server', () => {
windowManager.switchServer('server-3');
expect(getTabViewName).not.toBeCalled();
expect(windowManager.viewManager.showByName).not.toBeCalled();
});
it('should show first open tab in order when last active not defined', () => {
windowManager.switchServer('server-1');
expect(windowManager.viewManager.showByName).toHaveBeenCalledWith('server-1_tab-3');
});
it('should show last active tab of chosen server', () => {
windowManager.switchServer('server-2');
expect(windowManager.viewManager.showByName).toHaveBeenCalledWith('server-2_tab-2');
});
it('should wait for view to exist if specified', () => {
windowManager.viewManager.views.delete('server-1_tab-3');
windowManager.switchServer('server-1', true);
expect(windowManager.viewManager.showByName).not.toBeCalled();
jest.advanceTimersByTime(200);
expect(windowManager.viewManager.showByName).not.toBeCalled();
windowManager.viewManager.views.set('server-1_tab-3', {});
jest.advanceTimersByTime(200);
expect(windowManager.viewManager.showByName).toBeCalledWith('server-1_tab-3');
});
});
describe('handleHistory', () => {
const windowManager = new WindowManager();
windowManager.viewManager = {
getCurrentView: jest.fn(),
};
it('should only go to offset if it can', () => {
const view = {
view: {
webContents: {
goToOffset: jest.fn(),
canGoToOffset: () => false,
},
},
};
windowManager.viewManager.getCurrentView.mockReturnValue(view);
windowManager.handleHistory(null, 1);
expect(view.view.webContents.goToOffset).not.toBeCalled();
windowManager.viewManager.getCurrentView.mockReturnValue({
...view,
view: {
...view.view,
webContents: {
...view.view.webContents,
canGoToOffset: () => true,
},
},
});
windowManager.handleHistory(null, 1);
expect(view.view.webContents.goToOffset).toBeCalled();
});
it('should load base URL if an error occurs', () => {
const view = {
load: jest.fn(),
tab: {
url: 'http://server-1.com',
},
view: {
webContents: {
goToOffset: jest.fn(),
canGoToOffset: () => true,
},
},
};
view.view.webContents.goToOffset.mockImplementation(() => {
throw new Error('hi');
});
windowManager.viewManager.getCurrentView.mockReturnValue(view);
windowManager.handleHistory(null, 1);
expect(view.load).toBeCalledWith('http://server-1.com');
});
});
describe('selectTab', () => {
const windowManager = new WindowManager();
windowManager.config = {
teams: [
{
name: 'server-1',
order: 1,
tabs: [
{
name: 'tab-1',
order: 0,
isOpen: false,
},
{
name: 'tab-2',
order: 2,
isOpen: true,
},
{
name: 'tab-3',
order: 1,
isOpen: true,
},
],
},
],
};
windowManager.viewManager = {
getCurrentView: jest.fn(),
};
windowManager.switchTab = jest.fn();
afterEach(() => {
jest.resetAllMocks();
});
it('should select next server when open', () => {
windowManager.viewManager.getCurrentView.mockReturnValue({
tab: {
server: {
name: 'server-1',
},
type: 'tab-3',
},
});
windowManager.selectTab((order) => order + 1);
expect(windowManager.switchTab).toBeCalledWith('server-1', 'tab-2');
});
it('should select previous server when open', () => {
windowManager.viewManager.getCurrentView.mockReturnValue({
tab: {
server: {
name: 'server-1',
},
type: 'tab-2',
},
});
windowManager.selectTab((order, length) => (length + (order - 1)));
expect(windowManager.switchTab).toBeCalledWith('server-1', 'tab-3');
});
it('should skip over closed tab', () => {
windowManager.viewManager.getCurrentView.mockReturnValue({
tab: {
server: {
name: 'server-1',
},
type: 'tab-2',
},
});
windowManager.selectTab((order) => order + 1);
expect(windowManager.switchTab).toBeCalledWith('server-1', 'tab-3');
});
});
describe('handleBrowserHistoryPush', () => {
const windowManager = new WindowManager();
windowManager.config = {
teams: [
{
name: 'server-1',
url: 'http://server-1.com',
order: 0,
tabs: [
{
name: 'tab-messaging',
order: 0,
isOpen: true,
},
{
name: 'other_type_1',
order: 2,
isOpen: true,
},
{
name: 'other_type_2',
order: 1,
isOpen: false,
},
],
},
],
};
const view1 = {
name: 'server-1_tab-messaging',
isLoggedIn: true,
tab: {
type: TAB_MESSAGING,
server: {
url: 'http://server-1.com',
},
},
view: {
webContents: {
send: jest.fn(),
},
},
};
const view2 = {
...view1,
name: 'server-1_other_type_1',
tab: {
...view1.tab,
type: 'other_type_1',
},
view: {
webContents: {
send: jest.fn(),
},
},
};
const view3 = {
...view1,
name: 'server-1_other_type_2',
tab: {
...view1.tab,
type: 'other_type_2',
},
view: {
webContents: {
send: jest.fn(),
},
},
};
windowManager.viewManager = {
views: new Map([
['server-1_tab-messaging', view1],
['server-1_other_type_1', view2],
]),
closedViews: new Map([
['server-1_other_type_2', view3],
]),
openClosedTab: jest.fn(),
showByName: jest.fn(),
};
afterEach(() => {
jest.resetAllMocks();
});
it('should open closed view if pushing to it', () => {
urlUtils.getView.mockReturnValue({name: 'server-1_other_type_2'});
windowManager.viewManager.openClosedTab.mockImplementation((name) => {
const view = windowManager.viewManager.closedViews.get(name);
windowManager.viewManager.closedViews.delete(name);
windowManager.viewManager.views.set(name, view);
});
windowManager.handleBrowserHistoryPush(null, 'server-1_tab-messaging', '/other_type_2/subpath');
expect(windowManager.viewManager.openClosedTab).toBeCalledWith('server-1_other_type_2', 'http://server-1.com/other_type_2/subpath');
expect(windowManager.viewManager.showByName).toBeCalledWith('server-1_other_type_2');
});
it('should open redirect view if different from current view', () => {
urlUtils.getView.mockReturnValue({name: 'server-1_other_type_1'});
windowManager.handleBrowserHistoryPush(null, 'server-1_tab-messaging', '/other_type_1/subpath');
expect(windowManager.viewManager.showByName).toBeCalledWith('server-1_other_type_1');
});
it('should ignore redirects to "/" to Messages from other tabs', () => {
urlUtils.getView.mockReturnValue({name: 'server-1_tab-messaging'});
windowManager.handleBrowserHistoryPush(null, 'server-1_other_type_1', '/');
expect(view1.view.webContents.send).not.toBeCalled();
});
});
});

File diff suppressed because it is too large Load Diff