@@ -527,10 +527,7 @@ describe("ClineProvider", () => {
527527 cspSource : "vscode-webview://test-csp-source" ,
528528 } ,
529529 visible : true ,
530- onDidDispose : vi . fn ( ) . mockImplementation ( ( callback ) => {
531- callback ( )
532- return { dispose : vi . fn ( ) }
533- } ) ,
530+ onDidDispose : vi . fn ( ) ,
534531 onDidChangeVisibility : vi . fn ( ) . mockImplementation ( ( ) => {
535532 return { dispose : vi . fn ( ) }
536533 } ) ,
@@ -665,6 +662,7 @@ describe("ClineProvider", () => {
665662
666663 describe ( "webview heartbeat watchdog" , ( ) => {
667664 let visibilityCallback : ( ) => void
665+ let disposeCallback : ( ) => void
668666
669667 beforeEach ( ( ) => {
670668 // Fake timers must be active before resolveWebviewView so the
@@ -674,6 +672,10 @@ describe("ClineProvider", () => {
674672 visibilityCallback = cb
675673 return { dispose : vi . fn ( ) }
676674 } )
675+ mockWebviewView . onDidDispose = vi . fn ( ) . mockImplementation ( ( cb : ( ) => void ) => {
676+ disposeCallback = cb
677+ return { dispose : vi . fn ( ) }
678+ } )
677679 } )
678680
679681 afterEach ( async ( ) => {
@@ -743,6 +745,23 @@ describe("ClineProvider", () => {
743745 expect ( mockWebviewView . webview . html ) . toBe ( htmlAfterResolve )
744746 } )
745747
748+ test ( "stops watching and clears the view when the sidebar webview is disposed" , async ( ) => {
749+ await provider . resolveWebviewView ( mockWebviewView )
750+ const htmlAfterResolve = mockWebviewView . webview . html
751+
752+ // The provider outlives a disposed sidebar view; VS Code re-resolves
753+ // a fresh view later. Disposal must stop the watchdog and drop the
754+ // stale view reference so no recovery reload targets the dead view.
755+ disposeCallback ( )
756+ expect ( provider [ "webviewWatchdogInterval" ] ) . toBeNull ( )
757+ // @ts -ignore - accessing private property for testing
758+ expect ( provider . view ) . toBeUndefined ( )
759+
760+ await vi . advanceTimersByTimeAsync ( 180_000 )
761+
762+ expect ( mockWebviewView . webview . html ) . toBe ( htmlAfterResolve )
763+ } )
764+
746765 test ( "does not stack watchdog intervals when resolveWebviewView runs again" , async ( ) => {
747766 await provider . resolveWebviewView ( mockWebviewView )
748767 await provider . resolveWebviewView ( mockWebviewView )
@@ -781,10 +800,7 @@ describe("ClineProvider", () => {
781800 cspSource : "vscode-webview://test-csp-source" ,
782801 } ,
783802 visible : true ,
784- onDidDispose : vi . fn ( ) . mockImplementation ( ( callback : ( ) => void ) => {
785- callback ( )
786- return { dispose : vi . fn ( ) }
787- } ) ,
803+ onDidDispose : vi . fn ( ) ,
788804 onDidChangeVisibility : vi . fn ( ) . mockImplementation ( ( ) => ( { dispose : vi . fn ( ) } ) ) ,
789805 } as unknown as vscode . WebviewView
790806 const providerB = new ClineProvider (
@@ -801,7 +817,8 @@ describe("ClineProvider", () => {
801817 const htmlBeforeB = mockWebviewViewB . webview . html
802818
803819 await vi . advanceTimersByTimeAsync ( 119_000 )
804- // Keep provider B's heartbeat fresh so only A's watchdog fires.
820+ // B's view is still alive; refresh its heartbeat so a stale one
821+ // would reload it too, leaving only A's watchdog to fire.
805822 await webviewMessageHandler ( providerB , { type : "webviewHeartbeat" , timestamp : Date . now ( ) } )
806823 await vi . advanceTimersByTimeAsync ( 1_000 )
807824
@@ -3867,10 +3884,7 @@ describe("ClineProvider - Router Models", () => {
38673884 asWebviewUri : vi . fn ( ) ,
38683885 } ,
38693886 visible : true ,
3870- onDidDispose : vi . fn ( ) . mockImplementation ( ( callback ) => {
3871- callback ( )
3872- return { dispose : vi . fn ( ) }
3873- } ) ,
3887+ onDidDispose : vi . fn ( ) ,
38743888 onDidChangeVisibility : vi . fn ( ) . mockImplementation ( ( ) => {
38753889 return { dispose : vi . fn ( ) }
38763890 } ) ,
@@ -4221,10 +4235,7 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => {
42214235 asWebviewUri : vi . fn ( ) ,
42224236 } ,
42234237 visible : true ,
4224- onDidDispose : vi . fn ( ) . mockImplementation ( ( callback ) => {
4225- callback ( )
4226- return { dispose : vi . fn ( ) }
4227- } ) ,
4238+ onDidDispose : vi . fn ( ) ,
42284239 onDidChangeVisibility : vi . fn ( ) . mockImplementation ( ( ) => {
42294240 return { dispose : vi . fn ( ) }
42304241 } ) ,
0 commit comments