Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 17 additions & 37 deletions src/main/java/aquality/selenium/elements/interfaces/ByImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
/**
* Locator to search elements by image.
* Takes screenshot and finds match using openCV.
* Performs screenshot scaling if devicePixelRatio != 1.
* Then finds elements by coordinates using javascript.
*/
public class ByImage extends By {
private static boolean wasLibraryLoaded = false;
private final Mat template;
private final boolean doScaling;

private static void loadLibrary() {
if (!wasLibraryLoaded) {
Expand All @@ -39,19 +39,8 @@ private static void loadLibrary() {
* @param file image file to locate element by.
*/
public ByImage(File file) {
this(file, false);
}

/**
* Constructor accepting image file.
*
* @param file image file to locate element by.
* @param doScaling perform screenshot scaling if devicePixelRatio != 1
*/
public ByImage(File file, boolean doScaling) {
loadLibrary();
this.template = Imgcodecs.imread(file.getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
this.doScaling = doScaling;
}

/**
Expand All @@ -60,19 +49,8 @@ public ByImage(File file, boolean doScaling) {
* @param bytes image bytes to locate element by.
*/
public ByImage(byte[] bytes) {
this(bytes, false);
}

/**
* Constructor accepting image file.
*
* @param bytes image bytes to locate element by.
* @param doScaling perform screenshot scaling if devicePixelRatio != 1
*/
public ByImage(byte[] bytes, boolean doScaling) {
loadLibrary();
this.template = Imgcodecs.imdecode(new MatOfByte(bytes), Imgcodecs.IMREAD_UNCHANGED);
this.doScaling = doScaling;
}

@Override
Expand All @@ -82,21 +60,14 @@ public String toString() {

@Override
public List<WebElement> findElements(SearchContext context) {
byte[] screenshotBytes = getScreenshot(context);
Mat source = Imgcodecs.imdecode(new MatOfByte(screenshotBytes), Imgcodecs.IMREAD_UNCHANGED);
long devicePixelRatio = (long) AqualityServices.getBrowser().executeScript(JavaScript.GET_DEVICE_PIXEL_RATIO);
if (devicePixelRatio != 1 && doScaling) {
int scaledWidth = (int) (source.width() / devicePixelRatio);
int scaledHeight = (int) (source.height() / devicePixelRatio);
Imgproc.resize(source, source, new Size(scaledWidth, scaledHeight), 0, 0, Imgproc.INTER_AREA);
}
Mat source = getScreenshot(context);
Mat result = new Mat();
Imgproc.matchTemplate(source, template, result, Imgproc.TM_CCOEFF_NORMED);

float threshold = 1 - AqualityServices.getConfiguration().getVisualizationConfiguration().getDefaultThreshold();
Core.MinMaxLocResult minMaxLoc = Core.minMaxLoc(result);

int matchCounter = (result.width() - template.width() + 1) * (result.height() - template.height() + 1);
int matchCounter = Math.abs((result.width() - template.width() + 1) * (result.height() - template.height() + 1));
List<Point> matchLocations = new ArrayList<>();
while (matchCounter > 0 && minMaxLoc.maxVal >= threshold) {
matchCounter--;
Expand Down Expand Up @@ -149,11 +120,20 @@ protected static double distanceToPoint(Point matchLocation, WebElement element)
* Takes screenshot from searchContext if supported, or from browser.
*
* @param context search context for element location.
* @return captured screenshot as byte array.
* @return captured screenshot as Mat object.
*/
protected byte[] getScreenshot(SearchContext context) {
return !(context instanceof TakesScreenshot)
? AqualityServices.getBrowser().getScreenshot()
: ((TakesScreenshot) context).getScreenshotAs(OutputType.BYTES);
protected Mat getScreenshot(SearchContext context) {
byte[] screenshotBytes = context instanceof TakesScreenshot
? ((TakesScreenshot) context).getScreenshotAs(OutputType.BYTES)
: AqualityServices.getBrowser().getScreenshot();
boolean isBrowserScreenshot = context instanceof WebDriver || !(context instanceof TakesScreenshot);
Mat source = Imgcodecs.imdecode(new MatOfByte(screenshotBytes), Imgcodecs.IMREAD_UNCHANGED);
long devicePixelRatio = (long) AqualityServices.getBrowser().executeScript(JavaScript.GET_DEVICE_PIXEL_RATIO);
if (devicePixelRatio != 1 && isBrowserScreenshot) {
int scaledWidth = (int) (source.width() / devicePixelRatio);
int scaledHeight = (int) (source.height() / devicePixelRatio);
Imgproc.resize(source, source, new Size(scaledWidth, scaledHeight), 0, 0, Imgproc.INTER_AREA);
}
return source;
}
}
9 changes: 7 additions & 2 deletions src/test/java/tests/integration/LocatorTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import aquality.selenium.elements.interfaces.ILabel;
import automationpractice.forms.ChallengingDomForm;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.locators.RelativeLocator;
import org.testng.Assert;
Expand Down Expand Up @@ -38,14 +39,18 @@ public void testByImageLocator() {
Assert.assertFalse(form.getLabelByImage().state().isDisplayed(), "Should be impossible to find element on page by image when it is absent");
getBrowser().goTo(form.getUrl());
Assert.assertTrue(form.getLabelByImage().state().isDisplayed(), "Should be possible to find element on page by image");
Assert.assertEquals(form.getLabelByImage().getElement().getTagName(), "img", "Correct element must be found");

List<ILabel> childLabels = form.getChildLabelsByImage();
List<ILabel> docLabels = form.getLabelsByImage();
Assert.assertTrue(docLabels.size() > 1, "List of elements should be possible to find by image");
Assert.assertEquals(docLabels.size(), childLabels.size(), "Should be possible to find child elements by image with the same count");

ILabel screen = AqualityServices.getElementFactory().getLabel(new ByImage(AqualityServices.getBrowser().getScreenshot()), "full screen");
Assert.assertTrue(screen.state().waitForDisplayed(), "Should be possible to find element by full page screenshot");
ILabel documentByTag = AqualityServices.getElementFactory().getLabel(By.tagName("body"), "document by tag");
ILabel documentByImage = AqualityServices.getElementFactory().getLabel(new ByImage(documentByTag.getElement().getScreenshotAs(OutputType.BYTES)),
"full screen");
Assert.assertTrue(documentByImage.state().isDisplayed(), "Should be possible to find element by document screenshot");
Assert.assertEquals(documentByImage.getElement().getTagName(), "body", "Correct element must be found");
}

@Test
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/theinternet/forms/BrokenImagesForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import java.util.List;

public class BrokenImagesForm extends TheInternetForm {
private final By imageLocator = new ByImage(FileUtil.getResourceFileByName("brokenImage.png"), true);
private final By imageLocator = new ByImage(FileUtil.getResourceFileByName("brokenImage.png"));

public BrokenImagesForm(){
super(By.id("content"), "Broken Images form");
Expand Down