@@ -44,8 +44,9 @@ test('source authorization returns to its desktop screen and refreshes live', as
4444 }
4545 const tickets = new Map < string , unknown > ( )
4646 const attempts = new Map < string , string > ( )
47- const accountAttempts = new Map < string , string > ( )
47+ const accountAttempts = new Map < string , { session : string ; mcp : boolean } > ( )
4848 let accountConnected = false
49+ let mcpAccountConnected = false
4950 const startSessions : string [ ] = [ ]
5051 const callbackSessions : string [ ] = [ ]
5152 const githubAttempts = new Map < string , { session : string ; completed : boolean } > ( )
@@ -90,6 +91,16 @@ test('source authorization returns to its desktop screen and refreshes live', as
9091 availableMcpConnectors : [ ] ,
9192 canManage : false ,
9293 indexingAvailable : true ,
94+ viewerMcpAccounts : mcpAccountConnected
95+ ? [
96+ {
97+ credentialId : 'fixture-mcp-account' ,
98+ displayName : 'Fixture MCP account' ,
99+ mcpServerId : 'fixture-mcp' ,
100+ status : 'active' ,
101+ } ,
102+ ]
103+ : [ ] ,
93104 viewerAccounts : accountConnected
94105 ? [
95106 {
@@ -109,15 +120,13 @@ test('source authorization returns to its desktop screen and refreshes live', as
109120 path === '/api/organizations/fixture-organization/connected-accounts/connect' ||
110121 path === '/api/users/me/organization-accounts/fixture-account/reconnect'
111122 ) {
112- const completionId =
113- request . method === 'POST' && path . endsWith ( '/connect' )
114- ? ( await body ( ) ) . oauthCompletionId
115- : url . searchParams . get ( 'oauthCompletionId' )
123+ const input = request . method === 'POST' && path . endsWith ( '/connect' ) ? await body ( ) : null
124+ const completionId = input ?. oauthCompletionId ?? url . searchParams . get ( 'oauthCompletionId' )
116125 if ( ! completionId ) {
117126 json ( { error : 'Missing completion ID' } , 400 )
118127 return
119128 }
120- accountAttempts . set ( completionId , session )
129+ accountAttempts . set ( completionId , { session, mcp : Boolean ( input ?. mcpServerId ) } )
121130 json ( {
122131 invitationLink : `${ origin } /credential-groups/enroll/fixture-account-invitation` ,
123132 authorizationUrl : `${ origin } /account-provider?completionId=${ completionId } ` ,
@@ -126,13 +135,17 @@ test('source authorization returns to its desktop screen and refreshes live', as
126135 }
127136 if ( path === '/account-callback' ) {
128137 const completionId = url . searchParams . get ( 'completionId' ) ?? ''
129- if ( accountAttempts . get ( completionId ) !== session ) {
138+ const attempt = accountAttempts . get ( completionId )
139+ if ( attempt ?. session !== session ) {
130140 json ( { error : 'Wrong attempt' } , 403 )
131141 return
132142 }
133143 accountAttempts . delete ( completionId )
134144 const denied = url . searchParams . has ( 'error' )
135- if ( ! denied ) accountConnected = true
145+ if ( ! denied ) {
146+ if ( attempt . mcp ) mcpAccountConnected = true
147+ else accountConnected = true
148+ }
136149 redirect (
137150 `/credential-groups/complete?completionId=${ completionId } &organizationId=fixture-organization${ denied ? '&oauth=denied' : '' } `
138151 )
@@ -494,6 +507,7 @@ test('source authorization returns to its desktop screen and refreshes live', as
494507 'web authorization preserves the origin and refreshes after an isolated provider window' ,
495508 async ( ) => {
496509 accountConnected = false
510+ mcpAccountConnected = false
497511 await web . reload ( )
498512 await web . getByLabel ( 'Source draft' ) . fill ( 'Web draft retained' )
499513 await expect ( web . getByLabel ( 'Account count' ) ) . toHaveText ( '0' )
@@ -507,6 +521,19 @@ test('source authorization returns to its desktop screen and refreshes live', as
507521 expect ( web . url ( ) ) . toBe ( `${ origin } /o/fixture-organization/integrations?search=fixture` )
508522 }
509523 )
524+ await check ( 'overlapping connect and reconnect preserve the active authorization' , async ( ) => {
525+ const popupReady = context . waitForEvent ( 'page' )
526+ await web . getByRole ( 'button' , { name : 'Connect account' , exact : true } ) . click ( )
527+ const popup = await popupReady
528+ await popup . getByRole ( 'link' , { name : 'Authorize account' } ) . waitFor ( )
529+ const pendingAttempts = accountAttempts . size
530+ await web . getByRole ( 'button' , { name : 'Reconnect account' , exact : true } ) . click ( )
531+ await expect ( web . getByLabel ( 'Reconnect error' ) ) . toContainText ( 'Finish or cancel' )
532+ expect ( accountAttempts . size ) . toBe ( pendingAttempts )
533+ await expect ( web . getByLabel ( 'Account authorization' , { exact : true } ) ) . toHaveText ( 'pending' )
534+ await popup . getByRole ( 'link' , { name : 'Authorize account' } ) . click ( )
535+ await expect ( web . getByLabel ( 'Account authorization' , { exact : true } ) ) . toHaveText ( 'success' )
536+ } )
510537 await check ( 'web denial and cancellation leave the initiating page usable' , async ( ) => {
511538 const popupReady = context . waitForEvent ( 'page' )
512539 await web . getByRole ( 'button' , { name : 'Connect account' , exact : true } ) . click ( )
0 commit comments