test(startup): cover window initialization lifecycle
Extract the startup window boundary so hidden creation, ready-to-show ordering, single display, and load rejection handling are verified without Electron UI timing.
This commit is contained in:
parent
cfeb01e82a
commit
b075961802
@ -2,4 +2,18 @@ function configureStartupRenderer(app) {
|
|||||||
app.disableHardwareAcceleration();
|
app.disableHardwareAcceleration();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { configureStartupRenderer };
|
function createStartupWindow(BrowserWindow, options) {
|
||||||
|
const window = new BrowserWindow({ ...options, show: false });
|
||||||
|
window.once('ready-to-show', () => {
|
||||||
|
window.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
window,
|
||||||
|
load(target, onLoadError) {
|
||||||
|
return window.loadFile(target).catch(onLoadError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { configureStartupRenderer, createStartupWindow };
|
||||||
|
|||||||
11
main.js
11
main.js
@ -1,7 +1,7 @@
|
|||||||
process.env.UV_THREADPOOL_SIZE = process.env.UV_THREADPOOL_SIZE || '8';
|
process.env.UV_THREADPOOL_SIZE = process.env.UV_THREADPOOL_SIZE || '8';
|
||||||
const { monitorEventLoopDelay, PerformanceObserver } = require('perf_hooks');
|
const { monitorEventLoopDelay, PerformanceObserver } = require('perf_hooks');
|
||||||
const { app, BrowserWindow, ipcMain, dialog, clipboard, nativeTheme, Tray, Menu, nativeImage } = require('electron');
|
const { app, BrowserWindow, ipcMain, dialog, clipboard, nativeTheme, Tray, Menu, nativeImage } = require('electron');
|
||||||
const { configureStartupRenderer } = require('./lib/startup-renderer');
|
const { configureStartupRenderer, createStartupWindow } = require('./lib/startup-renderer');
|
||||||
configureStartupRenderer(app);
|
configureStartupRenderer(app);
|
||||||
nativeTheme.themeSource = 'dark';
|
nativeTheme.themeSource = 'dark';
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
@ -1222,13 +1222,12 @@ async function runHosterHealthCheck(config, requestedChecks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
mainWindow = new BrowserWindow({
|
const startupWindow = createStartupWindow(BrowserWindow, {
|
||||||
width: 1100,
|
width: 1100,
|
||||||
height: 750,
|
height: 750,
|
||||||
minWidth: 800,
|
minWidth: 800,
|
||||||
minHeight: 550,
|
minHeight: 550,
|
||||||
backgroundColor: '#16181c',
|
backgroundColor: '#16181c',
|
||||||
show: false,
|
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
@ -1236,11 +1235,9 @@ function createWindow() {
|
|||||||
preload: path.join(__dirname, 'preload.js')
|
preload: path.join(__dirname, 'preload.js')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
mainWindow = startupWindow.window;
|
||||||
|
|
||||||
mainWindow.webContents.setBackgroundThrottling(false);
|
mainWindow.webContents.setBackgroundThrottling(false);
|
||||||
mainWindow.once('ready-to-show', () => {
|
|
||||||
mainWindow.show();
|
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.webContents.on('render-process-gone', (_event, details) => {
|
mainWindow.webContents.on('render-process-gone', (_event, details) => {
|
||||||
_writeCrashLog('RENDER PROCESS GONE', new Error(details.reason || 'unknown'), details);
|
_writeCrashLog('RENDER PROCESS GONE', new Error(details.reason || 'unknown'), details);
|
||||||
@ -1286,7 +1283,7 @@ function createWindow() {
|
|||||||
debugLog(`CHILD PROCESS GONE: type=${details.type} reason=${details.reason} exitCode=${details.exitCode}`);
|
debugLog(`CHILD PROCESS GONE: type=${details.type} reason=${details.reason} exitCode=${details.exitCode}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindow.loadFile(path.join(__dirname, 'renderer', 'index.html')).catch((err) => {
|
startupWindow.load(path.join(__dirname, 'renderer', 'index.html'), (err) => {
|
||||||
_writeCrashLog('LOAD FILE FAILED', err);
|
_writeCrashLog('LOAD FILE FAILED', err);
|
||||||
debugLog(`LOAD FILE FAILED: ${err && err.stack ? err.stack : err}`);
|
debugLog(`LOAD FILE FAILED: ${err && err.stack ? err.stack : err}`);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,9 +1,68 @@
|
|||||||
const test = require('node:test');
|
const test = require('node:test');
|
||||||
const assert = require('node:assert/strict');
|
const assert = require('node:assert/strict');
|
||||||
const { configureStartupRenderer } = require('../lib/startup-renderer');
|
const { EventEmitter } = require('node:events');
|
||||||
|
const { configureStartupRenderer, createStartupWindow } = require('../lib/startup-renderer');
|
||||||
|
|
||||||
|
class TestBrowserWindow extends EventEmitter {
|
||||||
|
constructor(options) {
|
||||||
|
super();
|
||||||
|
this.options = options;
|
||||||
|
this.showCalls = 0;
|
||||||
|
this.startupEvents = [];
|
||||||
|
this.loadError = new Error('renderer load failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
once(eventName, listener) {
|
||||||
|
this.startupEvents.push(`listen:${eventName}`);
|
||||||
|
return super.once(eventName, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.showCalls++;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFile(target) {
|
||||||
|
this.startupEvents.push(`load:${target}`);
|
||||||
|
return Promise.reject(this.loadError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test('configureStartupRenderer disables hardware acceleration', () => {
|
test('configureStartupRenderer disables hardware acceleration', () => {
|
||||||
let calls = 0;
|
let calls = 0;
|
||||||
configureStartupRenderer({ disableHardwareAcceleration() { calls++; } });
|
configureStartupRenderer({ disableHardwareAcceleration() { calls++; } });
|
||||||
assert.equal(calls, 1);
|
assert.equal(calls, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('createStartupWindow forces the main window to start hidden', () => {
|
||||||
|
const startup = createStartupWindow(TestBrowserWindow, { width: 1100, show: true });
|
||||||
|
|
||||||
|
assert.equal(startup.window.options.width, 1100);
|
||||||
|
assert.equal(startup.window.options.show, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('startup load registers visibility before navigation and shows only once', async () => {
|
||||||
|
const startup = createStartupWindow(TestBrowserWindow, {});
|
||||||
|
const loading = startup.load('renderer/index.html', () => {});
|
||||||
|
|
||||||
|
assert.deepEqual(startup.window.startupEvents, [
|
||||||
|
'listen:ready-to-show',
|
||||||
|
'load:renderer/index.html'
|
||||||
|
]);
|
||||||
|
|
||||||
|
startup.window.emit('ready-to-show');
|
||||||
|
startup.window.emit('ready-to-show');
|
||||||
|
await loading;
|
||||||
|
|
||||||
|
assert.equal(startup.window.showCalls, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('startup load forwards a rejected navigation to the error handler', async () => {
|
||||||
|
const startup = createStartupWindow(TestBrowserWindow, {});
|
||||||
|
let handledError;
|
||||||
|
|
||||||
|
await startup.load('renderer/index.html', (err) => {
|
||||||
|
handledError = err;
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(handledError, startup.window.loadError);
|
||||||
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user