@@ -523,7 +523,7 @@ export async function homeChecks() {
523523 } )
524524 } )
525525 await check (
526- 'Workspaces holds its rows while its read is out, and keeps them if it fails' ,
526+ 'Workspaces shows its rows while their activity is out, and keeps them if it fails' ,
527527 async ( ) => {
528528 await withSession ( MEMBER , async ( { page } ) => {
529529 const release = createDeferred < void > ( )
@@ -535,10 +535,10 @@ export async function homeChecks() {
535535 } )
536536 await page . goto ( orgUrl ( '/home' ) )
537537 const region = homeRegion ( page , 'Workspaces' )
538- await expect ( region . getByRole ( 'status' , { name : 'Loading workspaces' } ) ) . toBeVisible ( )
539- await expect (
540- region . getByRole ( 'status ', { name : 'Loading workspaces' } ) . locator ( '> span' )
541- ) . toHaveCount ( 2 )
538+ // The names are already known, so the rows paint at once, with what they report still out.
539+ await expect ( region . getByRole ( 'link' , { name : MEMBER_ADMIN_WORKSPACE . name } ) ) . toBeVisible ( )
540+ await expect ( region . getByText ( '… ', { exact : true } ) . first ( ) ) . toBeVisible ( )
541+ await expect ( region . getByText ( 'Up to date' ) ) . toHaveCount ( 0 )
542542 release . resolve ( )
543543 await expect (
544544 region . getByText ( 'What these workspaces are doing could not load.' )
@@ -553,6 +553,82 @@ export async function homeChecks() {
553553 } ,
554554 { allow : [ / 5 0 0 G E T .* w o r k s p a c e - a c t i v i t y / ] }
555555 )
556+ for ( const [ persona , viewer ] of [
557+ [ 'member' , MEMBER ] ,
558+ [ 'builder' , ADMIN ] ,
559+ ] as const ) {
560+ await check ( `a ${ persona } 's Home does not jump from its skeleton to its content` , async ( ) => {
561+ await withSession ( viewer , async ( session ) => {
562+ const { page } = session
563+ await page . addInitScript ( ( ) => {
564+ let shift = 0
565+ new PerformanceObserver ( ( entries ) => {
566+ for ( const entry of entries . getEntries ( ) as ( PerformanceEntry & {
567+ value : number
568+ hadRecentInput : boolean
569+ } ) [ ] ) {
570+ if ( ! entry . hadRecentInput ) shift += entry . value
571+ }
572+ } ) . observe ( { type : 'layout-shift' , buffered : true } )
573+ ; ( window as Window & { homeLayoutShift ?: ( ) => number } ) . homeLayoutShift = ( ) => shift
574+ } )
575+ await page . goto ( orgUrl ( '/home' ) )
576+ await expect ( page . getByRole ( 'main' ) ) . toBeVisible ( )
577+ await session . settle ( )
578+ const shift = await page . evaluate ( ( ) =>
579+ ( window as Window & { homeLayoutShift ?: ( ) => number } ) . homeLayoutShift ?.( )
580+ )
581+ // 0.25 is where Core Web Vitals calls a page's shift poor.
582+ assert . ok ( ( shift ?? 0 ) < 0.25 , `Home shifted by ${ shift } as it loaded` )
583+ } )
584+ } )
585+ }
586+ await check ( "Home's sections stack without overlapping on a phone" , async ( ) => {
587+ await withSession (
588+ ADMIN ,
589+ async ( session ) => {
590+ const { page } = session
591+ await page . goto ( orgUrl ( '/home' ) )
592+ await expect ( homeRegion ( page , 'Workspaces' ) ) . toBeVisible ( )
593+ await session . settle ( )
594+ const seeMore = homeRegion ( page , 'Workspaces' ) . getByRole ( 'button' , { name : 'See more' } )
595+ if ( ( await seeMore . count ( ) ) > 0 ) await seeMore . click ( )
596+ const boxes = await page
597+ . getByRole ( 'main' )
598+ . getByRole ( 'region' )
599+ . evaluateAll ( ( regions ) =>
600+ regions
601+ . filter ( ( region ) => region . matches ( 'section' ) )
602+ . map ( ( region ) => {
603+ const { top, bottom } = region . getBoundingClientRect ( )
604+ const contentBottom = Math . max (
605+ bottom ,
606+ ...[ ...region . children ] . map ( ( child ) => child . getBoundingClientRect ( ) . bottom )
607+ )
608+ return { name : region . getAttribute ( 'aria-label' ) , top, bottom : contentBottom }
609+ } )
610+ . sort ( ( a , b ) => a . top - b . top )
611+ )
612+ assert . ok ( boxes . length > 1 , 'Home has no sections to compare' )
613+ for ( const [ index , box ] of boxes . slice ( 1 ) . entries ( ) ) {
614+ const above = boxes [ index ]
615+ assert . ok (
616+ above . bottom <= box . top + 1 ,
617+ `${ above . name } runs ${ Math . round ( above . bottom - box . top ) } px into ${ box . name } `
618+ )
619+ }
620+ } ,
621+ { viewport : PHONE }
622+ )
623+ } )
624+ await check ( 'Home, Search and Settings each name one main region' , async ( ) => {
625+ await withSession ( ADMIN , async ( { page } ) => {
626+ for ( const path of [ '/home' , '/search' , '/settings/general' ] ) {
627+ await page . goto ( orgUrl ( path ) )
628+ await expect ( page . getByRole ( 'main' ) ) . toHaveCount ( 1 )
629+ }
630+ } )
631+ } )
556632 await check ( 'Customize Home hides Workspaces across a reload, and stops its read' , async ( ) => {
557633 await onHome ( MEMBER , async ( session ) => {
558634 const { page } = session
0 commit comments