@@ -1328,6 +1328,92 @@ try {
13281328 window . dispatchEvent ( new Event ( 'online' ) )
13291329 } )
13301330 }
1331+ const workspaceListUrl = ( url : URL ) =>
1332+ url . origin === base . origin && url . pathname === '/api/workspaces'
1333+ for ( const [ status , name ] of [
1334+ [ 503 , 'A transient workspace-list refetch preserves the mounted picker and scroll position' ] ,
1335+ [ 401 , 'An expired workspace-list session replaces cached choices with sign-in recovery' ] ,
1336+ [ 403 , 'A forbidden workspace-list refetch removes cached choices' ] ,
1337+ [ 404 , 'A missing workspace-list refetch removes cached choices' ] ,
1338+ [ 400 , 'An invalid workspace-list request removes cached choices' ] ,
1339+ ] as const ) {
1340+ await check ( name , async ( ) => {
1341+ await navigate ( `${ base . origin } /extension` )
1342+ const choices = frame ( ) . getByRole ( 'navigation' , { name : 'Workspaces' , exact : true } )
1343+ await expect ( choices ) . toBeVisible ( )
1344+ const original = await choices . elementHandle ( )
1345+ assert ( original )
1346+ const scroller = frame ( ) . getByRole ( 'main' )
1347+ await scroller . evaluate ( ( element ) => {
1348+ element . scrollTop = element . scrollHeight
1349+ } )
1350+ const scrollTop = await scroller . evaluate ( ( element ) => element . scrollTop )
1351+ assert ( scrollTop > 0 , 'Workspace picker fixture did not overflow' )
1352+ const failures : Request [ ] = [ ]
1353+ await browserContext . route ( workspaceListUrl , async ( route ) => {
1354+ injectedRequests . add ( route . request ( ) )
1355+ await route . fulfill ( { status, json : { error : 'QA workspace-list failure' } } )
1356+ failures . push ( route . request ( ) )
1357+ } )
1358+ try {
1359+ await shell . clock . setFixedTime ( Date . now ( ) + 360_000 )
1360+ await reconnect ( )
1361+ await expect . poll ( ( ) => failures . length ) . toBe ( 2 )
1362+ await Promise . all (
1363+ failures . map ( async ( request ) => {
1364+ const response = await request . response ( )
1365+ assert ( response )
1366+ await response . finished ( )
1367+ } )
1368+ )
1369+ await frame ( ) . evaluate (
1370+ ( ) =>
1371+ new Promise < void > ( ( resolve ) =>
1372+ requestAnimationFrame ( ( ) => requestAnimationFrame ( ( ) => resolve ( ) ) )
1373+ )
1374+ )
1375+ if ( status === 503 ) {
1376+ assert (
1377+ await original . evaluate ( ( element ) => element . isConnected ) ,
1378+ 'A transient workspace-list refetch unmounted the picker'
1379+ )
1380+ assert . equal ( await scroller . evaluate ( ( element ) => element . scrollTop ) , scrollTop )
1381+ await screenshot ( 'workspace-picker-refetch-outage' )
1382+ await choices . getByRole ( 'link' , { name : 'Extension QA alpha' , exact : true } ) . click ( )
1383+ await expect ( frame ( ) . getByRole ( 'textbox' , { name : 'Message' , exact : true } ) ) . toBeVisible ( )
1384+ } else {
1385+ await expect ( choices ) . toHaveCount ( 0 )
1386+ if ( status === 401 )
1387+ await expect ( frame ( ) . getByRole ( 'link' , { name : 'Sign in' , exact : true } ) ) . toBeVisible ( )
1388+ else
1389+ await expect (
1390+ frame ( ) . getByRole ( 'button' , { name : 'Retry loading workspaces' , exact : true } )
1391+ ) . toBeVisible ( )
1392+ await screenshot ( `workspace-picker-refetch-${ status } ` )
1393+ }
1394+ } finally {
1395+ await original . dispose ( )
1396+ await browserContext . unroute ( workspaceListUrl )
1397+ await shell . clock . setFixedTime ( Date . now ( ) )
1398+ }
1399+ } )
1400+ }
1401+ await check ( 'An initial workspace-list 401 opens sign-in recovery' , async ( ) => {
1402+ await browserContext . route ( workspaceListUrl , async ( route ) => {
1403+ injectedRequests . add ( route . request ( ) )
1404+ await route . fulfill ( { status : 401 , json : { error : 'QA workspace-list session expired' } } )
1405+ } )
1406+ try {
1407+ await navigate ( `${ base . origin } /extension` )
1408+ await expect ( frame ( ) . getByRole ( 'link' , { name : 'Sign in' , exact : true } ) ) . toBeVisible ( )
1409+ await expect (
1410+ frame ( ) . getByRole ( 'navigation' , { name : 'Workspaces' , exact : true } )
1411+ ) . toHaveCount ( 0 )
1412+ await screenshot ( 'workspace-picker-initial-session-expired' )
1413+ } finally {
1414+ await browserContext . unroute ( workspaceListUrl )
1415+ }
1416+ } )
13311417 await check (
13321418 'Transient session and workspace refetch failures preserve the mounted composer and page draft' ,
13331419 async ( ) => {
0 commit comments