1+ import { beforeEach , describe , expect , it , vi , Mock } from 'vitest' ;
2+ import { InterceptRouteParams } from '@lobechat/electron-client-ipc' ;
3+
4+ import type { App } from '@/core/App' ;
5+ import type { IpcClientEventSender } from '@/types/ipcClientEvent' ;
6+ import { BrowsersIdentifiers , AppBrowsersIdentifiers } from '@/appBrowsers' ;
7+
8+ import BrowserWindowsCtr from '../BrowserWindowsCtr' ;
9+
10+ // 模拟 App 及其依赖项
11+ const mockToggleVisible = vi . fn ( ) ;
12+ const mockShowSettingsWindowWithTab = vi . fn ( ) ;
13+ const mockCloseWindow = vi . fn ( ) ;
14+ const mockMinimizeWindow = vi . fn ( ) ;
15+ const mockMaximizeWindow = vi . fn ( ) ;
16+ const mockRetrieveByIdentifier = vi . fn ( ) ;
17+ const mockGetMainWindow = vi . fn ( ( ) => ( {
18+ toggleVisible : mockToggleVisible ,
19+ } ) ) ;
20+ const mockShow = vi . fn ( ) ;
21+
22+ // mock findMatchingRoute and extractSubPath
23+ vi . mock ( '~common/routes' , async ( ) => ( {
24+ findMatchingRoute : vi . fn ( ) ,
25+ extractSubPath : vi . fn ( ) ,
26+ } ) ) ;
27+ const { findMatchingRoute, extractSubPath } = await import ( '~common/routes' ) ;
28+
29+ const mockApp = {
30+ browserManager : {
31+ getMainWindow : mockGetMainWindow ,
32+ showSettingsWindowWithTab : mockShowSettingsWindowWithTab ,
33+ closeWindow : mockCloseWindow ,
34+ minimizeWindow : mockMinimizeWindow ,
35+ maximizeWindow : mockMaximizeWindow ,
36+ retrieveByIdentifier : mockRetrieveByIdentifier . mockImplementation ( ( identifier : AppBrowsersIdentifiers | string ) => {
37+ if ( identifier === BrowsersIdentifiers . settings || identifier === 'some-other-window' ) {
38+ return { show : mockShow } ;
39+ }
40+ return { show : mockShow } ; // Default mock for other identifiers
41+ } ) ,
42+ } ,
43+ } as unknown as App ;
44+
45+ describe ( 'BrowserWindowsCtr' , ( ) => {
46+ let browserWindowsCtr : BrowserWindowsCtr ;
47+
48+ beforeEach ( ( ) => {
49+ vi . clearAllMocks ( ) ;
50+ browserWindowsCtr = new BrowserWindowsCtr ( mockApp ) ;
51+ } ) ;
52+
53+ describe ( 'toggleMainWindow' , ( ) => {
54+ it ( 'should get the main window and toggle its visibility' , async ( ) => {
55+ await browserWindowsCtr . toggleMainWindow ( ) ;
56+ expect ( mockGetMainWindow ) . toHaveBeenCalled ( ) ;
57+ expect ( mockToggleVisible ) . toHaveBeenCalled ( ) ;
58+ } ) ;
59+ } ) ;
60+
61+ describe ( 'openSettingsWindow' , ( ) => {
62+ it ( 'should show the settings window with the specified tab' , async ( ) => {
63+ const tab = 'appearance' ;
64+ const result = await browserWindowsCtr . openSettingsWindow ( tab ) ;
65+ expect ( mockShowSettingsWindowWithTab ) . toHaveBeenCalledWith ( tab ) ;
66+ expect ( result ) . toEqual ( { success : true } ) ;
67+ } ) ;
68+
69+ it ( 'should return error if showing settings window fails' , async ( ) => {
70+ const errorMessage = 'Failed to show' ;
71+ mockShowSettingsWindowWithTab . mockRejectedValueOnce ( new Error ( errorMessage ) ) ;
72+ const result = await browserWindowsCtr . openSettingsWindow ( 'display' ) ;
73+ expect ( result ) . toEqual ( { error : errorMessage , success : false } ) ;
74+ } ) ;
75+ } ) ;
76+
77+ const testSenderIdentifierString : string = 'test-window-event-id' ;
78+ const sender : IpcClientEventSender = {
79+ identifier : testSenderIdentifierString ,
80+ } ;
81+
82+ describe ( 'closeWindow' , ( ) => {
83+ it ( 'should close the window with the given sender identifier' , ( ) => {
84+ browserWindowsCtr . closeWindow ( undefined , sender ) ;
85+ expect ( mockCloseWindow ) . toHaveBeenCalledWith ( testSenderIdentifierString ) ;
86+ } ) ;
87+ } ) ;
88+
89+ describe ( 'minimizeWindow' , ( ) => {
90+ it ( 'should minimize the window with the given sender identifier' , ( ) => {
91+ browserWindowsCtr . minimizeWindow ( undefined , sender ) ;
92+ expect ( mockMinimizeWindow ) . toHaveBeenCalledWith ( testSenderIdentifierString ) ;
93+ } ) ;
94+ } ) ;
95+
96+ describe ( 'maximizeWindow' , ( ) => {
97+ it ( 'should maximize the window with the given sender identifier' , ( ) => {
98+ browserWindowsCtr . maximizeWindow ( undefined , sender ) ;
99+ expect ( mockMaximizeWindow ) . toHaveBeenCalledWith ( testSenderIdentifierString ) ;
100+ } ) ;
101+ } ) ;
102+
103+ describe ( 'interceptRoute' , ( ) => {
104+ const baseParams = { source : 'link-click' as const } ;
105+
106+ it ( 'should not intercept if no matching route is found' , async ( ) => {
107+ const params : InterceptRouteParams = { ...baseParams , path : '/unknown/route' , url : 'app://host/unknown/route' } ;
108+ ( findMatchingRoute as Mock ) . mockReturnValue ( undefined ) ;
109+ const result = await browserWindowsCtr . interceptRoute ( params ) ;
110+ expect ( findMatchingRoute ) . toHaveBeenCalledWith ( params . path ) ;
111+ expect ( result ) . toEqual ( { intercepted : false , path : params . path , source : params . source } ) ;
112+ } ) ;
113+
114+ it ( 'should show settings window if matched route target is settings' , async ( ) => {
115+ const params : InterceptRouteParams = { ...baseParams , path : '/settings/common' , url : 'app://host/settings/common' } ;
116+ const matchedRoute = { targetWindow : BrowsersIdentifiers . settings , pathPrefix : '/settings' } ;
117+ const subPath = 'common' ;
118+ ( findMatchingRoute as Mock ) . mockReturnValue ( matchedRoute ) ;
119+ ( extractSubPath as Mock ) . mockReturnValue ( subPath ) ;
120+
121+ const result = await browserWindowsCtr . interceptRoute ( params ) ;
122+
123+ expect ( findMatchingRoute ) . toHaveBeenCalledWith ( params . path ) ;
124+ expect ( extractSubPath ) . toHaveBeenCalledWith ( params . path , matchedRoute . pathPrefix ) ;
125+ expect ( mockShowSettingsWindowWithTab ) . toHaveBeenCalledWith ( subPath ) ;
126+ expect ( result ) . toEqual ( {
127+ intercepted : true ,
128+ path : params . path ,
129+ source : params . source ,
130+ subPath,
131+ targetWindow : matchedRoute . targetWindow ,
132+ } ) ;
133+ expect ( mockShow ) . not . toHaveBeenCalled ( ) ;
134+ } ) ;
135+
136+ it ( 'should open target window if matched route target is not settings' , async ( ) => {
137+ const params : InterceptRouteParams = { ...baseParams , path : '/other/page' , url : 'app://host/other/page' } ;
138+ const targetWindowIdentifier = 'some-other-window' as AppBrowsersIdentifiers ;
139+ const matchedRoute = { targetWindow : targetWindowIdentifier , pathPrefix : '/other' } ;
140+ ( findMatchingRoute as Mock ) . mockReturnValue ( matchedRoute ) ;
141+
142+ const result = await browserWindowsCtr . interceptRoute ( params ) ;
143+
144+ expect ( findMatchingRoute ) . toHaveBeenCalledWith ( params . path ) ;
145+ expect ( mockRetrieveByIdentifier ) . toHaveBeenCalledWith ( targetWindowIdentifier ) ;
146+ expect ( mockShow ) . toHaveBeenCalled ( ) ;
147+ expect ( result ) . toEqual ( {
148+ intercepted : true ,
149+ path : params . path ,
150+ source : params . source ,
151+ targetWindow : matchedRoute . targetWindow ,
152+ } ) ;
153+ expect ( mockShowSettingsWindowWithTab ) . not . toHaveBeenCalled ( ) ;
154+ } ) ;
155+
156+ it ( 'should return error if processing route interception fails for settings' , async ( ) => {
157+ const params : InterceptRouteParams = { ...baseParams , path : '/settings/general' , url : 'app://host/settings/general' } ;
158+ const matchedRoute = { targetWindow : BrowsersIdentifiers . settings , pathPrefix : '/settings' } ;
159+ const subPath = 'general' ;
160+ const errorMessage = 'Processing error for settings' ;
161+ ( findMatchingRoute as Mock ) . mockReturnValue ( matchedRoute ) ;
162+ ( extractSubPath as Mock ) . mockReturnValue ( subPath ) ;
163+ mockShowSettingsWindowWithTab . mockRejectedValueOnce ( new Error ( errorMessage ) ) ;
164+
165+ const result = await browserWindowsCtr . interceptRoute ( params ) ;
166+
167+ expect ( result ) . toEqual ( {
168+ error : errorMessage ,
169+ intercepted : false ,
170+ path : params . path ,
171+ source : params . source ,
172+ } ) ;
173+ } ) ;
174+
175+ it ( 'should return error if processing route interception fails for other window' , async ( ) => {
176+ const params : InterceptRouteParams = { ...baseParams , path : '/another/custom' , url : 'app://host/another/custom' } ;
177+ const targetWindowIdentifier = 'another-custom-window' as AppBrowsersIdentifiers ;
178+ const matchedRoute = { targetWindow : targetWindowIdentifier , pathPrefix : '/another' } ;
179+ const errorMessage = 'Processing error for other window' ;
180+ ( findMatchingRoute as Mock ) . mockReturnValue ( matchedRoute ) ;
181+ mockRetrieveByIdentifier . mockImplementationOnce ( ( ) => {
182+ throw new Error ( errorMessage ) ;
183+ } ) ;
184+
185+ const result = await browserWindowsCtr . interceptRoute ( params ) ;
186+
187+ expect ( result ) . toEqual ( {
188+ error : errorMessage ,
189+ intercepted : false ,
190+ path : params . path ,
191+ source : params . source ,
192+ } ) ;
193+ } ) ;
194+ } ) ;
195+ } ) ;
0 commit comments