@@ -12,6 +12,7 @@ import { mock, upcastPartial } from '../../../../../../base/test/common/mock.js'
1212import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js' ;
1313import { IAgentHostConnectionsService } from '../../../../../../platform/agentHost/common/agentHostConnectionsService.js' ;
1414import { IAgentConnection } from '../../../../../../platform/agentHost/common/agentService.js' ;
15+ import { IActionWidgetService } from '../../../../../../platform/actionWidget/browser/actionWidget.js' ;
1516import { ChangesetKind } from '../../../../../../platform/agentHost/common/changesetUri.js' ;
1617import { IAgentSubscription } from '../../../../../../platform/agentHost/common/state/agentSubscription.js' ;
1718import { ISessionArtifact , SessionArtifactType , withSessionArtifacts } from '../../../../../../platform/agentHost/common/sessionArtifacts.js' ;
@@ -66,6 +67,18 @@ class StaticAgentConnection extends mock<IAgentConnection>() {
6667 }
6768}
6869
70+ class TestOpenerService extends mock < IOpenerService > ( ) {
71+ readonly opened : { readonly resource : URI ; readonly openExternal : boolean | undefined } [ ] = [ ] ;
72+
73+ override async open ( resource : URI | string , options ?: Parameters < IOpenerService [ 'open' ] > [ 1 ] ) : Promise < boolean > {
74+ this . opened . push ( {
75+ resource : typeof resource === 'string' ? URI . parse ( resource ) : resource ,
76+ openExternal : options && 'openExternal' in options ? options . openExternal : undefined ,
77+ } ) ;
78+ return true ;
79+ }
80+ }
81+
6982suite ( 'AgentHostSessionInputPills' , ( ) => {
7083 const store = ensureNoDisposablesAreLeakedInTestSuite ( ) ;
7184
@@ -89,6 +102,7 @@ suite('AgentHostSessionInputPills', () => {
89102
90103 assert . deepStrictEqual ( {
91104 pullRequestUrls : metadata . pullRequestUrls ,
105+ pullRequestTitles : [ ...metadata . pullRequestTitles ] ,
92106 issueUrls : metadata . issueUrls ,
93107 issueTitles : [ ...metadata . issueTitles ] ,
94108 artifactIds : metadata . artifacts . map ( artifact => artifact . id ) ,
@@ -98,6 +112,7 @@ suite('AgentHostSessionInputPills', () => {
98112 'https://github.com/microsoft/vscode/pull/2' ,
99113 'https://github.com/microsoft/vscode/pull/1' ,
100114 ] ,
115+ pullRequestTitles : [ [ 'https://github.com/microsoft/vscode/pull/2' , 'Created PR' ] ] ,
101116 issueUrls : [ 'https://github.com/microsoft/vscode/issues/3' ] ,
102117 issueTitles : [ [ 'https://github.com/microsoft/vscode/issues/3' , 'Created Issue' ] ] ,
103118 artifactIds : [ 'website' ] ,
@@ -122,6 +137,7 @@ suite('AgentHostSessionInputPills', () => {
122137
123138 assert . deepStrictEqual ( {
124139 pullRequestUrls : metadata . pullRequestUrls ,
140+ pullRequestTitles : [ ...metadata . pullRequestTitles ] ,
125141 issueUrls : metadata . issueUrls ,
126142 issueTitles : [ ...metadata . issueTitles ] ,
127143 artifactIds : metadata . artifacts . map ( artifact => artifact . id ) ,
@@ -131,6 +147,10 @@ suite('AgentHostSessionInputPills', () => {
131147 'https://github.com/microsoft/vscode/pull/2' ,
132148 'https://github.com/microsoft/vscode/pull/1' ,
133149 ] ,
150+ pullRequestTitles : [
151+ [ 'https://github.com/microsoft/vscode/pull/2' , 'New PR' ] ,
152+ [ 'https://github.com/microsoft/vscode/pull/1' , 'Old PR' ] ,
153+ ] ,
134154 issueUrls : [
135155 'https://github.com/microsoft/vscode/issues/2' ,
136156 'https://github.com/microsoft/vscode/issues/1' ,
@@ -144,23 +164,43 @@ suite('AgentHostSessionInputPills', () => {
144164 } ) ;
145165 } ) ;
146166
147- test ( 'renders recorded issue titles in editor and panel pills' , ( ) => {
167+ test ( 'renders recorded GitHub titles in editor and panel pills' , ( ) => {
148168 const instantiationService = workbenchInstantiationService ( undefined , store ) ;
149169 const sessionResource = URI . parse ( 'agent-host-copilot:/session' ) ;
150170 const backendSession = URI . parse ( 'copilot:/session' ) ;
151171 const issueUrl = 'https://github.com/microsoft/vscode/issues/335383' ;
172+ const firstPullRequestUrl = 'https://github.com/microsoft/vscode/pull/335387' ;
173+ const secondPullRequestUrl = 'https://github.com/microsoft/vscode/pull/332982' ;
152174 const connection = new StaticAgentConnection ( new Map < StateComponents , SessionState | ChangesetState > ( [
153175 [ StateComponents . Session , {
154176 defaultChat : buildDefaultChatUri ( backendSession ) ,
155177 chats : [ ] ,
156- _meta : withSessionArtifacts ( undefined , [ {
157- id : 'issue' ,
158- type : SessionArtifactType . Issue ,
159- label : 'Agent Window issue pill discards the recorded issue title' ,
160- link : issueUrl ,
161- isGitHub : true ,
162- isArtifact : true ,
163- } ] ) ,
178+ _meta : withSessionArtifacts ( undefined , [
179+ {
180+ id : 'issue' ,
181+ type : SessionArtifactType . Issue ,
182+ label : 'Agent Window issue pill discards the recorded issue title' ,
183+ link : issueUrl ,
184+ isGitHub : true ,
185+ isArtifact : true ,
186+ } ,
187+ {
188+ id : 'first-pr' ,
189+ type : SessionArtifactType . PullRequest ,
190+ label : 'sessions: preserve recorded issue titles in pills' ,
191+ link : firstPullRequestUrl ,
192+ isGitHub : true ,
193+ isArtifact : true ,
194+ } ,
195+ {
196+ id : 'second-pr' ,
197+ type : SessionArtifactType . PullRequest ,
198+ label : 'Chat: unify Agent Host status pills across chat surfaces' ,
199+ link : secondPullRequestUrl ,
200+ isGitHub : true ,
201+ isArtifact : true ,
202+ } ,
203+ ] ) ,
164204 } as unknown as SessionState ] ,
165205 ] ) ) ;
166206 const persistentContent = document . createElement ( 'div' ) ;
@@ -187,18 +227,22 @@ suite('AgentHostSessionInputPills', () => {
187227 } ) ;
188228 const visibility = store . add ( instantiationService . createInstance ( SessionChatPillVisibility ) ) ;
189229 instantiationService . stub ( ISessionChatPillVisibilityService , visibility ) ;
230+ let dropdownLabels : readonly string [ ] = [ ] ;
231+ instantiationService . stub ( IActionWidgetService , upcastPartial < IActionWidgetService > ( {
232+ isVisible : false ,
233+ show : ( _user , _supportsPreview , items ) => {
234+ dropdownLabels = items . map ( item => item . label ?? '' ) ;
235+ } ,
236+ hide : ( ) => { } ,
237+ updateItems : ( ) => { } ,
238+ focusItemById : ( ) => { } ,
239+ } ) ) ;
190240 const [ clipboardService , configurationService , editorService ] = instantiationService . invokeFunction ( accessor => [
191241 accessor . get ( IClipboardService ) ,
192242 accessor . get ( IConfigurationService ) ,
193243 accessor . get ( IEditorService ) ,
194244 ] as const ) ;
195- const opened : { readonly resource : URI ; readonly openExternal : boolean | undefined } [ ] = [ ] ;
196- const openerService = upcastPartial < IOpenerService > ( {
197- open : async ( resource , options ) => {
198- opened . push ( { resource, openExternal : options ?. openExternal } ) ;
199- return true ;
200- } ,
201- } ) ;
245+ const openerService = new TestOpenerService ( ) ;
202246
203247 store . add ( new AgentHostSessionInputPills (
204248 widget ,
@@ -213,17 +257,37 @@ suite('AgentHostSessionInputPills', () => {
213257 visibility ,
214258 ) ) ;
215259
216- const button = persistentContent . querySelector < HTMLElement > ( '.chat-dropdown-pill-button' ) ;
217- button ?. click ( ) ;
260+ const buttons = [ ...persistentContent . querySelectorAll < HTMLElement > ( '.chat-dropdown-pill-button' ) ] ;
261+ const [ pullRequestButton , issueButton ] = buttons ;
262+ pullRequestButton ?. click ( ) ;
263+ issueButton ?. click ( ) ;
218264 assert . deepStrictEqual ( {
219- label : button ?. querySelector ( '.chat-pill-label' ) ?. textContent ,
220- ariaLabel : button ?. getAttribute ( 'aria-label' ) ,
221- ariaDescription : button ?. getAttribute ( 'aria-description' ) ,
222- opened : opened . map ( ( { resource, openExternal } ) => ( { resource : resource . toString ( true ) , openExternal } ) ) ,
265+ pullRequests : {
266+ label : pullRequestButton ?. querySelector ( '.chat-pill-label' ) ?. textContent ,
267+ ariaLabel : pullRequestButton ?. getAttribute ( 'aria-label' ) ,
268+ dropdownLabels,
269+ } ,
270+ issue : {
271+ label : issueButton ?. querySelector ( '.chat-pill-label' ) ?. textContent ,
272+ ariaLabel : issueButton ?. getAttribute ( 'aria-label' ) ,
273+ ariaDescription : issueButton ?. getAttribute ( 'aria-description' ) ,
274+ } ,
275+ opened : openerService . opened . map ( ( { resource, openExternal } ) => ( { resource : resource . toString ( true ) , openExternal } ) ) ,
223276 } , {
224- label : 'Issue #335383: Agent Window issue pill discards the recorded issue title' ,
225- ariaLabel : 'Open Issue #335383: Agent Window issue pill discards the recorded issue title' ,
226- ariaDescription : issueUrl ,
277+ pullRequests : {
278+ label : '2 Pull Requests' ,
279+ ariaLabel : 'Show 2 pull requests' ,
280+ dropdownLabels : [
281+ 'Pull Requests' ,
282+ 'Pull Request #332982: Chat: unify Agent Host status pills across chat surfaces' ,
283+ 'Pull Request #335387: sessions: preserve recorded issue titles in pills' ,
284+ ] ,
285+ } ,
286+ issue : {
287+ label : 'Issue #335383: Agent Window issue pill discards the recorded issue title' ,
288+ ariaLabel : 'Open Issue #335383: Agent Window issue pill discards the recorded issue title' ,
289+ ariaDescription : issueUrl ,
290+ } ,
227291 opened : [ { resource : issueUrl , openExternal : true } ] ,
228292 } ) ;
229293 } ) ;
@@ -575,13 +639,7 @@ suite('AgentHostSessionInputPills', () => {
575639 accessor . get ( IConfigurationService ) ,
576640 accessor . get ( IEditorService ) ,
577641 ] as const ) ;
578- const opened : { readonly resource : URI ; readonly openExternal : boolean | undefined } [ ] = [ ] ;
579- const openerService = upcastPartial < IOpenerService > ( {
580- open : async ( resource , options ) => {
581- opened . push ( { resource, openExternal : options ?. openExternal } ) ;
582- return true ;
583- } ,
584- } ) ;
642+ const openerService = new TestOpenerService ( ) ;
585643
586644 store . add ( new AgentHostSessionInputPills (
587645 widget ,
@@ -632,7 +690,7 @@ suite('AgentHostSessionInputPills', () => {
632690 multiple,
633691 filteredLabel,
634692 single,
635- opened : opened . map ( ( { resource, openExternal } ) => ( { resource : resource . toString ( true ) , openExternal } ) ) ,
693+ opened : openerService . opened . map ( ( { resource, openExternal } ) => ( { resource : resource . toString ( true ) , openExternal } ) ) ,
636694 filteredOnly : persistentContent . querySelector ( '.chat-dropdown-pill-button' ) ,
637695 canConfigure : persistentContent . querySelector ( '.chat-pills-row' ) ?. classList . contains ( 'empty' ) ,
638696 } , {
0 commit comments