const test = require('node:test'); const assert = require('node:assert/strict'); const { EventEmitter } = require('node:events'); const fs = require('node:fs'); const path = require('node:path'); const { configureStartupRenderer, createStartupWindow, resolveStartupLanguage, createStartupQuery } = require('../lib/startup-renderer'); class TestBrowserWindow extends EventEmitter { constructor(options) { super(); this.webContents = new EventEmitter(); this.options = options; this.showCalls = 0; this.startupEvents = []; this.loadError = new Error('renderer load failed'); this.loadOptions = null; } once(eventName, listener) { this.startupEvents.push(`listen:${eventName}`); return super.once(eventName, listener); } show() { this.showCalls++; } loadFile(target, options) { this.startupEvents.push(`load:${target}`); this.loadOptions = options; return this.loadError ? Promise.reject(this.loadError) : Promise.resolve(); } } test('configureStartupRenderer leaves hardware acceleration enabled for a local Windows session', () => { let calls = 0; configureStartupRenderer({ disableHardwareAcceleration() { calls++; } }, { SESSIONNAME: 'Console' }, 'win32'); assert.equal(calls, 0); }); test('configureStartupRenderer disables hardware acceleration for a Windows Remote Desktop session', () => { let calls = 0; configureStartupRenderer({ disableHardwareAcceleration() { calls++; } }, { SESSIONNAME: 'RDP-Tcp#12' }, 'win32'); assert.equal(calls, 1); }); test('resolveStartupLanguage accepts only the supported persisted language', () => { assert.equal(resolveStartupLanguage({ globalSettings: { language: 'de' } }), 'de'); assert.equal(resolveStartupLanguage({ globalSettings: { language: 'en' } }), 'en'); assert.equal(resolveStartupLanguage({ globalSettings: { language: 'fr' } }), 'en'); assert.equal(resolveStartupLanguage(null), 'en'); }); test('startup query carries language and installed version into the first renderer frame', () => { assert.deepEqual(createStartupQuery({ globalSettings: { language: 'de' } }, '2.1.25'), { language: 'de', version: '2.1.25' }); assert.deepEqual(createStartupQuery(null, 'invalid'), { language: 'en', version: '' }); }); 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('main window uses the branded application icon', () => { const projectRoot = path.join(__dirname, '..'); const mainSource = fs.readFileSync(path.join(projectRoot, 'main.js'), 'utf8'); const createWindowStart = mainSource.indexOf('function createWindow()'); const createWindowEnd = mainSource.indexOf('\nfunction createTray()', createWindowStart); const createWindowSource = mainSource.slice(createWindowStart, createWindowEnd); assert.equal(fs.existsSync(path.join(projectRoot, 'assets', 'app_icon.ico')), true); assert.match(createWindowSource, /icon:\s*path\.join\(__dirname, ['"]assets['"], ['"]app_icon\.ico['"]\)/u); }); test('startup load registers visibility before navigation and shows only once', async () => { const startup = createStartupWindow(TestBrowserWindow, {}); startup.window.loadError = null; 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'); assert.equal(startup.window.showCalls, 0); startup.window.webContents.emit('did-finish-load'); startup.window.emit('ready-to-show'); startup.window.webContents.emit('did-finish-load'); await loading; assert.equal(startup.window.showCalls, 1); }); test('startup waits for native paint readiness when renderer loading finishes first', () => { const startup = createStartupWindow(TestBrowserWindow, {}); startup.window.webContents.emit('did-finish-load'); assert.equal(startup.window.showCalls, 0); startup.window.emit('ready-to-show'); 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); }); test('startup load forwards navigation options before the renderer becomes visible', async () => { const startup = createStartupWindow(TestBrowserWindow, {}); const options = { query: { language: 'de' } }; await startup.load('renderer/index.html', () => {}, options); assert.deepEqual(startup.window.loadOptions, options); }); test('desktop drag and drop is accepted before asynchronous renderer initialization', () => { const projectRoot = path.join(__dirname, '..'); const appSource = fs.readFileSync(path.join(projectRoot, 'renderer', 'app.js'), 'utf8'); const earlyBinding = appSource.lastIndexOf('\nsetupDragDrop();'); const initialization = appSource.lastIndexOf('\ninit().then('); assert.notEqual(earlyBinding, -1); assert.notEqual(initialization, -1); assert.ok(earlyBinding < initialization); assert.match(appSource, /dataTransfer\.dropEffect\s*=\s*['"]copy['"]/u); }); test('upload sidebar renders and updates the remaining upload size', () => { const projectRoot = path.join(__dirname, '..'); const html = fs.readFileSync(path.join(projectRoot, 'renderer', 'index.html'), 'utf8'); const appSource = fs.readFileSync(path.join(projectRoot, 'renderer', 'app.js'), 'utf8'); assert.match(html, /Verbleibende Größe[\s\S]*id="uploadTelemetryRemainingSize"[^>]*>0 B { const projectRoot = path.join(__dirname, '..'); const html = fs.readFileSync(path.join(projectRoot, 'renderer', 'index.html'), 'utf8'); const appSource = fs.readFileSync(path.join(projectRoot, 'renderer', 'app.js'), 'utf8'); const mainSource = fs.readFileSync(path.join(projectRoot, 'main.js'), 'utf8'); const css = fs.readFileSync(path.join(projectRoot, 'renderer', 'styles.css'), 'utf8'); const updateButton = html.match(/