@@ -552,6 +552,91 @@ async function exerciseViewport(
552552 } )
553553 }
554554
555+ if ( viewport . name === 'desktop' ) {
556+ for ( const width of [ 390 , viewport . width ] ) {
557+ await check (
558+ `${ prefix } /${ width } px draft scroll boundaries preserve the surrounding page` ,
559+ page ,
560+ async ( ) => {
561+ await visit ( page , 'home' )
562+ const composer = page . getByRole ( 'textbox' , { name : 'Message' , exact : true } )
563+ const heading = page . getByRole ( 'heading' , { level : 1 } )
564+ const readScroll = ( ) =>
565+ composer . evaluate ( ( element ) => {
566+ let ancestor = element . parentElement
567+ while ( ancestor ) {
568+ if (
569+ ancestor . scrollHeight > ancestor . clientHeight &&
570+ getComputedStyle ( ancestor ) . overflowY === 'auto'
571+ ) {
572+ const bounds = ancestor . getBoundingClientRect ( )
573+ return {
574+ top : ancestor . scrollTop ,
575+ max : ancestor . scrollHeight - ancestor . clientHeight ,
576+ x : bounds . x + bounds . width / 2 ,
577+ y : bounds . y + bounds . height / 2 ,
578+ }
579+ }
580+ ancestor = ancestor . parentElement
581+ }
582+ throw new Error ( 'The long draft must have a scrollable editor' )
583+ } )
584+ const settle = ( ) =>
585+ page . evaluate (
586+ ( ) =>
587+ new Promise < void > ( ( resolve ) =>
588+ requestAnimationFrame ( ( ) => requestAnimationFrame ( ( ) => resolve ( ) ) )
589+ )
590+ )
591+ try {
592+ await page . setViewportSize ( { width, height : 520 } )
593+ await composer . fill ( 'A draft line\n' . repeat ( 30 ) )
594+ const editor = await readScroll ( )
595+ assert ( editor . max > 100 , 'The draft must overflow its editor' )
596+ await page . mouse . move ( editor . x , editor . y )
597+ await page . mouse . wheel ( 0 , 10_000 )
598+ await expect
599+ . poll (
600+ async ( ) => {
601+ const scroll = await readScroll ( )
602+ return Math . abs ( scroll . top - scroll . max )
603+ } ,
604+ { message : 'The draft must reach its lower scroll boundary' }
605+ )
606+ . toBeLessThanOrEqual ( 1 )
607+ const before = await heading . boundingBox ( )
608+ assert ( before )
609+ await page . mouse . wheel ( 0 , 600 )
610+ await settle ( )
611+ const after = await heading . boundingBox ( )
612+ assert ( after )
613+ if ( width < 768 ) {
614+ assert (
615+ Math . abs ( after . y - before . y ) <= 1 ,
616+ 'Scrolling past a mobile draft must not move Home'
617+ )
618+ await page . mouse . move ( width - 2 , 300 )
619+ await page . mouse . wheel ( 0 , 600 )
620+ await expect
621+ . poll ( async ( ) => ( await heading . boundingBox ( ) ) ?. y ?? before . y )
622+ . toBeLessThan ( before . y - 10 )
623+ } else {
624+ await expect
625+ . poll ( async ( ) => ( await heading . boundingBox ( ) ) ?. y ?? before . y , {
626+ message : 'Desktop scroll handoff must remain unchanged' ,
627+ } )
628+ . toBeLessThan ( before . y - 10 )
629+ }
630+ await capture ( page , `${ browserName } -${ width } -draft-scroll` )
631+ } finally {
632+ await composer . fill ( '' )
633+ await page . setViewportSize ( { width : viewport . width , height : viewport . height } )
634+ }
635+ }
636+ )
637+ }
638+ }
639+
555640 if ( viewport . width < 768 ) {
556641 await check (
557642 `${ prefix } /navigation opens, dismisses, and preserves desktop preferences` ,
0 commit comments