@@ -4,9 +4,11 @@ import * as sinon from 'sinon';
44import { EnvironmentManagers , InternalEnvironmentManager , PythonProjectManager } from '../../internal.api' ;
55import * as projectApi from '../../common/pickers/projects' ;
66import * as managerApi from '../../common/pickers/managers' ;
7- import { PythonEnvironment , PythonProject } from '../../api' ;
8- import { createAnyEnvironmentCommand } from '../../features/envCommands' ;
9- import { Uri } from 'vscode' ;
7+ import { PythonEnvironment , PythonEnvironmentApi , PythonProject } from '../../api' ;
8+ import { createAnyEnvironmentCommand , createTerminalCommand } from '../../features/envCommands' ;
9+ import { Terminal , Uri } from 'vscode' ;
10+ import { TerminalManager } from '../../features/terminal/terminalManager' ;
11+ import * as fs from 'fs-extra' ;
1012
1113suite ( 'Create Any Environment Command Tests' , ( ) => {
1214 let em : typeMoq . IMock < EnvironmentManagers > ;
@@ -175,3 +177,128 @@ suite('Create Any Environment Command Tests', () => {
175177 em . verifyAll ( ) ;
176178 } ) ;
177179} ) ;
180+
181+ suite ( 'Create Terminal Command Tests' , ( ) => {
182+ let api : typeMoq . IMock < PythonEnvironmentApi > ;
183+ let tm : typeMoq . IMock < TerminalManager > ;
184+ let env : typeMoq . IMock < PythonEnvironment > ;
185+ let terminal : typeMoq . IMock < Terminal > ;
186+ let pickProjectStub : sinon . SinonStub ;
187+ let fsStatStub : sinon . SinonStub ;
188+ let project1 : PythonProject = {
189+ uri : Uri . file ( '/workspace/folder1' ) ,
190+ name : 'folder1' ,
191+ } ;
192+ let project2 : PythonProject = {
193+ uri : Uri . file ( '/workspace/folder2' ) ,
194+ name : 'folder2' ,
195+ } ;
196+
197+ setup ( ( ) => {
198+ env = typeMoq . Mock . ofType < PythonEnvironment > ( ) ;
199+ env . setup ( ( e ) => e . envId ) . returns ( ( ) => ( { id : 'env1' , managerId : 'test' } ) ) ;
200+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
201+ env . setup ( ( e : any ) => e . then ) . returns ( ( ) => undefined ) ;
202+
203+ terminal = typeMoq . Mock . ofType < Terminal > ( ) ;
204+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
205+ terminal . setup ( ( t : any ) => t . then ) . returns ( ( ) => undefined ) ;
206+
207+ api = typeMoq . Mock . ofType < PythonEnvironmentApi > ( ) ;
208+ tm = typeMoq . Mock . ofType < TerminalManager > ( ) ;
209+
210+ pickProjectStub = sinon . stub ( projectApi , 'pickProject' ) ;
211+ // Stub fs.stat to return a directory stats object to avoid file system access
212+ fsStatStub = sinon . stub ( fs , 'stat' ) ;
213+ fsStatStub . resolves ( { isFile : ( ) => false , isDirectory : ( ) => true } as any ) ;
214+ } ) ;
215+
216+ teardown ( ( ) => {
217+ sinon . restore ( ) ;
218+ } ) ;
219+
220+ test ( 'Single project: should create terminal without prompting' , async ( ) => {
221+ // Setup: single project
222+ api . setup ( ( a ) => a . getPythonProjects ( ) ) . returns ( ( ) => [ project1 ] ) ;
223+ api . setup ( ( a ) => a . getEnvironment ( project1 . uri ) ) . returns ( ( ) => Promise . resolve ( env . object ) ) ;
224+ tm . setup ( ( t ) => t . create ( env . object , typeMoq . It . isAny ( ) ) ) . returns ( ( ) => Promise . resolve ( terminal . object ) ) ;
225+
226+ // pickProject should return the single project without prompting
227+ pickProjectStub . resolves ( project1 ) ;
228+
229+ const result = await createTerminalCommand ( undefined , api . object , tm . object ) ;
230+
231+ assert . strictEqual ( result , terminal . object , 'Expected terminal to be created' ) ;
232+ assert . strictEqual ( pickProjectStub . callCount , 1 , 'pickProject should be called once' ) ;
233+ } ) ;
234+
235+ test ( 'Multiple projects: should prompt user to select project' , async ( ) => {
236+ // Setup: multiple projects
237+ api . setup ( ( a ) => a . getPythonProjects ( ) ) . returns ( ( ) => [ project1 , project2 ] ) ;
238+ api . setup ( ( a ) => a . getEnvironment ( project2 . uri ) ) . returns ( ( ) => Promise . resolve ( env . object ) ) ;
239+ tm . setup ( ( t ) => t . create ( env . object , typeMoq . It . isAny ( ) ) ) . returns ( ( ) => Promise . resolve ( terminal . object ) ) ;
240+
241+ // User selects project2
242+ pickProjectStub . resolves ( project2 ) ;
243+
244+ const result = await createTerminalCommand ( undefined , api . object , tm . object ) ;
245+
246+ assert . strictEqual ( result , terminal . object , 'Expected terminal to be created' ) ;
247+ assert . strictEqual ( pickProjectStub . callCount , 1 , 'pickProject should be called once' ) ;
248+ // Verify pickProject was called with both projects
249+ assert . deepStrictEqual (
250+ pickProjectStub . firstCall . args [ 0 ] ,
251+ [ project1 , project2 ] ,
252+ 'pickProject should be called with all projects' ,
253+ ) ;
254+ } ) ;
255+
256+ test ( 'Uri context with single project: should create terminal without prompting' , async ( ) => {
257+ // Setup: single project
258+ api . setup ( ( a ) => a . getPythonProjects ( ) ) . returns ( ( ) => [ project1 ] ) ;
259+ api . setup ( ( a ) => a . getEnvironment ( project1 . uri ) ) . returns ( ( ) => Promise . resolve ( env . object ) ) ;
260+ tm . setup ( ( t ) => t . create ( env . object , typeMoq . It . isAny ( ) ) ) . returns ( ( ) => Promise . resolve ( terminal . object ) ) ;
261+
262+ // pickProject should return the single project without prompting
263+ pickProjectStub . resolves ( project1 ) ;
264+
265+ const result = await createTerminalCommand ( project1 . uri , api . object , tm . object ) ;
266+
267+ assert . strictEqual ( result , terminal . object , 'Expected terminal to be created' ) ;
268+ assert . strictEqual ( pickProjectStub . callCount , 1 , 'pickProject should be called once' ) ;
269+ } ) ;
270+
271+ test ( 'Uri context with multiple projects: should prompt user to select project' , async ( ) => {
272+ // Setup: multiple projects, context is project1.uri but user should still be prompted
273+ api . setup ( ( a ) => a . getPythonProjects ( ) ) . returns ( ( ) => [ project1 , project2 ] ) ;
274+ api . setup ( ( a ) => a . getEnvironment ( project2 . uri ) ) . returns ( ( ) => Promise . resolve ( env . object ) ) ;
275+ tm . setup ( ( t ) => t . create ( env . object , typeMoq . It . isAny ( ) ) ) . returns ( ( ) => Promise . resolve ( terminal . object ) ) ;
276+
277+ // User selects project2 (different from context)
278+ pickProjectStub . resolves ( project2 ) ;
279+
280+ const result = await createTerminalCommand ( project1 . uri , api . object , tm . object ) ;
281+
282+ assert . strictEqual ( result , terminal . object , 'Expected terminal to be created' ) ;
283+ assert . strictEqual ( pickProjectStub . callCount , 1 , 'pickProject should be called once' ) ;
284+ // Verify pickProject was called with all projects, not just the context
285+ assert . deepStrictEqual (
286+ pickProjectStub . firstCall . args [ 0 ] ,
287+ [ project1 , project2 ] ,
288+ 'pickProject should be called with all projects' ,
289+ ) ;
290+ } ) ;
291+
292+ test ( 'User cancels project selection: should not create terminal' , async ( ) => {
293+ // Setup: multiple projects
294+ api . setup ( ( a ) => a . getPythonProjects ( ) ) . returns ( ( ) => [ project1 , project2 ] ) ;
295+
296+ // User cancels selection
297+ pickProjectStub . resolves ( undefined ) ;
298+
299+ const result = await createTerminalCommand ( undefined , api . object , tm . object ) ;
300+
301+ assert . strictEqual ( result , undefined , 'Expected no terminal to be created when user cancels' ) ;
302+ assert . strictEqual ( pickProjectStub . callCount , 1 , 'pickProject should be called once' ) ;
303+ } ) ;
304+ } ) ;
0 commit comments