Skip to content

Commit 1a09053

Browse files
committed
Add overloaded shootElement for dynamically finding elements to avoid StaleElementReference
1 parent 555c841 commit 1a09053

File tree

2 files changed

+252
-1
lines changed

2 files changed

+252
-1
lines changed

src/main/java/com/assertthat/selenium_shutterbug/core/Shutterbug.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,19 @@ public static ElementSnapshot shootElement(WebDriver driver,
201201
return shootElement(driver, element, capture, true);
202202
}
203203

204+
/**
205+
* To be used when need to screenshot particular element.
206+
*
207+
* @param driver WebDriver instance
208+
* @param element By element locator
209+
* @return ElementSnapshot instance
210+
*/
211+
public static ElementSnapshot shootElement(WebDriver driver,
212+
By element,
213+
CaptureElement capture) {
214+
return shootElement(driver, element, capture, true);
215+
}
216+
204217
/**
205218
* To be used when need to screenshot particular element.
206219
* Doesn't account for scrollable elements.
@@ -251,6 +264,39 @@ public static ElementSnapshot shootElement(WebDriver driver,
251264
return elementSnapshot;
252265
}
253266

267+
/**
268+
* To be used when need to screenshot particular element.
269+
* Can take screenshots of scrollable elements if Capture type is supplied.
270+
*
271+
* @param driver WebDriver instance
272+
* @param by By element locator
273+
* @param capture Capture type
274+
* @param useDevicePixelRatio whether to account for device pixel ratio
275+
* @return ElementSnapshot instance
276+
*/
277+
public static ElementSnapshot shootElement(WebDriver driver,
278+
By by,
279+
CaptureElement capture,
280+
boolean useDevicePixelRatio) {
281+
Browser browser = new Browser(driver, useDevicePixelRatio);
282+
ElementSnapshot elementSnapshot = new ElementSnapshot(driver, browser.getDevicePixelRatio());
283+
browser.scrollToElement(by);
284+
switch (capture) {
285+
case VERTICAL_SCROLL:
286+
elementSnapshot.setImage(browser.takeFullElementVerticalScreenshotScroll(by));
287+
break;
288+
case HORIZONTAL_SCROLL:
289+
elementSnapshot.setImage(browser.takeFullElementHorizontalScreenshotScroll(by));
290+
break;
291+
case FULL_SCROLL:
292+
elementSnapshot.setImage(browser.takeFullElementScreenshotScroll(by));
293+
break;
294+
default:
295+
elementSnapshot.setImage(browser.takeElementViewportScreenshot(by));
296+
}
297+
return elementSnapshot;
298+
}
299+
254300
/**
255301
* To be used when need to screenshot particular element by vertically centering it within viewport.
256302
*

src/main/java/com/assertthat/selenium_shutterbug/utils/web/Browser.java

Lines changed: 206 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,59 @@ public BufferedImage takeFullElementScreenshotScroll(WebElement element) {
368368
return combinedImage;
369369
}
370370

371+
public BufferedImage takeFullElementScreenshotScroll(By element) {
372+
Coordinates coordinates = getCoordinates(element);
373+
final int scrollableHeight = coordinates.getScrollHeight();
374+
final int scrollableWidth = coordinates.getScrollWidth();
375+
int elementWidth = coordinates.getWidth();
376+
int elementHeight = coordinates.getHeight();
377+
final int scrollBarMaxWidth = getElementScrollBarWidth(element);
378+
final int scrollBarMaxHeight = getElementScrollBarHeight(element);
379+
380+
381+
if (elementWidth < scrollableWidth || (elementHeight < scrollableHeight && elementWidth - scrollBarMaxHeight < scrollableWidth)) {
382+
elementHeight -= scrollBarMaxHeight;
383+
}
384+
if (elementHeight < scrollableHeight) {
385+
elementWidth -= scrollBarMaxWidth;
386+
}
387+
BufferedImage combinedImage = new BufferedImage(scrollableWidth,
388+
scrollableHeight,
389+
BufferedImage.TYPE_INT_ARGB);
390+
Graphics2D g = combinedImage.createGraphics();
391+
392+
int horizontalIterations =
393+
(int) Math.ceil((double) scrollableWidth / elementWidth);
394+
int verticalIterations = (int) Math.ceil((double) scrollableHeight / elementHeight);
395+
wait(beforeShootCondition, beforeShootTimeout);
396+
outer_loop:
397+
for (int j = 0; j < verticalIterations; j++) {
398+
399+
this.scrollElement(element, 0,
400+
j *
401+
elementHeight);
402+
for (int i = 0; i < horizontalIterations; i++) {
403+
this.scrollElement(element,
404+
i *
405+
elementWidth, j *
406+
elementHeight);
407+
wait(betweenScrollTimeout);
408+
BufferedImage image = takeFullPageElementScreenshot();
409+
image = image.getSubimage(coordinates.getAbsoluteX(),
410+
coordinates.getAbsoluteY(),
411+
elementWidth,
412+
elementHeight);
413+
g.drawImage(image, this.getElementCurrentScrollX(element),
414+
this.getElementCurrentScrollY(element), null);
415+
if (scrollableWidth == image.getWidth(null) && scrollableHeight == image.getHeight(null)) {
416+
break outer_loop;
417+
}
418+
}
419+
}
420+
g.dispose();
421+
return combinedImage;
422+
}
423+
371424
public BufferedImage takeFullElementVerticalScreenshotScroll(WebElement element) {
372425
Coordinates coordinates = getCoordinates(element);
373426

@@ -414,6 +467,52 @@ public BufferedImage takeFullElementVerticalScreenshotScroll(WebElement element)
414467
return combinedImage;
415468
}
416469

470+
public BufferedImage takeFullElementVerticalScreenshotScroll(By element) {
471+
Coordinates coordinates = getCoordinates(element);
472+
473+
final int scrollableHeight = coordinates.getScrollHeight();
474+
final int scrollableWidth = coordinates.getScrollWidth();
475+
int elementWidth = coordinates.getWidth();
476+
int elementHeight = coordinates.getHeight();
477+
final int scrollBarMaxWidth = getElementScrollBarWidth(element);
478+
final int scrollBarMaxHeight = getElementScrollBarHeight(element);
479+
480+
481+
if (elementWidth < scrollableWidth || (elementHeight < scrollableHeight && elementWidth - scrollBarMaxHeight < scrollableWidth)) {
482+
elementHeight -= scrollBarMaxHeight;
483+
}
484+
if (elementHeight < scrollableHeight) {
485+
elementWidth -= scrollBarMaxWidth;
486+
}
487+
BufferedImage combinedImage = new BufferedImage(elementWidth,
488+
scrollableHeight,
489+
BufferedImage.TYPE_INT_ARGB);
490+
Graphics2D g = combinedImage.createGraphics();
491+
492+
int verticalIterations =
493+
(int) Math.ceil(((double) scrollableHeight) / elementHeight);
494+
BufferedImage image;
495+
for (int j = 0; j < verticalIterations; j++) {
496+
this.scrollElement(element, getElementCurrentScrollX(element),
497+
j *
498+
elementHeight);
499+
wait(betweenScrollTimeout);
500+
image = takeFullPageElementScreenshot();
501+
image = image.getSubimage(coordinates.getAbsoluteX(),
502+
coordinates.getAbsoluteY(),
503+
elementWidth,
504+
elementHeight);
505+
506+
g.drawImage(image, 0, getElementCurrentScrollY(element),
507+
null);
508+
if (scrollableHeight == image.getHeight(null)) {
509+
break;
510+
}
511+
}
512+
g.dispose();
513+
return combinedImage;
514+
}
515+
417516
public BufferedImage takeFullElementHorizontalScreenshotScroll(WebElement element) {
418517
Coordinates coordinates = getCoordinates(element);
419518
final int scrollableHeight = coordinates.getScrollHeight();
@@ -459,6 +558,51 @@ public BufferedImage takeFullElementHorizontalScreenshotScroll(WebElement elemen
459558
return combinedImage;
460559
}
461560

561+
public BufferedImage takeFullElementHorizontalScreenshotScroll(By element) {
562+
Coordinates coordinates = getCoordinates(element);
563+
final int scrollableHeight = coordinates.getScrollHeight();
564+
final int scrollableWidth = coordinates.getScrollWidth();
565+
int elementWidth = coordinates.getWidth();
566+
int elementHeight = coordinates.getHeight();
567+
final int scrollBarMaxWidth = getElementScrollBarWidth(element);
568+
final int scrollBarMaxHeight = getElementScrollBarHeight(element);
569+
570+
571+
if (elementWidth < scrollableWidth || (elementHeight < scrollableHeight && elementWidth - scrollBarMaxHeight < scrollableWidth)) {
572+
elementHeight -= scrollBarMaxHeight;
573+
}
574+
if (elementHeight < scrollableHeight) {
575+
elementWidth -= scrollBarMaxWidth;
576+
}
577+
BufferedImage combinedImage = new BufferedImage(scrollableWidth,
578+
elementHeight,
579+
BufferedImage.TYPE_INT_ARGB);
580+
Graphics2D g = combinedImage.createGraphics();
581+
582+
int horizontalIterations =
583+
(int) Math.ceil(((double) scrollableWidth) / elementWidth);
584+
BufferedImage image;
585+
for (int j = 0; j < horizontalIterations; j++) {
586+
this.scrollElement(element,
587+
j *
588+
elementWidth, getElementCurrentScrollY(element));
589+
wait(betweenScrollTimeout);
590+
image = takeFullPageElementScreenshot();
591+
image = image.getSubimage(coordinates.getAbsoluteX(),
592+
coordinates.getAbsoluteY(),
593+
elementWidth,
594+
elementHeight);
595+
596+
g.drawImage(image, getElementCurrentScrollX(element), 0,
597+
null);
598+
if (scrollableWidth == image.getWidth(null)) {
599+
break;
600+
}
601+
}
602+
g.dispose();
603+
return combinedImage;
604+
}
605+
462606
public BufferedImage takeElementViewportScreenshot(WebElement element) {
463607
Coordinates coordinates = getCoordinates(element);
464608
final int scrollableHeight = coordinates.getScrollHeight();
@@ -490,9 +634,39 @@ public BufferedImage takeElementViewportScreenshot(WebElement element) {
490634
return combinedImage;
491635
}
492636

637+
public BufferedImage takeElementViewportScreenshot(By element) {
638+
Coordinates coordinates = getCoordinates(element);
639+
final int scrollableHeight = coordinates.getScrollHeight();
640+
final int scrollableWidth = coordinates.getScrollWidth();
641+
int elementWidth = coordinates.getWidth();
642+
int elementHeight = coordinates.getHeight();
643+
final int scrollBarMaxWidth = getElementScrollBarWidth(element);
644+
final int scrollBarMaxHeight = getElementScrollBarHeight(element);
645+
646+
if (elementWidth < scrollableWidth || (elementHeight < scrollableHeight && elementWidth - scrollBarMaxHeight < scrollableWidth)) {
647+
elementHeight -= scrollBarMaxHeight;
648+
}
649+
if (elementHeight < scrollableHeight) {
650+
elementWidth -= scrollBarMaxWidth;
651+
}
652+
BufferedImage combinedImage = new BufferedImage(elementWidth,
653+
elementHeight,
654+
BufferedImage.TYPE_INT_ARGB);
655+
Graphics2D g = combinedImage.createGraphics();
656+
wait(betweenScrollTimeout);
657+
BufferedImage image = takeFullPageElementScreenshot();
658+
image = image.getSubimage(coordinates.getAbsoluteX(),
659+
coordinates.getAbsoluteY(),
660+
elementWidth,
661+
elementHeight);
662+
g.drawImage(image, 0, 0,
663+
null);
664+
g.dispose();
665+
return combinedImage;
666+
}
493667

494-
public BufferedImage takeFrameViewportScreenshot(Coordinates coordinates) {
495668

669+
public BufferedImage takeFrameViewportScreenshot(Coordinates coordinates) {
496670
final int scrollableHeight = coordinates.getScrollHeight();
497671
final int scrollableWidth = coordinates.getScrollWidth();
498672
int elementWidth = coordinates.getWidth();
@@ -591,6 +765,14 @@ public int getElementScrollBarHeight(WebElement element) {
591765
return (int) (Double.parseDouble(executeJsScript(Browser.ELEMENT_SCROLL_BAR_HEIGHT, element).toString()) * devicePixelRatio);
592766
}
593767

768+
public int getElementScrollBarWidth(By by) {
769+
return (int) (Double.parseDouble(executeJsScript(Browser.ELEMENT_SCROLL_BAR_WIDTH, driver.findElement(by)).toString()) * devicePixelRatio);
770+
}
771+
772+
public int getElementScrollBarHeight(By by) {
773+
return (int) (Double.parseDouble(executeJsScript(Browser.ELEMENT_SCROLL_BAR_HEIGHT, driver.findElement(by)).toString()) * devicePixelRatio);
774+
}
775+
594776
public int getCurrentScrollY() {
595777
return (int) (Double.parseDouble(executeJsScript(Browser.CURRENT_SCROLL_Y_JS).toString()) * devicePixelRatio);
596778
}
@@ -603,6 +785,14 @@ public int getElementCurrentScrollY(WebElement element) {
603785
return (int) (Double.parseDouble(executeJsScript(Browser.ELEMENT_CURRENT_SCROLL_Y_JS, element).toString()) * devicePixelRatio);
604786
}
605787

788+
public int getElementCurrentScrollX(By by){
789+
return (int) (Double.parseDouble(executeJsScript(Browser.ELEMENT_CURRENT_SCROLL_X_JS, driver.findElement(by)).toString()) * devicePixelRatio);
790+
}
791+
792+
public int getElementCurrentScrollY(By by) {
793+
return (int) (Double.parseDouble(executeJsScript(Browser.ELEMENT_CURRENT_SCROLL_Y_JS, driver.findElement(by)).toString()) * devicePixelRatio);
794+
}
795+
606796
public int getDocWidth() {
607797
if (docWidth == -1)
608798
docWidth =
@@ -640,10 +830,19 @@ public Coordinates getCoordinates(WebElement element) {
640830
devicePixelRatio);
641831
}
642832

833+
public Coordinates getCoordinates(By by) {
834+
return getCoordinates(driver.findElement(by));
835+
}
836+
643837
public void scrollToElement(WebElement element) {
644838
executeJsScript(SCROLL_INTO_VIEW_JS, element);
645839
}
646840

841+
public void scrollToElement(By by) {
842+
driver.findElement(by);
843+
executeJsScript(SCROLL_INTO_VIEW_JS, by);
844+
}
845+
647846
public void scrollToElementVerticalCentered(WebElement element) {
648847
executeJsScript(SCROLL_INTO_VIEW_VERTICAL_CENTERED_JS, element);
649848
}
@@ -662,6 +861,12 @@ public void scrollElement(WebElement element, int x, int y) {
662861
y / devicePixelRatio);
663862
}
664863

864+
865+
public void scrollElement(By by, int x, int y) {
866+
executeJsScript(SCROLL_ELEMENT, driver.findElement(by), x / devicePixelRatio,
867+
y / devicePixelRatio);
868+
}
869+
665870
public Object executeJsScript(String filePath, Object... arg) {
666871
String script = FileUtil.getJsScript(filePath);
667872
JavascriptExecutor js = (JavascriptExecutor) driver;

0 commit comments

Comments
 (0)