Skip to content

Commit aa72ae9

Browse files
committed
test: cover EventManager registration and dispose
Sync and async handler registration, dispose unregistering all handlers. Restore ipcMain.removeListener on the electron mock; eventmanager.ts uses it in unregisterAllEventHandlers and the mock had dropped it. Note: AI-assisted (Claude Code). Manually verified: dispose tests failed with "removeListener is not a function" until the mock was restored, then 305/305 green.
1 parent 8f68fb5 commit aa72ae9

2 files changed

Lines changed: 203 additions & 0 deletions

File tree

test/setup/electron-mock.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ vi.mock('electron', () => ({
1717
on: vi.fn(),
1818
handle: vi.fn(),
1919
removeHandler: vi.fn(),
20+
removeListener: vi.fn(),
2021
removeAllListeners: vi.fn(),
2122
emit: vi.fn()
2223
},

test/unit/eventmanager.test.ts

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest';
2+
import { ipcMain } from 'electron';
3+
import { EventManager } from '../../src/main/eventmanager';
4+
import { EventTypeMain } from '../../src/main/eventtypes';
5+
6+
const mockIpcMain = vi.mocked(ipcMain);
7+
8+
beforeEach(() => {
9+
vi.clearAllMocks();
10+
});
11+
12+
describe('EventManager.registerEventHandler', () => {
13+
it('calls ipcMain.on with the event type and handler', () => {
14+
const mgr = new EventManager();
15+
const handler = vi.fn();
16+
mgr.registerEventHandler(EventTypeMain.OpenFile, handler);
17+
expect(mockIpcMain.on).toHaveBeenCalledWith(
18+
EventTypeMain.OpenFile,
19+
handler
20+
);
21+
});
22+
23+
it('registers multiple handlers for same event type', () => {
24+
const mgr = new EventManager();
25+
const h1 = vi.fn();
26+
const h2 = vi.fn();
27+
mgr.registerEventHandler(EventTypeMain.OpenFile, h1);
28+
mgr.registerEventHandler(EventTypeMain.OpenFile, h2);
29+
expect(mockIpcMain.on).toHaveBeenCalledTimes(2);
30+
expect(mockIpcMain.on).toHaveBeenCalledWith(EventTypeMain.OpenFile, h1);
31+
expect(mockIpcMain.on).toHaveBeenCalledWith(EventTypeMain.OpenFile, h2);
32+
});
33+
34+
it('registers handlers for different event types independently', () => {
35+
const mgr = new EventManager();
36+
const h1 = vi.fn();
37+
const h2 = vi.fn();
38+
mgr.registerEventHandler(EventTypeMain.OpenFile, h1);
39+
mgr.registerEventHandler(EventTypeMain.OpenFolder, h2);
40+
expect(mockIpcMain.on).toHaveBeenCalledWith(EventTypeMain.OpenFile, h1);
41+
expect(mockIpcMain.on).toHaveBeenCalledWith(EventTypeMain.OpenFolder, h2);
42+
});
43+
});
44+
45+
describe('EventManager.registerSyncEventHandler', () => {
46+
it('calls ipcMain.handle with the event type and handler', () => {
47+
const mgr = new EventManager();
48+
const handler = vi.fn();
49+
mgr.registerSyncEventHandler(EventTypeMain.IsDarkTheme, handler);
50+
expect(mockIpcMain.handle).toHaveBeenCalledWith(
51+
EventTypeMain.IsDarkTheme,
52+
handler
53+
);
54+
});
55+
56+
it('registers multiple sync handlers for same event', () => {
57+
const mgr = new EventManager();
58+
const h1 = vi.fn();
59+
const h2 = vi.fn();
60+
mgr.registerSyncEventHandler(EventTypeMain.IsDarkTheme, h1);
61+
mgr.registerSyncEventHandler(EventTypeMain.IsDarkTheme, h2);
62+
expect(mockIpcMain.handle).toHaveBeenCalledTimes(2);
63+
});
64+
});
65+
66+
describe('EventManager.unregisterEventHandler', () => {
67+
it('calls ipcMain.removeListener and removes from internal map', () => {
68+
const mgr = new EventManager();
69+
const handler = vi.fn();
70+
mgr.registerEventHandler(EventTypeMain.OpenFile, handler);
71+
vi.clearAllMocks();
72+
mgr.unregisterEventHandler(EventTypeMain.OpenFile, handler);
73+
expect(mockIpcMain.removeListener).toHaveBeenCalledWith(
74+
EventTypeMain.OpenFile,
75+
handler
76+
);
77+
});
78+
79+
it('no-ops when event type was never registered', () => {
80+
const mgr = new EventManager();
81+
const handler = vi.fn();
82+
expect(() =>
83+
mgr.unregisterEventHandler(EventTypeMain.OpenFile, handler)
84+
).not.toThrow();
85+
expect(mockIpcMain.removeListener).not.toHaveBeenCalled();
86+
});
87+
88+
it('no-ops when handler is not in the list', () => {
89+
const mgr = new EventManager();
90+
const h1 = vi.fn();
91+
const h2 = vi.fn();
92+
mgr.registerEventHandler(EventTypeMain.OpenFile, h1);
93+
vi.clearAllMocks();
94+
mgr.unregisterEventHandler(EventTypeMain.OpenFile, h2);
95+
expect(mockIpcMain.removeListener).not.toHaveBeenCalled();
96+
});
97+
98+
it('only removes the specified handler, leaves others intact', () => {
99+
const mgr = new EventManager();
100+
const h1 = vi.fn();
101+
const h2 = vi.fn();
102+
mgr.registerEventHandler(EventTypeMain.OpenFile, h1);
103+
mgr.registerEventHandler(EventTypeMain.OpenFile, h2);
104+
vi.clearAllMocks();
105+
mgr.unregisterEventHandler(EventTypeMain.OpenFile, h1);
106+
expect(mockIpcMain.removeListener).toHaveBeenCalledTimes(1);
107+
expect(mockIpcMain.removeListener).toHaveBeenCalledWith(
108+
EventTypeMain.OpenFile,
109+
h1
110+
);
111+
});
112+
});
113+
114+
describe('EventManager.unregisterSyncEventHandler', () => {
115+
it('calls ipcMain.removeListener for sync handler', () => {
116+
const mgr = new EventManager();
117+
const handler = vi.fn();
118+
mgr.registerSyncEventHandler(EventTypeMain.IsDarkTheme, handler);
119+
vi.clearAllMocks();
120+
mgr.unregisterSyncEventHandler(EventTypeMain.IsDarkTheme, handler);
121+
expect(mockIpcMain.removeListener).toHaveBeenCalledWith(
122+
EventTypeMain.IsDarkTheme,
123+
handler
124+
);
125+
});
126+
127+
it('no-ops when event type was never registered', () => {
128+
const mgr = new EventManager();
129+
expect(() =>
130+
mgr.unregisterSyncEventHandler(EventTypeMain.IsDarkTheme, vi.fn())
131+
).not.toThrow();
132+
expect(mockIpcMain.removeListener).not.toHaveBeenCalled();
133+
});
134+
});
135+
136+
describe('EventManager.unregisterAllEventHandlers', () => {
137+
it('removes all async handlers and calls ipcMain.removeListener for each', () => {
138+
const mgr = new EventManager();
139+
const h1 = vi.fn();
140+
const h2 = vi.fn();
141+
mgr.registerEventHandler(EventTypeMain.OpenFile, h1);
142+
mgr.registerEventHandler(EventTypeMain.OpenFolder, h2);
143+
vi.clearAllMocks();
144+
mgr.unregisterAllEventHandlers();
145+
expect(mockIpcMain.removeListener).toHaveBeenCalledWith(
146+
EventTypeMain.OpenFile,
147+
h1
148+
);
149+
expect(mockIpcMain.removeListener).toHaveBeenCalledWith(
150+
EventTypeMain.OpenFolder,
151+
h2
152+
);
153+
});
154+
155+
it('no-ops when no handlers registered', () => {
156+
const mgr = new EventManager();
157+
expect(() => mgr.unregisterAllEventHandlers()).not.toThrow();
158+
expect(mockIpcMain.removeListener).not.toHaveBeenCalled();
159+
});
160+
});
161+
162+
describe('EventManager.unregisterAllSyncEventHandlers', () => {
163+
it('removes all sync handlers', () => {
164+
const mgr = new EventManager();
165+
const h1 = vi.fn();
166+
const h2 = vi.fn();
167+
mgr.registerSyncEventHandler(EventTypeMain.IsDarkTheme, h1);
168+
mgr.registerSyncEventHandler(EventTypeMain.IsDarkTheme, h2);
169+
vi.clearAllMocks();
170+
mgr.unregisterAllSyncEventHandlers();
171+
expect(mockIpcMain.removeListener).toHaveBeenCalledTimes(2);
172+
});
173+
});
174+
175+
describe('EventManager.dispose', () => {
176+
it('removes all async and sync handlers and returns a resolved Promise', async () => {
177+
const mgr = new EventManager();
178+
const async1 = vi.fn();
179+
const sync1 = vi.fn();
180+
mgr.registerEventHandler(EventTypeMain.OpenFile, async1);
181+
mgr.registerSyncEventHandler(EventTypeMain.IsDarkTheme, sync1);
182+
vi.clearAllMocks();
183+
await expect(mgr.dispose()).resolves.toBeUndefined();
184+
expect(mockIpcMain.removeListener).toHaveBeenCalledWith(
185+
EventTypeMain.OpenFile,
186+
async1
187+
);
188+
expect(mockIpcMain.removeListener).toHaveBeenCalledWith(
189+
EventTypeMain.IsDarkTheme,
190+
sync1
191+
);
192+
});
193+
194+
it('is safe to call dispose twice', async () => {
195+
const mgr = new EventManager();
196+
mgr.registerEventHandler(EventTypeMain.OpenFile, vi.fn());
197+
await mgr.dispose();
198+
vi.clearAllMocks();
199+
await expect(mgr.dispose()).resolves.toBeUndefined();
200+
expect(mockIpcMain.removeListener).not.toHaveBeenCalled();
201+
});
202+
});

0 commit comments

Comments
 (0)