Skip to content

Commit 1478647

Browse files
author
Mykola Mokhnach
authored
Add support for "-image" locator type (#990)
* Add support for "-image" locator type * Improve docstrings * Fix typos * Update string representation * Make Codacy happy * Rename the locator
1 parent 65a708f commit 1478647

File tree

4 files changed

+151
-14
lines changed

4 files changed

+151
-14
lines changed

src/main/java/io/appium/java_client/AppiumDriver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
*/
6565
@SuppressWarnings("unchecked")
6666
public class AppiumDriver<T extends WebElement>
67-
extends DefaultGenericMobileDriver<T> implements ComparesImages {
67+
extends DefaultGenericMobileDriver<T> implements ComparesImages, FindsByImage<T> {
6868

6969
private static final ErrorHandler errorHandler = new ErrorHandler(new ErrorCodesMobile(), true);
7070
// frequently used command parameters
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* See the NOTICE file distributed with this work for additional
5+
* information regarding copyright ownership.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appium.java_client;
18+
19+
import org.openqa.selenium.NoSuchElementException;
20+
import org.openqa.selenium.WebElement;
21+
22+
import java.util.List;
23+
24+
public interface FindsByImage<T extends WebElement> extends FindsByFluentSelector<T> {
25+
/**
26+
* Performs the lookup for a single element by matching its image template
27+
* to the current full screen shot. This type of locator requires OpenCV libraries
28+
* and bindings for NodeJS to be installed on the server machine. Lookup options
29+
* fine-tuning might be done via {@link HasSettings#setSetting(Setting, Object)}.
30+
*
31+
* @param b64Template base64-encoded template image string. Supported image formats are the same
32+
* as for OpenCV library.
33+
* @return The first element that matches the given selector
34+
* @throws NoSuchElementException when no element is found
35+
* @see <a href="https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md">
36+
* The documentation on Image Comparison Features</a>
37+
* @see <a href="https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js">
38+
* The settings available for lookup fine-tuning</a>
39+
* @since Appium 1.8.2
40+
*/
41+
default T findElementByImage(String b64Template) {
42+
return findElement(MobileSelector.IMAGE.toString(), b64Template);
43+
}
44+
45+
/**
46+
* Performs the lookup for a list of elements by matching them to image template
47+
* in the current full screen shot. This type of locator requires OpenCV libraries
48+
* and bindings for NodeJS to be installed on the server machine. Lookup options
49+
* fine-tuning might be done via {@link HasSettings#setSetting(Setting, Object)}.
50+
*
51+
* @param b64Template base64-encoded template image string. Supported image formats are the same
52+
* as for OpenCV library.
53+
* @return a list of elements that match the given selector or an empty list
54+
* @see <a href="https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md">
55+
* The documentation on Image Comparison Features</a>
56+
* @see <a href="https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js">
57+
* The settings available for lookup fine-tuning</a>
58+
* @since Appium 1.8.2
59+
*/
60+
default List<T> findElementsByImage(String b64Template) {
61+
return findElements(MobileSelector.IMAGE.toString(), b64Template);
62+
}
63+
}

src/main/java/io/appium/java_client/MobileBy.java

Lines changed: 85 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,23 @@ public static By iOSNsPredicateString(final String iOSNsPredicateString) {
120120
public static By windowsAutomation(final String windowsAutomation) {
121121
return new ByWindowsAutomation(windowsAutomation);
122122
}
123+
124+
/**
125+
* This locator strategy is available only if OpenCV libraries and
126+
* NodeJS bindings are installed on the server machine.
127+
*
128+
* @see <a href="https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md">
129+
* The documentation on Image Comparison Features</a>
130+
* @see <a href="https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js">
131+
* The settings available for lookup fine-tuning</a>
132+
* @since Appium 1.8.2
133+
* @param b64Template base64-encoded template image string. Supported image formats are the same
134+
* as for OpenCV library.
135+
* @return an instance of {@link ByImage}
136+
*/
137+
public static By image(final String b64Template) {
138+
return new ByImage(b64Template);
139+
}
123140

124141
public static class ByIosUIAutomation extends MobileBy implements Serializable {
125142

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

149-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
166+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
150167
return super.findElements(context);
151168
}
152169

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

174-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
191+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
175192
return super.findElement(context);
176193
}
177194

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

214-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
231+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
215232
return super.findElements(context);
216233
}
217234

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

239-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
256+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
240257
return super.findElement(context);
241258
}
242259

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

278-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
295+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
279296
return super.findElements(context);
280297
}
281298

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

303-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
320+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
304321
return super.findElement(context);
305322
}
306323

@@ -336,7 +353,7 @@ protected ByIosClassChain(String locatorString) {
336353
.findElementsByIosClassChain(getLocatorString());
337354
}
338355

339-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
356+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
340357
return super.findElements(context);
341358
}
342359

@@ -360,7 +377,7 @@ protected ByIosClassChain(String locatorString) {
360377
.findElementByIosClassChain(getLocatorString());
361378
}
362379

363-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
380+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
364381
return super.findElement(context);
365382
}
366383

@@ -396,7 +413,7 @@ protected ByIosNsPredicate(String locatorString) {
396413
.findElementsByIosNsPredicate(getLocatorString());
397414
}
398415

399-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
416+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
400417
return super.findElements(context);
401418
}
402419

@@ -420,7 +437,7 @@ protected ByIosNsPredicate(String locatorString) {
420437
.findElementByIosNsPredicate(getLocatorString());
421438
}
422439

423-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
440+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
424441
return super.findElement(context);
425442
}
426443

@@ -456,7 +473,7 @@ protected ByWindowsAutomation(String locatorString) {
456473
.findElementsByWindowsUIAutomation(getLocatorString());
457474
}
458475

459-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
476+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
460477
return super.findElements(context);
461478
}
462479

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

483-
if (FindsByFluentSelector.class.isAssignableFrom(context.getClass())) {
500+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
484501
return super.findElement(context);
485502
}
486503

487504
throw formIllegalArgumentException(contextClass, FindsByIosNSPredicate.class,
488505
FindsByWindowsAutomation.class);
489506
}
490507
}
508+
509+
public static class ByImage extends MobileBy implements Serializable {
510+
511+
protected ByImage(String b64Template) {
512+
super(MobileSelector.IMAGE, b64Template);
513+
}
514+
515+
/**
516+
* {@inheritDoc}
517+
*
518+
* @throws WebDriverException when current session doesn't support the given selector or when
519+
* value of the selector is not consistent.
520+
* @throws IllegalArgumentException when it is impossible to find something on the given
521+
* {@link SearchContext} instance
522+
*/
523+
@SuppressWarnings("unchecked")
524+
@Override public List<WebElement> findElements(SearchContext context) {
525+
Class<?> contextClass = context.getClass();
526+
527+
if (FindsByImage.class.isAssignableFrom(contextClass)) {
528+
return FindsByImage.class.cast(context).findElementsByImage(getLocatorString());
529+
}
530+
531+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
532+
return super.findElements(context);
533+
}
534+
535+
throw formIllegalArgumentException(contextClass, FindsByImage.class, FindsByFluentSelector.class);
536+
}
537+
538+
/**
539+
* {@inheritDoc}
540+
*
541+
* @throws WebDriverException when current session doesn't support the given selector or when
542+
* value of the selector is not consistent.
543+
* @throws IllegalArgumentException when it is impossible to find something on the given
544+
* {@link SearchContext} instance
545+
*/
546+
@Override public WebElement findElement(SearchContext context) {
547+
Class<?> contextClass = context.getClass();
548+
549+
if (FindsByImage.class.isAssignableFrom(contextClass)) {
550+
return FindsByImage.class.cast(context).findElementByImage(getLocatorString());
551+
}
552+
553+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
554+
return super.findElement(context);
555+
}
556+
557+
throw formIllegalArgumentException(contextClass, FindsByImage.class, FindsByFluentSelector.class);
558+
}
559+
560+
@Override public String toString() {
561+
return "By.Image: " + getLocatorString();
562+
}
563+
}
491564
}
492565

493566

src/main/java/io/appium/java_client/MobileSelector.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ public enum MobileSelector {
2222
IOS_UI_AUTOMATION("-ios uiautomation"),
2323
IOS_PREDICATE_STRING("-ios predicate string"),
2424
IOS_CLASS_CHAIN("-ios class chain"),
25-
WINDOWS_UI_AUTOMATION("-windows uiautomation");
25+
WINDOWS_UI_AUTOMATION("-windows uiautomation"),
26+
IMAGE("-image");
2627

2728
private final String selector;
2829

0 commit comments

Comments
 (0)