Skip to content
This repository has been archived by the owner on Jan 12, 2021. It is now read-only.

Commit

Permalink
Merge pull request #37 from dpulitano/master
Browse files Browse the repository at this point in the history
Add ScreenshotUtils code and tests, scrollTo code and test, focusOn code and test, isElementVisibleWithinBoundsOfScreen code
  • Loading branch information
kood1 committed Mar 18, 2014
2 parents ac21968 + 418f3cc commit d5c5f0f
Show file tree
Hide file tree
Showing 9 changed files with 475 additions and 1 deletion.
217 changes: 217 additions & 0 deletions src/main/java/org/finra/jtaf/ewd/utils/ScreenshotUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/*
* (C) Copyright 2013 Java Test Automation Framework Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.finra.jtaf.ewd.utils;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.finra.jtaf.ewd.session.SessionManager;
import org.finra.jtaf.ewd.timer.WidgetTimeoutException;
import org.finra.jtaf.ewd.widget.IElement;
import org.finra.jtaf.ewd.widget.WidgetException;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.Point;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.internal.Locatable;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.remote.RemoteWebDriver;

/***
* Utilities to take and compare screenshots of elements
*
* @author Pulitand
*
*/
public class ScreenshotUtils {

private static Logger log = Logger.getLogger(ScreenshotUtils.class);

/***
* You can use this method to take your control pictures. Note that the file should be a png.
*
* @param element
* @param toSaveAs
* @throws IOException
* @throws WidgetException
* @throws WidgetTimeoutException
* @throws Exception
*/
public static void takeScreenshotOfElement(IElement element, File toSaveAs) throws IOException, WidgetException {

for(int i = 0; i < 10; i++) { //Loop up to 10x to ensure a clean screenshot was taken
log.info("Taking screen shot of locator " + element.getLocator() + " ... attempt #" + (i+1));

//Take picture of the page
WebDriver wd = SessionManager.getInstance().getCurrentSession().getWrappedDriver();
File screenshot;
boolean isRemote = false;
if(!(wd instanceof RemoteWebDriver)) {
screenshot = ((TakesScreenshot) wd).getScreenshotAs(OutputType.FILE);
}
else {
Augmenter augmenter = new Augmenter();
screenshot = ((TakesScreenshot) augmenter.augment(wd)).getScreenshotAs(OutputType.FILE);
isRemote = true;
}
BufferedImage fullImage = ImageIO.read(screenshot);
element.waitForElementPresent();
WebElement ele = element.getWebElement();

//Parse out the picture of the element
Point point = ele.getLocation();
int eleWidth = ele.getSize().getWidth();
int eleHeight = ele.getSize().getHeight();
int x;
int y;
if(isRemote) {
x = ((Locatable)ele).getCoordinates().inViewPort().getX();
y = ((Locatable)ele).getCoordinates().inViewPort().getY();
}
else {
x = point.getX();
y = point.getY();
element.scrollTo();
}
log.debug("Screenshot coordinates x: "+x+", y: "+y);
BufferedImage eleScreenshot = fullImage.getSubimage(x, y, eleWidth, eleHeight);
ImageIO.write(eleScreenshot, "png", screenshot);

//Ensure clean snapshot (sometimes WebDriver takes bad pictures and they turn out all black)
if(!isBlack(ImageIO.read(screenshot))) {
FileUtils.copyFile(screenshot, toSaveAs);
break;
}
}
}

/***
* Prereq: The page on which you are taking the screenshot is fully loaded
*
* Take a screenshot of the element identified by element and save the file as toSaveAs
* (Note that this file should be saved as a png).
* Test that the control picture, controlPicture, is both the same size as,
* and, has a similarity value greater than or equal to the threshold.
*
* @param element - the element to be tested
* @param controlPicture - the file of the picture that will serve as the control
* @param toSaveAs - for example, save the file at "testData/textFieldWidget/screenshot.png"
* @param threshold - you are asserting that the similarity between the two pictures
* is a double greater than or equal to this double (between 0.0 and 1.0)
* @throws Exception
*/
public static boolean isSimilarToScreenshot(IElement element, File controlPicture, File
toSaveAs, double threshold) throws IOException, WidgetException {

takeScreenshotOfElement(element, toSaveAs);

log.info("Screenshot was successful. Comparing against control...");

BufferedImage var = ImageIO.read(toSaveAs);
BufferedImage cont = ImageIO.read(controlPicture);

return isSimilar(var, cont, threshold);
}

/***
* Prereq: The page on which you are taking the screenshot is fully loaded
*
* Take a screenshot of the element identified by element and save the file as toSaveAs
* (Note that this file should be saved as a png).
* Test that the control picture, controlPicture, is both the same size as,
* and, has a similarity value greater than or equal to the default threshold of .85.
*
* @param element - the element to be tested
* @param controlPicture - the file of the picture that will serve as the control
* @param toSaveAs - for example, save the file at "testData/textFieldWidget/screenshot.png"
* @throws WidgetException
* @throws IOException
* @throws Exception
*/
public static boolean isSimilarToScreenshot(IElement element, File controlPicture,
File toSaveAs) throws IOException, WidgetException {
return isSimilarToScreenshot(element, controlPicture, toSaveAs, .85);
}

private static boolean isBlack(BufferedImage var) {
double[] varArr = new double[var.getWidth()*var.getHeight()*3];
//unroll pixels
for(int i = 0; i < var.getHeight(); i++) {
for(int j = 0; j < var.getWidth(); j++) {
varArr[i*var.getWidth() + j + 0] = new Color(var.getRGB(j, i)).getRed();
varArr[i*var.getWidth() + j + 1] = new Color(var.getRGB(j, i)).getGreen();
varArr[i*var.getWidth() + j + 2] = new Color(var.getRGB(j, i)).getBlue();
}
}

//test if all are black
for(int i=0;i!=varArr.length;i++){
if(varArr[i] != 0)
return false;
}
return true;
}

private static boolean isSimilar(BufferedImage var, BufferedImage cont, double threshold) {
return similarity(var, cont) >= threshold;
}

//Returns a double between 0 and 1.0
private static double similarity(BufferedImage var, BufferedImage cont) {

double[] varArr = new double[var.getWidth()*var.getHeight()*3];
double[] contArr = new double[cont.getWidth()*cont.getHeight()*3];

if (varArr.length!=contArr.length)
throw new IllegalStateException("The pictures are different sizes!");

//unroll pixels
for(int i = 0; i < var.getHeight(); i++) {
for(int j = 0; j < var.getWidth(); j++) {
varArr[i*var.getWidth() + j + 0] = new Color(var.getRGB(j, i)).getRed();
contArr[i*cont.getWidth() + j + 0] = new Color(cont.getRGB(j, i)).getRed();
varArr[i*var.getWidth() + j + 1] = new Color(var.getRGB(j, i)).getGreen();
contArr[i*cont.getWidth() + j + 1] = new Color(cont.getRGB(j, i)).getGreen();
varArr[i*var.getWidth() + j + 2] = new Color(var.getRGB(j, i)).getBlue();
contArr[i*cont.getWidth() + j + 2] = new Color(cont.getRGB(j, i)).getBlue();
}
}

double mins=0;
double maxs=0;
for(int i=0;i!=varArr.length;i++){
if (varArr[i]>contArr[i]){
mins+=contArr[i];
maxs+=varArr[i];
} else {
mins+=varArr[i];
maxs+=contArr[i];
}
}
return mins/maxs;
}


}
22 changes: 22 additions & 0 deletions src/main/java/org/finra/jtaf/ewd/widget/IElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ public interface IElement {
*/
public boolean isElementVisible(long time) throws WidgetException;

/***
* Implementing this method would allow for checking whether an element
* is within the bounds of the window
* @return
* @throws WidgetException
*/
public boolean isWithinBoundsOfWindow() throws WidgetException;

/**
* Implementing this method would allow for the test to wait until the
* element is present before progressing
Expand Down Expand Up @@ -482,5 +490,19 @@ public void waitForNotAttribute(String attributeName, String pattern, long timeo
* @throws WidgetException
*/
public void eval(String javascript) throws WidgetException;

/**
* Implementing this method would allow for the element to be scrolled to on the page
*
* @throws WidgetException
*/
public void scrollTo() throws WidgetException;

/**
* Implementing this method would allow for the element to be brought into focus
*
* @throws WidgetException
*/
public void focusOn() throws WidgetException;

}
59 changes: 59 additions & 0 deletions src/main/java/org/finra/jtaf/ewd/widget/element/Element.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@
import org.finra.jtaf.ewd.widget.IElement;
import org.finra.jtaf.ewd.widget.WidgetException;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.internal.Locatable;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
Expand Down Expand Up @@ -211,6 +213,43 @@ public boolean isElementVisible(long timeout) throws WidgetException {
private boolean isVisible() throws WidgetException {
return findElement().isDisplayed();
}

/***
* Determine if the element is within the bounds of the window
*
* @return true or false
* @throws WidgetException
*/
public boolean isWithinBoundsOfWindow() throws WidgetException {

JavascriptExecutor js = ((JavascriptExecutor) getGUIDriver().getWrappedDriver());

// execute javascript to get scroll values
Object top = js.executeScript("return document.body.scrollTop;");
Object left = js.executeScript("return document.body.scrollLeft;");

int scrollTop;
int scrollLeft;
try {
scrollTop = Integer.parseInt(top+"");
scrollLeft = Integer.parseInt(left+"");
}
catch(NumberFormatException e) {
throw new WidgetException("There was an error parsing the scroll values from the page", getLocator());
}

// calculate bounds
Dimension dim = getGUIDriver().getWrappedDriver().manage().window().getSize();
int windowWidth = dim.getWidth();
int windowHeight = dim.getHeight();
int x = ((Locatable)getWebElement()).getCoordinates().onPage().getX();
int y = ((Locatable)getWebElement()).getCoordinates().onPage().getY();
int relX = x - scrollLeft;
int relY = y - scrollTop;
return relX + findElement().getSize().getWidth() <= windowWidth &&
relY + findElement().getSize().getHeight() <= windowHeight &&
relX >= 0 && relY >= 0;
}

/*
* (non-Javadoc)
Expand Down Expand Up @@ -1218,4 +1257,24 @@ private void doHighlight(String colorMode) throws WidgetException {
setBackgroundColor(highDriver.getHighlightColor(colorMode));
}
}

/***
* Scroll to this element
*/
@Override
public void scrollTo() throws WidgetException {
WebElement we = findElement();
Locatable l = ((Locatable)we);
l.getCoordinates().inViewPort();
}

/***
* Focus on this element
*/
@Override
public void focusOn() throws WidgetException {
WebDriver driver = SessionManager.getInstance().getCurrentSession().getWrappedDriver();
JavascriptExecutor jse = (JavascriptExecutor) driver;
jse.executeScript("arguments[0].focus();", findElement());
}
}
Loading

0 comments on commit d5c5f0f

Please sign in to comment.