@@ -20,7 +20,12 @@ vi.mock("../state-manager")
2020
2121vi . mock ( "vscode" , ( ) => ( {
2222 window : { onDidChangeActiveTextEditor : vi . fn ( ) , activeTextEditor : undefined } ,
23- workspace : { workspaceFolders : undefined , getWorkspaceFolder : vi . fn ( ) , getConfiguration : vi . fn ( ) } ,
23+ workspace : {
24+ workspaceFolders : undefined ,
25+ getWorkspaceFolder : vi . fn ( ) ,
26+ getConfiguration : vi . fn ( ) ,
27+ onDidChangeWorkspaceFolders : vi . fn ( ) ,
28+ } ,
2429} ) )
2530
2631function makeSource ( workspacePath : string ) {
@@ -59,6 +64,8 @@ function makeSource(workspacePath: string) {
5964describe ( "CodeIndexStatusManager" , ( ) => {
6065 let editorChanged : ( ) => void
6166 let disposeEditor : ReturnType < typeof vi . fn < ( ) => void > >
67+ let workspaceFoldersChanged : ( ) => void
68+ let disposeWorkspaceFolders : ReturnType < typeof vi . fn < ( ) => void > >
6269
6370 beforeEach ( ( ) => {
6471 vi . clearAllMocks ( )
@@ -74,6 +81,11 @@ describe("CodeIndexStatusManager", () => {
7481 editorChanged = ( ) => listener ( undefined )
7582 return { dispose : disposeEditor }
7683 } )
84+ disposeWorkspaceFolders = vi . fn ( )
85+ vi . mocked ( vscode . workspace . onDidChangeWorkspaceFolders ) . mockImplementation ( ( listener ) => {
86+ workspaceFoldersChanged = ( ) => listener ( { added : [ ] , removed : [ ] } )
87+ return { dispose : disposeWorkspaceFolders }
88+ } )
7789 } )
7890
7991 it ( "resolves the workspace before requesting its manager" , ( ) => {
@@ -308,4 +320,87 @@ describe("CodeIndexStatusManager", () => {
308320 expect ( source . subscription . dispose ) . not . toHaveBeenCalled ( )
309321 manager . dispose ( )
310322 } )
323+
324+ it ( "switches to the newly selected workspace on a workspace-folder change alone" , ( ) => {
325+ const first = makeSource ( "/first" )
326+ const second = makeSource ( "/second" )
327+ const resolve = vi . fn ( ( path : string ) => ( path === "/first" ? first : second ) )
328+ const publish = vi . fn ( )
329+ const manager = new CodeIndexStatusManager ( resolve )
330+ manager [ "publishStatus" ] = publish
331+ try {
332+ manager . init ( )
333+ expect ( publish ) . toHaveBeenCalledExactlyOnceWith ( first . status )
334+ Object . defineProperty ( vscode . workspace , "workspaceFolders" , {
335+ configurable : true ,
336+ value : [ { uri : makeUri ( "/second" ) , name : "second" , index : 0 } ] ,
337+ } )
338+ workspaceFoldersChanged ( )
339+ expect ( resolve ) . toHaveBeenLastCalledWith ( "/second" )
340+ expect ( first . subscription . dispose ) . toHaveBeenCalledTimes ( 1 )
341+ expect ( second . onProgressUpdate ) . toHaveBeenCalledTimes ( 1 )
342+ expect ( publish ) . toHaveBeenCalledTimes ( 2 )
343+ expect ( publish ) . toHaveBeenLastCalledWith ( second . status )
344+ first . emit ( )
345+ expect ( publish ) . toHaveBeenCalledTimes ( 2 )
346+ second . status . message = "Updated"
347+ second . emit ( )
348+ expect ( publish ) . toHaveBeenCalledTimes ( 3 )
349+ expect ( publish ) . toHaveBeenLastCalledWith ( second . status )
350+ } finally {
351+ manager . dispose ( )
352+ }
353+ } )
354+
355+ it ( "keeps the subscription when a workspace-folder change leaves the selection unchanged" , ( ) => {
356+ const source = makeSource ( "/first" )
357+ const publish = vi . fn ( )
358+ const manager = new CodeIndexStatusManager ( ( ) => source )
359+ manager [ "publishStatus" ] = publish
360+ manager . init ( )
361+ workspaceFoldersChanged ( )
362+ expect ( source . onProgressUpdate ) . toHaveBeenCalledTimes ( 1 )
363+ expect ( source . subscription . dispose ) . not . toHaveBeenCalled ( )
364+ expect ( publish ) . toHaveBeenCalledExactlyOnceWith ( source . status )
365+ manager . dispose ( )
366+ } )
367+
368+ it ( "releases the subscription when a workspace-folder change leaves no workspace" , ( ) => {
369+ const source = makeSource ( "/first" )
370+ const publish = vi . fn ( )
371+ const manager = new CodeIndexStatusManager ( ( ) => source )
372+ manager [ "publishStatus" ] = publish
373+ manager . init ( )
374+ Object . defineProperty ( vscode . workspace , "workspaceFolders" , { configurable : true , value : [ ] } )
375+ workspaceFoldersChanged ( )
376+ expect ( source . subscription . dispose ) . toHaveBeenCalledTimes ( 1 )
377+ source . emit ( )
378+ expect ( publish ) . toHaveBeenCalledExactlyOnceWith ( source . status )
379+ manager . dispose ( )
380+ } )
381+
382+ it ( "releases the workspace-folder listener on disposal even when its cleanup fails" , ( ) => {
383+ const source = makeSource ( "/first" )
384+ const manager = new CodeIndexStatusManager ( ( ) => source )
385+ const folderError = new Error ( "workspace folders cleanup failed" )
386+ const log = vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => { } )
387+ try {
388+ manager . init ( )
389+ disposeWorkspaceFolders . mockImplementationOnce ( ( ) => {
390+ throw folderError
391+ } )
392+ expect ( ( ) => manager . dispose ( ) ) . not . toThrow ( )
393+ expect ( log ) . toHaveBeenCalledExactlyOnceWith (
394+ "[CodeIndexStatusManager] Failed to dispose workspace folders subscription:" ,
395+ folderError ,
396+ )
397+ expect ( disposeEditor ) . toHaveBeenCalledTimes ( 1 )
398+ expect ( source . subscription . dispose ) . toHaveBeenCalledTimes ( 1 )
399+ manager . dispose ( )
400+ expect ( disposeWorkspaceFolders ) . toHaveBeenCalledTimes ( 1 )
401+ expect ( log ) . toHaveBeenCalledTimes ( 1 )
402+ } finally {
403+ log . mockRestore ( )
404+ }
405+ } )
311406} )
0 commit comments