Skip to content

Commit

Permalink
Add support for "-image" locator type (#990)
Browse files Browse the repository at this point in the history
* Add support for "-image" locator type

* Improve docstrings

* Fix typos

* Update string representation

* Make Codacy happy

* Rename the locator
  • Loading branch information
Mykola Mokhnach committed Aug 6, 2018
1 parent 65a708f commit 1478647
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 14 deletions.
2 changes: 1 addition & 1 deletion src/main/java/io/appium/java_client/AppiumDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
*/
@SuppressWarnings("unchecked")
public class AppiumDriver<T extends WebElement>
extends DefaultGenericMobileDriver<T> implements ComparesImages {
extends DefaultGenericMobileDriver<T> implements ComparesImages, FindsByImage<T> {

private static final ErrorHandler errorHandler = new ErrorHandler(new ErrorCodesMobile(), true);
// frequently used command parameters
Expand Down
63 changes: 63 additions & 0 deletions src/main/java/io/appium/java_client/FindsByImage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* 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 io.appium.java_client;

import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;

import java.util.List;

public interface FindsByImage<T extends WebElement> extends FindsByFluentSelector<T> {
/**
* Performs the lookup for a single element by matching its image template
* to the current full screen shot. This type of locator requires OpenCV libraries
* and bindings for NodeJS to be installed on the server machine. Lookup options
* fine-tuning might be done via {@link HasSettings#setSetting(Setting, Object)}.
*
* @param b64Template base64-encoded template image string. Supported image formats are the same
* as for OpenCV library.
* @return The first element that matches the given selector
* @throws NoSuchElementException when no element is found
* @see <a href="https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md">
* The documentation on Image Comparison Features</a>
* @see <a href="https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js">
* The settings available for lookup fine-tuning</a>
* @since Appium 1.8.2
*/
default T findElementByImage(String b64Template) {
return findElement(MobileSelector.IMAGE.toString(), b64Template);
}

/**
* Performs the lookup for a list of elements by matching them to image template
* in the current full screen shot. This type of locator requires OpenCV libraries
* and bindings for NodeJS to be installed on the server machine. Lookup options
* fine-tuning might be done via {@link HasSettings#setSetting(Setting, Object)}.
*
* @param b64Template base64-encoded template image string. Supported image formats are the same
* as for OpenCV library.
* @return a list of elements that match the given selector or an empty list
* @see <a href="https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md">
* The documentation on Image Comparison Features</a>
* @see <a href="https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js">
* The settings available for lookup fine-tuning</a>
* @since Appium 1.8.2
*/
default List<T> findElementsByImage(String b64Template) {
return findElements(MobileSelector.IMAGE.toString(), b64Template);
}
}
97 changes: 85 additions & 12 deletions src/main/java/io/appium/java_client/MobileBy.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,23 @@ public static By iOSNsPredicateString(final String iOSNsPredicateString) {
public static By windowsAutomation(final String windowsAutomation) {
return new ByWindowsAutomation(windowsAutomation);
}

/**
* This locator strategy is available only if OpenCV libraries and
* NodeJS bindings are installed on the server machine.
*
* @see <a href="https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md">
* The documentation on Image Comparison Features</a>
* @see <a href="https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js">
* The settings available for lookup fine-tuning</a>
* @since Appium 1.8.2
* @param b64Template base64-encoded template image string. Supported image formats are the same
* as for OpenCV library.
* @return an instance of {@link ByImage}
*/
public static By image(final String b64Template) {
return new ByImage(b64Template);
}

public static class ByIosUIAutomation extends MobileBy implements Serializable {

Expand All @@ -146,7 +163,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
.findElementsByIosUIAutomation(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElements(context);
}

Expand All @@ -171,7 +188,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
.findElementByIosUIAutomation(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElement(context);
}

Expand Down Expand Up @@ -211,7 +228,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
.findElementsByAndroidUIAutomator(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElements(context);
}

Expand All @@ -236,7 +253,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
.findElementByAndroidUIAutomator(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElement(context);
}

Expand Down Expand Up @@ -275,7 +292,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
.findElementsByAccessibilityId(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElements(context);
}

Expand All @@ -300,7 +317,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
.findElementByAccessibilityId(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElement(context);
}

Expand Down Expand Up @@ -336,7 +353,7 @@ protected ByIosClassChain(String locatorString) {
.findElementsByIosClassChain(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElements(context);
}

Expand All @@ -360,7 +377,7 @@ protected ByIosClassChain(String locatorString) {
.findElementByIosClassChain(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElement(context);
}

Expand Down Expand Up @@ -396,7 +413,7 @@ protected ByIosNsPredicate(String locatorString) {
.findElementsByIosNsPredicate(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElements(context);
}

Expand All @@ -420,7 +437,7 @@ protected ByIosNsPredicate(String locatorString) {
.findElementByIosNsPredicate(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElement(context);
}

Expand Down Expand Up @@ -456,7 +473,7 @@ protected ByWindowsAutomation(String locatorString) {
.findElementsByWindowsUIAutomation(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElements(context);
}

Expand All @@ -480,14 +497,70 @@ protected ByWindowsAutomation(String locatorString) {
.findElementByWindowsUIAutomation(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElement(context);
}

throw formIllegalArgumentException(contextClass, FindsByIosNSPredicate.class,
FindsByWindowsAutomation.class);
}
}

public static class ByImage extends MobileBy implements Serializable {

protected ByImage(String b64Template) {
super(MobileSelector.IMAGE, b64Template);
}

/**
* {@inheritDoc}
*
* @throws WebDriverException when current session doesn't support the given selector or when
* value of the selector is not consistent.
* @throws IllegalArgumentException when it is impossible to find something on the given
* {@link SearchContext} instance
*/
@SuppressWarnings("unchecked")
@Override public List<WebElement> findElements(SearchContext context) {
Class<?> contextClass = context.getClass();

if (FindsByImage.class.isAssignableFrom(contextClass)) {
return FindsByImage.class.cast(context).findElementsByImage(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElements(context);
}

throw formIllegalArgumentException(contextClass, FindsByImage.class, FindsByFluentSelector.class);
}

/**
* {@inheritDoc}
*
* @throws WebDriverException when current session doesn't support the given selector or when
* value of the selector is not consistent.
* @throws IllegalArgumentException when it is impossible to find something on the given
* {@link SearchContext} instance
*/
@Override public WebElement findElement(SearchContext context) {
Class<?> contextClass = context.getClass();

if (FindsByImage.class.isAssignableFrom(contextClass)) {
return FindsByImage.class.cast(context).findElementByImage(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElement(context);
}

throw formIllegalArgumentException(contextClass, FindsByImage.class, FindsByFluentSelector.class);
}

@Override public String toString() {
return "By.Image: " + getLocatorString();
}
}
}


3 changes: 2 additions & 1 deletion src/main/java/io/appium/java_client/MobileSelector.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public enum MobileSelector {
IOS_UI_AUTOMATION("-ios uiautomation"),
IOS_PREDICATE_STRING("-ios predicate string"),
IOS_CLASS_CHAIN("-ios class chain"),
WINDOWS_UI_AUTOMATION("-windows uiautomation");
WINDOWS_UI_AUTOMATION("-windows uiautomation"),
IMAGE("-image");

private final String selector;

Expand Down

0 comments on commit 1478647

Please sign in to comment.