Skip to content

Commit 0f654c9

Browse files
adding responsive capture feature
1 parent 47c2f77 commit 0f654c9

1 file changed

Lines changed: 106 additions & 18 deletions

File tree

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

Lines changed: 106 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)