Files
Toju/electron/idle/idle-monitor.spec.ts
2026-06-05 18:34:01 +02:00

136 lines
3.2 KiB
TypeScript

import {
describe,
it,
expect,
vi,
beforeEach,
afterEach
} from 'vitest';
const {
mockGetSystemIdleTime,
mockSend,
mockGetMainWindow
} = vi.hoisted(() => {
const send = vi.fn();
const getSystemIdleTime = vi.fn(() => 0);
const getMainWindow = vi.fn(() => ({
isDestroyed: () => false,
webContents: { send }
}));
return {
mockGetSystemIdleTime: getSystemIdleTime,
mockSend: send,
mockGetMainWindow: getMainWindow
};
});
vi.mock('electron', () => ({
powerMonitor: {
getSystemIdleTime: mockGetSystemIdleTime
}
}));
vi.mock('../window/create-window', () => ({
getMainWindow: mockGetMainWindow
}));
import {
startIdleMonitor,
stopIdleMonitor,
getIdleState
} from './idle-monitor';
describe('idle-monitor', () => {
beforeEach(() => {
vi.useFakeTimers();
mockGetSystemIdleTime.mockReturnValue(0);
mockSend.mockClear();
});
afterEach(() => {
stopIdleMonitor();
vi.useRealTimers();
});
it('returns active when idle time is below threshold', () => {
mockGetSystemIdleTime.mockReturnValue(0);
expect(getIdleState()).toBe('active');
});
it('returns idle when idle time exceeds 15 minutes', () => {
mockGetSystemIdleTime.mockReturnValue(15 * 60);
expect(getIdleState()).toBe('idle');
});
it('sends idle-state-changed to renderer when transitioning to idle', () => {
startIdleMonitor();
mockGetSystemIdleTime.mockReturnValue(15 * 60);
vi.advanceTimersByTime(10_000);
expect(mockSend).toHaveBeenCalledWith('idle-state-changed', 'idle');
});
it('sends idle-state-changed to renderer when transitioning back to active', () => {
startIdleMonitor();
// Go idle
mockGetSystemIdleTime.mockReturnValue(15 * 60);
vi.advanceTimersByTime(10_000);
mockSend.mockClear();
// Go active
mockGetSystemIdleTime.mockReturnValue(5);
vi.advanceTimersByTime(10_000);
expect(mockSend).toHaveBeenCalledWith('idle-state-changed', 'active');
});
it('does not fire duplicates when state stays the same', () => {
startIdleMonitor();
mockGetSystemIdleTime.mockReturnValue(15 * 60);
vi.advanceTimersByTime(10_000);
vi.advanceTimersByTime(10_000);
vi.advanceTimersByTime(10_000);
// Only one transition, so only one call
const idleCalls = mockSend.mock.calls.filter(
([channel, state]: [string, string]) => channel === 'idle-state-changed' && state === 'idle'
);
expect(idleCalls.length).toBe(1);
});
it('stops polling after stopIdleMonitor', () => {
startIdleMonitor();
mockGetSystemIdleTime.mockReturnValue(15 * 60);
vi.advanceTimersByTime(10_000);
mockSend.mockClear();
stopIdleMonitor();
mockGetSystemIdleTime.mockReturnValue(0);
vi.advanceTimersByTime(10_000);
expect(mockSend).not.toHaveBeenCalled();
});
it('does not notify when main window is null', () => {
mockGetMainWindow.mockReturnValue(null);
startIdleMonitor();
mockGetSystemIdleTime.mockReturnValue(15 * 60);
vi.advanceTimersByTime(10_000);
expect(mockSend).not.toHaveBeenCalled();
mockGetMainWindow.mockReturnValue({
isDestroyed: () => false,
webContents: { send: mockSend }
});
});
});