[MM-40277][MM-40279][MM-40301][MM-40307][MM-40310][MM-40313] Unit tests and refactors for main module (#1876)
* Refactor autoLauncher and remove unnecessary file * [MM-40277] Unit tests for main/badge * [MM-40279] Unit tests for main/certificateStore * [MM-40301] Unit tests for main/contextMenu, also remove workaround * [MM-40307] Unit tests for main/CriticalErrorHandler * [MM-40310] Unit tests for main/utils * [MM-40313] Unit tests for main/Validator * Lint fix * Added globals * More things that should probably already be merged * PR feedback
This commit is contained in:
@@ -17,35 +17,44 @@ export default class AutoLauncher {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isEnabled() {
|
async upgradeAutoLaunch() {
|
||||||
return this.appLauncher.isEnabled();
|
if (process.platform === 'darwin') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const appLauncher = new AutoLaunch({
|
||||||
|
name: 'Mattermost',
|
||||||
|
});
|
||||||
|
const enabled = await appLauncher.isEnabled();
|
||||||
|
if (enabled) {
|
||||||
|
await appLauncher.enable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async blankPromise() {
|
isEnabled() {
|
||||||
return null;
|
return this.appLauncher.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
async enable() {
|
async enable() {
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
log.warn('In development mode, autostart config never effects');
|
log.warn('In development mode, autostart config never effects');
|
||||||
return this.blankPromise();
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
const enabled = await this.isEnabled();
|
const enabled = await this.isEnabled();
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return this.appLauncher.enable();
|
return this.appLauncher.enable();
|
||||||
}
|
}
|
||||||
return this.blankPromise();
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async disable() {
|
async disable() {
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
log.warn('In development mode, autostart config never effects');
|
log.warn('In development mode, autostart config never effects');
|
||||||
return this.blankPromise();
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
const enabled = await this.isEnabled();
|
const enabled = await this.isEnabled();
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
return this.appLauncher.disable();
|
return this.appLauncher.disable();
|
||||||
}
|
}
|
||||||
return this.blankPromise();
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
107
src/main/CriticalErrorHandler.test.js
Normal file
107
src/main/CriticalErrorHandler.test.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import {spawn} from 'child_process';
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {app, dialog} from 'electron';
|
||||||
|
|
||||||
|
import CriticalErrorHandler from './CriticalErrorHandler';
|
||||||
|
|
||||||
|
jest.mock('path', () => ({
|
||||||
|
join: jest.fn().mockImplementation((...args) => args.join('/')),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('electron', () => ({
|
||||||
|
app: {
|
||||||
|
name: 'Mattermost',
|
||||||
|
getVersion: () => '5.0.0',
|
||||||
|
getPath: (folder) => `/${folder}`,
|
||||||
|
relaunch: jest.fn(),
|
||||||
|
isReady: jest.fn(),
|
||||||
|
exit: jest.fn(),
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
showMessageBox: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('electron-log', () => ({
|
||||||
|
error: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('fs', () => ({
|
||||||
|
writeFileSync: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('child_process', () => ({
|
||||||
|
spawn: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('main/CriticalErrorHandler', () => {
|
||||||
|
const criticalErrorHandler = new CriticalErrorHandler();
|
||||||
|
beforeEach(() => {
|
||||||
|
criticalErrorHandler.setMainWindow({});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('windowUnresponsiveHandler', () => {
|
||||||
|
it('should do nothing when mainWindow is null', () => {
|
||||||
|
criticalErrorHandler.setMainWindow(null);
|
||||||
|
criticalErrorHandler.windowUnresponsiveHandler();
|
||||||
|
expect(dialog.showMessageBox).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call app.relaunch when user elects not to wait', async () => {
|
||||||
|
const promise = Promise.resolve({response: 0});
|
||||||
|
dialog.showMessageBox.mockImplementation(() => promise);
|
||||||
|
criticalErrorHandler.windowUnresponsiveHandler();
|
||||||
|
await promise;
|
||||||
|
expect(app.relaunch).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('processUncaughtExceptionHandler', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
app.isReady.mockImplementation(() => true);
|
||||||
|
criticalErrorHandler.setMainWindow({isVisible: true});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if app is not ready', () => {
|
||||||
|
app.isReady.mockImplementation(() => false);
|
||||||
|
expect(() => {
|
||||||
|
criticalErrorHandler.processUncaughtExceptionHandler(new Error('test'));
|
||||||
|
}).toThrow(Error);
|
||||||
|
expect(dialog.showMessageBox).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do nothing if main window is null or not visible', () => {
|
||||||
|
criticalErrorHandler.setMainWindow(null);
|
||||||
|
criticalErrorHandler.processUncaughtExceptionHandler(new Error('test'));
|
||||||
|
expect(dialog.showMessageBox).not.toBeCalled();
|
||||||
|
|
||||||
|
criticalErrorHandler.setMainWindow({isVisible: false});
|
||||||
|
criticalErrorHandler.processUncaughtExceptionHandler(new Error('test'));
|
||||||
|
expect(dialog.showMessageBox).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open external file on Show Details', async () => {
|
||||||
|
path.join.mockImplementation(() => 'testfile.txt');
|
||||||
|
const promise = Promise.resolve({response: process.platform === 'darwin' ? 2 : 0});
|
||||||
|
dialog.showMessageBox.mockImplementation(() => promise);
|
||||||
|
criticalErrorHandler.processUncaughtExceptionHandler(new Error('test'));
|
||||||
|
await promise;
|
||||||
|
expect(spawn).toBeCalledWith(expect.any(String), expect.arrayContaining(['testfile.txt']), expect.any(Object));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should restart app on Reopen', async () => {
|
||||||
|
path.join.mockImplementation(() => 'testfile.txt');
|
||||||
|
const promise = Promise.resolve({response: process.platform === 'darwin' ? 0 : 2});
|
||||||
|
dialog.showMessageBox.mockImplementation(() => promise);
|
||||||
|
criticalErrorHandler.processUncaughtExceptionHandler(new Error('test'));
|
||||||
|
await promise;
|
||||||
|
expect(app.relaunch).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -57,7 +57,8 @@ export default class CriticalErrorHandler {
|
|||||||
defaultId: 0,
|
defaultId: 0,
|
||||||
}).then(({response}) => {
|
}).then(({response}) => {
|
||||||
if (response === 0) {
|
if (response === 0) {
|
||||||
throw new Error('BrowserWindow \'unresponsive\' event has been emitted');
|
log.error('BrowserWindow \'unresponsive\' event has been emitted');
|
||||||
|
app.relaunch();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
205
src/main/Validator.test.js
Normal file
205
src/main/Validator.test.js
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import * as Validator from './Validator';
|
||||||
|
|
||||||
|
jest.mock('electron-log', () => ({
|
||||||
|
error: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('main/Validator', () => {
|
||||||
|
describe('validateV0ConfigData', () => {
|
||||||
|
const config = {url: 'http://server-1.com'};
|
||||||
|
|
||||||
|
it('should return null when not provided object', () => {
|
||||||
|
expect(Validator.validateV0ConfigData('notanobject')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return complete object when it is valid', () => {
|
||||||
|
expect(Validator.validateV0ConfigData(config)).toStrictEqual(config);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove fields that arent part of the schema', () => {
|
||||||
|
const modifiedConfig = {...config, anotherField: 'value'};
|
||||||
|
expect(Validator.validateV0ConfigData(modifiedConfig)).toStrictEqual(config);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('validateV1ConfigData', () => {
|
||||||
|
const config = {
|
||||||
|
autostart: true,
|
||||||
|
enableHardwareAcceleration: true,
|
||||||
|
minimizeToTray: false,
|
||||||
|
showTrayIcon: false,
|
||||||
|
showUnreadBadge: true,
|
||||||
|
spellCheckerLocale: 'en-US',
|
||||||
|
teams: [
|
||||||
|
{
|
||||||
|
name: 'server-1',
|
||||||
|
url: 'http://server-1.com',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
trayIconTheme: 'light',
|
||||||
|
useSpellChecker: true,
|
||||||
|
version: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should remove invalid urls', () => {
|
||||||
|
const modifiedConfig = {
|
||||||
|
...config,
|
||||||
|
teams: [
|
||||||
|
...config.teams,
|
||||||
|
{
|
||||||
|
name: 'server-2',
|
||||||
|
url: 'a-bad>url',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(Validator.validateV1ConfigData(modifiedConfig)).toStrictEqual(config);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clean URLs with backslashes', () => {
|
||||||
|
const modifiedConfig = {
|
||||||
|
...config,
|
||||||
|
teams: [
|
||||||
|
...config.teams,
|
||||||
|
{
|
||||||
|
name: 'server-2',
|
||||||
|
url: 'http:\\\\server-2.com\\subpath',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(Validator.validateV1ConfigData(modifiedConfig)).toStrictEqual({
|
||||||
|
...config,
|
||||||
|
teams: [
|
||||||
|
...config.teams,
|
||||||
|
{
|
||||||
|
name: 'server-2',
|
||||||
|
url: 'http://server-2.com/subpath',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate bad spell checker locales', () => {
|
||||||
|
const modifiedConfig = {
|
||||||
|
...config,
|
||||||
|
spellCheckerLocale: 'not-a-locale',
|
||||||
|
};
|
||||||
|
expect(Validator.validateV1ConfigData(modifiedConfig)).toStrictEqual(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('validateV2ConfigData', () => {
|
||||||
|
const config = {
|
||||||
|
autostart: true,
|
||||||
|
darkMode: false,
|
||||||
|
enableHardwareAcceleration: true,
|
||||||
|
minimizeToTray: false,
|
||||||
|
showTrayIcon: false,
|
||||||
|
showUnreadBadge: true,
|
||||||
|
spellCheckerLocale: 'en-US',
|
||||||
|
spellCheckerURL: 'http://spellcheckerservice.com',
|
||||||
|
teams: [
|
||||||
|
{
|
||||||
|
name: 'server-1',
|
||||||
|
url: 'http://server-1.com',
|
||||||
|
order: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
trayIconTheme: 'light',
|
||||||
|
useSpellChecker: true,
|
||||||
|
version: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should remove invalid spellchecker URLs', () => {
|
||||||
|
const modifiedConfig = {
|
||||||
|
...config,
|
||||||
|
spellCheckerURL: 'a-bad>url',
|
||||||
|
};
|
||||||
|
expect(Validator.validateV2ConfigData(modifiedConfig)).not.toHaveProperty('spellCheckerURL');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('validateV3ConfigData', () => {
|
||||||
|
const config = {
|
||||||
|
autostart: true,
|
||||||
|
darkMode: false,
|
||||||
|
enableHardwareAcceleration: true,
|
||||||
|
lastActiveTeam: 0,
|
||||||
|
minimizeToTray: false,
|
||||||
|
showTrayIcon: false,
|
||||||
|
showUnreadBadge: true,
|
||||||
|
spellCheckerLocales: ['en-US'],
|
||||||
|
spellCheckerURL: 'http://spellcheckerservice.com',
|
||||||
|
teams: [
|
||||||
|
{
|
||||||
|
lastActiveTab: 0,
|
||||||
|
name: 'server-1',
|
||||||
|
url: 'http://server-1.com',
|
||||||
|
order: 1,
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
name: 'tab-1',
|
||||||
|
isOpen: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
trayIconTheme: 'light',
|
||||||
|
useSpellChecker: true,
|
||||||
|
version: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should ensure messaging tab is open', () => {
|
||||||
|
const modifiedConfig = {
|
||||||
|
...config,
|
||||||
|
teams: [
|
||||||
|
{
|
||||||
|
...config.teams[0],
|
||||||
|
tabs: [
|
||||||
|
...config.teams[0].tabs,
|
||||||
|
{
|
||||||
|
name: 'TAB_MESSAGING',
|
||||||
|
isOpen: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(Validator.validateV3ConfigData(modifiedConfig)).toStrictEqual({
|
||||||
|
...config,
|
||||||
|
teams: [
|
||||||
|
{
|
||||||
|
...config.teams[0],
|
||||||
|
tabs: [
|
||||||
|
...config.teams[0].tabs,
|
||||||
|
{
|
||||||
|
name: 'TAB_MESSAGING',
|
||||||
|
isOpen: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('validateAllowedProtocols', () => {
|
||||||
|
const allowedProtocols = [
|
||||||
|
'spotify:',
|
||||||
|
'steam:',
|
||||||
|
'mattermost:',
|
||||||
|
];
|
||||||
|
|
||||||
|
it('should accept valid protocols', () => {
|
||||||
|
expect(Validator.validateAllowedProtocols(allowedProtocols)).toStrictEqual(allowedProtocols);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject invalid protocols', () => {
|
||||||
|
expect(Validator.validateAllowedProtocols([...allowedProtocols, 'not-a-protocol'])).toStrictEqual(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -5,7 +5,7 @@ import log from 'electron-log';
|
|||||||
import Joi from '@hapi/joi';
|
import Joi from '@hapi/joi';
|
||||||
|
|
||||||
import {Args} from 'types/args';
|
import {Args} from 'types/args';
|
||||||
import {ConfigV0, ConfigV1, ConfigV2, ConfigV3} from 'types/config';
|
import {ConfigV0, ConfigV1, ConfigV2, ConfigV3, TeamWithTabs} from 'types/config';
|
||||||
import {SavedWindowState} from 'types/mainWindow';
|
import {SavedWindowState} from 'types/mainWindow';
|
||||||
import {AppState} from 'types/appState';
|
import {AppState} from 'types/appState';
|
||||||
import {ComparableCertificate} from 'types/certificate';
|
import {ComparableCertificate} from 'types/certificate';
|
||||||
@@ -168,27 +168,6 @@ export function validateV0ConfigData(data: ConfigV0) {
|
|||||||
return validateAgainstSchema(data, configDataSchemaV0);
|
return validateAgainstSchema(data, configDataSchemaV0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate v.1 config.json
|
|
||||||
export function validateV1ConfigData(data: ConfigV1) {
|
|
||||||
if (Array.isArray(data.teams) && data.teams.length) {
|
|
||||||
// first replace possible backslashes with forward slashes
|
|
||||||
let teams = data.teams.map(({name, url}) => {
|
|
||||||
let updatedURL = url;
|
|
||||||
if (updatedURL.includes('\\')) {
|
|
||||||
updatedURL = updatedURL.toLowerCase().replace(/\\/gi, '/');
|
|
||||||
}
|
|
||||||
return {name, url: updatedURL};
|
|
||||||
});
|
|
||||||
|
|
||||||
// next filter out urls that are still invalid so all is not lost
|
|
||||||
teams = teams.filter(({url}) => urlUtils.isValidURL(url));
|
|
||||||
|
|
||||||
// replace original teams
|
|
||||||
data.teams = teams;
|
|
||||||
}
|
|
||||||
return validateAgainstSchema(data, configDataSchemaV1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanURL(url: string): string {
|
function cleanURL(url: string): string {
|
||||||
let updatedURL = url;
|
let updatedURL = url;
|
||||||
if (updatedURL.includes('\\')) {
|
if (updatedURL.includes('\\')) {
|
||||||
@@ -197,22 +176,45 @@ function cleanURL(url: string): string {
|
|||||||
return updatedURL;
|
return updatedURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateV2ConfigData(data: ConfigV2) {
|
function cleanTeam<T extends {name: string; url: string}>(team: T) {
|
||||||
if (Array.isArray(data.teams) && data.teams.length) {
|
return {
|
||||||
// first replace possible backslashes with forward slashes
|
...team,
|
||||||
let teams = data.teams.map((team) => {
|
url: cleanURL(team.url),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanTeamWithTabs(team: TeamWithTabs) {
|
||||||
|
return {
|
||||||
|
...cleanTeam(team),
|
||||||
|
tabs: team.tabs.map((tab) => {
|
||||||
return {
|
return {
|
||||||
...team,
|
...tab,
|
||||||
url: cleanURL(team.url),
|
isOpen: tab.name === TAB_MESSAGING ? true : tab.isOpen,
|
||||||
};
|
};
|
||||||
});
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanTeams<T extends {name: string; url: string}>(teams: T[], func: (team: T) => T) {
|
||||||
|
let newTeams = teams;
|
||||||
|
if (Array.isArray(newTeams) && newTeams.length) {
|
||||||
|
// first replace possible backslashes with forward slashes
|
||||||
|
newTeams = newTeams.map((team) => func(team));
|
||||||
|
|
||||||
// next filter out urls that are still invalid so all is not lost
|
// next filter out urls that are still invalid so all is not lost
|
||||||
teams = teams.filter(({url}) => urlUtils.isValidURL(url));
|
newTeams = newTeams.filter(({url}) => urlUtils.isValidURL(url));
|
||||||
|
|
||||||
// replace original teams
|
|
||||||
data.teams = teams;
|
|
||||||
}
|
}
|
||||||
|
return newTeams;
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate v.1 config.json
|
||||||
|
export function validateV1ConfigData(data: ConfigV1) {
|
||||||
|
data.teams = cleanTeams(data.teams, cleanTeam);
|
||||||
|
return validateAgainstSchema(data, configDataSchemaV1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateV2ConfigData(data: ConfigV2) {
|
||||||
|
data.teams = cleanTeams(data.teams, cleanTeam);
|
||||||
if (data.spellCheckerURL && !urlUtils.isValidURL(data.spellCheckerURL)) {
|
if (data.spellCheckerURL && !urlUtils.isValidURL(data.spellCheckerURL)) {
|
||||||
log.error('Invalid download location for spellchecker dictionary, removing from config');
|
log.error('Invalid download location for spellchecker dictionary, removing from config');
|
||||||
delete data.spellCheckerURL;
|
delete data.spellCheckerURL;
|
||||||
@@ -221,29 +223,7 @@ export function validateV2ConfigData(data: ConfigV2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function validateV3ConfigData(data: ConfigV3) {
|
export function validateV3ConfigData(data: ConfigV3) {
|
||||||
if (Array.isArray(data.teams) && data.teams.length) {
|
data.teams = cleanTeams(data.teams, cleanTeamWithTabs);
|
||||||
// first replace possible backslashes with forward slashes
|
|
||||||
let teams = data.teams.map((team) => {
|
|
||||||
return {
|
|
||||||
...team,
|
|
||||||
url: cleanURL(team.url),
|
|
||||||
|
|
||||||
// Force messaging to stay open regardless of user config
|
|
||||||
tabs: team.tabs.map((tab) => {
|
|
||||||
return {
|
|
||||||
...tab,
|
|
||||||
isOpen: tab.name === TAB_MESSAGING ? true : tab.isOpen,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// next filter out urls that are still invalid so all is not lost
|
|
||||||
teams = teams.filter(({url}) => urlUtils.isValidURL(url));
|
|
||||||
|
|
||||||
// replace original teams
|
|
||||||
data.teams = teams;
|
|
||||||
}
|
|
||||||
if (data.spellCheckerURL && !urlUtils.isValidURL(data.spellCheckerURL)) {
|
if (data.spellCheckerURL && !urlUtils.isValidURL(data.spellCheckerURL)) {
|
||||||
log.error('Invalid download location for spellchecker dictionary, removing from config');
|
log.error('Invalid download location for spellchecker dictionary, removing from config');
|
||||||
delete data.spellCheckerURL;
|
delete data.spellCheckerURL;
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
// Copyright (c) 2015-2016 Yuya Ochiai
|
|
||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
import AutoLaunch from 'auto-launch';
|
|
||||||
|
|
||||||
async function upgradeAutoLaunch() {
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const appLauncher = new AutoLaunch({
|
|
||||||
name: 'Mattermost',
|
|
||||||
});
|
|
||||||
const enabled = await appLauncher.isEnabled();
|
|
||||||
if (enabled) {
|
|
||||||
await appLauncher.enable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default upgradeAutoLaunch;
|
|
79
src/main/badge.test.js
Normal file
79
src/main/badge.test.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import {app} from 'electron';
|
||||||
|
|
||||||
|
import * as Badge from './badge';
|
||||||
|
|
||||||
|
import {setOverlayIcon} from './windows/windowManager';
|
||||||
|
|
||||||
|
jest.mock('electron', () => ({
|
||||||
|
app: {
|
||||||
|
dock: {
|
||||||
|
setBadge: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('./appState', () => ({
|
||||||
|
updateBadge: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('./windows/windowManager', () => ({
|
||||||
|
setOverlayIcon: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
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));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show mention count when has mention count', () => {
|
||||||
|
Badge.showBadgeWindows(false, 50, false);
|
||||||
|
expect(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);
|
||||||
|
});
|
||||||
|
|
||||||
|
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));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show dot when has unreads', () => {
|
||||||
|
Badge.setUnreadBadgeSetting(true);
|
||||||
|
Badge.showBadgeWindows(false, 0, true);
|
||||||
|
expect(setOverlayIcon).toBeCalledWith('•', expect.any(String), expect.any(Boolean));
|
||||||
|
Badge.setUnreadBadgeSetting(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('showBadgeOSX', () => {
|
||||||
|
it('should show dot when session expired', () => {
|
||||||
|
Badge.showBadgeOSX(true, 7, false);
|
||||||
|
expect(app.dock.setBadge).toBeCalledWith('•');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show mention count when has mention count', () => {
|
||||||
|
Badge.showBadgeOSX(false, 50, false);
|
||||||
|
expect(app.dock.setBadge).toBeCalledWith('50');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show dot when has unreads but setting is off', () => {
|
||||||
|
Badge.showBadgeOSX(false, 0, true);
|
||||||
|
expect(app.dock.setBadge).not.toBeCalledWith('•');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show dot when has unreads', () => {
|
||||||
|
Badge.setUnreadBadgeSetting(true);
|
||||||
|
Badge.showBadgeOSX(false, 0, true);
|
||||||
|
expect(app.dock.setBadge).toBeCalledWith('•');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -13,7 +13,7 @@ const MAX_WIN_COUNT = 99;
|
|||||||
|
|
||||||
let showUnreadBadgeSetting: boolean;
|
let showUnreadBadgeSetting: boolean;
|
||||||
|
|
||||||
function showBadgeWindows(sessionExpired: boolean, mentionCount: number, showUnreadBadge: boolean) {
|
export function showBadgeWindows(sessionExpired: boolean, mentionCount: number, showUnreadBadge: boolean) {
|
||||||
let description = 'You have no unread messages';
|
let description = 'You have no unread messages';
|
||||||
let text;
|
let text;
|
||||||
if (sessionExpired) {
|
if (sessionExpired) {
|
||||||
@@ -29,7 +29,7 @@ function showBadgeWindows(sessionExpired: boolean, mentionCount: number, showUnr
|
|||||||
WindowManager.setOverlayIcon(text, description, mentionCount > 99);
|
WindowManager.setOverlayIcon(text, description, mentionCount > 99);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showBadgeOSX(sessionExpired: boolean, mentionCount: number, showUnreadBadge: boolean) {
|
export function showBadgeOSX(sessionExpired: boolean, mentionCount: number, showUnreadBadge: boolean) {
|
||||||
let badge = '';
|
let badge = '';
|
||||||
if (sessionExpired) {
|
if (sessionExpired) {
|
||||||
badge = '•';
|
badge = '•';
|
||||||
|
102
src/main/certificateStore.test.js
Normal file
102
src/main/certificateStore.test.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
import {validateCertificateStore} from './Validator';
|
||||||
|
|
||||||
|
import CertificateStore from './certificateStore';
|
||||||
|
|
||||||
|
jest.mock('./Validator', () => ({
|
||||||
|
validateCertificateStore: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('fs', () => ({
|
||||||
|
readFileSync: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const certificateData = {
|
||||||
|
'https://server-1.com': {
|
||||||
|
data: 'somerandomdata',
|
||||||
|
issuerName: 'someissuer',
|
||||||
|
dontTrust: false,
|
||||||
|
},
|
||||||
|
'https://server-2.com': {
|
||||||
|
data: 'somerandomdata',
|
||||||
|
issuerName: 'someissuer',
|
||||||
|
dontTrust: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('main/certificateStore', () => {
|
||||||
|
it('should fail gracefully when loaded data is malformed', () => {
|
||||||
|
validateCertificateStore.mockImplementation(() => null);
|
||||||
|
|
||||||
|
let certificateStore;
|
||||||
|
expect(() => {
|
||||||
|
certificateStore = new CertificateStore('somefilename');
|
||||||
|
}).not.toThrow(Error);
|
||||||
|
expect(certificateStore.data).toStrictEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isTrusted', () => {
|
||||||
|
let certificateStore;
|
||||||
|
beforeAll(() => {
|
||||||
|
validateCertificateStore.mockImplementation((data) => JSON.parse(data));
|
||||||
|
fs.readFileSync.mockImplementation(() => JSON.stringify(certificateData));
|
||||||
|
certificateStore = new CertificateStore('somefilename');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true for stored matching certificate', () => {
|
||||||
|
certificateStore = new CertificateStore('somefilename');
|
||||||
|
|
||||||
|
expect(certificateStore.isTrusted('https://server-1.com', {
|
||||||
|
data: 'somerandomdata',
|
||||||
|
issuerName: 'someissuer',
|
||||||
|
})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for missing url', () => {
|
||||||
|
expect(certificateStore.isTrusted('https://server-3.com', {
|
||||||
|
data: 'somerandomdata',
|
||||||
|
issuerName: 'someissuer',
|
||||||
|
})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for unmatching cert', () => {
|
||||||
|
expect(certificateStore.isTrusted('https://server-1.com', {
|
||||||
|
data: 'someotherrandomdata',
|
||||||
|
issuerName: 'someissuer',
|
||||||
|
})).toBe(false);
|
||||||
|
|
||||||
|
expect(certificateStore.isTrusted('https://server-1.com', {
|
||||||
|
data: 'somerandomdata',
|
||||||
|
issuerName: 'someotherissuer',
|
||||||
|
})).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isExplicitlyUntrusted', () => {
|
||||||
|
let certificateStore;
|
||||||
|
beforeAll(() => {
|
||||||
|
validateCertificateStore.mockImplementation((data) => JSON.parse(data));
|
||||||
|
fs.readFileSync.mockImplementation(() => JSON.stringify(certificateData));
|
||||||
|
certificateStore = new CertificateStore('somefilename');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true for explicitly untrusted cert', () => {
|
||||||
|
expect(certificateStore.isExplicitlyUntrusted('https://server-2.com', {
|
||||||
|
data: 'somerandomdata',
|
||||||
|
issuerName: 'someissuer',
|
||||||
|
})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for trusted cert', () => {
|
||||||
|
expect(certificateStore.isExplicitlyUntrusted('https://server-1.com', {
|
||||||
|
data: 'somerandomdata',
|
||||||
|
issuerName: 'someissuer',
|
||||||
|
})).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
82
src/main/contextMenu.test.js
Normal file
82
src/main/contextMenu.test.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import ContextMenu from './contextMenu';
|
||||||
|
|
||||||
|
jest.mock('electron-context-menu', () => {
|
||||||
|
return () => jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('main/contextMenu', () => {
|
||||||
|
describe('shouldShowMenu', () => {
|
||||||
|
const contextMenu = new ContextMenu();
|
||||||
|
|
||||||
|
it('should not show menu on internal link', () => {
|
||||||
|
expect(contextMenu.menuOptions.shouldShowMenu(null, {
|
||||||
|
mediaType: 'none',
|
||||||
|
linkURL: 'http://server-1.com/subpath#',
|
||||||
|
pageURL: 'http://server-1.com/subpath',
|
||||||
|
srcURL: '',
|
||||||
|
misspelledWord: '',
|
||||||
|
selectionText: '',
|
||||||
|
})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show menu on buttons', () => {
|
||||||
|
expect(contextMenu.menuOptions.shouldShowMenu(null, {
|
||||||
|
mediaType: 'none',
|
||||||
|
linkURL: '',
|
||||||
|
pageURL: 'http://server-1.com/subpath',
|
||||||
|
srcURL: '',
|
||||||
|
misspelledWord: '',
|
||||||
|
selectionText: '',
|
||||||
|
})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show menu on editables', () => {
|
||||||
|
expect(contextMenu.menuOptions.shouldShowMenu(null, {
|
||||||
|
mediaType: 'none',
|
||||||
|
linkURL: '',
|
||||||
|
pageURL: 'http://server-1.com/subpath',
|
||||||
|
srcURL: '',
|
||||||
|
misspelledWord: '',
|
||||||
|
selectionText: '',
|
||||||
|
isEditable: true,
|
||||||
|
})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show menu on images', () => {
|
||||||
|
expect(contextMenu.menuOptions.shouldShowMenu(null, {
|
||||||
|
mediaType: 'image',
|
||||||
|
linkURL: '',
|
||||||
|
pageURL: 'http://server-1.com/subpath',
|
||||||
|
srcURL: 'http://server-1.com/subpath/image.png',
|
||||||
|
misspelledWord: '',
|
||||||
|
selectionText: '',
|
||||||
|
isEditable: true,
|
||||||
|
})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show menu on external links', () => {
|
||||||
|
expect(contextMenu.menuOptions.shouldShowMenu(null, {
|
||||||
|
mediaType: 'none',
|
||||||
|
linkURL: 'http://server-2.com/link',
|
||||||
|
pageURL: 'http://server-1.com/subpath',
|
||||||
|
srcURL: '',
|
||||||
|
misspelledWord: '',
|
||||||
|
selectionText: '',
|
||||||
|
isEditable: true,
|
||||||
|
})).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('reload', () => {
|
||||||
|
it('should call dispose on reload', () => {
|
||||||
|
const contextMenu = new ContextMenu();
|
||||||
|
const fn = contextMenu.menuDispose;
|
||||||
|
contextMenu.reload();
|
||||||
|
expect(fn).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -2,7 +2,7 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {BrowserView, BrowserWindow, ContextMenuParams, Event, WebContents} from 'electron';
|
import {BrowserView, BrowserWindow, ContextMenuParams, Event} from 'electron';
|
||||||
import electronContextMenu, {Options} from 'electron-context-menu';
|
import electronContextMenu, {Options} from 'electron-context-menu';
|
||||||
|
|
||||||
import urlUtils from 'common/utils/url';
|
import urlUtils from 'common/utils/url';
|
||||||
@@ -51,11 +51,7 @@ export default class ContextMenu {
|
|||||||
reload = () => {
|
reload = () => {
|
||||||
this.dispose();
|
this.dispose();
|
||||||
|
|
||||||
/**
|
const options = {window: this.view, ...this.menuOptions};
|
||||||
* Work-around issue with passing `WebContents` to `electron-context-menu` in Electron 11
|
|
||||||
* @see https://github.com/sindresorhus/electron-context-menu/issues/123
|
|
||||||
*/
|
|
||||||
const options = {window: {webContents: this.view.webContents, inspectElement: this.view.webContents.inspectElement.bind(this.view.webContents), isDestroyed: this.view.webContents.isDestroyed.bind(this.view.webContents), off: this.view.webContents.off.bind(this.view.webContents)} as unknown as WebContents, ...this.menuOptions};
|
|
||||||
this.menuDispose = electronContextMenu(options);
|
this.menuDispose = electronContextMenu(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -55,7 +55,6 @@ import {protocols} from '../../electron-builder.json';
|
|||||||
|
|
||||||
import AutoLauncher from './AutoLauncher';
|
import AutoLauncher from './AutoLauncher';
|
||||||
import CriticalErrorHandler from './CriticalErrorHandler';
|
import CriticalErrorHandler from './CriticalErrorHandler';
|
||||||
import upgradeAutoLaunch from './autoLaunch';
|
|
||||||
import CertificateStore from './certificateStore';
|
import CertificateStore from './certificateStore';
|
||||||
import TrustedOriginsStore from './trustedOrigins';
|
import TrustedOriginsStore from './trustedOrigins';
|
||||||
import {createMenu as createAppMenu} from './menus/app';
|
import {createMenu as createAppMenu} from './menus/app';
|
||||||
@@ -91,6 +90,7 @@ const {
|
|||||||
} = electron;
|
} = electron;
|
||||||
const criticalErrorHandler = new CriticalErrorHandler();
|
const criticalErrorHandler = new CriticalErrorHandler();
|
||||||
const userActivityMonitor = new UserActivityMonitor();
|
const userActivityMonitor = new UserActivityMonitor();
|
||||||
|
const autoLauncher = new AutoLauncher();
|
||||||
const certificateErrorCallbacks = new Map();
|
const certificateErrorCallbacks = new Map();
|
||||||
|
|
||||||
// Keep a global reference of the window object, if you don't, the window will
|
// Keep a global reference of the window object, if you don't, the window will
|
||||||
@@ -281,8 +281,7 @@ function handleConfigUpdate(newConfig: CombinedConfig) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||||
const appLauncher = new AutoLauncher();
|
const autoStartTask = config.autostart ? autoLauncher.enable() : autoLauncher.disable();
|
||||||
const autoStartTask = config.autostart ? appLauncher.enable() : appLauncher.disable();
|
|
||||||
autoStartTask.then(() => {
|
autoStartTask.then(() => {
|
||||||
log.info('config.autostart has been configured:', newConfig.autostart);
|
log.info('config.autostart has been configured:', newConfig.autostart);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
@@ -684,7 +683,7 @@ function initializeAfterAppReady() {
|
|||||||
appVersion.lastAppVersion = app.getVersion();
|
appVersion.lastAppVersion = app.getVersion();
|
||||||
|
|
||||||
if (!global.isDev) {
|
if (!global.isDev) {
|
||||||
upgradeAutoLaunch();
|
autoLauncher.upgradeAutoLaunch();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.isDev) {
|
if (global.isDev) {
|
||||||
|
100
src/main/utils.test.js
Normal file
100
src/main/utils.test.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import {BACK_BAR_HEIGHT, TAB_BAR_HEIGHT} from 'common/utils/constants';
|
||||||
|
import {runMode} from 'common/utils/util';
|
||||||
|
|
||||||
|
import * as Utils from './utils';
|
||||||
|
|
||||||
|
jest.mock('electron', () => ({
|
||||||
|
app: {
|
||||||
|
getLoginItemSettings: () => ({
|
||||||
|
wasOpenedAsHidden: true,
|
||||||
|
}),
|
||||||
|
getAppPath: () => '/path/to/app',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('common/utils/util', () => ({
|
||||||
|
runMode: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('path', () => {
|
||||||
|
const original = jest.requireActual('path');
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
resolve: (basePath, ...restOfPath) => original.join('/path/to/app/src/main', ...restOfPath),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('main/utils', () => {
|
||||||
|
describe('shouldBeHiddenOnStartup', () => {
|
||||||
|
let originalPlatform;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
originalPlatform = process.platform;
|
||||||
|
Object.defineProperty(process, 'platform', {
|
||||||
|
value: 'darwin',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be hidden on mac when opened as hidden', () => {
|
||||||
|
expect(Utils.shouldBeHiddenOnStartup({})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
Object.defineProperty(process, 'platform', {
|
||||||
|
value: originalPlatform,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getWindowBoundaries', () => {
|
||||||
|
it('should include tab bar height', () => {
|
||||||
|
expect(Utils.getWindowBoundaries({
|
||||||
|
getContentBounds: () => ({width: 500, height: 400}),
|
||||||
|
})).toStrictEqual({
|
||||||
|
x: 0,
|
||||||
|
y: TAB_BAR_HEIGHT,
|
||||||
|
width: 500,
|
||||||
|
height: 400 - TAB_BAR_HEIGHT,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should include back bar height when specified', () => {
|
||||||
|
expect(Utils.getWindowBoundaries({
|
||||||
|
getContentBounds: () => ({width: 500, height: 400}),
|
||||||
|
}, true)).toStrictEqual({
|
||||||
|
x: 0,
|
||||||
|
y: TAB_BAR_HEIGHT + BACK_BAR_HEIGHT,
|
||||||
|
width: 500,
|
||||||
|
height: 400 - TAB_BAR_HEIGHT - BACK_BAR_HEIGHT,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getLocalURLString', () => {
|
||||||
|
it('should return URL relative to current run directory', () => {
|
||||||
|
runMode.mockImplementation(() => 'development');
|
||||||
|
expect(Utils.getLocalURLString('index.html')).toStrictEqual('file:///path/to/app/dist/renderer/index.html');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return URL relative to current run directory in production', () => {
|
||||||
|
runMode.mockImplementation(() => 'production');
|
||||||
|
expect(Utils.getLocalURLString('index.html')).toStrictEqual('file:///path/to/app/renderer/index.html');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should include query string when specified', () => {
|
||||||
|
const queryMap = new Map([['key', 'value']]);
|
||||||
|
runMode.mockImplementation(() => 'development');
|
||||||
|
expect(Utils.getLocalURLString('index.html', queryMap)).toStrictEqual('file:///path/to/app/dist/renderer/index.html?key=value');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return URL relative to current run directory when using main process', () => {
|
||||||
|
runMode.mockImplementation(() => 'development');
|
||||||
|
expect(Utils.getLocalURLString('index.html', null, true)).toStrictEqual('file:///path/to/app/dist/index.html');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -2,7 +2,7 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import electron, {app, BrowserWindow} from 'electron';
|
import {app, BrowserWindow} from 'electron';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import {Args} from 'types/args';
|
import {Args} from 'types/args';
|
||||||
@@ -49,7 +49,7 @@ export function getLocalURL(urlPath: string, query?: Map<string, string>, isMain
|
|||||||
const hostname = '';
|
const hostname = '';
|
||||||
const port = '';
|
const port = '';
|
||||||
if (mode === PRODUCTION) {
|
if (mode === PRODUCTION) {
|
||||||
pathname = path.join(electron.app.getAppPath(), `${processPath}/${urlPath}`);
|
pathname = path.join(app.getAppPath(), `${processPath}/${urlPath}`);
|
||||||
} else {
|
} else {
|
||||||
pathname = path.resolve(__dirname, `../../dist/${processPath}/${urlPath}`); // TODO: find a better way to work with webpack on this
|
pathname = path.resolve(__dirname, `../../dist/${processPath}/${urlPath}`); // TODO: find a better way to work with webpack on this
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ export function getLocalURL(urlPath: string, query?: Map<string, string>, isMain
|
|||||||
|
|
||||||
export function getLocalPreload(file: string) {
|
export function getLocalPreload(file: string) {
|
||||||
if (Utils.runMode() === PRODUCTION) {
|
if (Utils.runMode() === PRODUCTION) {
|
||||||
return path.join(electron.app.getAppPath(), `${file}`);
|
return path.join(app.getAppPath(), `${file}`);
|
||||||
}
|
}
|
||||||
return path.resolve(__dirname, `../../dist/${file}`);
|
return path.resolve(__dirname, `../../dist/${file}`);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user