Skip to content

Commit f10393a

Browse files
authored
add custom locator strategy (#1041)
* add custom locator strategy * use correct strategy constant * fix lint issues * add doc links
1 parent 18eba21 commit f10393a

File tree

4 files changed

+126
-2
lines changed

4 files changed

+126
-2
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, FindsByImage<T> {
67+
extends DefaultGenericMobileDriver<T> implements ComparesImages, FindsByImage<T>, FindsByCustom<T> {
6868

6969
private static final ErrorHandler errorHandler = new ErrorHandler(new ErrorCodesMobile(), true);
7070
// frequently used command parameters
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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 FindsByCustom<T extends WebElement> extends FindsByFluentSelector<T> {
25+
/**
26+
* Performs the lookup for a single element by sending a selector to a custom element finding
27+
* plugin. This type of locator requires the use of the 'customFindModules' capability and a
28+
* separately-installed element finding plugin.
29+
*
30+
* @param selector selector to pass to the custom element finding plugin
31+
* @return The first element that matches the given selector
32+
* @see <a href="https://appium.io/docs/en/advanced-concepts/element-finding-plugins/">
33+
* The documentation on custom element finding plugins and their use</a>
34+
* @throws NoSuchElementException when no element is found
35+
* @since Appium 1.9.2
36+
*/
37+
default T findElementByCustom(String selector) {
38+
return findElement(MobileSelector.CUSTOM.toString(), selector);
39+
}
40+
41+
/**
42+
* Performs the lookup for a single element by sending a selector to a custom element finding
43+
* plugin. This type of locator requires the use of the 'customFindModules' capability and a
44+
* separately-installed element finding plugin.
45+
*
46+
* @param selector selector to pass to the custom element finding plugin
47+
* @return a list of elements that match the given selector or an empty list
48+
* @see <a href="https://appium.io/docs/en/advanced-concepts/element-finding-plugins/">
49+
* The documentation on custom element finding plugins and their use</a>
50+
* @since Appium 1.9.2
51+
*/
52+
default List<T> findElementsByCustom(String selector) {
53+
return findElements(MobileSelector.CUSTOM.toString(), selector);
54+
}
55+
}

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,18 @@ public static By AndroidViewTag(final String tag) {
147147
public static By image(final String b64Template) {
148148
return new ByImage(b64Template);
149149
}
150+
151+
/**
152+
* This type of locator requires the use of the 'customFindModules' capability and a
153+
* separately-installed element finding plugin.
154+
*
155+
* @param selector selector to pass to the custom element finding plugin
156+
* @return an instance of {@link ByCustom}
157+
* @since Appium 1.9.2
158+
*/
159+
public static By custom(final String selector) {
160+
return new ByCustom(selector);
161+
}
150162

151163
public static class ByIosUIAutomation extends MobileBy implements Serializable {
152164

@@ -572,6 +584,62 @@ protected ByImage(String b64Template) {
572584
}
573585
}
574586

587+
public static class ByCustom extends MobileBy implements Serializable {
588+
589+
protected ByCustom(String selector) {
590+
super(MobileSelector.CUSTOM, selector);
591+
}
592+
593+
/**
594+
* {@inheritDoc}
595+
*
596+
* @throws WebDriverException when current session doesn't support the given selector or when
597+
* value of the selector is not consistent.
598+
* @throws IllegalArgumentException when it is impossible to find something on the given
599+
* {@link SearchContext} instance
600+
*/
601+
@SuppressWarnings("unchecked")
602+
@Override public List<WebElement> findElements(SearchContext context) {
603+
Class<?> contextClass = context.getClass();
604+
605+
if (FindsByCustom.class.isAssignableFrom(contextClass)) {
606+
return FindsByCustom.class.cast(context).findElementsByCustom(getLocatorString());
607+
}
608+
609+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
610+
return super.findElements(context);
611+
}
612+
613+
throw formIllegalArgumentException(contextClass, FindsByCustom.class, FindsByFluentSelector.class);
614+
}
615+
616+
/**
617+
* {@inheritDoc}
618+
*
619+
* @throws WebDriverException when current session doesn't support the given selector or when
620+
* value of the selector is not consistent.
621+
* @throws IllegalArgumentException when it is impossible to find something on the given
622+
* {@link SearchContext} instance
623+
*/
624+
@Override public WebElement findElement(SearchContext context) {
625+
Class<?> contextClass = context.getClass();
626+
627+
if (FindsByCustom.class.isAssignableFrom(contextClass)) {
628+
return FindsByCustom.class.cast(context).findElementByCustom(getLocatorString());
629+
}
630+
631+
if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
632+
return super.findElement(context);
633+
}
634+
635+
throw formIllegalArgumentException(contextClass, FindsByCustom.class, FindsByFluentSelector.class);
636+
}
637+
638+
@Override public String toString() {
639+
return "By.Custom: " + getLocatorString();
640+
}
641+
}
642+
575643
public static class ByAndroidViewTag extends MobileBy implements Serializable {
576644

577645
public ByAndroidViewTag(String tag) {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ public enum MobileSelector {
2424
IOS_CLASS_CHAIN("-ios class chain"),
2525
WINDOWS_UI_AUTOMATION("-windows uiautomation"),
2626
IMAGE("-image"),
27-
ANDROID_VIEWTAG("-android viewtag");
27+
ANDROID_VIEWTAG("-android viewtag"),
28+
CUSTOM("-custom");
2829

2930
private final String selector;
3031

0 commit comments

Comments
 (0)