@lobehub/lobehub 2.0.0-next.142 → 2.0.0-next.143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/apps/desktop/package.json +1 -0
- package/apps/desktop/src/main/core/ui/__tests__/MenuManager.test.ts +320 -0
- package/apps/desktop/src/main/core/ui/__tests__/Tray.test.ts +518 -0
- package/apps/desktop/src/main/core/ui/__tests__/TrayManager.test.ts +360 -0
- package/apps/desktop/src/main/menus/impls/BaseMenuPlatform.test.ts +49 -0
- package/apps/desktop/src/main/menus/impls/linux.test.ts +552 -0
- package/apps/desktop/src/main/menus/impls/macOS.test.ts +464 -0
- package/apps/desktop/src/main/menus/impls/windows.test.ts +429 -0
- package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +2 -2
- package/apps/desktop/src/main/services/__tests__/fileSearchSrv.test.ts +402 -0
- package/apps/desktop/src/main/utils/__tests__/file-system.test.ts +91 -0
- package/apps/desktop/src/main/utils/__tests__/logger.test.ts +229 -0
- package/apps/desktop/src/preload/electronApi.test.ts +142 -0
- package/apps/desktop/src/preload/invoke.test.ts +145 -0
- package/apps/desktop/src/preload/routeInterceptor.test.ts +374 -0
- package/apps/desktop/src/preload/streamer.test.ts +365 -0
- package/apps/desktop/vitest.config.mts +1 -0
- package/changelog/v1.json +9 -0
- package/locales/ar/marketAuth.json +13 -0
- package/locales/bg-BG/marketAuth.json +13 -0
- package/locales/de-DE/marketAuth.json +13 -0
- package/locales/en-US/marketAuth.json +13 -0
- package/locales/es-ES/marketAuth.json +13 -0
- package/locales/fa-IR/marketAuth.json +13 -0
- package/locales/fr-FR/marketAuth.json +13 -0
- package/locales/it-IT/marketAuth.json +13 -0
- package/locales/ja-JP/marketAuth.json +13 -0
- package/locales/ko-KR/marketAuth.json +13 -0
- package/locales/nl-NL/marketAuth.json +13 -0
- package/locales/pl-PL/marketAuth.json +13 -0
- package/locales/pt-BR/marketAuth.json +13 -0
- package/locales/ru-RU/marketAuth.json +13 -0
- package/locales/tr-TR/marketAuth.json +13 -0
- package/locales/vi-VN/marketAuth.json +13 -0
- package/locales/zh-CN/marketAuth.json +13 -0
- package/locales/zh-TW/marketAuth.json +13 -0
- package/package.json +1 -1
- package/packages/database/src/models/user.ts +2 -0
- package/packages/types/src/discover/mcp.ts +2 -1
- package/packages/types/src/tool/plugin.ts +2 -1
- package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishButton.tsx +0 -2
- package/src/app/[variants]/(main)/discover/(detail)/mcp/features/Sidebar/ActionButton/index.tsx +33 -7
- package/src/features/PluginStore/McpList/List/Action.tsx +20 -1
- package/src/layout/AuthProvider/MarketAuth/MarketAuthConfirmModal.tsx +158 -0
- package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +130 -14
- package/src/libs/mcp/types.ts +8 -0
- package/src/locales/default/marketAuth.ts +13 -0
- package/src/server/routers/lambda/market/index.ts +85 -2
- package/src/server/services/discover/index.ts +45 -4
- package/src/services/discover.ts +1 -1
- package/src/services/mcp.ts +18 -3
- package/src/store/tool/slices/mcpStore/action.test.ts +141 -0
- package/src/store/tool/slices/mcpStore/action.ts +153 -11
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { Menu, app, shell } from 'electron';
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import type { App } from '@/core/App';
|
|
5
|
+
|
|
6
|
+
import { MacOSMenu } from './macOS';
|
|
7
|
+
|
|
8
|
+
// Mock Electron modules
|
|
9
|
+
vi.mock('electron', () => ({
|
|
10
|
+
Menu: {
|
|
11
|
+
buildFromTemplate: vi.fn((template) => ({ template })),
|
|
12
|
+
setApplicationMenu: vi.fn(),
|
|
13
|
+
},
|
|
14
|
+
app: {
|
|
15
|
+
getName: vi.fn(() => 'LobeChat'),
|
|
16
|
+
getPath: vi.fn((type: string) => {
|
|
17
|
+
if (type === 'logs') return '/path/to/logs';
|
|
18
|
+
if (type === 'userData') return '/path/to/userData';
|
|
19
|
+
if (type === 'cache') return '/path/to/cache';
|
|
20
|
+
return '/path/to/default';
|
|
21
|
+
}),
|
|
22
|
+
},
|
|
23
|
+
shell: {
|
|
24
|
+
openExternal: vi.fn(),
|
|
25
|
+
openPath: vi.fn(() => Promise.resolve('')),
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
// Mock isDev
|
|
30
|
+
vi.mock('@/const/env', () => ({
|
|
31
|
+
isDev: false,
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
// Mock App instance
|
|
35
|
+
const createMockApp = () => {
|
|
36
|
+
const mockT = vi.fn((key: string, params?: any) => {
|
|
37
|
+
const translations: Record<string, string> = {
|
|
38
|
+
'macOS.about': `About ${params?.appName || 'App'}`,
|
|
39
|
+
'common.checkUpdates': 'Check for Updates',
|
|
40
|
+
'macOS.preferences': 'Preferences',
|
|
41
|
+
'macOS.services': 'Services',
|
|
42
|
+
'macOS.hide': `Hide ${params?.appName || 'App'}`,
|
|
43
|
+
'macOS.hideOthers': 'Hide Others',
|
|
44
|
+
'macOS.unhide': 'Show All',
|
|
45
|
+
'file.quit': 'Quit',
|
|
46
|
+
'file.title': 'File',
|
|
47
|
+
'file.preferences': 'Preferences',
|
|
48
|
+
'window.close': 'Close Window',
|
|
49
|
+
'window.title': 'Window',
|
|
50
|
+
'window.minimize': 'Minimize',
|
|
51
|
+
'edit.title': 'Edit',
|
|
52
|
+
'edit.undo': 'Undo',
|
|
53
|
+
'edit.redo': 'Redo',
|
|
54
|
+
'edit.cut': 'Cut',
|
|
55
|
+
'edit.copy': 'Copy',
|
|
56
|
+
'edit.paste': 'Paste',
|
|
57
|
+
'edit.selectAll': 'Select All',
|
|
58
|
+
'edit.speech': 'Speech',
|
|
59
|
+
'edit.startSpeaking': 'Start Speaking',
|
|
60
|
+
'edit.stopSpeaking': 'Stop Speaking',
|
|
61
|
+
'edit.delete': 'Delete',
|
|
62
|
+
'view.title': 'View',
|
|
63
|
+
'view.reload': 'Reload',
|
|
64
|
+
'view.forceReload': 'Force Reload',
|
|
65
|
+
'view.resetZoom': 'Actual Size',
|
|
66
|
+
'view.zoomIn': 'Zoom In',
|
|
67
|
+
'view.zoomOut': 'Zoom Out',
|
|
68
|
+
'view.toggleFullscreen': 'Toggle Full Screen',
|
|
69
|
+
'help.title': 'Help',
|
|
70
|
+
'help.visitWebsite': 'Visit Website',
|
|
71
|
+
'help.githubRepo': 'GitHub Repository',
|
|
72
|
+
'help.reportIssue': 'Report Issue',
|
|
73
|
+
'help.about': 'About',
|
|
74
|
+
'dev.title': 'Developer',
|
|
75
|
+
'dev.devPanel': 'Dev Panel',
|
|
76
|
+
'dev.refreshMenu': 'Refresh Menu',
|
|
77
|
+
'dev.devTools': 'Developer Tools',
|
|
78
|
+
'dev.reload': 'Reload',
|
|
79
|
+
'dev.forceReload': 'Force Reload',
|
|
80
|
+
'tray.show': `Show ${params?.appName || 'App'}`,
|
|
81
|
+
'tray.quit': 'Quit',
|
|
82
|
+
};
|
|
83
|
+
return translations[key] || key;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
i18n: {
|
|
88
|
+
ns: vi.fn(() => mockT),
|
|
89
|
+
},
|
|
90
|
+
browserManager: {
|
|
91
|
+
getMainWindow: vi.fn(() => ({
|
|
92
|
+
loadUrl: vi.fn(),
|
|
93
|
+
show: vi.fn(),
|
|
94
|
+
})),
|
|
95
|
+
showMainWindow: vi.fn(),
|
|
96
|
+
retrieveByIdentifier: vi.fn(() => ({
|
|
97
|
+
show: vi.fn(),
|
|
98
|
+
})),
|
|
99
|
+
},
|
|
100
|
+
updaterManager: {
|
|
101
|
+
checkForUpdates: vi.fn(),
|
|
102
|
+
simulateUpdateAvailable: vi.fn(),
|
|
103
|
+
simulateDownloadProgress: vi.fn(),
|
|
104
|
+
simulateUpdateDownloaded: vi.fn(),
|
|
105
|
+
},
|
|
106
|
+
menuManager: {
|
|
107
|
+
rebuildAppMenu: vi.fn(),
|
|
108
|
+
},
|
|
109
|
+
storeManager: {
|
|
110
|
+
openInEditor: vi.fn(),
|
|
111
|
+
},
|
|
112
|
+
} as unknown as App;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
describe('MacOSMenu', () => {
|
|
116
|
+
let macOSMenu: MacOSMenu;
|
|
117
|
+
let mockApp: App;
|
|
118
|
+
|
|
119
|
+
beforeEach(() => {
|
|
120
|
+
vi.clearAllMocks();
|
|
121
|
+
mockApp = createMockApp();
|
|
122
|
+
macOSMenu = new MacOSMenu(mockApp);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('buildAndSetAppMenu', () => {
|
|
126
|
+
it('should build and set application menu', () => {
|
|
127
|
+
const menu = macOSMenu.buildAndSetAppMenu();
|
|
128
|
+
|
|
129
|
+
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
|
130
|
+
expect(Menu.setApplicationMenu).toHaveBeenCalled();
|
|
131
|
+
expect(menu).toBeDefined();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should include developer menu when showDevItems is true', () => {
|
|
135
|
+
const menu = macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
|
136
|
+
|
|
137
|
+
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
|
138
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
139
|
+
const devMenu = template.find((item: any) => item.label === 'Developer');
|
|
140
|
+
expect(devMenu).toBeDefined();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should not include developer menu when showDevItems is false', () => {
|
|
144
|
+
const menu = macOSMenu.buildAndSetAppMenu({ showDevItems: false });
|
|
145
|
+
|
|
146
|
+
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
|
147
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
148
|
+
const devMenu = template.find((item: any) => item.label === 'Developer');
|
|
149
|
+
expect(devMenu).toBeUndefined();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should create menu with correct structure', () => {
|
|
153
|
+
macOSMenu.buildAndSetAppMenu();
|
|
154
|
+
|
|
155
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
156
|
+
expect(template).toBeInstanceOf(Array);
|
|
157
|
+
expect(template.length).toBeGreaterThan(0);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('buildContextMenu', () => {
|
|
162
|
+
it('should build chat context menu', () => {
|
|
163
|
+
const menu = macOSMenu.buildContextMenu('chat');
|
|
164
|
+
|
|
165
|
+
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
|
166
|
+
expect(menu).toBeDefined();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should build editor context menu', () => {
|
|
170
|
+
const menu = macOSMenu.buildContextMenu('editor');
|
|
171
|
+
|
|
172
|
+
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
|
173
|
+
expect(menu).toBeDefined();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should build default context menu for unknown type', () => {
|
|
177
|
+
const menu = macOSMenu.buildContextMenu('unknown');
|
|
178
|
+
|
|
179
|
+
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
|
180
|
+
expect(menu).toBeDefined();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should pass data to chat context menu', () => {
|
|
184
|
+
const data = { messageId: '123' };
|
|
185
|
+
macOSMenu.buildContextMenu('chat', data);
|
|
186
|
+
|
|
187
|
+
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe('buildTrayMenu', () => {
|
|
192
|
+
it('should build tray menu', () => {
|
|
193
|
+
const menu = macOSMenu.buildTrayMenu();
|
|
194
|
+
|
|
195
|
+
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
|
196
|
+
expect(menu).toBeDefined();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should include show and quit items in tray menu', () => {
|
|
200
|
+
macOSMenu.buildTrayMenu();
|
|
201
|
+
|
|
202
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
203
|
+
expect(template.length).toBeGreaterThan(0);
|
|
204
|
+
expect(template.some((item: any) => item.label?.includes('Show'))).toBe(true);
|
|
205
|
+
expect(template.some((item: any) => item.label === 'Quit')).toBe(true);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('refresh', () => {
|
|
210
|
+
it('should rebuild application menu', () => {
|
|
211
|
+
macOSMenu.refresh();
|
|
212
|
+
|
|
213
|
+
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
|
214
|
+
expect(Menu.setApplicationMenu).toHaveBeenCalled();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should pass options to rebuild', () => {
|
|
218
|
+
macOSMenu.refresh({ showDevItems: true });
|
|
219
|
+
|
|
220
|
+
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('menu item click handlers', () => {
|
|
225
|
+
it('should handle check for updates click', () => {
|
|
226
|
+
macOSMenu.buildAndSetAppMenu();
|
|
227
|
+
|
|
228
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
229
|
+
const appMenu = template[0];
|
|
230
|
+
const checkUpdatesItem = appMenu.submenu.find(
|
|
231
|
+
(item: any) => item.label === 'Check for Updates',
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
expect(checkUpdatesItem).toBeDefined();
|
|
235
|
+
checkUpdatesItem.click();
|
|
236
|
+
expect(mockApp.updaterManager.checkForUpdates).toHaveBeenCalledWith({ manual: true });
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should handle preferences click', async () => {
|
|
240
|
+
macOSMenu.buildAndSetAppMenu();
|
|
241
|
+
|
|
242
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
243
|
+
const appMenu = template[0];
|
|
244
|
+
const preferencesItem = appMenu.submenu.find((item: any) => item.label === 'Preferences');
|
|
245
|
+
|
|
246
|
+
expect(preferencesItem).toBeDefined();
|
|
247
|
+
await preferencesItem.click();
|
|
248
|
+
expect(mockApp.browserManager.getMainWindow).toHaveBeenCalled();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should handle visit website click', async () => {
|
|
252
|
+
macOSMenu.buildAndSetAppMenu();
|
|
253
|
+
|
|
254
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
255
|
+
const helpMenu = template.find((item: any) => item.label === 'Help');
|
|
256
|
+
const visitWebsiteItem = helpMenu.submenu.find((item: any) => item.label === 'Visit Website');
|
|
257
|
+
|
|
258
|
+
expect(visitWebsiteItem).toBeDefined();
|
|
259
|
+
await visitWebsiteItem.click();
|
|
260
|
+
expect(shell.openExternal).toHaveBeenCalledWith('https://lobehub.com');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should handle github repo click', async () => {
|
|
264
|
+
macOSMenu.buildAndSetAppMenu();
|
|
265
|
+
|
|
266
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
267
|
+
const helpMenu = template.find((item: any) => item.label === 'Help');
|
|
268
|
+
const githubItem = helpMenu.submenu.find((item: any) => item.label === 'GitHub Repository');
|
|
269
|
+
|
|
270
|
+
expect(githubItem).toBeDefined();
|
|
271
|
+
await githubItem.click();
|
|
272
|
+
expect(shell.openExternal).toHaveBeenCalledWith('https://github.com/lobehub/lobe-chat');
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should handle open logs directory click', () => {
|
|
276
|
+
macOSMenu.buildAndSetAppMenu();
|
|
277
|
+
|
|
278
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
279
|
+
const helpMenu = template.find((item: any) => item.label === 'Help');
|
|
280
|
+
const logsItem = helpMenu.submenu.find((item: any) => item.label === '打开日志目录');
|
|
281
|
+
|
|
282
|
+
expect(logsItem).toBeDefined();
|
|
283
|
+
logsItem.click();
|
|
284
|
+
expect(app.getPath).toHaveBeenCalledWith('logs');
|
|
285
|
+
expect(shell.openPath).toHaveBeenCalledWith('/path/to/logs');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should handle tray show click', () => {
|
|
289
|
+
macOSMenu.buildTrayMenu();
|
|
290
|
+
|
|
291
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
292
|
+
const showItem = template.find((item: any) => item.label?.includes('Show'));
|
|
293
|
+
|
|
294
|
+
expect(showItem).toBeDefined();
|
|
295
|
+
showItem.click();
|
|
296
|
+
expect(mockApp.browserManager.showMainWindow).toHaveBeenCalled();
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('menu accelerators', () => {
|
|
301
|
+
it('should set correct accelerator for preferences', () => {
|
|
302
|
+
macOSMenu.buildAndSetAppMenu();
|
|
303
|
+
|
|
304
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
305
|
+
const appMenu = template[0];
|
|
306
|
+
const preferencesItem = appMenu.submenu.find((item: any) => item.label === 'Preferences');
|
|
307
|
+
|
|
308
|
+
expect(preferencesItem.accelerator).toBe('Command+,');
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should set correct accelerator for quit', () => {
|
|
312
|
+
macOSMenu.buildAndSetAppMenu();
|
|
313
|
+
|
|
314
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
315
|
+
const appMenu = template[0];
|
|
316
|
+
const quitItem = appMenu.submenu.find((item: any) => item.label === 'Quit');
|
|
317
|
+
|
|
318
|
+
expect(quitItem.accelerator).toBe('Command+Q');
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('should set correct accelerator for copy in edit menu', () => {
|
|
322
|
+
macOSMenu.buildAndSetAppMenu();
|
|
323
|
+
|
|
324
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
325
|
+
const editMenu = template.find((item: any) => item.label === 'Edit');
|
|
326
|
+
const copyItem = editMenu.submenu.find((item: any) => item.label === 'Copy');
|
|
327
|
+
|
|
328
|
+
expect(copyItem.accelerator).toBe('Command+C');
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
describe('developer menu items', () => {
|
|
333
|
+
it('should include dev panel in developer menu', () => {
|
|
334
|
+
macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
|
335
|
+
|
|
336
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
337
|
+
const devMenu = template.find((item: any) => item.label === 'Developer');
|
|
338
|
+
const devPanelItem = devMenu.submenu.find((item: any) => item.label === 'Dev Panel');
|
|
339
|
+
|
|
340
|
+
expect(devPanelItem).toBeDefined();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('should handle dev panel click', () => {
|
|
344
|
+
macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
|
345
|
+
|
|
346
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
347
|
+
const devMenu = template.find((item: any) => item.label === 'Developer');
|
|
348
|
+
const devPanelItem = devMenu.submenu.find((item: any) => item.label === 'Dev Panel');
|
|
349
|
+
|
|
350
|
+
devPanelItem.click();
|
|
351
|
+
expect(mockApp.browserManager.retrieveByIdentifier).toHaveBeenCalledWith('devtools');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should handle refresh menu click', () => {
|
|
355
|
+
macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
|
356
|
+
|
|
357
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
358
|
+
const devMenu = template.find((item: any) => item.label === 'Developer');
|
|
359
|
+
const refreshMenuItem = devMenu.submenu.find((item: any) => item.label === 'Refresh Menu');
|
|
360
|
+
|
|
361
|
+
refreshMenuItem.click();
|
|
362
|
+
expect(mockApp.menuManager.rebuildAppMenu).toHaveBeenCalled();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should include updater simulation submenu', () => {
|
|
366
|
+
macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
|
367
|
+
|
|
368
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
369
|
+
const devMenu = template.find((item: any) => item.label === 'Developer');
|
|
370
|
+
const updaterMenu = devMenu.submenu.find((item: any) => item.label === '自动更新测试模拟');
|
|
371
|
+
|
|
372
|
+
expect(updaterMenu).toBeDefined();
|
|
373
|
+
expect(updaterMenu.submenu).toBeInstanceOf(Array);
|
|
374
|
+
expect(updaterMenu.submenu.length).toBeGreaterThan(0);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
describe('context menu templates', () => {
|
|
379
|
+
it('should include copy and paste in chat context menu', () => {
|
|
380
|
+
macOSMenu.buildContextMenu('chat');
|
|
381
|
+
|
|
382
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
383
|
+
const copyItem = template.find((item: any) => item.role === 'copy');
|
|
384
|
+
const pasteItem = template.find((item: any) => item.role === 'paste');
|
|
385
|
+
|
|
386
|
+
expect(copyItem).toBeDefined();
|
|
387
|
+
expect(pasteItem).toBeDefined();
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('should include cut in editor context menu but not in chat', () => {
|
|
391
|
+
macOSMenu.buildContextMenu('editor');
|
|
392
|
+
const editorTemplate = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
393
|
+
|
|
394
|
+
vi.clearAllMocks();
|
|
395
|
+
|
|
396
|
+
macOSMenu.buildContextMenu('chat');
|
|
397
|
+
const chatTemplate = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
398
|
+
|
|
399
|
+
const editorCutItem = editorTemplate.find((item: any) => item.role === 'cut');
|
|
400
|
+
const chatCutItem = chatTemplate.find((item: any) => item.role === 'cut');
|
|
401
|
+
|
|
402
|
+
expect(editorCutItem).toBeDefined();
|
|
403
|
+
expect(chatCutItem).toBeUndefined();
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('should include delete in editor context menu', () => {
|
|
407
|
+
macOSMenu.buildContextMenu('editor');
|
|
408
|
+
|
|
409
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
410
|
+
const deleteItem = template.find((item: any) => item.role === 'delete');
|
|
411
|
+
|
|
412
|
+
expect(deleteItem).toBeDefined();
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
describe('menu roles', () => {
|
|
417
|
+
it('should set window role for window menu', () => {
|
|
418
|
+
macOSMenu.buildAndSetAppMenu();
|
|
419
|
+
|
|
420
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
421
|
+
const windowMenu = template.find((item: any) => item.label === 'Window');
|
|
422
|
+
|
|
423
|
+
expect(windowMenu.role).toBe('windowMenu');
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('should set help role for help menu', () => {
|
|
427
|
+
macOSMenu.buildAndSetAppMenu();
|
|
428
|
+
|
|
429
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
430
|
+
const helpMenu = template.find((item: any) => item.label === 'Help');
|
|
431
|
+
|
|
432
|
+
expect(helpMenu.role).toBe('help');
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('should set services submenu in app menu', () => {
|
|
436
|
+
macOSMenu.buildAndSetAppMenu();
|
|
437
|
+
|
|
438
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
439
|
+
const appMenu = template[0];
|
|
440
|
+
const servicesItem = appMenu.submenu.find((item: any) => item.role === 'services');
|
|
441
|
+
|
|
442
|
+
expect(servicesItem).toBeDefined();
|
|
443
|
+
expect(servicesItem.label).toBe('Services');
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
describe('i18n integration', () => {
|
|
448
|
+
it('should use i18n for all menu labels', () => {
|
|
449
|
+
macOSMenu.buildAndSetAppMenu();
|
|
450
|
+
|
|
451
|
+
expect(mockApp.i18n.ns).toHaveBeenCalledWith('menu');
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('should pass app name to translations', () => {
|
|
455
|
+
macOSMenu.buildAndSetAppMenu();
|
|
456
|
+
|
|
457
|
+
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
|
458
|
+
const appMenu = template[0];
|
|
459
|
+
|
|
460
|
+
expect(app.getName).toHaveBeenCalled();
|
|
461
|
+
expect(appMenu.label).toBe('LobeChat');
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
});
|