@@ -62,8 +62,64 @@ vi.mock("./api", () => ({
6262createStreamingFetchAdapter :vi . fn ( ) ,
6363} ) )
6464
65+ // Create a testable WorkspaceProvider class that allows mocking of protected methods
66+ class TestableWorkspaceProvider extends WorkspaceProvider {
67+ public createEventEmitter ( ) {
68+ return super . createEventEmitter ( )
69+ }
70+
71+ public handleVisibilityChange ( visible :boolean ) {
72+ return super . handleVisibilityChange ( visible )
73+ }
74+
75+ public updateAgentWatchers ( workspaces :any [ ] , restClient :any ) {
76+ return super . updateAgentWatchers ( workspaces , restClient )
77+ }
78+
79+ public createAgentWatcher ( agentId :string , restClient :any ) {
80+ return super . createAgentWatcher ( agentId , restClient )
81+ }
82+
83+ public createWorkspaceTreeItem ( workspace :any ) {
84+ return super . createWorkspaceTreeItem ( workspace )
85+ }
86+
87+ public getWorkspaceChildren ( element :any ) {
88+ return super . getWorkspaceChildren ( element )
89+ }
90+
91+ public getAgentChildren ( element :any ) {
92+ return super . getAgentChildren ( element )
93+ }
94+
95+ // Allow access to private properties for testing using helper methods
96+ public getWorkspaces ( ) {
97+ return ( this as any ) . workspaces
98+ }
99+
100+ public setWorkspaces ( value :any ) {
101+ ; ( this as any ) . workspaces = value
102+ }
103+
104+ public getFetching ( ) {
105+ return ( this as any ) . fetching
106+ }
107+
108+ public setFetching ( value :boolean ) {
109+ ; ( this as any ) . fetching = value
110+ }
111+
112+ public getVisible ( ) {
113+ return ( this as any ) . visible
114+ }
115+
116+ public setVisible ( value :boolean ) {
117+ ; ( this as any ) . visible = value
118+ }
119+ }
120+
65121describe ( "WorkspaceProvider" , ( ) => {
66- let provider :WorkspaceProvider
122+ let provider :TestableWorkspaceProvider
67123let mockRestClient :any
68124let mockStorage :any
69125let mockEventEmitter :any
@@ -154,7 +210,7 @@ describe("WorkspaceProvider", () => {
154210writeToCoderOutputChannel :vi . fn ( ) ,
155211}
156212
157- provider = new WorkspaceProvider (
213+ provider = new TestableWorkspaceProvider (
158214WorkspaceQuery . Mine ,
159215mockRestClient ,
160216mockStorage ,
@@ -173,18 +229,20 @@ describe("WorkspaceProvider", () => {
173229
174230describe ( "constructor" , ( ) => {
175231it ( "should create provider with correct initial state" , ( ) => {
176- const provider = new WorkspaceProvider (
232+ const provider = new TestableWorkspaceProvider (
177233WorkspaceQuery . All ,
178234mockRestClient ,
179235mockStorage ,
18023610
181237)
182238
183239expect ( provider ) . toBeDefined ( )
240+ expect ( provider . getVisible ( ) ) . toBe ( false )
241+ expect ( provider . getWorkspaces ( ) ) . toBeUndefined ( )
184242} )
185243
186244it ( "should create provider without timer" , ( ) => {
187- const provider = new WorkspaceProvider (
245+ const provider = new TestableWorkspaceProvider (
188246WorkspaceQuery . Mine ,
189247mockRestClient ,
190248mockStorage
@@ -194,6 +252,15 @@ describe("WorkspaceProvider", () => {
194252} )
195253} )
196254
255+ describe ( "createEventEmitter" , ( ) => {
256+ it ( "should create and return event emitter" , ( ) => {
257+ const emitter = provider . createEventEmitter ( )
258+
259+ expect ( vscode . EventEmitter ) . toHaveBeenCalled ( )
260+ expect ( emitter ) . toBe ( mockEventEmitter )
261+ } )
262+ } )
263+
197264describe ( "fetchAndRefresh" , ( ) => {
198265it ( "should not fetch when not visible" , async ( ) => {
199266provider . setVisibility ( false )
@@ -203,13 +270,30 @@ describe("WorkspaceProvider", () => {
203270expect ( mockRestClient . getWorkspaces ) . not . toHaveBeenCalled ( )
204271} )
205272
273+ it ( "should not fetch when already fetching" , async ( ) => {
274+ // Mock the handleVisibilityChange to prevent automatic fetchAndRefresh
275+ const handleVisibilitySpy = vi . spyOn ( provider , "handleVisibilityChange" ) . mockImplementation ( ( ) => { } )
276+ provider . setVisibility ( true )
277+ handleVisibilitySpy . mockRestore ( )
278+
279+ provider . setFetching ( true )
280+
281+ await provider . fetchAndRefresh ( )
282+
283+ expect ( mockRestClient . getWorkspaces ) . not . toHaveBeenCalled ( )
284+ } )
285+
206286it ( "should fetch workspaces successfully" , async ( ) => {
207287mockRestClient . getWorkspaces . mockResolvedValue ( {
208288workspaces :[ mockWorkspace ] ,
209289count :1 ,
210290} )
211291
292+ // Mock the handleVisibilityChange to prevent automatic fetchAndRefresh
293+ const handleVisibilitySpy = vi . spyOn ( provider , "handleVisibilityChange" ) . mockImplementation ( ( ) => { } )
212294provider . setVisibility ( true )
295+ handleVisibilitySpy . mockRestore ( )
296+
213297await provider . fetchAndRefresh ( )
214298
215299expect ( mockRestClient . getWorkspaces ) . toHaveBeenCalledWith ( {
@@ -221,7 +305,11 @@ describe("WorkspaceProvider", () => {
221305it ( "should handle fetch errors gracefully" , async ( ) => {
222306mockRestClient . getWorkspaces . mockRejectedValue ( new Error ( "Network error" ) )
223307
308+ // Mock the handleVisibilityChange to prevent automatic fetchAndRefresh
309+ const handleVisibilitySpy = vi . spyOn ( provider , "handleVisibilityChange" ) . mockImplementation ( ( ) => { } )
224310provider . setVisibility ( true )
311+ handleVisibilitySpy . mockRestore ( )
312+
225313await provider . fetchAndRefresh ( )
226314
227315expect ( mockEventEmitter . fire ) . toHaveBeenCalled ( )
@@ -240,7 +328,11 @@ describe("WorkspaceProvider", () => {
240328count :0 ,
241329} )
242330
331+ // Mock the handleVisibilityChange to prevent automatic fetchAndRefresh
332+ const handleVisibilitySpy = vi . spyOn ( provider , "handleVisibilityChange" ) . mockImplementation ( ( ) => { } )
243333provider . setVisibility ( true )
334+ handleVisibilitySpy . mockRestore ( )
335+
244336await provider . fetchAndRefresh ( )
245337
246338expect ( mockStorage . writeToCoderOutputChannel ) . toHaveBeenCalledWith (
@@ -252,19 +344,43 @@ describe("WorkspaceProvider", () => {
252344} )
253345
254346describe ( "setVisibility" , ( ) => {
347+ it ( "should set visibility and call handleVisibilityChange" , ( ) => {
348+ const handleVisibilitySpy = vi . spyOn ( provider , "handleVisibilityChange" ) . mockImplementation ( ( ) => { } )
349+
350+ provider . setVisibility ( true )
351+
352+ expect ( provider . getVisible ( ) ) . toBe ( true )
353+ expect ( handleVisibilitySpy ) . toHaveBeenCalledWith ( true )
354+ } )
355+ } )
356+
357+ describe ( "handleVisibilityChange" , ( ) => {
255358it ( "should start fetching when becoming visible for first time" , async ( ) => {
256359const fetchSpy = vi . spyOn ( provider , "fetchAndRefresh" ) . mockResolvedValue ( )
257360
258- provider . setVisibility ( true )
361+ provider . handleVisibilityChange ( true )
259362
260363expect ( fetchSpy ) . toHaveBeenCalled ( )
261364} )
262365
366+ it ( "should not fetch when workspaces already exist" , ( ) => {
367+ const fetchSpy = vi . spyOn ( provider , "fetchAndRefresh" ) . mockResolvedValue ( )
368+
369+ // Set workspaces to simulate having fetched before
370+ provider . setWorkspaces ( [ ] )
371+
372+ provider . handleVisibilityChange ( true )
373+
374+ expect ( fetchSpy ) . not . toHaveBeenCalled ( )
375+ } )
376+
263377it ( "should cancel pending refresh when becoming invisible" , ( ) => {
264378vi . useFakeTimers ( )
265379
266- provider . setVisibility ( true )
267- provider . setVisibility ( false )
380+ // First set visible to potentially schedule refresh
381+ provider . handleVisibilityChange ( true )
382+ // Then set invisible to cancel
383+ provider . handleVisibilityChange ( false )
268384
269385// Fast-forward time - should not trigger refresh
270386vi . advanceTimersByTime ( 10000 )
@@ -299,7 +415,11 @@ describe("WorkspaceProvider", () => {
299415count :1 ,
300416} )
301417
418+ // Mock the handleVisibilityChange to prevent automatic fetchAndRefresh
419+ const handleVisibilitySpy = vi . spyOn ( provider , "handleVisibilityChange" ) . mockImplementation ( ( ) => { } )
302420provider . setVisibility ( true )
421+ handleVisibilitySpy . mockRestore ( )
422+
303423await provider . fetchAndRefresh ( )
304424
305425const children = await provider . getChildren ( )
@@ -333,13 +453,50 @@ describe("WorkspaceProvider", () => {
333453} )
334454} )
335455
336- describe ( "fetch method edge cases" , ( ) => {
456+ describe ( "createWorkspaceTreeItem" , ( ) => {
457+ it ( "should create workspace tree item with app status" , async ( ) => {
458+ const { extractAgents} = await import ( "./api-helper" )
459+
460+ const agentWithApps = {
461+ ...mockAgent ,
462+ apps :[
463+ {
464+ display_name :"Test App" ,
465+ url :"https://app.example.com" ,
466+ command :"npm start" ,
467+ } ,
468+ ] ,
469+ }
470+
471+ vi . mocked ( extractAgents ) . mockReturnValue ( [ agentWithApps ] )
472+
473+ const result = provider . createWorkspaceTreeItem ( mockWorkspace )
474+
475+ expect ( result ) . toBeInstanceOf ( WorkspaceTreeItem )
476+ expect ( result . appStatus ) . toEqual ( [
477+ {
478+ name :"Test App" ,
479+ url :"https://app.example.com" ,
480+ agent_id :"agent-1" ,
481+ agent_name :"main" ,
482+ command :"npm start" ,
483+ workspace_name :"test-workspace" ,
484+ } ,
485+ ] )
486+ } )
487+ } )
488+
489+ describe ( "edge cases" , ( ) => {
337490it ( "should throw error when not logged in" , async ( ) => {
338491mockRestClient . getAxiosInstance . mockReturnValue ( {
339492defaults :{ baseURL :undefined } ,
340493} )
341494
495+ // Mock the handleVisibilityChange to prevent automatic fetchAndRefresh
496+ const handleVisibilitySpy = vi . spyOn ( provider , "handleVisibilityChange" ) . mockImplementation ( ( ) => { } )
342497provider . setVisibility ( true )
498+ handleVisibilitySpy . mockRestore ( )
499+
343500await provider . fetchAndRefresh ( )
344501
345502// Should result in empty workspaces due to error handling
@@ -348,7 +505,7 @@ describe("WorkspaceProvider", () => {
348505} )
349506
350507it ( "should handle workspace query for All workspaces" , async ( ) => {
351- const allProvider = new WorkspaceProvider (
508+ const allProvider = new TestableWorkspaceProvider (
352509WorkspaceQuery . All ,
353510mockRestClient ,
354511mockStorage ,
@@ -360,7 +517,11 @@ describe("WorkspaceProvider", () => {
360517count :1 ,
361518} )
362519
520+ // Mock the handleVisibilityChange to prevent automatic fetchAndRefresh
521+ const handleVisibilitySpy = vi . spyOn ( allProvider , "handleVisibilityChange" ) . mockImplementation ( ( ) => { } )
363522allProvider . setVisibility ( true )
523+ handleVisibilitySpy . mockRestore ( )
524+
364525await allProvider . fetchAndRefresh ( )
365526
366527expect ( mockRestClient . getWorkspaces ) . toHaveBeenCalledWith ( {