22
33import { Terminal } from "../Terminal"
44import { TerminalRegistry } from "../TerminalRegistry"
5+ import * as vscode from "vscode"
56
67const PAGER = process . platform === "win32" ? "" : "cat"
78
89// Mock vscode.window.createTerminal
910const mockCreateTerminal = jest . fn ( )
1011
12+ // Event handlers for testing
13+ let mockStartHandler : any = null
14+ let mockEndHandler : any = null
15+
1116jest . mock ( "vscode" , ( ) => ( {
1217 window : {
1318 createTerminal : ( ...args : any [ ] ) => {
1419 mockCreateTerminal ( ...args )
1520 return {
21+ name : "Roo Code" ,
1622 exitStatus : undefined ,
23+ dispose : jest . fn ( ) ,
24+ show : jest . fn ( ) ,
25+ hide : jest . fn ( ) ,
26+ sendText : jest . fn ( ) ,
1727 }
1828 } ,
1929 onDidCloseTerminal : jest . fn ( ) . mockReturnValue ( { dispose : jest . fn ( ) } ) ,
30+ onDidStartTerminalShellExecution : jest . fn ( ) . mockImplementation ( ( handler ) => {
31+ mockStartHandler = handler
32+ return { dispose : jest . fn ( ) }
33+ } ) ,
34+ onDidEndTerminalShellExecution : jest . fn ( ) . mockImplementation ( ( handler ) => {
35+ mockEndHandler = handler
36+ return { dispose : jest . fn ( ) }
37+ } ) ,
2038 } ,
2139 ThemeIcon : jest . fn ( ) ,
2240} ) )
@@ -28,6 +46,15 @@ jest.mock("execa", () => ({
2846describe ( "TerminalRegistry" , ( ) => {
2947 beforeEach ( ( ) => {
3048 mockCreateTerminal . mockClear ( )
49+
50+ // Reset event handlers
51+ mockStartHandler = null
52+ mockEndHandler = null
53+
54+ // Clear terminals array for each test
55+ ; ( TerminalRegistry as any ) . terminals = [ ]
56+ ; ( TerminalRegistry as any ) . nextTerminalId = 1
57+ ; ( TerminalRegistry as any ) . isInitialized = false
3158 } )
3259
3360 describe ( "createTerminal" , ( ) => {
@@ -113,4 +140,188 @@ describe("TerminalRegistry", () => {
113140 }
114141 } )
115142 } )
143+
144+ describe ( "busy flag management" , ( ) => {
145+ let mockVsTerminal : any
146+
147+ beforeEach ( ( ) => {
148+ mockVsTerminal = {
149+ name : "Roo Code" ,
150+ exitStatus : undefined ,
151+ dispose : jest . fn ( ) ,
152+ show : jest . fn ( ) ,
153+ hide : jest . fn ( ) ,
154+ sendText : jest . fn ( ) ,
155+ }
156+ mockCreateTerminal . mockReturnValue ( mockVsTerminal )
157+ } )
158+
159+ // Helper function to get the created Roo terminal and its underlying VSCode terminal
160+ const createTerminalAndGetVsTerminal = ( path : string = "/test/path" ) => {
161+ const rooTerminal = TerminalRegistry . createTerminal ( path , "vscode" )
162+ // Get the actual VSCode terminal that was created and stored
163+ const vsTerminal = ( rooTerminal as any ) . terminal
164+ return { rooTerminal, vsTerminal }
165+ }
166+
167+ it ( "should initialize terminal with busy = false" , ( ) => {
168+ const terminal = TerminalRegistry . createTerminal ( "/test/path" , "vscode" )
169+ expect ( terminal . busy ) . toBe ( false )
170+ } )
171+
172+ it ( "should set busy = true when shell execution starts" , ( ) => {
173+ // Initialize the registry to set up event handlers
174+ TerminalRegistry . initialize ( )
175+
176+ // Create a terminal and get the actual VSCode terminal
177+ const { rooTerminal, vsTerminal } = createTerminalAndGetVsTerminal ( )
178+ expect ( rooTerminal . busy ) . toBe ( false )
179+
180+ // Simulate shell execution start event
181+ const execution = {
182+ commandLine : { value : "echo test" } ,
183+ read : jest . fn ( ) . mockReturnValue ( { } ) ,
184+ } as any
185+
186+ if ( mockStartHandler ) {
187+ mockStartHandler ( {
188+ terminal : vsTerminal ,
189+ execution,
190+ } )
191+ }
192+
193+ expect ( rooTerminal . busy ) . toBe ( true )
194+ } )
195+
196+ it ( "should set busy = false when shell execution ends for Roo terminals" , ( ) => {
197+ // Initialize the registry to set up event handlers
198+ TerminalRegistry . initialize ( )
199+
200+ // Create a terminal and get the actual VSCode terminal
201+ const { rooTerminal, vsTerminal } = createTerminalAndGetVsTerminal ( )
202+ rooTerminal . busy = true
203+
204+ // Set up a mock process to simulate running state
205+ const mockProcess = {
206+ command : "echo test" ,
207+ isHot : false ,
208+ hasUnretrievedOutput : ( ) => false ,
209+ }
210+ rooTerminal . process = mockProcess as any
211+
212+ // Simulate shell execution end event
213+ const execution = {
214+ commandLine : { value : "echo test" } ,
215+ } as any
216+
217+ if ( mockEndHandler ) {
218+ mockEndHandler ( {
219+ terminal : vsTerminal ,
220+ execution,
221+ exitCode : 0 ,
222+ } )
223+ }
224+
225+ expect ( rooTerminal . busy ) . toBe ( false )
226+ } )
227+
228+ it ( "should set busy = false when shell execution ends for non-Roo terminals (manual commands)" , ( ) => {
229+ // Initialize the registry to set up event handlers
230+ TerminalRegistry . initialize ( )
231+
232+ // Simulate a shell execution end event for a terminal not in our registry
233+ const unknownVsTerminal = {
234+ name : "Unknown Terminal" ,
235+ }
236+
237+ const execution = {
238+ commandLine : { value : "sleep 30" } ,
239+ } as any
240+
241+ // This should not throw an error and should handle the case gracefully
242+ expect ( ( ) => {
243+ if ( mockEndHandler ) {
244+ mockEndHandler ( {
245+ terminal : unknownVsTerminal ,
246+ execution,
247+ exitCode : 0 ,
248+ } )
249+ }
250+ } ) . not . toThrow ( )
251+ } )
252+
253+ it ( "should handle busy flag reset when terminal process is not running" , ( ) => {
254+ // Initialize the registry to set up event handlers
255+ TerminalRegistry . initialize ( )
256+
257+ // Create a terminal and get the actual VSCode terminal
258+ const { rooTerminal, vsTerminal } = createTerminalAndGetVsTerminal ( )
259+ rooTerminal . busy = true
260+
261+ // Ensure terminal.running returns false (no active process)
262+ Object . defineProperty ( rooTerminal , "running" , {
263+ get : ( ) => false ,
264+ configurable : true ,
265+ } )
266+
267+ // Simulate shell execution end event
268+ const execution = {
269+ commandLine : { value : "echo test" } ,
270+ } as any
271+
272+ if ( mockEndHandler ) {
273+ mockEndHandler ( {
274+ terminal : vsTerminal ,
275+ execution,
276+ exitCode : 0 ,
277+ } )
278+ }
279+
280+ // Should reset busy flag even when not running
281+ expect ( rooTerminal . busy ) . toBe ( false )
282+ } )
283+
284+ it ( "should maintain busy state during command execution lifecycle" , ( ) => {
285+ // Initialize the registry to set up event handlers
286+ TerminalRegistry . initialize ( )
287+
288+ // Create a terminal and get the actual VSCode terminal
289+ const { rooTerminal, vsTerminal } = createTerminalAndGetVsTerminal ( )
290+ expect ( rooTerminal . busy ) . toBe ( false )
291+
292+ // Start execution
293+ const execution = {
294+ commandLine : { value : "npm test" } ,
295+ read : jest . fn ( ) . mockReturnValue ( { } ) ,
296+ } as any
297+
298+ if ( mockStartHandler ) {
299+ mockStartHandler ( {
300+ terminal : vsTerminal ,
301+ execution,
302+ } )
303+ }
304+
305+ expect ( rooTerminal . busy ) . toBe ( true )
306+
307+ // Set up mock process for running state
308+ const mockProcess = {
309+ command : "npm test" ,
310+ isHot : true ,
311+ hasUnretrievedOutput : ( ) => true ,
312+ }
313+ rooTerminal . process = mockProcess as any
314+
315+ // End execution
316+ if ( mockEndHandler ) {
317+ mockEndHandler ( {
318+ terminal : vsTerminal ,
319+ execution,
320+ exitCode : 0 ,
321+ } )
322+ }
323+
324+ expect ( rooTerminal . busy ) . toBe ( false )
325+ } )
326+ } )
116327} )
0 commit comments