@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.
Files changed (54) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/apps/desktop/package.json +1 -0
  3. package/apps/desktop/src/main/core/ui/__tests__/MenuManager.test.ts +320 -0
  4. package/apps/desktop/src/main/core/ui/__tests__/Tray.test.ts +518 -0
  5. package/apps/desktop/src/main/core/ui/__tests__/TrayManager.test.ts +360 -0
  6. package/apps/desktop/src/main/menus/impls/BaseMenuPlatform.test.ts +49 -0
  7. package/apps/desktop/src/main/menus/impls/linux.test.ts +552 -0
  8. package/apps/desktop/src/main/menus/impls/macOS.test.ts +464 -0
  9. package/apps/desktop/src/main/menus/impls/windows.test.ts +429 -0
  10. package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +2 -2
  11. package/apps/desktop/src/main/services/__tests__/fileSearchSrv.test.ts +402 -0
  12. package/apps/desktop/src/main/utils/__tests__/file-system.test.ts +91 -0
  13. package/apps/desktop/src/main/utils/__tests__/logger.test.ts +229 -0
  14. package/apps/desktop/src/preload/electronApi.test.ts +142 -0
  15. package/apps/desktop/src/preload/invoke.test.ts +145 -0
  16. package/apps/desktop/src/preload/routeInterceptor.test.ts +374 -0
  17. package/apps/desktop/src/preload/streamer.test.ts +365 -0
  18. package/apps/desktop/vitest.config.mts +1 -0
  19. package/changelog/v1.json +9 -0
  20. package/locales/ar/marketAuth.json +13 -0
  21. package/locales/bg-BG/marketAuth.json +13 -0
  22. package/locales/de-DE/marketAuth.json +13 -0
  23. package/locales/en-US/marketAuth.json +13 -0
  24. package/locales/es-ES/marketAuth.json +13 -0
  25. package/locales/fa-IR/marketAuth.json +13 -0
  26. package/locales/fr-FR/marketAuth.json +13 -0
  27. package/locales/it-IT/marketAuth.json +13 -0
  28. package/locales/ja-JP/marketAuth.json +13 -0
  29. package/locales/ko-KR/marketAuth.json +13 -0
  30. package/locales/nl-NL/marketAuth.json +13 -0
  31. package/locales/pl-PL/marketAuth.json +13 -0
  32. package/locales/pt-BR/marketAuth.json +13 -0
  33. package/locales/ru-RU/marketAuth.json +13 -0
  34. package/locales/tr-TR/marketAuth.json +13 -0
  35. package/locales/vi-VN/marketAuth.json +13 -0
  36. package/locales/zh-CN/marketAuth.json +13 -0
  37. package/locales/zh-TW/marketAuth.json +13 -0
  38. package/package.json +1 -1
  39. package/packages/database/src/models/user.ts +2 -0
  40. package/packages/types/src/discover/mcp.ts +2 -1
  41. package/packages/types/src/tool/plugin.ts +2 -1
  42. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishButton.tsx +0 -2
  43. package/src/app/[variants]/(main)/discover/(detail)/mcp/features/Sidebar/ActionButton/index.tsx +33 -7
  44. package/src/features/PluginStore/McpList/List/Action.tsx +20 -1
  45. package/src/layout/AuthProvider/MarketAuth/MarketAuthConfirmModal.tsx +158 -0
  46. package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +130 -14
  47. package/src/libs/mcp/types.ts +8 -0
  48. package/src/locales/default/marketAuth.ts +13 -0
  49. package/src/server/routers/lambda/market/index.ts +85 -2
  50. package/src/server/services/discover/index.ts +45 -4
  51. package/src/services/discover.ts +1 -1
  52. package/src/services/mcp.ts +18 -3
  53. package/src/store/tool/slices/mcpStore/action.test.ts +141 -0
  54. package/src/store/tool/slices/mcpStore/action.ts +153 -11
@@ -0,0 +1,360 @@
1
+ import { nativeTheme } from 'electron';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import type { App } from '../../App';
5
+ import { Tray } from '../Tray';
6
+ import { TrayManager } from '../TrayManager';
7
+
8
+ // Mock electron modules
9
+ vi.mock('electron', () => ({
10
+ nativeTheme: {
11
+ shouldUseDarkColors: false,
12
+ },
13
+ }));
14
+
15
+ // Mock logger
16
+ vi.mock('@/utils/logger', () => ({
17
+ createLogger: () => ({
18
+ debug: vi.fn(),
19
+ info: vi.fn(),
20
+ warn: vi.fn(),
21
+ error: vi.fn(),
22
+ }),
23
+ }));
24
+
25
+ // Mock environment constants
26
+ vi.mock('@/const/env', () => ({
27
+ isMac: true,
28
+ }));
29
+
30
+ // Mock package.json
31
+ vi.mock('@/../../package.json', () => ({
32
+ name: 'test-app',
33
+ }));
34
+
35
+ // Mock Tray class
36
+ vi.mock('../Tray', () => ({
37
+ Tray: vi.fn(),
38
+ }));
39
+
40
+ describe('TrayManager', () => {
41
+ let trayManager: TrayManager;
42
+ let mockApp: App;
43
+ let mockTray: any;
44
+
45
+ beforeEach(() => {
46
+ vi.clearAllMocks();
47
+
48
+ // Mock Tray instance
49
+ mockTray = {
50
+ identifier: 'main',
51
+ broadcast: vi.fn(),
52
+ destroy: vi.fn(),
53
+ updateIcon: vi.fn(),
54
+ updateTooltip: vi.fn(),
55
+ };
56
+
57
+ // Mock App
58
+ mockApp = {} as unknown as App;
59
+
60
+ // Mock Tray constructor
61
+ vi.mocked(Tray).mockImplementation(() => mockTray);
62
+
63
+ trayManager = new TrayManager(mockApp);
64
+ });
65
+
66
+ describe('constructor', () => {
67
+ it('should initialize TrayManager with app instance', () => {
68
+ expect(trayManager.app).toBe(mockApp);
69
+ expect(trayManager.trays).toBeInstanceOf(Map);
70
+ expect(trayManager.trays.size).toBe(0);
71
+ });
72
+ });
73
+
74
+ describe('initializeTrays', () => {
75
+ it('should initialize main tray', () => {
76
+ trayManager.initializeTrays();
77
+
78
+ expect(Tray).toHaveBeenCalled();
79
+ expect(trayManager.trays.has('main')).toBe(true);
80
+ });
81
+
82
+ it('should call initializeMainTray', () => {
83
+ const spy = vi.spyOn(trayManager, 'initializeMainTray');
84
+
85
+ trayManager.initializeTrays();
86
+
87
+ expect(spy).toHaveBeenCalled();
88
+ });
89
+ });
90
+
91
+ describe('initializeMainTray', () => {
92
+ it('should create main tray with dark icon on macOS when dark mode is enabled', () => {
93
+ Object.defineProperty(nativeTheme, 'shouldUseDarkColors', {
94
+ value: true,
95
+ writable: true,
96
+ configurable: true,
97
+ });
98
+
99
+ const result = trayManager.initializeMainTray();
100
+
101
+ expect(Tray).toHaveBeenCalledWith(
102
+ expect.objectContaining({
103
+ iconPath: 'tray-dark.png',
104
+ identifier: 'main',
105
+ tooltip: 'test-app',
106
+ }),
107
+ mockApp,
108
+ );
109
+ expect(result).toBe(mockTray);
110
+ });
111
+
112
+ it('should create main tray with light icon on macOS when light mode is enabled', () => {
113
+ Object.defineProperty(nativeTheme, 'shouldUseDarkColors', {
114
+ value: false,
115
+ writable: true,
116
+ configurable: true,
117
+ });
118
+
119
+ trayManager.initializeMainTray();
120
+
121
+ expect(Tray).toHaveBeenCalledWith(
122
+ expect.objectContaining({
123
+ iconPath: 'tray-light.png',
124
+ identifier: 'main',
125
+ tooltip: 'test-app',
126
+ }),
127
+ mockApp,
128
+ );
129
+ });
130
+
131
+ it('should add created tray to trays map', () => {
132
+ trayManager.initializeMainTray();
133
+
134
+ expect(trayManager.trays.has('main')).toBe(true);
135
+ expect(trayManager.trays.get('main')).toBe(mockTray);
136
+ });
137
+
138
+ it('should return existing tray if already initialized', () => {
139
+ const firstTray = trayManager.initializeMainTray();
140
+ vi.clearAllMocks();
141
+
142
+ const secondTray = trayManager.initializeMainTray();
143
+
144
+ expect(firstTray).toBe(secondTray);
145
+ expect(Tray).not.toHaveBeenCalled();
146
+ });
147
+ });
148
+
149
+ describe('getMainTray', () => {
150
+ it('should return main tray when it exists', () => {
151
+ trayManager.initializeMainTray();
152
+
153
+ const result = trayManager.getMainTray();
154
+
155
+ expect(result).toBe(mockTray);
156
+ });
157
+
158
+ it('should return undefined when main tray does not exist', () => {
159
+ const result = trayManager.getMainTray();
160
+
161
+ expect(result).toBeUndefined();
162
+ });
163
+ });
164
+
165
+ describe('retrieveByIdentifier', () => {
166
+ it('should return tray by identifier when it exists', () => {
167
+ trayManager.initializeMainTray();
168
+
169
+ const result = trayManager.retrieveByIdentifier('main');
170
+
171
+ expect(result).toBe(mockTray);
172
+ });
173
+
174
+ it('should return undefined when tray with identifier does not exist', () => {
175
+ const result = trayManager.retrieveByIdentifier('main');
176
+
177
+ expect(result).toBeUndefined();
178
+ });
179
+ });
180
+
181
+ describe('broadcastToAllTrays', () => {
182
+ it('should broadcast event to all trays', () => {
183
+ trayManager.initializeMainTray();
184
+
185
+ const event = 'test-event' as any;
186
+ const data = { test: 'data' };
187
+
188
+ trayManager.broadcastToAllTrays(event, data);
189
+
190
+ expect(mockTray.broadcast).toHaveBeenCalledWith(event, data);
191
+ });
192
+
193
+ it('should handle multiple trays', () => {
194
+ // Create main tray
195
+ trayManager.initializeMainTray();
196
+
197
+ // Mock another tray
198
+ const mockTray2 = {
199
+ identifier: 'secondary',
200
+ broadcast: vi.fn(),
201
+ destroy: vi.fn(),
202
+ };
203
+ trayManager.trays.set('secondary' as any, mockTray2 as any);
204
+
205
+ const event = 'test-event' as any;
206
+ const data = { test: 'data' };
207
+
208
+ trayManager.broadcastToAllTrays(event, data);
209
+
210
+ expect(mockTray.broadcast).toHaveBeenCalledWith(event, data);
211
+ expect(mockTray2.broadcast).toHaveBeenCalledWith(event, data);
212
+ });
213
+
214
+ it('should not throw when no trays exist', () => {
215
+ const event = 'test-event' as any;
216
+ const data = { test: 'data' };
217
+
218
+ expect(() => trayManager.broadcastToAllTrays(event, data)).not.toThrow();
219
+ });
220
+ });
221
+
222
+ describe('broadcastToTray', () => {
223
+ it('should broadcast event to specific tray', () => {
224
+ trayManager.initializeMainTray();
225
+
226
+ const event = 'test-event' as any;
227
+ const data = { test: 'data' };
228
+
229
+ trayManager.broadcastToTray('main', event, data);
230
+
231
+ expect(mockTray.broadcast).toHaveBeenCalledWith(event, data);
232
+ });
233
+
234
+ it('should not throw when tray does not exist', () => {
235
+ const event = 'test-event' as any;
236
+ const data = { test: 'data' };
237
+
238
+ expect(() => trayManager.broadcastToTray('main', event, data)).not.toThrow();
239
+ });
240
+
241
+ it('should not call broadcast when tray does not exist', () => {
242
+ const event = 'test-event' as any;
243
+ const data = { test: 'data' };
244
+
245
+ trayManager.broadcastToTray('main', event, data);
246
+
247
+ expect(mockTray.broadcast).not.toHaveBeenCalled();
248
+ });
249
+ });
250
+
251
+ describe('destroyAll', () => {
252
+ it('should destroy all trays', () => {
253
+ trayManager.initializeMainTray();
254
+
255
+ trayManager.destroyAll();
256
+
257
+ expect(mockTray.destroy).toHaveBeenCalled();
258
+ expect(trayManager.trays.size).toBe(0);
259
+ });
260
+
261
+ it('should destroy multiple trays', () => {
262
+ // Create main tray
263
+ trayManager.initializeMainTray();
264
+
265
+ // Mock another tray
266
+ const mockTray2 = {
267
+ identifier: 'secondary',
268
+ broadcast: vi.fn(),
269
+ destroy: vi.fn(),
270
+ };
271
+ trayManager.trays.set('secondary' as any, mockTray2 as any);
272
+
273
+ trayManager.destroyAll();
274
+
275
+ expect(mockTray.destroy).toHaveBeenCalled();
276
+ expect(mockTray2.destroy).toHaveBeenCalled();
277
+ expect(trayManager.trays.size).toBe(0);
278
+ });
279
+
280
+ it('should clear trays map after destroying', () => {
281
+ trayManager.initializeMainTray();
282
+
283
+ trayManager.destroyAll();
284
+
285
+ expect(trayManager.trays.size).toBe(0);
286
+ });
287
+
288
+ it('should not throw when no trays exist', () => {
289
+ expect(() => trayManager.destroyAll()).not.toThrow();
290
+ });
291
+ });
292
+
293
+ describe('retrieveOrInitialize (private method)', () => {
294
+ it('should create new tray when it does not exist', () => {
295
+ const options = {
296
+ iconPath: 'test.png',
297
+ identifier: 'main',
298
+ tooltip: 'Test',
299
+ };
300
+
301
+ const result = trayManager['retrieveOrInitialize'](options);
302
+
303
+ expect(Tray).toHaveBeenCalledWith(options, mockApp);
304
+ expect(result).toBe(mockTray);
305
+ expect(trayManager.trays.has('main')).toBe(true);
306
+ });
307
+
308
+ it('should return existing tray when it already exists', () => {
309
+ const options = {
310
+ iconPath: 'test.png',
311
+ identifier: 'main',
312
+ tooltip: 'Test',
313
+ };
314
+
315
+ const firstResult = trayManager['retrieveOrInitialize'](options);
316
+ vi.clearAllMocks();
317
+
318
+ const secondResult = trayManager['retrieveOrInitialize'](options);
319
+
320
+ expect(firstResult).toBe(secondResult);
321
+ expect(Tray).not.toHaveBeenCalled();
322
+ });
323
+ });
324
+
325
+ describe('integration tests', () => {
326
+ it('should handle complete tray manager lifecycle', () => {
327
+ // Initialize trays
328
+ trayManager.initializeTrays();
329
+ expect(trayManager.trays.size).toBe(1);
330
+
331
+ // Get main tray
332
+ const mainTray = trayManager.getMainTray();
333
+ expect(mainTray).toBeDefined();
334
+
335
+ // Broadcast to all
336
+ trayManager.broadcastToAllTrays('test-event' as any, { data: 'test' });
337
+ expect(mockTray.broadcast).toHaveBeenCalled();
338
+
339
+ // Broadcast to specific tray
340
+ vi.clearAllMocks();
341
+ trayManager.broadcastToTray('main', 'test-event' as any, { data: 'test' });
342
+ expect(mockTray.broadcast).toHaveBeenCalled();
343
+
344
+ // Destroy all
345
+ trayManager.destroyAll();
346
+ expect(mockTray.destroy).toHaveBeenCalled();
347
+ expect(trayManager.trays.size).toBe(0);
348
+ });
349
+
350
+ it('should handle multiple initialization calls safely', () => {
351
+ trayManager.initializeTrays();
352
+ trayManager.initializeTrays();
353
+ trayManager.initializeTrays();
354
+
355
+ // Should only create one tray instance
356
+ expect(Tray).toHaveBeenCalledTimes(1);
357
+ expect(trayManager.trays.size).toBe(1);
358
+ });
359
+ });
360
+ });
@@ -0,0 +1,49 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ import type { App } from '@/core/App';
4
+
5
+ import { BaseMenuPlatform } from './BaseMenuPlatform';
6
+
7
+ // Create a concrete implementation for testing
8
+ class TestMenuPlatform extends BaseMenuPlatform {}
9
+
10
+ // Mock App instance
11
+ const mockApp = {
12
+ i18n: {
13
+ ns: vi.fn(),
14
+ },
15
+ browserManager: {
16
+ getMainWindow: vi.fn(),
17
+ showMainWindow: vi.fn(),
18
+ retrieveByIdentifier: vi.fn(),
19
+ },
20
+ updaterManager: {
21
+ checkForUpdates: vi.fn(),
22
+ },
23
+ menuManager: {
24
+ rebuildAppMenu: vi.fn(),
25
+ },
26
+ storeManager: {
27
+ openInEditor: vi.fn(),
28
+ },
29
+ } as unknown as App;
30
+
31
+ describe('BaseMenuPlatform', () => {
32
+ let menuPlatform: TestMenuPlatform;
33
+
34
+ beforeEach(() => {
35
+ vi.clearAllMocks();
36
+ menuPlatform = new TestMenuPlatform(mockApp);
37
+ });
38
+
39
+ describe('constructor', () => {
40
+ it('should initialize with app instance', () => {
41
+ expect(menuPlatform['app']).toBe(mockApp);
42
+ });
43
+
44
+ it('should store app reference for subclasses', () => {
45
+ const anotherInstance = new TestMenuPlatform(mockApp);
46
+ expect(anotherInstance['app']).toBe(mockApp);
47
+ });
48
+ });
49
+ });