diff --git a/src/main/java/io/appium/java_client/AppiumDriver.java b/src/main/java/io/appium/java_client/AppiumDriver.java index b444bbc7d..97b05d27b 100644 --- a/src/main/java/io/appium/java_client/AppiumDriver.java +++ b/src/main/java/io/appium/java_client/AppiumDriver.java @@ -64,7 +64,7 @@ */ @SuppressWarnings("unchecked") public class AppiumDriver - extends DefaultGenericMobileDriver implements ComparesImages { + extends DefaultGenericMobileDriver implements ComparesImages, FindsByImage { private static final ErrorHandler errorHandler = new ErrorHandler(new ErrorCodesMobile(), true); // frequently used command parameters diff --git a/src/main/java/io/appium/java_client/FindsByImage.java b/src/main/java/io/appium/java_client/FindsByImage.java new file mode 100644 index 000000000..76de64fd1 --- /dev/null +++ b/src/main/java/io/appium/java_client/FindsByImage.java @@ -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 extends FindsByFluentSelector { + /** + * 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 + * The documentation on Image Comparison Features + * @see + * The settings available for lookup fine-tuning + * @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 + * The documentation on Image Comparison Features + * @see + * The settings available for lookup fine-tuning + * @since Appium 1.8.2 + */ + default List findElementsByImage(String b64Template) { + return findElements(MobileSelector.IMAGE.toString(), b64Template); + } +} diff --git a/src/main/java/io/appium/java_client/MobileBy.java b/src/main/java/io/appium/java_client/MobileBy.java index 12f8de36a..3928799be 100644 --- a/src/main/java/io/appium/java_client/MobileBy.java +++ b/src/main/java/io/appium/java_client/MobileBy.java @@ -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 + * The documentation on Image Comparison Features + * @see + * The settings available for lookup fine-tuning + * @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 { @@ -146,7 +163,7 @@ public List findElements(SearchContext context) throws WebDriverExce .findElementsByIosUIAutomation(getLocatorString()); } - if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) { + if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) { return super.findElements(context); } @@ -171,7 +188,7 @@ public List findElements(SearchContext context) throws WebDriverExce .findElementByIosUIAutomation(getLocatorString()); } - if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) { + if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) { return super.findElement(context); } @@ -211,7 +228,7 @@ public List findElements(SearchContext context) throws WebDriverExce .findElementsByAndroidUIAutomator(getLocatorString()); } - if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) { + if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) { return super.findElements(context); } @@ -236,7 +253,7 @@ public List findElements(SearchContext context) throws WebDriverExce .findElementByAndroidUIAutomator(getLocatorString()); } - if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) { + if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) { return super.findElement(context); } @@ -275,7 +292,7 @@ public List findElements(SearchContext context) throws WebDriverExce .findElementsByAccessibilityId(getLocatorString()); } - if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) { + if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) { return super.findElements(context); } @@ -300,7 +317,7 @@ public List findElements(SearchContext context) throws WebDriverExce .findElementByAccessibilityId(getLocatorString()); } - if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) { + if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) { return super.findElement(context); } @@ -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); } @@ -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); } @@ -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); } @@ -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); } @@ -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); } @@ -480,7 +497,7 @@ protected ByWindowsAutomation(String locatorString) { .findElementByWindowsUIAutomation(getLocatorString()); } - if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) { + if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) { return super.findElement(context); } @@ -488,6 +505,62 @@ protected ByWindowsAutomation(String locatorString) { 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 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(); + } + } } diff --git a/src/main/java/io/appium/java_client/MobileSelector.java b/src/main/java/io/appium/java_client/MobileSelector.java index d3afac5bd..acc07343c 100644 --- a/src/main/java/io/appium/java_client/MobileSelector.java +++ b/src/main/java/io/appium/java_client/MobileSelector.java @@ -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;