Skip to content

Feat : responsive capture feature and Cross Origin Iframe capture#302

Open
yashmahamulkar-bs wants to merge 7 commits intomasterfrom
PPLT-5031
Open

Feat : responsive capture feature and Cross Origin Iframe capture#302
yashmahamulkar-bs wants to merge 7 commits intomasterfrom
PPLT-5031

Conversation

@yashmahamulkar-bs
Copy link

This pull request introduces several improvements and refactors to the Percy.java codebase, 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:

  • Added logic to process and serialize cross-origin iframes during DOM snapshotting, ensuring that CORS iframes are properly handled and included in snapshots. Helper methods like isUnsupportedIframeSrc, getOrigin, and processFrame were introduced to support this. (src/main/java/io/percy/selenium/Percy.java)
  • Refactored responsive DOM capture to fetch responsive widths from the Percy CLI server (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]
  • Removed the old getWidthsForMultiDom method in favor of the new approach, simplifying width configuration logic. (src/main/java/io/percy/selenium/Percy.java)

Configuration and dependency updates:

  • Introduced new environment variables: PERCY_RESPONSIVE_CAPTURE_RELOAD_PAGE and PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT, allowing users to control page reloads and minimum height during responsive capture. (src/main/java/io/percy/selenium/Percy.java)
  • Updated @percy/cli dependency to version 1.31.10-alpha.0 in package.json to support new features. (package.json)

Other improvements:

  • Improved window resize event handling in changeWindowDimensionAndWait to safely handle temporary null states during page reloads. (src/main/java/io/percy/selenium/Percy.java)
  • Added missing java.net.URI import for URL parsing utilities. (src/main/java/io/percy/selenium/Percy.java)

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-config client (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 under corsIframes in the DOM payload.
  • Updated @percy/cli dev 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;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines +780 to 785
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;
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-config fetch + 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 SdkTest coverage for responsive capture gating, widths-config parsing/errors, and CORS iframe serialization; updated @percy/cli dev 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.

Comment on lines +677 to +684
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");
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

}

// Wait for window resize event using WebDriverWait
// Made changes to handle handles the temporary null state of resizeCountObj during page reload
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines +825 to 829
}
protected static void log(String message) {
log(message, "info");
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

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"));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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."
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

queryParam = "?widths=" + joined;
}

int timeout = 30000; // 30 seconds
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use existing constant here

Comment on lines +267 to +298
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);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break into smaller helper functions add comments on what this do in each

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +800 to +823

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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too much if else
make this better, add helper fucntions etc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants