eigent/test/unit/electron/main/windowLifecycle.test.ts
Tong Chen af93bb3065
feat: Add Lint & Format (#878)
Co-authored-by: a7m-1st <Ahmed.jimi.awelkeir500@gmail.com>
Co-authored-by: eigent-ai <camel@eigent.ai>
Co-authored-by: Wendong-Fan <133094783+Wendong-Fan@users.noreply.github.com>
Co-authored-by: Wendong-Fan <w3ndong.fan@gmail.com>
2026-02-01 23:16:18 +08:00

443 lines
14 KiB
TypeScript

// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
// 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.
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
/**
* Tests for window event setup and lifecycle management in createWindow function
* Covers dev tools shortcuts, external link handling, before close handling,
* auto-update integration, webview manager, and file reader initialization
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { setupMockEnvironment } from '../../../mocks/environmentMocks';
describe('createWindow - Window Event Setup and Lifecycle', () => {
let mockEnv: ReturnType<typeof setupMockEnvironment>;
let mockWebContents: any;
let mockWindow: any;
let mockFileReader: any;
let mockWebViewManager: any;
let mockUpdate: any;
let mockMenu: any;
beforeEach(() => {
mockEnv = setupMockEnvironment();
// Mock webContents
mockWebContents = {
on: vi.fn(),
once: vi.fn(),
executeJavaScript: vi.fn(),
send: vi.fn(),
loadURL: vi.fn(),
loadFile: vi.fn(),
openDevTools: vi.fn(),
toggleDevTools: vi.fn(),
};
// Mock window
mockWindow = {
webContents: mockWebContents,
reload: vi.fn(),
};
// Mock FileReader class
mockFileReader = vi.fn();
// Mock WebViewManager class
mockWebViewManager = vi.fn().mockImplementation(() => ({
createWebview: vi.fn(),
}));
// Mock update function
mockUpdate = vi.fn();
// Mock Menu
mockMenu = {
setApplicationMenu: vi.fn(),
};
// Reset all mocks
vi.clearAllMocks();
});
afterEach(() => {
mockEnv.reset();
});
describe('FileReader and WebViewManager Initialization', () => {
it.skip('should create 8 webviews with correct IDs', () => {
const _webViewManager = new mockWebViewManager(mockWindow);
const instance = mockWebViewManager.mock.instances[0];
// Simulate the loop that creates webviews
for (let i = 1; i <= 8; i++) {
instance.createWebview(i === 1 ? undefined : i.toString());
}
expect(instance.createWebview).toHaveBeenCalledTimes(8);
expect(instance.createWebview).toHaveBeenNthCalledWith(1, undefined);
expect(instance.createWebview).toHaveBeenNthCalledWith(2, '2');
expect(instance.createWebview).toHaveBeenNthCalledWith(3, '3');
expect(instance.createWebview).toHaveBeenNthCalledWith(4, '4');
expect(instance.createWebview).toHaveBeenNthCalledWith(5, '5');
expect(instance.createWebview).toHaveBeenNthCalledWith(6, '6');
expect(instance.createWebview).toHaveBeenNthCalledWith(7, '7');
expect(instance.createWebview).toHaveBeenNthCalledWith(8, '8');
});
});
describe('Window Event Listeners Setup', () => {
it('should disable application menu', () => {
// Simulate setupWindowEventListeners
mockMenu.setApplicationMenu(null);
expect(mockMenu.setApplicationMenu).toHaveBeenCalledWith(null);
});
it('should set up application menu only once', () => {
// Simulate multiple calls to setupWindowEventListeners
mockMenu.setApplicationMenu(null);
mockMenu.setApplicationMenu(null);
expect(mockMenu.setApplicationMenu).toHaveBeenCalledTimes(2);
expect(mockMenu.setApplicationMenu).toHaveBeenCalledWith(null);
});
});
describe('DevTools Shortcuts Setup', () => {
it('should set up before-input-event listener for dev tools shortcuts', () => {
// Simulate setupDevToolsShortcuts
mockWebContents.on('before-input-event', expect.any(Function));
expect(mockWebContents.on).toHaveBeenCalledWith(
'before-input-event',
expect.any(Function)
);
});
it('should handle F12 key to toggle dev tools', () => {
let beforeInputCallback: any;
mockWebContents.on.mockImplementation((event: string, callback: any) => {
if (event === 'before-input-event') {
beforeInputCallback = callback;
}
});
// Simulate setupDevToolsShortcuts
mockWebContents.on('before-input-event', (event: any, input: any) => {
if (input.key === 'F12' && input.type === 'keyDown') {
mockWebContents.toggleDevTools();
}
});
// Trigger F12 key
if (beforeInputCallback) {
const mockEvent = { preventDefault: vi.fn() };
const mockInput = { key: 'F12', type: 'keyDown' };
beforeInputCallback(mockEvent, mockInput);
}
expect(mockWebContents.toggleDevTools).toHaveBeenCalled();
});
it('should handle Ctrl+Shift+I to toggle dev tools on Windows/Linux', () => {
let beforeInputCallback: any;
mockWebContents.on.mockImplementation((event: string, callback: any) => {
if (event === 'before-input-event') {
beforeInputCallback = callback;
}
});
// Simulate setupDevToolsShortcuts
mockWebContents.on('before-input-event', (event: any, input: any) => {
if (
input.control &&
input.shift &&
input.key.toLowerCase() === 'i' &&
input.type === 'keyDown'
) {
mockWebContents.toggleDevTools();
}
});
// Trigger Ctrl+Shift+I
if (beforeInputCallback) {
const mockEvent = { preventDefault: vi.fn() };
const mockInput = {
control: true,
shift: true,
key: 'I',
type: 'keyDown',
};
beforeInputCallback(mockEvent, mockInput);
}
expect(mockWebContents.toggleDevTools).toHaveBeenCalled();
});
it('should handle Cmd+Shift+I to toggle dev tools on Mac', () => {
let beforeInputCallback: any;
mockWebContents.on.mockImplementation((event: string, callback: any) => {
if (event === 'before-input-event') {
beforeInputCallback = callback;
}
});
// Simulate setupDevToolsShortcuts
mockWebContents.on('before-input-event', (event: any, input: any) => {
if (
input.meta &&
input.shift &&
input.key.toLowerCase() === 'i' &&
input.type === 'keyDown'
) {
mockWebContents.toggleDevTools();
}
});
// Trigger Cmd+Shift+I
if (beforeInputCallback) {
const mockEvent = { preventDefault: vi.fn() };
const mockInput = {
meta: true,
shift: true,
key: 'I',
type: 'keyDown',
};
beforeInputCallback(mockEvent, mockInput);
}
expect(mockWebContents.toggleDevTools).toHaveBeenCalled();
});
it('should not trigger dev tools on key up events', () => {
let beforeInputCallback: any;
mockWebContents.on.mockImplementation((event: string, callback: any) => {
if (event === 'before-input-event') {
beforeInputCallback = callback;
}
});
// Simulate setupDevToolsShortcuts
mockWebContents.on('before-input-event', (event: any, input: any) => {
if (input.key === 'F12' && input.type === 'keyDown') {
mockWebContents.toggleDevTools();
}
});
// Trigger F12 key up (should not toggle)
if (beforeInputCallback) {
const mockEvent = { preventDefault: vi.fn() };
const mockInput = { key: 'F12', type: 'keyUp' };
beforeInputCallback(mockEvent, mockInput);
}
expect(mockWebContents.toggleDevTools).not.toHaveBeenCalled();
});
it('should not trigger dev tools on wrong key combinations', () => {
let beforeInputCallback: any;
mockWebContents.on.mockImplementation((event: string, callback: any) => {
if (event === 'before-input-event') {
beforeInputCallback = callback;
}
});
// Simulate setupDevToolsShortcuts
mockWebContents.on('before-input-event', (event: any, input: any) => {
if (
input.control &&
input.shift &&
input.key.toLowerCase() === 'i' &&
input.type === 'keyDown'
) {
mockWebContents.toggleDevTools();
}
});
// Trigger wrong combination (Ctrl+I without Shift)
if (beforeInputCallback) {
const mockEvent = { preventDefault: vi.fn() };
const mockInput = {
control: true,
shift: false,
key: 'I',
type: 'keyDown',
};
beforeInputCallback(mockEvent, mockInput);
}
expect(mockWebContents.toggleDevTools).not.toHaveBeenCalled();
});
});
describe('Auto-Update Integration', () => {
it('should call update function with window reference', () => {
// Simulate auto-update setup
mockUpdate(mockWindow);
expect(mockUpdate).toHaveBeenCalledWith(mockWindow);
});
it('should call update function only once', () => {
// Simulate auto-update setup
mockUpdate(mockWindow);
expect(mockUpdate).toHaveBeenCalledTimes(1);
});
});
describe('Event Handler Organization', () => {
it('should set up event handlers in correct order', () => {
const eventSetupOrder: string[] = [];
// Mock all the setup functions to track order
const setupWindowEventListeners = () => {
eventSetupOrder.push('windowEventListeners');
mockMenu.setApplicationMenu(null);
};
const setupDevToolsShortcuts = () => {
eventSetupOrder.push('devToolsShortcuts');
mockWebContents.on('before-input-event', vi.fn());
};
const setupExternalLinkHandling = () => {
eventSetupOrder.push('externalLinkHandling');
};
const handleBeforeClose = () => {
eventSetupOrder.push('beforeClose');
};
// Simulate the order in createWindow
setupWindowEventListeners();
setupDevToolsShortcuts();
setupExternalLinkHandling();
handleBeforeClose();
expect(eventSetupOrder).toEqual([
'windowEventListeners',
'devToolsShortcuts',
'externalLinkHandling',
'beforeClose',
]);
});
});
describe('Window State Management', () => {
it('should handle window ready state correctly', async () => {
let didFinishLoadCallback: (() => void) | undefined;
// Mock the did-finish-load event listener
mockWebContents.once.mockImplementation(
(event: string, callback: () => void) => {
if (event === 'did-finish-load') {
didFinishLoadCallback = callback;
}
}
);
// Simulate waiting for window ready
const windowReadyPromise = new Promise<void>((resolve) => {
mockWebContents.once('did-finish-load', () => {
resolve();
});
});
// Trigger the event
if (didFinishLoadCallback) {
didFinishLoadCallback();
}
// Should resolve without throwing
await expect(windowReadyPromise).resolves.toBeUndefined();
});
it('should log appropriate messages during window setup', () => {
// In a real test, you would verify that appropriate log messages are called
// This ensures the window setup process is properly logged
const mockLog = {
info: vi.fn(),
error: vi.fn(),
};
// Simulate logging calls that would happen during window setup
mockLog.info(
'Window content loaded, starting dependency check immediately...'
);
mockLog.info('.eigent directory structure ensured');
expect(mockLog.info).toHaveBeenCalledWith(
'Window content loaded, starting dependency check immediately...'
);
expect(mockLog.info).toHaveBeenCalledWith(
'.eigent directory structure ensured'
);
});
});
describe('Integration Points', () => {
it('should properly coordinate between file reader and webview manager', () => {
const _fileReader = new mockFileReader(mockWindow);
const _webViewManager = new mockWebViewManager(mockWindow);
// Both should be initialized with the same window
expect(mockFileReader).toHaveBeenCalledWith(mockWindow);
expect(mockWebViewManager).toHaveBeenCalledWith(mockWindow);
});
it('should handle window initialization errors gracefully', () => {
// Mock FileReader to throw during initialization
mockFileReader.mockImplementation(() => {
throw new Error('FileReader initialization failed');
});
// Should handle gracefully in real implementation
expect(() => {
try {
new mockFileReader(mockWindow);
} catch (error) {
// Log error but don't stop execution
console.error('FileReader initialization error:', error);
}
}).not.toThrow();
});
});
describe('Memory Management', () => {
it('should properly clean up event listeners when window is destroyed', () => {
// In a real scenario, you would test that event listeners are removed
// when the window is closed to prevent memory leaks
const mockRemoveListener = vi.fn();
mockWebContents.removeListener = mockRemoveListener;
// Simulate cleanup
const cleanup = () => {
mockWebContents.removeListener('before-input-event', vi.fn());
mockWebContents.removeListener('dom-ready', vi.fn());
};
cleanup();
expect(mockRemoveListener).toHaveBeenCalledTimes(2);
});
});
});