Skip to content

Commit 861e6e4

Browse files
Refactoring code
1 parent 98c8590 commit 861e6e4

2 files changed

Lines changed: 367 additions & 69 deletions

File tree

src/main/java/io/percy/selenium/Percy.java

Lines changed: 110 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public class Percy {
5353
private static String PERCY_RESPONSIVE_CAPTURE_RELOAD_PAGE = System.getenv().getOrDefault("PERCY_RESPONSIVE_CAPTURE_RELOAD_PAGE", "false").toLowerCase();
5454

5555
private static boolean PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT = Boolean.parseBoolean(System.getenv().getOrDefault("PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT", "false"));
56+
private static final int WIDTHS_CONFIG_TIMEOUT_MS = 30000;
5657
// for logging
5758
private static String LABEL = "[\u001b[35m" + (PERCY_DEBUG ? "percy:java" : "percy") + "\u001b[39m]";
5859

@@ -252,51 +253,12 @@ public JSONObject snapshot(String name, @Nullable List<Integer> widths, Integer
252253
}
253254

254255
private List<Map<String, Object>> getResponsiveWidths(List<Integer> widths) {
255-
String queryParam = "";
256-
if (widths != null && !widths.isEmpty()) {
257-
String joined = widths.stream().map(String::valueOf).collect(Collectors.joining(","));
258-
queryParam = "?widths=" + joined;
259-
}
260-
261-
int timeout = 30000; // 30 seconds
262-
RequestConfig requestConfig = RequestConfig.custom()
263-
.setSocketTimeout(timeout)
264-
.setConnectTimeout(timeout)
265-
.build();
256+
String queryParam = buildWidthsQueryParam(widths);
257+
RequestConfig requestConfig = buildRequestConfig(WIDTHS_CONFIG_TIMEOUT_MS);
266258

267259
try (CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build()) {
268-
HttpGet httpget = new HttpGet(PERCY_SERVER_ADDRESS + "/percy/widths-config" + queryParam);
269-
HttpResponse response = httpClient.execute(httpget);
270-
int statusCode = response.getStatusLine().getStatusCode();
271-
272-
if (statusCode != 200) {
273-
EntityUtils.consume(response.getEntity());
274-
log("Update Percy CLI to the latest version to use responsiveSnapshotCapture");
275-
throw new RuntimeException(
276-
"Failed to fetch widths-config (HTTP " + statusCode + ")");
277-
}
278-
279-
String responseString = EntityUtils.toString(response.getEntity(), "UTF-8");
280-
JSONObject json = new JSONObject(responseString);
281-
282-
if (!json.has("widths") || json.isNull("widths")) {
283-
log("Update Percy CLI to the latest version to use responsiveSnapshotCapture");
284-
throw new RuntimeException(
285-
"Missing \"widths\" in widths-config response");
286-
}
287-
288-
JSONArray widthsArray = json.getJSONArray("widths");
289-
List<Map<String, Object>> result = new ArrayList<>();
290-
for (int i = 0; i < widthsArray.length(); i++) {
291-
JSONObject entry = widthsArray.getJSONObject(i);
292-
Map<String, Object> item = new HashMap<>();
293-
item.put("width", entry.getInt("width"));
294-
if (entry.has("height") && !entry.isNull("height")) {
295-
item.put("height", entry.getInt("height"));
296-
}
297-
result.add(item);
298-
}
299-
return result;
260+
HttpResponse response = fetchWidthsConfigResponse(httpClient, queryParam);
261+
return parseWidthsConfigResponse(response);
300262
} catch (RuntimeException re) {
301263
throw re;
302264
} catch (Exception ex) {
@@ -306,6 +268,63 @@ private List<Map<String, Object>> getResponsiveWidths(List<Integer> widths) {
306268
"Failed to fetch widths-config: " + ex.getMessage(), ex);
307269
}
308270
}
271+
272+
// Builds the optional `?widths=` query string from SDK-provided widths.
273+
private String buildWidthsQueryParam(List<Integer> widths) {
274+
if (widths == null || widths.isEmpty()) {
275+
return "";
276+
}
277+
String joined = widths.stream().map(String::valueOf).collect(Collectors.joining(","));
278+
return "?widths=" + joined;
279+
}
280+
281+
// Creates HTTP request timeout configuration for the widths-config endpoint.
282+
private RequestConfig buildRequestConfig(int timeoutMs) {
283+
return RequestConfig.custom()
284+
.setSocketTimeout(timeoutMs)
285+
.setConnectTimeout(timeoutMs)
286+
.build();
287+
}
288+
289+
// Calls Percy CLI widths-config endpoint and validates that the HTTP status is successful.
290+
private HttpResponse fetchWidthsConfigResponse(CloseableHttpClient httpClient, String queryParam) throws Exception {
291+
HttpGet httpget = new HttpGet(PERCY_SERVER_ADDRESS + "/percy/widths-config" + queryParam);
292+
HttpResponse response = httpClient.execute(httpget);
293+
int statusCode = response.getStatusLine().getStatusCode();
294+
295+
if (statusCode != 200) {
296+
EntityUtils.consume(response.getEntity());
297+
log("Update Percy CLI to the latest version to use responsiveSnapshotCapture");
298+
throw new RuntimeException("Failed to fetch widths-config (HTTP " + statusCode + ")");
299+
}
300+
301+
return response;
302+
}
303+
304+
// Parses widths-config JSON and converts the payload to SDK width/height maps.
305+
private List<Map<String, Object>> parseWidthsConfigResponse(HttpResponse response) throws Exception {
306+
String responseString = EntityUtils.toString(response.getEntity(), "UTF-8");
307+
JSONObject json = new JSONObject(responseString);
308+
309+
if (!json.has("widths") || json.isNull("widths")) {
310+
log("Update Percy CLI to the latest version to use responsiveSnapshotCapture");
311+
throw new RuntimeException("Missing \"widths\" in widths-config response");
312+
}
313+
314+
JSONArray widthsArray = json.getJSONArray("widths");
315+
List<Map<String, Object>> result = new ArrayList<>();
316+
for (int i = 0; i < widthsArray.length(); i++) {
317+
JSONObject entry = widthsArray.getJSONObject(i);
318+
Map<String, Object> item = new HashMap<>();
319+
item.put("width", entry.getInt("width"));
320+
if (entry.has("height") && !entry.isNull("height")) {
321+
item.put("height", entry.getInt("height"));
322+
}
323+
result.add(item);
324+
}
325+
return result;
326+
}
327+
309328
private boolean isCaptureResponsiveDOM(Map<String, Object> options) {
310329
if (cliConfig.has("percy") && !cliConfig.isNull("percy")) {
311330
JSONObject percyProperty = cliConfig.getJSONObject("percy");
@@ -785,6 +804,53 @@ private List<Integer> extractResponsiveWidths(Map<String, Object> options) {
785804
return coercedWidths.isEmpty() ? null : coercedWidths;
786805
}
787806

807+
// Resolves final viewport height for responsive capture using minHeight config when enabled.
808+
private int resolveResponsiveTargetHeight(Map<String, Object> options, JavascriptExecutor jse, int currentHeight) {
809+
if (!PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT) {
810+
log("PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT is disabled, using current window height: " + currentHeight, "debug");
811+
return currentHeight;
812+
}
813+
814+
Integer minHeight = resolveConfiguredMinHeight(options);
815+
if (minHeight == null) {
816+
log("minHeight not found in options or cliConfig, using current window height: " + currentHeight, "debug");
817+
return currentHeight;
818+
}
819+
820+
return calculateTargetHeight(jse, minHeight, currentHeight);
821+
}
822+
823+
// Reads minHeight from snapshot options first, then falls back to CLI snapshot config.
824+
private Integer resolveConfiguredMinHeight(Map<String, Object> options) {
825+
Object minHeightObj = options.get("minHeight");
826+
if (minHeightObj == null && cliConfig != null && cliConfig.has("snapshot")) {
827+
JSONObject snapshotConfig = cliConfig.getJSONObject("snapshot");
828+
if (snapshotConfig.has("minHeight")) {
829+
minHeightObj = snapshotConfig.getInt("minHeight");
830+
}
831+
}
832+
833+
if (minHeightObj == null) {
834+
return null;
835+
}
836+
837+
try {
838+
return Integer.parseInt(minHeightObj.toString());
839+
} catch (NumberFormatException e) {
840+
log("Invalid minHeight value " + minHeightObj + "; expected integer, using current window height instead.", "debug");
841+
return null;
842+
}
843+
}
844+
845+
// Converts content minHeight into browser outer height while preserving fallback behavior.
846+
private int calculateTargetHeight(JavascriptExecutor jse, int minHeight, int fallbackHeight) {
847+
Object result = jse.executeScript("return window.outerHeight - window.innerHeight + " + minHeight);
848+
if (result instanceof Number) {
849+
return ((Number) result).intValue();
850+
}
851+
return fallbackHeight;
852+
}
853+
788854
public List<Map<String, Object>> captureResponsiveDom(WebDriver driver, Set<Cookie> cookies, Map<String, Object> options) {
789855
List<Integer> responsiveWidths = extractResponsiveWidths(options);
790856
List<Map<String, Object>> widths = getResponsiveWidths(responsiveWidths);
@@ -796,32 +862,7 @@ public List<Map<String, Object>> captureResponsiveDom(WebDriver driver, Set<Cook
796862
int resizeCount = 0;
797863
JavascriptExecutor jse = (JavascriptExecutor) driver;
798864
jse.executeScript("PercyDOM.waitForResize()");
799-
int targetHeight = currentHeight;
800-
801-
if (PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT) {
802-
Object minHeightObj = options.get("minHeight");
803-
if (minHeightObj == null && cliConfig != null && cliConfig.has("snapshot")) {
804-
JSONObject snapshotConfig = cliConfig.getJSONObject("snapshot");
805-
if (snapshotConfig.has("minHeight")) {
806-
minHeightObj = snapshotConfig.getInt("minHeight");
807-
}
808-
}
809-
if (minHeightObj != null) {
810-
try {
811-
int minHeight = Integer.parseInt(minHeightObj.toString());
812-
Object result = jse.executeScript("return window.outerHeight - window.innerHeight + " + minHeight);
813-
if (result instanceof Number) {
814-
targetHeight = ((Number) result).intValue();
815-
}
816-
} catch (NumberFormatException e) {
817-
log("Invalid minHeight value " + minHeightObj + "; expected integer, using current window height instead.", "debug");
818-
}
819-
} else {
820-
log("minHeight not found in options or cliConfig, using current window height: " + targetHeight, "debug");
821-
}
822-
} else {
823-
log("PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT is disabled, using current window height: " + targetHeight, "debug");
824-
}
865+
int targetHeight = resolveResponsiveTargetHeight(options, jse, currentHeight);
825866
for (Map<String, Object> widthMap : widths) {
826867
Object widthObj = widthMap.get("width");
827868
if (!(widthObj instanceof Number)) {

0 commit comments

Comments
 (0)