@@ -250,6 +250,61 @@ public JSONObject snapshot(String name, @Nullable List<Integer> widths, Integer
250250 return snapshot (name , options );
251251 }
252252
253+ private List <Map <String , Object >> getResponsiveWidths (List <Integer > widths ) {
254+ String queryParam = "" ;
255+ if (widths != null && !widths .isEmpty ()) {
256+ String joined = widths .stream ().map (String ::valueOf ).collect (Collectors .joining ("," ));
257+ queryParam = "?widths=" + joined ;
258+ }
259+
260+ int timeout = 30000 ; // 30 seconds
261+ RequestConfig requestConfig = RequestConfig .custom ()
262+ .setSocketTimeout (timeout )
263+ .setConnectTimeout (timeout )
264+ .build ();
265+
266+ try (CloseableHttpClient httpClient = HttpClients .custom ().setDefaultRequestConfig (requestConfig ).build ()) {
267+ HttpGet httpget = new HttpGet (PERCY_SERVER_ADDRESS + "/percy/widths-config" + queryParam );
268+ HttpResponse response = httpClient .execute (httpget );
269+ int statusCode = response .getStatusLine ().getStatusCode ();
270+
271+ if (statusCode != 200 ) {
272+ EntityUtils .consume (response .getEntity ());
273+ log ("Update Percy CLI to the latest version to use responsiveSnapshotCapture" );
274+ throw new RuntimeException (
275+ "Failed to fetch widths-config (HTTP " + statusCode + ")" );
276+ }
277+
278+ String responseString = EntityUtils .toString (response .getEntity (), "UTF-8" );
279+ JSONObject json = new JSONObject (responseString );
280+
281+ if (!json .has ("widths" ) || json .isNull ("widths" )) {
282+ log ("Update Percy CLI to the latest version to use responsiveSnapshotCapture" );
283+ throw new RuntimeException (
284+ "Missing \" widths\" in widths-config response" );
285+ }
286+
287+ JSONArray widthsArray = json .getJSONArray ("widths" );
288+ List <Map <String , Object >> result = new ArrayList <>();
289+ for (int i = 0 ; i < widthsArray .length (); i ++) {
290+ JSONObject entry = widthsArray .getJSONObject (i );
291+ Map <String , Object > item = new HashMap <>();
292+ item .put ("width" , entry .getInt ("width" ));
293+ if (entry .has ("height" ) && !entry .isNull ("height" )) {
294+ item .put ("height" , entry .getInt ("height" ));
295+ }
296+ result .add (item );
297+ }
298+ return result ;
299+ } catch (RuntimeException re ) {
300+ throw re ;
301+ } catch (Exception ex ) {
302+ log ("Update Percy CLI to the latest version to use responsiveSnapshotCapture" );
303+ log ("Failed to fetch widths-config: " + ex .getMessage (), "debug" );
304+ throw new RuntimeException (
305+ "Failed to fetch widths-config: " + ex .getMessage (), ex );
306+ }
307+ }
253308 private boolean isCaptureResponsiveDOM (Map <String , Object > options ) {
254309 if (cliConfig .has ("percy" ) && !cliConfig .isNull ("percy" )) {
255310 JSONObject percyProperty = cliConfig .getJSONObject ("percy" );
@@ -523,6 +578,26 @@ private Map<String, Object> getSerializedDOM(JavascriptExecutor jse, Set<Cookie>
523578 Map <String , Object > mutableSnapshot = new HashMap <>(domSnapshot );
524579 mutableSnapshot .put ("cookies" , cookies );
525580
581+ // If PercyDOM serialized any processed cross-origin iframe frames, expose
582+ // them on the snapshot as `corsIframes` so @percy/core can stitch them.
583+ try {
584+ Object processedFrames = null ;
585+ if (domSnapshot .containsKey ("processedFrames" )) {
586+ processedFrames = domSnapshot .get ("processedFrames" );
587+ } else if (domSnapshot .containsKey ("frames" )) {
588+ processedFrames = domSnapshot .get ("frames" );
589+ }
590+
591+ if (processedFrames instanceof List <?>) {
592+ List <?> pfList = (List <?>) processedFrames ;
593+ if (!pfList .isEmpty ()) {
594+ mutableSnapshot .put ("corsIframes" , pfList );
595+ }
596+ }
597+ } catch (Exception e ) {
598+ log ("Failed to attach corsIframes to domSnapshot: " + e .getMessage (), "debug" );
599+ }
600+
526601 return mutableSnapshot ;
527602 }
528603
@@ -615,52 +690,65 @@ private static void changeWindowDimensionAndWait(WebDriver driver, int width, in
615690
616691 // Capture responsive DOM for different widths
617692 public List <Map <String , Object >> captureResponsiveDom (WebDriver driver , Set <Cookie > cookies , Map <String , Object > options ) {
618- List <Integer > widths = getWidthsForMultiDom (options );
619-
693+ List <Map <String , Object >> widths = getResponsiveWidths ((List <Integer >) options .get ("widths" ));
620694 List <Map <String , Object >> domSnapshots = new ArrayList <>();
621-
622695 Dimension windowSize = driver .manage ().window ().getSize ();
623696 int currentWidth = windowSize .getWidth ();
624697 int currentHeight = windowSize .getHeight ();
698+ log ("Initial window size: " + currentWidth + "x" + currentHeight , "debug" );
625699 int lastWindowWidth = currentWidth ;
626700 int resizeCount = 0 ;
627701 JavascriptExecutor jse = (JavascriptExecutor ) driver ;
628-
629- // Inject JS to count window resize events
630702 jse .executeScript ("PercyDOM.waitForResize()" );
631-
632- for (int width : widths ) {
703+ int targetHeight = currentHeight ;
704+
705+ if (PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT ) {
706+ Integer minHeight = (Integer ) options .get ("minHeight" );
707+ if (minHeight == null && cliConfig != null && cliConfig .has ("snapshot" )) {
708+ JSONObject snapshotConfig = cliConfig .getJSONObject ("snapshot" );
709+ if (snapshotConfig .has ("minHeight" )) {
710+ minHeight = snapshotConfig .getInt ("minHeight" );
711+ }
712+ }
713+ if (minHeight != null ) {
714+ Object result = jse .executeScript ("return window.outerHeight - window.innerHeight + " + minHeight );
715+ if (result instanceof Number ) {
716+ targetHeight = ((Number ) result ).intValue ();
717+ log ("Calculated target height: " + targetHeight , "debug" );
718+ }
719+ }
720+ }
721+ for (Map <String , Object > widthMap : widths ) {
722+ int width = (int ) widthMap .get ("width" );
633723 if (lastWindowWidth != width ) {
634724 resizeCount ++;
635- changeWindowDimensionAndWait (driver , width , currentHeight , resizeCount );
725+ changeWindowDimensionAndWait (driver , width , targetHeight , resizeCount );
636726 lastWindowWidth = width ;
637727 }
638-
639728 if ("true" .equals (PERCY_RESPONSIVE_CAPTURE_RELOAD_PAGE )) {
640729 log ("Reloading page for width: " + width , "debug" );
641730 driver .navigate ().refresh ();
642731 jse .executeScript (fetchPercyDOM ());
643732 jse .executeScript ("PercyDOM.waitForResize()" );
733+ resizeCount = 0 ;
644734 }
645-
646735 try {
647- int sleepTime = Integer .parseInt (RESONSIVE_CAPTURE_SLEEP_TIME );
648- Thread .sleep (sleepTime * 1000 ); // Sleep if needed
736+ if (RESONSIVE_CAPTURE_SLEEP_TIME != null && !RESONSIVE_CAPTURE_SLEEP_TIME .isEmpty ()) {
737+ int sleepTime = Integer .parseInt (RESONSIVE_CAPTURE_SLEEP_TIME );
738+ Thread .sleep (sleepTime * 1000L );
739+ }
649740 } catch (InterruptedException | NumberFormatException ignored ) {
650741 }
651742 Map <String , Object > domSnapshot = getSerializedDOM (jse , cookies , options );
652743 domSnapshot .put ("width" , width );
653744 domSnapshots .add (domSnapshot );
654745 }
655-
656- // Revert to the original window size
657746 changeWindowDimensionAndWait (driver , currentWidth , currentHeight , resizeCount + 1 );
658747
659748 return domSnapshots ;
660- }
661-
662- protected static void log (String message ) {
663- log (message , "info" );
749+ }
750+ protected static void log (String message ) {
751+ log (message , "info" );
664752 }
665753
666754 protected static void log (String message , String level ) {
0 commit comments