diff --git a/src/__tests__/electron.js b/src/__tests__/electron.js
index 747c6af3..b42fd813 100644
--- a/src/__tests__/electron.js
+++ b/src/__tests__/electron.js
@@ -33,6 +33,7 @@ const mockBrowserWindowInstance = () => {
setFullScreen: jest.fn(),
webContents: {
loadedUrl: '',
+ browserWindowInstance: () => instance,
copy: jest.fn(),
copyImageAt: jest.fn(),
cut: jest.fn(),
@@ -68,6 +69,9 @@ const mockElectronInstance = ({...overriddenProps} = {}) => {
getPath: jest.fn(),
setPath: jest.fn()
},
+ contextBridge: {
+ exposeInMainWorld: jest.fn()
+ },
globalShortcut: {
listeners: {},
register: jest.fn((accelerator, callback) => {
@@ -87,6 +91,9 @@ const mockElectronInstance = ({...overriddenProps} = {}) => {
delete instance.ipcMain.listeners[eventName];
})
},
+ ipcRenderer: {
+ send: jest.fn()
+ },
nativeTheme: {},
session: {
fromPartition: jest.fn(() => ({
@@ -96,6 +103,7 @@ const mockElectronInstance = ({...overriddenProps} = {}) => {
},
...overriddenProps
};
+ instance.BrowserWindow.fromWebContents = jest.fn(webContents => webContents.browserWindowInstance());
return instance;
};
diff --git a/src/about/__tests__/about.browser.test.mjs b/src/about/__tests__/about.browser.test.mjs
new file mode 100644
index 00000000..2cd29847
--- /dev/null
+++ b/src/about/__tests__/about.browser.test.mjs
@@ -0,0 +1,49 @@
+/*
+ Copyright 2022 Marc Nuri San Felix
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+import {jest} from '@jest/globals';
+import {loadDOM} from '../../__tests__/index.mjs';
+import {fireEvent} from '@testing-library/dom';
+
+describe('About in Browser test suite', () => {
+ beforeEach(async () => {
+ jest.resetModules();
+ window.electron = {
+ close: jest.fn(),
+ versions: {electron: '1.33.7', chrome: '1337', node: '42', v8: '13.37'}
+ };
+ await loadDOM({meta: import.meta, path: ['..', 'index.html']});
+ });
+ test('close, click should close dialog', () => {
+ // When
+ fireEvent.click(document.querySelector('.top-app-bar .leading-navigation-icon'));
+ // Then
+ expect(window.electron.close).toHaveBeenCalledTimes(1);
+ });
+ test.each([
+ {label: 'Electron', expectedVersion: '1.33.7'},
+ {label: 'Chromium', expectedVersion: '1337'},
+ {label: 'Node', expectedVersion: '42'},
+ {label: 'V8', expectedVersion: '13.37'}
+ ])('Should display $label with version $expectedVersion', ({label, expectedVersion}) => {
+ const versions = Array.from(document.querySelectorAll('.about-content__version'))
+ .map(v => ({
+ label: v.querySelector('.version__component').textContent,
+ version: v.querySelector('.version__value').textContent
+ }));
+ expect(versions).toContainEqual({label, version: expectedVersion});
+ });
+});
+
diff --git a/src/about/__tests__/index.html.test.mjs b/src/about/__tests__/index.html.test.mjs
new file mode 100644
index 00000000..7559a8e4
--- /dev/null
+++ b/src/about/__tests__/index.html.test.mjs
@@ -0,0 +1,33 @@
+/*
+ Copyright 2022 Marc Nuri San Felix
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+import {jest} from '@jest/globals';
+import {loadDOM} from '../../__tests__/index.mjs';
+
+describe('About index.html test suite', () => {
+ beforeEach(async () => {
+ jest.resetModules();
+ window.electron = {
+ close: jest.fn(),
+ versions: {}
+ };
+ await loadDOM({meta: import.meta, path: ['..', 'index.html']});
+ });
+ test('loads required styles', () => {
+ expect(Array.from(document.querySelectorAll('link[rel=stylesheet]'))
+ .map(link => link.getAttribute('href')))
+ .toEqual(['./about.browser.css']);
+ });
+});
diff --git a/src/about/__tests__/index.test.js b/src/about/__tests__/index.test.js
new file mode 100644
index 00000000..ec4401c2
--- /dev/null
+++ b/src/about/__tests__/index.test.js
@@ -0,0 +1,67 @@
+/*
+ Copyright 2022 Marc Nuri San Felix
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+describe('About module test suite', () => {
+ let electron;
+ let sender;
+ let about;
+ beforeEach(() => {
+ jest.resetModules();
+ jest.mock('electron', () => require('../../__tests__').mockElectronInstance());
+ electron = require('electron');
+ sender = electron.browserWindowInstance.webContents;
+ about = require('../');
+ });
+ describe('openAboutDialog', () => {
+ describe('webPreferences', () => {
+ test('is sandboxed', () => {
+ // When
+ about.openAboutDialog({sender});
+ // Then
+ const BrowserView = electron.BrowserView;
+ expect(BrowserView).toHaveBeenCalledTimes(1);
+ expect(BrowserView).toHaveBeenCalledWith({
+ webPreferences: expect.objectContaining({sandbox: true, nodeIntegration: false})
+ });
+ });
+ test('has no node integration', () => {
+ // When
+ about.openAboutDialog({sender});
+ // Then
+ expect(electron.BrowserView).toHaveBeenCalledWith({
+ webPreferences: expect.objectContaining({nodeIntegration: false})
+ });
+ });
+ test('has context isolation', () => {
+ // When
+ about.openAboutDialog({sender});
+ // Then
+ expect(electron.BrowserView).toHaveBeenCalledWith({
+ webPreferences: expect.objectContaining({contextIsolation: true})
+ });
+ });
+ });
+ test('should open dialog and add event listeners', () => {
+ // When
+ about.openAboutDialog({sender: electron.browserWindowInstance.webContents});
+ // Then
+ expect(electron.browserViewInstance.webContents.loadURL).toHaveBeenCalledTimes(1);
+ expect(electron.browserViewInstance.webContents.loadURL)
+ .toHaveBeenCalledWith(expect.stringMatching(/.+?\/index.html$/)); // NOSONAR
+ expect(electron.browserViewInstance.webContents.on).toHaveBeenCalledWith('will-navigate', expect.any(Function));
+ });
+ });
+});
+
diff --git a/src/about/__tests__/preload.test.js b/src/about/__tests__/preload.test.js
new file mode 100644
index 00000000..6a501582
--- /dev/null
+++ b/src/about/__tests__/preload.test.js
@@ -0,0 +1,53 @@
+/*
+ Copyright 2022 Marc Nuri San Felix
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+describe('About Module preload test suite', () => {
+ let electron;
+ beforeEach(() => {
+ jest.resetModules();
+ jest.mock('electron', () => require('../../__tests__').mockElectronInstance());
+ electron = require('electron');
+ });
+ describe('preload (just for coverage and sanity, see bundle tests)', () => {
+ beforeEach(() => {
+ global.APP_EVENTS = require('../../constants').APP_EVENTS;
+ require('../preload');
+ });
+ describe('creates an API', () => {
+ test('with entries', () => {
+ expect(electron.contextBridge.exposeInMainWorld).toHaveBeenCalledWith('electron', {
+ close: expect.toBeFunction(),
+ versions: expect.toBeObject()
+ });
+ });
+ test('with close function', () => {
+ const electronApi = electron.contextBridge.exposeInMainWorld.mock.calls[0][1];
+ electronApi.close();
+ expect(electron.ipcRenderer.send).toHaveBeenCalledWith('closeDialog');
+ });
+ });
+ });
+ describe('preload.bundle', () => {
+ beforeEach(() => {
+ require('../../../bundles/about.preload');
+ });
+ test('creates an API', () => {
+ expect(electron.contextBridge.exposeInMainWorld).toHaveBeenCalledWith('electron', {
+ close: expect.toBeFunction(),
+ versions: expect.toBeObject()
+ });
+ });
+ });
+});
diff --git a/src/about/about.browser.css b/src/about/about.browser.css
new file mode 100644
index 00000000..42f3a90a
--- /dev/null
+++ b/src/about/about.browser.css
@@ -0,0 +1,40 @@
+/*
+ Copyright 2022 Marc Nuri San Felix
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+@import '../styles/main.css';
+
+:root {
+ overflow: auto;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+}
+
+.about-content {
+ padding: 16px;
+}
+.about-content .about-content__release {
+ margin: 8px 0;
+}
+.about-content .version__component {
+ font-weight: var(--md-ref-typeface-weight-medium);
+ margin-right: 8px;
+}
+.about-content .version__value {
+ font-family: monospace;
+ color: var(--md-sys-color-on-surface-variant);
+}
diff --git a/src/about/about.browser.mjs b/src/about/about.browser.mjs
new file mode 100644
index 00000000..03ed1aa0
--- /dev/null
+++ b/src/about/about.browser.mjs
@@ -0,0 +1,63 @@
+/*
+ Copyright 2022 Marc Nuri San Felix
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+import {ELECTRONIM_VERSION, html, render, Card, TopAppBar} from '../components/index.mjs';
+
+const {close, versions} = window.electron;
+
+const TWITTER_LINK = 'https://twitter.com/share?url=https://github.com/manusa/electronim&text=I%27m%20using%20ElectronIM%20as%20my%20communications%20center%20and%20I%20love%20it%2C%20you%20should%20try%20it%20out%20too%21';
+
+const getAppMenu = () => document.querySelector('.about-root');
+
+const Version = ({component, value}) => html`
+
diff --git a/src/app-menu/preload.js b/src/app-menu/preload.js
index 764db475..f7f2467a 100644
--- a/src/app-menu/preload.js
+++ b/src/app-menu/preload.js
@@ -18,6 +18,7 @@ const {contextBridge, ipcRenderer} = require('electron');
contextBridge.exposeInMainWorld('electron', {
close: () => ipcRenderer.send(APP_EVENTS.appMenuClose),
+ aboutOpenDialog: () => ipcRenderer.send(APP_EVENTS.aboutOpenDialog),
helpOpenDialog: () => ipcRenderer.send(APP_EVENTS.helpOpenDialog),
settingsOpenDialog: () => ipcRenderer.send(APP_EVENTS.settingsOpenDialog)
});
diff --git a/src/components/card.mjs b/src/components/card.mjs
new file mode 100644
index 00000000..33107944
--- /dev/null
+++ b/src/components/card.mjs
@@ -0,0 +1,37 @@
+/*
+ Copyright 2022 Marc Nuri San Felix
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+import {html} from './index.mjs';
+
+/**
+ A card based on Material design (3) guidelines.
+ */
+export const Card = ({
+ headline,
+ subHeadline,
+ children
+}) => html`
+
+
+
${headline}
+ ${subHeadline && html`
${subHeadline}
`}
+
${children}
+
+
+`;
+
+Card.Divider = () => html`
+
+`;
diff --git a/src/components/index.mjs b/src/components/index.mjs
index e6a3508f..3d3649ea 100644
--- a/src/components/index.mjs
+++ b/src/components/index.mjs
@@ -16,8 +16,9 @@
export {APP_EVENTS, ELECTRONIM_VERSION} from '../../bundles/constants.mjs';
export {html, render, useLayoutEffect, useReducer, useState} from '../../bundles/preact.mjs';
+export {Card} from './card.mjs';
export {Checkbox, Field, HorizontalField, Select, sizes} from './form/index.mjs';
export {DropDown} from './drop-down.mjs';
export {Icon} from './icon.mjs';
export {Panel} from './panel.mjs';
-export {TopBar} from './top-bar.mjs';
+export {TopBar, TopAppBar} from './top-bar.mjs';
diff --git a/src/components/top-bar.mjs b/src/components/top-bar.mjs
index af5f6ea8..1cfc16b4 100644
--- a/src/components/top-bar.mjs
+++ b/src/components/top-bar.mjs
@@ -38,3 +38,23 @@ export const TopBar = ({
`;
};
+
+/**
+ * A top app bar based on Material design (3) guidelines.
+ *
+ * @param icon represented using a Material Icon codepoint.
+ * @param iconClick callback function to be executed when the icon is clicked.
+ * @param headline the headline of the app bar.
+ * @see https://m3.material.io/components/top-app-bar
+ */
+export const TopAppBar = ({
+ icon,
+ iconClick = () => {},
+ headline
+}) => html`
+
+
${icon}
+
${headline}
+
+
+`;
diff --git a/src/constants/index.js b/src/constants/index.js
index 4b7428ca..906f95e3 100644
--- a/src/constants/index.js
+++ b/src/constants/index.js
@@ -25,6 +25,7 @@ const findRootDir = () => {
};
const APP_EVENTS = {
+ aboutOpenDialog: 'aboutOpenDialog',
activateTab: 'activateTab',
activateTabInContainer: 'activateTabInContainer',
addTabs: 'addTabs',
diff --git a/src/help/__tests__/index.test.js b/src/help/__tests__/index.test.js
index f3a83f03..1fc549a8 100644
--- a/src/help/__tests__/index.test.js
+++ b/src/help/__tests__/index.test.js
@@ -14,30 +14,23 @@
limitations under the License.
*/
describe('Help module test suite', () => {
- let mockBrowserView;
+ let electron;
+ let sender;
let help;
beforeEach(() => {
jest.resetModules();
- mockBrowserView = require('../../__tests__').mockBrowserWindowInstance();
- jest.mock('electron', () => ({
- BrowserView: jest.fn(() => mockBrowserView)
- }));
+ jest.mock('electron', () => require('../../__tests__').mockElectronInstance());
+ electron = require('electron');
+ sender = electron.browserWindowInstance.webContents;
help = require('../');
});
describe('openHelpDialog', () => {
- let mainWindow;
- beforeEach(() => {
- mainWindow = {
- getContentBounds: jest.fn(() => ({width: 13, height: 37})),
- setBrowserView: jest.fn()
- };
- });
describe('webPreferences', () => {
test('is sandboxed', () => {
// When
- help.openHelpDialog(mainWindow)();
+ help.openHelpDialog({sender});
// Then
- const BrowserView = require('electron').BrowserView;
+ const BrowserView = electron.BrowserView;
expect(BrowserView).toHaveBeenCalledTimes(1);
expect(BrowserView).toHaveBeenCalledWith({
webPreferences: expect.objectContaining({sandbox: true, nodeIntegration: false})
@@ -45,28 +38,29 @@ describe('Help module test suite', () => {
});
test('has no node integration', () => {
// When
- help.openHelpDialog(mainWindow)();
+ help.openHelpDialog({sender});
// Then
- expect(require('electron').BrowserView).toHaveBeenCalledWith({
+ expect(electron.BrowserView).toHaveBeenCalledWith({
webPreferences: expect.objectContaining({nodeIntegration: false})
});
});
test('has context isolation', () => {
// When
- help.openHelpDialog(mainWindow)();
+ help.openHelpDialog({sender});
// Then
- expect(require('electron').BrowserView).toHaveBeenCalledWith({
+ expect(electron.BrowserView).toHaveBeenCalledWith({
webPreferences: expect.objectContaining({contextIsolation: true})
});
});
});
test('should open dialog and add event listeners', () => {
// When
- help.openHelpDialog(mainWindow)();
+ help.openHelpDialog({sender: electron.browserWindowInstance.webContents});
// Then
- expect(mockBrowserView.webContents.loadURL).toHaveBeenCalledTimes(1);
- expect(mockBrowserView.webContents.loadURL).toHaveBeenCalledWith(expect.stringMatching(/.+?\/index.html$/));
- expect(mockBrowserView.webContents.on).toHaveBeenCalledWith('will-navigate', expect.any(Function));
+ expect(electron.browserViewInstance.webContents.loadURL).toHaveBeenCalledTimes(1);
+ expect(electron.browserViewInstance.webContents.loadURL)
+ .toHaveBeenCalledWith(expect.stringMatching(/.+?\/index.html$/)); // NOSONAR
+ expect(electron.browserViewInstance.webContents.on).toHaveBeenCalledWith('will-navigate', expect.any(Function));
});
});
});
diff --git a/src/help/__tests__/preload.test.js b/src/help/__tests__/preload.test.js
index 9df51158..14d2b66d 100644
--- a/src/help/__tests__/preload.test.js
+++ b/src/help/__tests__/preload.test.js
@@ -17,10 +17,7 @@ describe('Help Module preload test suite', () => {
let electron;
beforeEach(() => {
jest.resetModules();
- jest.mock('electron', () => ({
- contextBridge: {exposeInMainWorld: jest.fn()},
- ipcRenderer: {send: jest.fn()}
- }));
+ jest.mock('electron', () => require('../../__tests__').mockElectronInstance());
electron = require('electron');
window.APP_EVENTS = require('../../constants').APP_EVENTS;
});
diff --git a/src/help/index.js b/src/help/index.js
index 43fe7c1f..b37e22c1 100644
--- a/src/help/index.js
+++ b/src/help/index.js
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
-const {BrowserView} = require('electron');
+const {BrowserView, BrowserWindow} = require('electron');
const path = require('path');
const {handleRedirect} = require('../tab-manager/redirect');
const {showDialog} = require('../browser-window');
@@ -25,13 +25,13 @@ const webPreferences = {
preload: path.resolve(__dirname, '..', '..', 'bundles', 'help.preload.js')
};
-const openHelpDialog = mainWindow => () => {
+const openHelpDialog = event => {
const helpView = new BrowserView({webPreferences});
helpView.webContents.loadURL(`file://${__dirname}/index.html`);
const handleRedirectForCurrentUrl = handleRedirect(helpView);
helpView.webContents.on('will-navigate', handleRedirectForCurrentUrl);
helpView.webContents.on('new-window', handleRedirectForCurrentUrl);
- showDialog(mainWindow, helpView);
+ showDialog(BrowserWindow.fromWebContents(event.sender), helpView);
};
module.exports = {openHelpDialog};
diff --git a/src/main/__tests__/global-listeners.test.js b/src/main/__tests__/global-listeners.test.js
index 546edc99..e35cbd93 100644
--- a/src/main/__tests__/global-listeners.test.js
+++ b/src/main/__tests__/global-listeners.test.js
@@ -50,13 +50,25 @@ describe('Main :: Global listeners module test suite', () => {
x: 0, y: 0
}));
});
- test('appMenuClose, should hide app-menu', () => {
- // When
- eventBus.listeners.appMenuClose();
- // Then
- expect(browserWindow.removeBrowserView).toHaveBeenCalledWith(
- expect.objectContaining({isAppMenu: true})
- );
+ describe('appMenuClose', () => {
+ test('with menu hidden, should return', () => {
+ // Given
+ browserWindow.getBrowserViews = jest.fn(() => []);
+ // When
+ eventBus.listeners.appMenuClose();
+ // Then
+ expect(browserWindow.removeBrowserView).not.toHaveBeenCalled();
+ });
+ test('with menu visible, should hide app-menu', () => {
+ // Given
+ browserWindow.getBrowserViews = jest.fn(() => [{isAppMenu: true}]);
+ // When
+ eventBus.listeners.appMenuClose();
+ // Then
+ expect(browserWindow.removeBrowserView).toHaveBeenCalledWith(
+ expect.objectContaining({isAppMenu: true})
+ );
+ });
});
describe('closeDialog', () => {
describe('with dialog visible (<= 1 view)', () => {
@@ -121,7 +133,7 @@ describe('Main :: Global listeners module test suite', () => {
});
test('helpOpenDialog, should open help dialog', () => {
// When
- eventBus.listeners.helpOpenDialog();
+ eventBus.listeners.helpOpenDialog({sender: browserWindow.webContents});
// Then
const browserView = electron.BrowserView.mock.results
.map(r => r.value).filter(bv => bv.webContents.loadedUrl.endsWith('/help/index.html'))[0];
diff --git a/src/main/index.js b/src/main/index.js
index 4ca520f0..43a1e59d 100644
--- a/src/main/index.js
+++ b/src/main/index.js
@@ -16,6 +16,7 @@
const {BrowserWindow, Notification, app, desktopCapturer, ipcMain: eventBus, nativeTheme} = require('electron');
const {registerGlobalShortcuts} = require('./keyboard-shortcuts');
const {APP_EVENTS} = require('../constants');
+const {openAboutDialog} = require('../about');
const {newAppMenu, isNotAppMenu} = require('../app-menu');
const {TABS_CONTAINER_HEIGHT, newTabContainer, isNotTabContainer} = require('../chrome-tabs');
const {openHelpDialog} = require('../help');
@@ -164,6 +165,9 @@ const appMenuOpen = () => {
};
const appMenuClose = () => {
+ if (!mainWindow.getBrowserViews().find(bv => bv.isAppMenu)) {
+ return;
+ }
mainWindow.removeBrowserView(appMenu);
activateTab(tabManager.getActiveTab());
};
@@ -194,6 +198,7 @@ const saveSettings = (_event, settings) => {
};
const initGlobalListeners = () => {
+ eventBus.on(APP_EVENTS.aboutOpenDialog, openAboutDialog);
eventBus.on(APP_EVENTS.appMenuOpen, appMenuOpen);
eventBus.on(APP_EVENTS.appMenuClose, appMenuClose);
eventBus.on(APP_EVENTS.closeDialog, closeDialog);
@@ -201,7 +206,7 @@ const initGlobalListeners = () => {
eventBus.handle(APP_EVENTS.dictionaryGetAvailableNative, getAvailableNativeDictionaries);
eventBus.handle(APP_EVENTS.dictionaryGetEnabled, getEnabledDictionaries);
eventBus.on(APP_EVENTS.fullscreenToggle, fullscreenToggle);
- eventBus.on(APP_EVENTS.helpOpenDialog, openHelpDialog(mainWindow));
+ eventBus.on(APP_EVENTS.helpOpenDialog, openHelpDialog);
eventBus.handle(APP_EVENTS.settingsLoad, loadSettings);
eventBus.on(APP_EVENTS.settingsOpenDialog, openSettingsDialog(mainWindow));
eventBus.on(APP_EVENTS.settingsSave, saveSettings);
diff --git a/src/settings/__tests__/index.html.test.mjs b/src/settings/__tests__/index.html.test.mjs
new file mode 100644
index 00000000..0c26585e
--- /dev/null
+++ b/src/settings/__tests__/index.html.test.mjs
@@ -0,0 +1,31 @@
+/*
+ Copyright 2022 Marc Nuri San Felix
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+import {jest} from '@jest/globals';
+import {loadDOM} from '../../__tests__/index.mjs';
+import {ipcRenderer} from './settings.browser.mjs';
+
+describe('Settings index.html test suite', () => {
+ beforeEach(async () => {
+ jest.resetModules();
+ window.ipcRenderer = ipcRenderer();
+ await loadDOM({meta: import.meta, path: ['..', 'index.html']});
+ });
+ test('loads required styles', () => {
+ expect(Array.from(document.querySelectorAll('link[rel=stylesheet]'))
+ .map(link => link.getAttribute('href')))
+ .toEqual(['./settings.browser.css']);
+ });
+});
diff --git a/src/settings/__tests__/preload.test.js b/src/settings/__tests__/preload.test.js
index 75848635..28e981e8 100644
--- a/src/settings/__tests__/preload.test.js
+++ b/src/settings/__tests__/preload.test.js
@@ -13,17 +13,15 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
-const {waitFor} = require('@testing-library/dom');
describe('Settings Module preload test suite', () => {
beforeEach(() => {
jest.resetModules();
+ jest.mock('electron', () => require('../../__tests__').mockElectronInstance({
+ ipcRenderer: 'the-ipc-renderer'
+ }));
});
describe('preload (just for coverage and sanity, see bundle tests)', () => {
beforeEach(() => {
- jest.mock('../settings.browser.css', () => {});
- jest.mock('electron', () => ({
- ipcRenderer: 'the-ipc-renderer'
- }));
require('../preload');
});
test('adds required variables', () => {
@@ -34,19 +32,8 @@ describe('Settings Module preload test suite', () => {
beforeEach(() => {
require('../../../bundles/settings.preload');
});
- test('loads styles in order', async () => {
- // When
- document.body.append(document.createElement('div'));
- // Then
- await waitFor(() => expect(document.head.children.length).toBeGreaterThan(0));
- const styles = Array.from(document.querySelectorAll('style'));
- expect(styles).toHaveLength(10);
- expect(styles[1].innerHTML).toMatch(/:root \{.+--color-accent-fg:/s); // Variables
- expect(styles[2].innerHTML).toContain('html.electronim,'); // Base
- expect(styles[3].innerHTML).toContain('.electronim h1,'); // Typography
- expect(styles[5].innerHTML).toContain('.electronim .control .checkbox {'); // CheckBox
- expect(styles[6].innerHTML).toContain('.electronim .top-bar.navbar {'); // NavBar
- expect(styles[9].innerHTML).toContain('.settings.container {'); // Settings-specific
+ test('adds required variables', () => {
+ expect(window.ipcRenderer).toEqual('the-ipc-renderer');
});
});
});
diff --git a/src/settings/index.html b/src/settings/index.html
index 93bea7f1..b42e572e 100644
--- a/src/settings/index.html
+++ b/src/settings/index.html
@@ -20,12 +20,13 @@
ElectronIM Settings
+
+
-