@@ -23,16 +23,21 @@ import { TestInstantiationService } from '../../../../../../../platform/instanti
2323import { IStorageService } from '../../../../../../../platform/storage/common/storage.js' ;
2424import { ITelemetryService } from '../../../../../../../platform/telemetry/common/telemetry.js' ;
2525import { NullTelemetryService } from '../../../../../../../platform/telemetry/common/telemetryUtils.js' ;
26- import { IWorkbenchLayoutService } from '../../../../../../../workbench/services/layout/browser/layoutService.js' ;
26+ import { IView } from '../../../../../../../workbench/common/views.js' ;
27+ import { IViewsService } from '../../../../../../../workbench/services/views/common/viewsService.js' ;
28+ import { IAgentWorkbenchLayoutService } from '../../../../../../browser/workbench.js' ;
2729import { Menus } from '../../../../../../browser/menus.js' ;
2830import { IAgentHostSessionsProvider , LOCAL_AGENT_HOST_PROVIDER_ID } from '../../../../../../common/agentHostSessionsProvider.js' ;
31+ import { ISessionChangesService } from '../../../../../../contrib/changes/browser/sessionChangesService.js' ;
32+ import { CHANGES_VIEW_ID } from '../../../../../../contrib/changes/common/changes.js' ;
2933import { ISessionsProvidersService } from '../../../../../../services/sessions/browser/sessionsProvidersService.js' ;
3034import { IActiveSession } from '../../../../../../services/sessions/common/sessionsManagement.js' ;
3135import { ISessionWorkspace } from '../../../../../../services/sessions/common/session.js' ;
3236import { ISessionsProvider } from '../../../../../../services/sessions/common/sessionsProvider.js' ;
3337import { AgentHostSessionConfigPicker , IConfigPickerItem , PickerActionViewItem } from '../../../browser/agentHostSessionConfigPicker.js' ;
3438
3539const SESSION_ID = 'local-agent-host:s1' ;
40+ const SESSION_RESOURCE = URI . parse ( 'agent-session:/s1' ) ;
3641
3742function makeWorkspace ( uncommittedChanges : number | undefined , branchName = 'main' ) : ISessionWorkspace {
3843 const root = URI . file ( '/repo' ) ;
@@ -195,6 +200,7 @@ function branchState(container: HTMLElement): { icon: string | undefined; ariaLa
195200class CapturingActionWidgetHolder {
196201 delegate : IActionListDelegate < IConfigPickerItem > | undefined ;
197202 items : readonly IActionListItem < IConfigPickerItem > [ ] = [ ] ;
203+ readonly events : string [ ] = [ ] ;
198204}
199205
200206function setupServices ( store : Pick < ReturnType < typeof ensureNoDisposablesAreLeakedInTestSuite > , 'add' > ) {
@@ -205,7 +211,7 @@ function setupServices(store: Pick<ReturnType<typeof ensureNoDisposablesAreLeake
205211 const instantiationService = store . add ( new TestInstantiationService ( ) ) ;
206212 instantiationService . stub ( IActionWidgetService , {
207213 isVisible : false ,
208- hide : ( ) => { } ,
214+ hide : ( ) => actionWidget . events . push ( 'hide' ) ,
209215 show : ( _user , _supportsPreview , items : readonly IActionListItem < IConfigPickerItem > [ ] , delegate : IActionListDelegate < IConfigPickerItem > ) => {
210216 actionWidget . items = items ;
211217 actionWidget . delegate = delegate ;
@@ -219,9 +225,26 @@ function setupServices(store: Pick<ReturnType<typeof ensureNoDisposablesAreLeake
219225 instantiationService . stub ( IContextKeyService , new ( class extends mock < IContextKeyService > ( ) {
220226 override readonly onDidChangeContext = Event . None ;
221227 } ) ( ) ) ;
222- instantiationService . stub ( IWorkbenchLayoutService , new ( class extends mock < IWorkbenchLayoutService > ( ) {
228+ instantiationService . stub ( IAgentWorkbenchLayoutService , new ( class extends mock < IAgentWorkbenchLayoutService > ( ) {
223229 // No `phone-layout` class → `isPhoneLayout` is false → isolation renders as a checkbox.
224230 override readonly mainContainer = document . createElement ( 'div' ) ;
231+ override readonly isSinglePaneLayoutEnabled = true ;
232+ override suppressEditorPartAutoVisibility ( ) {
233+ actionWidget . events . push ( 'suppressEditorPartAutoVisibility' ) ;
234+ return { dispose : ( ) => actionWidget . events . push ( 'releaseEditorPartAutoVisibility' ) } ;
235+ }
236+ } ) ( ) ) ;
237+ instantiationService . stub ( ISessionChangesService , new ( class extends mock < ISessionChangesService > ( ) {
238+ override async openChangesEditor ( sessionResource : URI ) : Promise < undefined > {
239+ actionWidget . events . push ( `openChangesEditor:${ sessionResource . toString ( ) } ` ) ;
240+ return undefined ;
241+ }
242+ } ) ( ) ) ;
243+ instantiationService . stub ( IViewsService , new ( class extends mock < IViewsService > ( ) {
244+ override async openView < T extends IView > ( id : string , focus ?: boolean ) : Promise < T | null > {
245+ actionWidget . events . push ( `openView:${ id } :${ focus } ` ) ;
246+ return null ;
247+ }
225248 } ) ( ) ) ;
226249 instantiationService . set ( ISessionsProvidersService , new ( class extends mock < ISessionsProvidersService > ( ) {
227250 override readonly onDidChangeProviders = Event . None ;
@@ -236,6 +259,7 @@ function setupServices(store: Pick<ReturnType<typeof ensureNoDisposablesAreLeake
236259 const sessionObs = observableValue < IActiveSession | undefined > ( 'activeSession' , {
237260 providerId : LOCAL_AGENT_HOST_PROVIDER_ID ,
238261 sessionId : SESSION_ID ,
262+ resource : SESSION_RESOURCE ,
239263 workspace,
240264 } as IActiveSession ) ;
241265 return { instantiationService, provider, sessionObs, workspaceObs, actionWidget } ;
@@ -400,6 +424,7 @@ suite('Agent Host Session Config Picker', () => {
400424 checked : item . item ?. checked ,
401425 detail : item . detail ,
402426 ariaDescription : item . ariaDescription ,
427+ toolbarActions : item . toolbarActions ?. map ( action => ( { id : action . id , label : action . label } ) ) ,
403428 } ) ) ;
404429
405430 services . workspaceObs . set ( makeWorkspace ( 1 , 'dev' ) , undefined ) ;
@@ -412,13 +437,19 @@ suite('Agent Host Session Config Picker', () => {
412437 ariaDescription : singularItem ?. ariaDescription ,
413438 } ;
414439
440+ services . workspaceObs . set ( makeWorkspace ( 0 , 'dev' ) , undefined ) ;
441+ branchSlot ( container ) ! . querySelector < HTMLElement > ( 'a.action-label' ) !
442+ . dispatchEvent ( new MouseEvent ( 'click' , { bubbles : true , cancelable : true } ) ) ;
443+ await new Promise ( resolve => setTimeout ( resolve ) ) ;
444+ const cleanToolbarActions = services . actionWidget . items . find ( item => item . label === 'dev' ) ?. toolbarActions ;
445+
415446 services . provider . completions = [ { value : 'main' , label : 'main' } ] ;
416447 branchSlot ( container ) ! . querySelector < HTMLElement > ( 'a.action-label' ) !
417448 . dispatchEvent ( new MouseEvent ( 'click' , { bubbles : true , cancelable : true } ) ) ;
418449 await new Promise ( resolve => setTimeout ( resolve ) ) ;
419450 const singleResultKinds = services . actionWidget . items . map ( item => item . kind ) ;
420451
421- assert . deepStrictEqual ( { plural, singular, singleResultKinds } , {
452+ assert . deepStrictEqual ( { plural, singular, cleanToolbarActions , singleResultKinds } , {
422453 plural : [
423454 {
424455 kind : ActionListItemKind . Action ,
@@ -427,6 +458,7 @@ suite('Agent Host Session Config Picker', () => {
427458 checked : true ,
428459 detail : undefined ,
429460 ariaDescription : undefined ,
461+ toolbarActions : undefined ,
430462 } ,
431463 {
432464 kind : ActionListItemKind . Separator ,
@@ -435,6 +467,7 @@ suite('Agent Host Session Config Picker', () => {
435467 checked : undefined ,
436468 detail : undefined ,
437469 ariaDescription : undefined ,
470+ toolbarActions : undefined ,
438471 } ,
439472 {
440473 kind : ActionListItemKind . Action ,
@@ -443,16 +476,45 @@ suite('Agent Host Session Config Picker', () => {
443476 checked : false ,
444477 detail : '2 uncommitted files' ,
445478 ariaDescription : '2 uncommitted files' ,
479+ toolbarActions : [ {
480+ id : 'sessions.agentHost.showBranchChanges' ,
481+ label : 'Show Changes' ,
482+ } ] ,
446483 } ,
447484 ] ,
448485 singular : {
449486 detail : '1 uncommitted file' ,
450487 ariaDescription : '1 uncommitted file' ,
451488 } ,
489+ cleanToolbarActions : undefined ,
452490 singleResultKinds : [ ActionListItemKind . Action ] ,
453491 } ) ;
454492 } ) ;
455493
494+ test ( 'dirty branch action selects the Changes tab before focusing the Changes view' , async ( ) => {
495+ const services = setupServices ( store ) ;
496+ services . provider . config = makeDynamicBranchConfig ( 'main' ) ;
497+ services . provider . completions = [
498+ { value : 'main' , label : 'main' } ,
499+ { value : 'dev' , label : 'dev' } ,
500+ ] ;
501+ services . workspaceObs . set ( makeWorkspace ( 1 , 'dev' ) , undefined ) ;
502+ const { container } = renderPicker ( store , services ) ;
503+
504+ branchSlot ( container ) ! . querySelector < HTMLElement > ( 'a.action-label' ) ! . click ( ) ;
505+ await new Promise ( resolve => setTimeout ( resolve ) ) ;
506+ const action = services . actionWidget . items . find ( item => item . label === 'dev' ) ?. toolbarActions ?. [ 0 ] ;
507+ await action ?. run ( ) ;
508+
509+ assert . deepStrictEqual ( services . actionWidget . events , [
510+ 'hide' ,
511+ 'suppressEditorPartAutoVisibility' ,
512+ `openChangesEditor:${ SESSION_RESOURCE . toString ( ) } ` ,
513+ 'releaseEditorPartAutoVisibility' ,
514+ `openView:${ CHANGES_VIEW_ID } :true` ,
515+ ] ) ;
516+ } ) ;
517+
456518 test ( 'a picker recreated on a session switch still renders the provider-seeded chips (disabled) while resolving' , ( ) => {
457519 const services = setupServices ( store ) ;
458520 const { provider } = services ;
0 commit comments