[MM-40406] Add more singletons, refactor main.ts into pieces, add tests and some cleanup + tests for additional coverage (#1890)
* Refactor main.ts dependencies into singleton pattern * Split main.ts into testable pieces, some other refactoring for singleton pattern * Unit tests for main/app/app * Unit tests for main/app/config * Unit tests for main/app/initialize * Unit tests for main/app/intercom * Unit tests for main/app/utils * Add some more tests to get to 70% coverage * Fix for linux * Fix for alternate data dir paths * Fix E2E test
This commit is contained in:
287
src/main/app/initialize.test.js
Normal file
287
src/main/app/initialize.test.js
Normal file
@@ -0,0 +1,287 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import {app, session} from 'electron';
|
||||
|
||||
import Config from 'common/config';
|
||||
import urlUtils from 'common/utils/url';
|
||||
|
||||
import parseArgs from 'main/ParseArgs';
|
||||
import WindowManager from 'main/windows/windowManager';
|
||||
|
||||
import {initialize} from './initialize';
|
||||
import {clearAppCache, getDeeplinkingURL, wasUpdated} from './utils';
|
||||
|
||||
jest.mock('fs', () => ({
|
||||
unlinkSync: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('path', () => {
|
||||
const original = jest.requireActual('path');
|
||||
return {
|
||||
...original,
|
||||
resolve: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('electron', () => ({
|
||||
app: {
|
||||
on: jest.fn(),
|
||||
exit: jest.fn(),
|
||||
getPath: jest.fn(),
|
||||
setPath: jest.fn(),
|
||||
disableHardwareAcceleration: jest.fn(),
|
||||
enableSandbox: jest.fn(),
|
||||
requestSingleInstanceLock: jest.fn(),
|
||||
setAsDefaultProtocolClient: jest.fn(),
|
||||
setAppUserModelId: jest.fn(),
|
||||
getVersion: jest.fn(),
|
||||
whenReady: jest.fn(),
|
||||
},
|
||||
ipcMain: {
|
||||
on: jest.fn(),
|
||||
handle: jest.fn(),
|
||||
emit: jest.fn(),
|
||||
},
|
||||
session: {
|
||||
defaultSession: {
|
||||
setSpellCheckerDictionaryDownloadURL: jest.fn(),
|
||||
setPermissionRequestHandler: jest.fn(),
|
||||
on: jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('electron-devtools-installer', () => {
|
||||
return () => ({
|
||||
REACT_DEVELOPER_TOOLS: 'react-developer-tools',
|
||||
});
|
||||
});
|
||||
|
||||
const isDev = false;
|
||||
jest.mock('electron-is-dev', () => isDev);
|
||||
|
||||
jest.mock('electron-log', () => ({
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../electron-builder.json', () => ([
|
||||
{
|
||||
name: 'Mattermost',
|
||||
schemes: [
|
||||
'mattermost',
|
||||
],
|
||||
},
|
||||
]));
|
||||
|
||||
jest.mock('common/config', () => ({
|
||||
once: jest.fn(),
|
||||
on: jest.fn(),
|
||||
init: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('common/utils/url', () => ({
|
||||
isTrustedURL: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('main/allowProtocolDialog', () => ({
|
||||
init: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/app/app', () => ({}));
|
||||
jest.mock('main/app/config', () => ({
|
||||
handleConfigUpdate: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/app/intercom', () => ({}));
|
||||
jest.mock('main/app/utils', () => ({
|
||||
clearAppCache: jest.fn(),
|
||||
getDeeplinkingURL: jest.fn(),
|
||||
handleUpdateMenuEvent: jest.fn(),
|
||||
shouldShowTrayIcon: jest.fn(),
|
||||
updateServerInfos: jest.fn(),
|
||||
updateSpellCheckerLocales: jest.fn(),
|
||||
wasUpdated: jest.fn(),
|
||||
initCookieManager: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/AppVersionManager', () => ({}));
|
||||
jest.mock('main/authManager', () => ({}));
|
||||
jest.mock('main/AutoLauncher', () => ({
|
||||
upgradeAutoLaunch: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/badge', () => ({
|
||||
setupBadge: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/certificateManager', () => ({}));
|
||||
jest.mock('main/CriticalErrorHandler', () => ({
|
||||
processUncaughtExceptionHandler: jest.fn(),
|
||||
setMainWindow: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/notifications', () => ({
|
||||
displayDownloadCompleted: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/ParseArgs', () => jest.fn());
|
||||
jest.mock('main/tray/tray', () => ({
|
||||
refreshTrayImages: jest.fn(),
|
||||
setupTray: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/trustedOrigins', () => ({
|
||||
load: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/UserActivityMonitor', () => ({
|
||||
on: jest.fn(),
|
||||
startMonitoring: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/windows/windowManager', () => ({
|
||||
getMainWindow: jest.fn(),
|
||||
showMainWindow: jest.fn(),
|
||||
sendToMattermostViews: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('main/app/initialize', () => {
|
||||
beforeEach(() => {
|
||||
parseArgs.mockReturnValue({});
|
||||
Config.once.mockImplementation((event, cb) => {
|
||||
if (event === 'update') {
|
||||
cb();
|
||||
}
|
||||
});
|
||||
Config.data = {};
|
||||
Config.teams = [];
|
||||
app.whenReady.mockResolvedValue();
|
||||
app.requestSingleInstanceLock.mockReturnValue(true);
|
||||
app.getPath.mockImplementation((p) => `/basedir/${p}`);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
delete Config.data;
|
||||
});
|
||||
|
||||
it('should initialize without errors', async () => {
|
||||
await initialize();
|
||||
});
|
||||
|
||||
describe('initializeArgs', () => {
|
||||
it('should set datadir when specified', async () => {
|
||||
path.resolve.mockImplementation((p) => `/basedir${p}`);
|
||||
parseArgs.mockReturnValue({
|
||||
dataDir: '/some/dir',
|
||||
});
|
||||
await initialize();
|
||||
expect(app.setPath).toHaveBeenCalledWith('userData', '/basedir/some/dir');
|
||||
});
|
||||
|
||||
it('should show version and exit when specified', async () => {
|
||||
jest.spyOn(process.stdout, 'write').mockImplementation(() => {});
|
||||
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {});
|
||||
parseArgs.mockReturnValue({
|
||||
version: true,
|
||||
});
|
||||
await initialize();
|
||||
expect(exitSpy).toHaveBeenCalledWith(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initializeConfig', () => {
|
||||
it('should disable hardware acceleration when specified', async () => {
|
||||
Config.enableHardwareAcceleration = false;
|
||||
await initialize();
|
||||
expect(app.disableHardwareAcceleration).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('initializeBeforeAppReady', () => {
|
||||
it('should exit the app when single instance lock fails', () => {
|
||||
app.requestSingleInstanceLock.mockReturnValue(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initializeAfterAppReady', () => {
|
||||
it('should set spell checker URL if applicable', async () => {
|
||||
Config.spellCheckerURL = 'http://server-1.com';
|
||||
await initialize();
|
||||
expect(session.defaultSession.setSpellCheckerDictionaryDownloadURL).toHaveBeenCalledWith('http://server-1.com/');
|
||||
});
|
||||
|
||||
it('should clear app cache if last version opened was older', async () => {
|
||||
wasUpdated.mockReturnValue(true);
|
||||
await initialize();
|
||||
expect(clearAppCache).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should perform deeplink on win32', async () => {
|
||||
getDeeplinkingURL.mockReturnValue('mattermost://server-1.com');
|
||||
const originalPlatform = process.platform;
|
||||
Object.defineProperty(process, 'argv', {
|
||||
value: ['mattermost', 'mattermost://server-1.com'],
|
||||
});
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'win32',
|
||||
});
|
||||
|
||||
await initialize();
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: originalPlatform,
|
||||
});
|
||||
|
||||
expect(WindowManager.showMainWindow).toHaveBeenCalledWith('mattermost://server-1.com');
|
||||
});
|
||||
|
||||
it('should setup save dialog correctly', async () => {
|
||||
const item = {
|
||||
getFilename: () => 'filename.txt',
|
||||
on: jest.fn(),
|
||||
setSaveDialogOptions: jest.fn(),
|
||||
};
|
||||
Config.downloadLocation = '/some/dir';
|
||||
path.resolve.mockImplementation((base, p) => `${base}/${p}`);
|
||||
session.defaultSession.on.mockImplementation((event, cb) => {
|
||||
if (event === 'will-download') {
|
||||
cb(null, item, {id: 0});
|
||||
}
|
||||
});
|
||||
|
||||
await initialize();
|
||||
expect(item.setSaveDialogOptions).toHaveBeenCalledWith(expect.objectContaining({
|
||||
title: 'filename.txt',
|
||||
defaultPath: '/some/dir/filename.txt',
|
||||
}));
|
||||
});
|
||||
|
||||
it('should allow permission requests for supported types from trusted URLs', async () => {
|
||||
let callback = jest.fn();
|
||||
session.defaultSession.setPermissionRequestHandler.mockImplementation((cb) => {
|
||||
cb({id: 1, getURL: () => 'http://server-1.com'}, 'bad-permission', callback);
|
||||
});
|
||||
await initialize();
|
||||
expect(callback).toHaveBeenCalledWith(false);
|
||||
|
||||
callback = jest.fn();
|
||||
WindowManager.getMainWindow.mockReturnValue({webContents: {id: 1}});
|
||||
session.defaultSession.setPermissionRequestHandler.mockImplementation((cb) => {
|
||||
cb({id: 1, getURL: () => 'http://server-1.com'}, 'openExternal', callback);
|
||||
});
|
||||
await initialize();
|
||||
expect(callback).toHaveBeenCalledWith(true);
|
||||
|
||||
urlUtils.isTrustedURL.mockImplementation((url) => url === 'http://server-1.com');
|
||||
|
||||
callback = jest.fn();
|
||||
session.defaultSession.setPermissionRequestHandler.mockImplementation((cb) => {
|
||||
cb({id: 2, getURL: () => 'http://server-1.com'}, 'openExternal', callback);
|
||||
});
|
||||
await initialize();
|
||||
expect(callback).toHaveBeenCalledWith(true);
|
||||
|
||||
callback = jest.fn();
|
||||
session.defaultSession.setPermissionRequestHandler.mockImplementation((cb) => {
|
||||
cb({id: 2, getURL: () => 'http://server-2.com'}, 'openExternal', callback);
|
||||
});
|
||||
await initialize();
|
||||
expect(callback).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user