Feat : responsive capture feature and Cross Origin Iframe capture#302
Feat : responsive capture feature and Cross Origin Iframe capture#302yashmahamulkar-bs wants to merge 7 commits intomasterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR enhances the Java Selenium SDK’s snapshotting to better support responsive DOM capture (by retrieving width/height config from the Percy CLI) and to include cross-origin iframe DOM snapshots during serialization.
Changes:
- Added
/percy/widths-configclient (getResponsiveWidths) and refactored responsive capture to use it, with optional reload/min-height behavior via env vars. - Added cross-origin iframe detection + per-iframe serialization (
processFrame) and attaches results undercorsIframesin the DOM payload. - Updated
@percy/clidev dependency to a newer alpha version to support the new CLI endpoint/features.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| src/main/java/io/percy/selenium/Percy.java | Adds widths-config fetching, responsive capture updates, and cross-origin iframe snapshot serialization. |
| package.json | Bumps @percy/cli version to support new responsive capture behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (resizeCountObj == null) { | ||
| return false; | ||
| } | ||
| return (long) resizeCountObj == resizeCount; |
| for (Map<String, Object> widthMap : widths) { | ||
| int width = (int) widthMap.get("width"); | ||
| if (lastWindowWidth != width) { | ||
| resizeCount++; | ||
| changeWindowDimensionAndWait(driver, width, currentHeight, resizeCount); | ||
| changeWindowDimensionAndWait(driver, width, targetHeight, resizeCount); | ||
| lastWindowWidth = width; |
There was a problem hiding this comment.
Pull request overview
This PR enhances the Percy Selenium Java SDK’s snapshotting flow by improving responsive DOM capture (width/height config fetched from the Percy CLI) and adding cross-origin iframe serialization so CORS iframes can be included in DOM snapshots.
Changes:
- Added
/percy/widths-configfetch + new env-var driven behavior for responsive capture (reload between widths, optional min-height handling). - Added cross-origin iframe processing and inclusion in the serialized snapshot payload (
corsIframes). - Expanded
SdkTestcoverage for responsive capture gating, widths-config parsing/errors, and CORS iframe serialization; updated@percy/clidev dependency.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| src/main/java/io/percy/selenium/Percy.java | Implements responsive-width fetching, optional reload/min-height handling, and CORS iframe capture during DOM serialization. |
| src/test/java/io/percy/selenium/SdkTest.java | Adds tests validating responsive capture logic, widths-config behavior, and cross-origin iframe serialization. |
| package.json | Updates Percy CLI dev dependency to an alpha version needed for new features. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try { | ||
| Map<String, Object> result = processFrame(frame, options); | ||
| if (result != null) { | ||
| processedFrames.add(result); | ||
| } | ||
| } catch (Exception e) { | ||
| log("Skipping frame \"" + frameSrc + "\" due to error: " + e.getMessage(), "debug"); | ||
| } |
| } | ||
|
|
||
| // Wait for window resize event using WebDriverWait | ||
| // Made changes to handle handles the temporary null state of resizeCountObj during page reload |
| } | ||
| protected static void log(String message) { | ||
| log(message, "info"); | ||
| } | ||
|
|
| public List<Map<String, Object>> captureResponsiveDom(WebDriver driver, Set<Cookie> cookies, Map<String, Object> options) { | ||
| List<Integer> widths = getWidthsForMultiDom(options); | ||
|
|
||
| List<Map<String, Object>> widths = getResponsiveWidths((List<Integer>) options.get("widths")); |
There was a problem hiding this comment.
created extractResponsiveWidths function for the above and added it into the code
| driver.switchTo().defaultContent(); | ||
| } catch (Exception err) { | ||
| throw new RuntimeException( | ||
| "Fatal: could not exit iframe context after processing \"" + finalFrameUrl + "\". Driver may be unstable." |
| queryParam = "?widths=" + joined; | ||
| } | ||
|
|
||
| int timeout = 30000; // 30 seconds |
There was a problem hiding this comment.
use existing constant here
| try (CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build()) { | ||
| HttpGet httpget = new HttpGet(PERCY_SERVER_ADDRESS + "/percy/widths-config" + queryParam); | ||
| HttpResponse response = httpClient.execute(httpget); | ||
| int statusCode = response.getStatusLine().getStatusCode(); | ||
|
|
||
| if (statusCode != 200) { | ||
| EntityUtils.consume(response.getEntity()); | ||
| log("Update Percy CLI to the latest version to use responsiveSnapshotCapture"); | ||
| throw new RuntimeException( | ||
| "Failed to fetch widths-config (HTTP " + statusCode + ")"); | ||
| } | ||
|
|
||
| String responseString = EntityUtils.toString(response.getEntity(), "UTF-8"); | ||
| JSONObject json = new JSONObject(responseString); | ||
|
|
||
| if (!json.has("widths") || json.isNull("widths")) { | ||
| log("Update Percy CLI to the latest version to use responsiveSnapshotCapture"); | ||
| throw new RuntimeException( | ||
| "Missing \"widths\" in widths-config response"); | ||
| } | ||
|
|
||
| JSONArray widthsArray = json.getJSONArray("widths"); | ||
| List<Map<String, Object>> result = new ArrayList<>(); | ||
| for (int i = 0; i < widthsArray.length(); i++) { | ||
| JSONObject entry = widthsArray.getJSONObject(i); | ||
| Map<String, Object> item = new HashMap<>(); | ||
| item.put("width", entry.getInt("width")); | ||
| if (entry.has("height") && !entry.isNull("height")) { | ||
| item.put("height", entry.getInt("height")); | ||
| } | ||
| result.add(item); | ||
| } |
There was a problem hiding this comment.
break into smaller helper functions add comments on what this do in each
There was a problem hiding this comment.
Following is refactoring done
buildWidthsQueryParam
Takes List is of intgrer 400, 80 and Joins them with commas and prepends ?widths=.
Result: "?widths=400,800"
buildRequestConfig
Creates a configuration object. and A RequestConfig where connection and socket timeouts are set to 1 second.
fetchWidthsConfigResponse
Constructs the URL: http://localhost:5338/percy/widths-config?widths=400,800.
Execution: The httpClient executes the GET request.
Check: If the CLI returns 200 OK, it returns the response object. If the CLI is old and doesn't have this endpoint (returning 404), it throws a RuntimeException.
parseWidthsConfigResponse
Percy CLI returns this JSON:
JSON { "widths": [ { "width": 400, "height": 800 }, { "width": 800, "height": 1000 } ] }
Checks if "widths" key exists (it does).
Loops through the JSONArray:
Index 0: Map contains {"width": 400, "height": 800}.
Index 1: Map contains {"width": 800, "height": 1000}.
Result: A List containing these two HashMap objects.
|
|
||
| if (PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT) { | ||
| Object minHeightObj = options.get("minHeight"); | ||
| if (minHeightObj == null && cliConfig != null && cliConfig.has("snapshot")) { | ||
| JSONObject snapshotConfig = cliConfig.getJSONObject("snapshot"); | ||
| if (snapshotConfig.has("minHeight")) { | ||
| minHeightObj = snapshotConfig.getInt("minHeight"); | ||
| } | ||
| } | ||
| if (minHeightObj != null) { | ||
| try { | ||
| int minHeight = Integer.parseInt(minHeightObj.toString()); | ||
| Object result = jse.executeScript("return window.outerHeight - window.innerHeight + " + minHeight); | ||
| if (result instanceof Number) { | ||
| targetHeight = ((Number) result).intValue(); | ||
| } | ||
| } catch (NumberFormatException e) { | ||
| log("Invalid minHeight value " + minHeightObj + "; expected integer, using current window height instead.", "debug"); | ||
| } | ||
| } else { | ||
| log("minHeight not found in options or cliConfig, using current window height: " + targetHeight, "debug"); | ||
| } | ||
| } else { | ||
| log("PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT is disabled, using current window height: " + targetHeight, "debug"); |
There was a problem hiding this comment.
too much if else
make this better, add helper fucntions etc
This pull request introduces several improvements and refactors to the
Percy.javacodebase, primarily enhancing responsive DOM capture and cross-origin iframe support. The changes also update dependencies and improve configuration flexibility through environment variables.Responsive DOM capture and cross-origin iframe support:
isUnsupportedIframeSrc,getOrigin, andprocessFramewere introduced to support this. (src/main/java/io/percy/selenium/Percy.java)getResponsiveWidths), supporting both provided and config-driven widths, and allowing for optional minimum height and page reloads between widths (controlled by new environment variables). (src/main/java/io/percy/selenium/Percy.java) [1] [2]getWidthsForMultiDommethod in favor of the new approach, simplifying width configuration logic. (src/main/java/io/percy/selenium/Percy.java)Configuration and dependency updates:
PERCY_RESPONSIVE_CAPTURE_RELOAD_PAGEandPERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT, allowing users to control page reloads and minimum height during responsive capture. (src/main/java/io/percy/selenium/Percy.java)@percy/clidependency to version1.31.10-alpha.0inpackage.jsonto support new features. (package.json)Other improvements:
changeWindowDimensionAndWaitto safely handle temporary null states during page reloads. (src/main/java/io/percy/selenium/Percy.java)java.net.URIimport for URL parsing utilities. (src/main/java/io/percy/selenium/Percy.java)