@@ -70,29 +70,52 @@ function routesFor(persona: Persona): { name: string; path: string }[] {
7070 return shared
7171}
7272
73- /** The app scrolls inside its own panes, so the viewport grows to what they hold before the shot. */
73+ /** How far a probe grows the viewport to see which panes follow it. */
74+ const PANE_PROBE_PX = 200
75+ /** Home's composer types its suggestion out, then holds it this long before deleting it. */
76+ const TYPED_PLACEHOLDER = '[placeholder^="Ask Sim to "]:not([placeholder$="..."])'
77+
78+ /**
79+ * The app scrolls inside its own panes, so the viewport grows to what they hold before the shot. A
80+ * pane with a ceiling of its own, such as a Home widget's list, stays a scroller at any height and
81+ * shows no more for it: only the panes that follow the viewport count.
82+ */
83+ async function viewportHolding ( page : Page , viewport : Viewport ) {
84+ const scrollers = await page . evaluateHandle ( ( ) =>
85+ Array . from ( document . querySelectorAll ( 'body *' ) )
86+ . filter ( ( element ) => element . scrollHeight - element . clientHeight > 1 )
87+ . map ( ( element ) => ( {
88+ element,
89+ clientHeight : element . clientHeight ,
90+ overflow : element . scrollHeight - element . clientHeight ,
91+ } ) )
92+ )
93+ await page . setViewportSize ( { width : viewport . width , height : viewport . height + PANE_PROBE_PX } )
94+ await sleep ( 150 )
95+ const overflow = await scrollers . evaluate ( ( measured ) =>
96+ measured
97+ . filter ( ( { element, clientHeight } ) => element . clientHeight > clientHeight )
98+ . reduce ( ( tallest , { overflow } ) => Math . max ( tallest , overflow ) , 0 )
99+ )
100+ await scrollers . dispose ( )
101+ return Math . min ( viewport . height + overflow , 3600 )
102+ }
103+
74104export async function fullPageScreenshot ( page : Page , path : string , viewport : Viewport ) {
75- const height = await page . evaluate ( ( ) => {
76- let tallest = innerHeight
77- for ( const element of Array . from ( document . querySelectorAll ( 'body *' ) ) ) {
78- const overflow = element . scrollHeight - element . clientHeight
79- if ( overflow > 1 ) tallest = Math . max ( tallest , innerHeight + overflow )
80- }
81- return Math . min ( tallest , 3600 )
105+ const height = await viewportHolding ( page , viewport )
106+ await page . setViewportSize ( { width : viewport . width , height } )
107+ await sleep ( 150 )
108+ await page . waitForFunction ( ( typing ) => ! document . querySelector ( typing ) , TYPED_PLACEHOLDER , {
109+ timeout : 10_000 ,
82110 } )
83- const grows = height > viewport . height
84- if ( grows ) {
85- await page . setViewportSize ( { width : viewport . width , height } )
86- await sleep ( 150 )
87- }
88111 await page . screenshot ( {
89112 path,
90113 fullPage : true ,
91114 animations : 'disabled' ,
92115 style : 'nextjs-portal { display: none; }' ,
93116 timeout : 20_000 ,
94117 } )
95- if ( grows ) await page . setViewportSize ( { width : viewport . width , height : viewport . height } )
118+ await page . setViewportSize ( { width : viewport . width , height : viewport . height } )
96119}
97120
98121/** Long enough for a persona's walk to list every route that failed. */
0 commit comments