Skip to content

Commit 3432ef7

Browse files
committed
Merge pull request appium#140 from clicman/master
[+] Add support for selendroid locators in page object annotations
2 parents 0721b56 + eaaed61 commit 3432ef7

File tree

7 files changed

+255
-4
lines changed

7 files changed

+255
-4
lines changed

src/main/java/io/appium/java_client/pagefactory/AppiumAnnotations.java

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,15 @@ By getBy(Annotation annotation) {
137137

138138
private final Field mobileField;
139139
private final String platform;
140+
private final String automation;
140141

141-
AppiumAnnotations(Field field, String platform) {
142+
AppiumAnnotations(Field field, String platform, String automation) {
142143
super(field);
143144
mobileField = field;
144145
this.platform = String.valueOf(platform).
145146
toUpperCase().trim();
147+
this.automation = String.valueOf(automation).
148+
toUpperCase().trim();
146149
}
147150

148151
private static void checkDisallowedAnnotationPairs(Annotation a1,
@@ -161,14 +164,25 @@ private void assertValidAnnotations() {
161164
.getAnnotation(AndroidFindBys.class);
162165
AndroidFindAll androidFindAll = mobileField.
163166
getAnnotation(AndroidFindAll.class);
164-
167+
168+
SelendroidFindBy selendroidBy = mobileField
169+
.getAnnotation(SelendroidFindBy.class);
170+
SelendroidFindBys selendroidBys = mobileField
171+
.getAnnotation(SelendroidFindBys.class);
172+
SelendroidFindAll selendroidFindAll = mobileField.
173+
getAnnotation(SelendroidFindAll.class);
174+
165175
iOSFindBy iOSBy = mobileField.getAnnotation(iOSFindBy.class);
166176
iOSFindBys iOSBys = mobileField.getAnnotation(iOSFindBys.class);
167177
iOSFindAll iOSFindAll = mobileField.getAnnotation(iOSFindAll.class);
168178

169179
checkDisallowedAnnotationPairs(androidBy, androidBys);
170180
checkDisallowedAnnotationPairs(androidBy, androidFindAll);
171181
checkDisallowedAnnotationPairs(androidBys, androidFindAll);
182+
183+
checkDisallowedAnnotationPairs(selendroidBy, selendroidBys);
184+
checkDisallowedAnnotationPairs(selendroidBy, selendroidFindAll);
185+
checkDisallowedAnnotationPairs(selendroidBys, selendroidFindAll);
172186

173187
checkDisallowedAnnotationPairs(iOSBy, iOSBys);
174188
checkDisallowedAnnotationPairs(iOSBy, iOSFindAll);
@@ -248,12 +262,34 @@ private <T extends By> T getComplexMobileBy(Annotation[] annotations, Class<T> r
248262
public By buildBy() {
249263
assertValidAnnotations();
250264

265+
SelendroidFindBy selendroidBy = mobileField
266+
.getAnnotation(SelendroidFindBy.class);
267+
if (selendroidBy != null && ANDROID.toUpperCase().equals(platform) &&
268+
"Selendroid".toUpperCase().equals(automation)) {
269+
return getMobileBy(selendroidBy, getFilledValue(selendroidBy));
270+
}
271+
272+
SelendroidFindBys selendroidBys = mobileField
273+
.getAnnotation(SelendroidFindBys.class);
274+
if (selendroidBys != null && ANDROID.toUpperCase().equals(platform) &&
275+
"Selendroid".toUpperCase().equals(automation)) {
276+
return getComplexMobileBy(selendroidBys.value(), ByChained.class);
277+
}
278+
279+
SelendroidFindAll selendroidAll = mobileField
280+
.getAnnotation(SelendroidFindAll.class);
281+
if (selendroidAll != null && ANDROID.toUpperCase().equals(platform) &&
282+
"Selendroid".toUpperCase().equals(automation)) {
283+
return getComplexMobileBy(selendroidAll.value(), ByAll.class);
284+
}
285+
286+
251287
AndroidFindBy androidBy = mobileField
252288
.getAnnotation(AndroidFindBy.class);
253289
if (androidBy != null && ANDROID.toUpperCase().equals(platform)) {
254290
return getMobileBy(androidBy, getFilledValue(androidBy));
255291
}
256-
292+
257293
AndroidFindBys androidBys = mobileField
258294
.getAnnotation(AndroidFindBys.class);
259295
if (androidBys != null && ANDROID.toUpperCase().equals(platform)) {
@@ -265,6 +301,7 @@ public By buildBy() {
265301
return getComplexMobileBy(androidFindAll.value(), ByAll.class);
266302
}
267303

304+
268305
iOSFindBy iOSBy = mobileField.getAnnotation(iOSFindBy.class);
269306
if (iOSBy != null && IOS.toUpperCase().equals(platform)) {
270307
return getMobileBy(iOSBy, getFilledValue(iOSBy));

src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ public List<WebElement> apply(By by) {
7171
.valueOf(((HasCapabilities) unpackWebDriverFromSearchContext())
7272
.getCapabilities().getCapability(
7373
MobileCapabilityType.PLATFORM_NAME));
74-
AppiumAnnotations annotations = new AppiumAnnotations(field, platform);
74+
String automation = String
75+
.valueOf(((HasCapabilities) unpackWebDriverFromSearchContext())
76+
.getCapabilities().getCapability(
77+
MobileCapabilityType.AUTOMATION_NAME));
78+
79+
AppiumAnnotations annotations = new AppiumAnnotations(field, platform, automation);
7580
this.timeOutContainer = timeOutContainer;
7681
shouldCache = annotations.isLookupCached();
7782
by = annotations.buildBy();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.appium.java_client.pagefactory;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Used to mark a field on a Page/Screen Object to indicate that lookup should use a series of {@link SelendroidFindBy} tags
10+
* It will then search for all elements that match any criteria. Note that elements
11+
* are not guaranteed to be in document order.
12+
*/
13+
@Retention(RetentionPolicy.RUNTIME)
14+
@Target({ElementType.FIELD, ElementType.TYPE})
15+
public @interface SelendroidFindAll {
16+
SelendroidFindBy[] value();
17+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.appium.java_client.pagefactory;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
9+
/**
10+
* Used to mark a field on a Page Object to indicate an alternative mechanism for locating the
11+
* element or a list of elements. Used in conjunction with
12+
* {@link org.openqa.selenium.support.PageFactory}
13+
* this allows users to quickly and easily create PageObjects.
14+
* using Android UI selectors, accessibility, id, name, class name, tag and xpath
15+
*/
16+
@Retention(RetentionPolicy.RUNTIME)
17+
@Target({ElementType.FIELD, ElementType.TYPE})
18+
public @interface SelendroidFindBy {
19+
String id() default "";
20+
String name() default "";
21+
String className() default "";
22+
String tagName() default "";
23+
String xpath() default "";
24+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.appium.java_client.pagefactory;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Used to mark a field on a Page Object to indicate that lookup should use a series of @SelendroidFindBy tags
10+
*/
11+
@Retention(RetentionPolicy.RUNTIME)
12+
@Target({ElementType.FIELD, ElementType.TYPE})
13+
public @interface SelendroidFindBys {
14+
SelendroidFindBy[] value();
15+
}

src/test/java/io/appium/java_client/pagefactory_tests/AndroidPageObjectTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.appium.java_client.pagefactory.AndroidFindBy;
88
import io.appium.java_client.pagefactory.AndroidFindBys;
99
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
10+
import io.appium.java_client.pagefactory.SelendroidFindBy;
1011
import io.appium.java_client.pagefactory.iOSFindBy;
1112
import io.appium.java_client.pagefactory.iOSFindBys;
1213
import io.appium.java_client.remote.MobileCapabilityType;
@@ -148,6 +149,10 @@ public class AndroidPageObjectTest {
148149
@AndroidFindBy(id = "android:id/FakeId")
149150
})
150151
private WebElement findAllElementView;
152+
153+
@AndroidFindBy(id = "android:id/text1")
154+
@SelendroidFindBy(id = "Invalid Identifier")
155+
private WebElement textAndroidId;
151156

152157
@Before
153158
public void setUp() throws Exception {
@@ -311,4 +316,9 @@ public void findAllElementTest(){
311316
public void findAllElementsTest(){
312317
Assert.assertNotEquals(0, findAllElementViews.size());
313318
}
319+
320+
@Test
321+
public void findByAndroidAnnotationOnlyTest(){
322+
Assert.assertNotEquals(null, textAndroidId.getAttribute("text"));
323+
}
314324
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package io.appium.java_client.pagefactory_tests;
2+
3+
import io.appium.java_client.android.AndroidDriver;
4+
import io.appium.java_client.pagefactory.AndroidFindBy;
5+
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
6+
import io.appium.java_client.pagefactory.SelendroidFindAll;
7+
import io.appium.java_client.pagefactory.SelendroidFindBy;
8+
import io.appium.java_client.pagefactory.SelendroidFindBys;
9+
import io.appium.java_client.remote.MobileCapabilityType;
10+
import org.openqa.selenium.WebElement;
11+
import java.io.File;
12+
import java.net.URL;
13+
import java.util.List;
14+
import java.util.concurrent.TimeUnit;
15+
import org.junit.After;
16+
import org.junit.Assert;
17+
import org.junit.Before;
18+
import org.junit.Test;
19+
import org.openqa.selenium.WebDriver;
20+
import org.openqa.selenium.remote.DesiredCapabilities;
21+
import org.openqa.selenium.support.FindBy;
22+
import org.openqa.selenium.support.PageFactory;
23+
24+
public class SelendroidModeTest {
25+
26+
private WebDriver driver;
27+
28+
@SelendroidFindBy(id = "text1")
29+
private WebElement textId;
30+
31+
@AndroidFindBy(id = "Invalid Identifier")
32+
@SelendroidFindBy(id = "text1")
33+
private WebElement textSelendroidId;
34+
35+
@SelendroidFindBy(name = "Accessibility")
36+
private WebElement textName;
37+
38+
@AndroidFindBy(name = "Accessibility")
39+
private WebElement textNameAndroid;
40+
41+
@FindBy(name = "Accessibility")
42+
private WebElement textNameDefault;
43+
44+
@SelendroidFindBy(xpath = "//TextView[@value='Accessibility']")
45+
private WebElement textXpath;
46+
47+
@SelendroidFindBys({
48+
@SelendroidFindBy(id = "text1")})
49+
private WebElement textIds;
50+
51+
@SelendroidFindAll({
52+
@SelendroidFindBy(id = "text1")})
53+
private WebElement textAll;
54+
55+
@SelendroidFindAll({
56+
@SelendroidFindBy(id = "text1")})
57+
private List<WebElement> textsAll;
58+
59+
@SelendroidFindBy(className = "android.widget.TextView")
60+
private WebElement textClass;
61+
62+
@SelendroidFindBy(tagName = "TextView")
63+
private WebElement textTag;
64+
65+
@Before
66+
public void setUp() throws Exception {
67+
File appDir = new File("src/test/java/io/appium/java_client");
68+
File app = new File(appDir, "ApiDemos-debug.apk");
69+
DesiredCapabilities capabilities = new DesiredCapabilities();
70+
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator");
71+
capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
72+
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "Selendroid");
73+
driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
74+
75+
//This time out is set because test can be run on slow Android SDK emulator
76+
PageFactory.initElements(new AppiumFieldDecorator(driver, 5, TimeUnit.SECONDS), this);
77+
}
78+
79+
@After
80+
public void tearDown() throws Exception {
81+
driver.quit();
82+
}
83+
84+
@Test
85+
public void findByIdElementTest() {
86+
Assert.assertNotEquals(null, textId.getAttribute("text"));
87+
}
88+
89+
@Test
90+
public void findBySelendroidSelectorTest() {
91+
Assert.assertNotEquals(null, textSelendroidId.getAttribute("text"));
92+
}
93+
94+
@Test
95+
public void findByElementByNameTest() {
96+
Assert.assertEquals("Accessibility", textName.getText());
97+
}
98+
99+
@Test
100+
public void findByElementByNameAndroidTest() {
101+
Assert.assertEquals("Accessibility", textNameAndroid.getText());
102+
}
103+
104+
@Test
105+
public void findByElementByNameDefaultTest() {
106+
Assert.assertEquals("Accessibility", textNameDefault.getText());
107+
}
108+
109+
@Test
110+
public void findByElementByXpathTest() {
111+
Assert.assertEquals("Accessibility", textXpath.getText());
112+
}
113+
114+
@Test
115+
public void findByElementByIdsTest() {
116+
Assert.assertNotNull(textIds.getText());
117+
}
118+
119+
@Test
120+
public void findByElementByTestAllTest() {
121+
Assert.assertNotNull(textAll.getText());
122+
}
123+
124+
@Test
125+
public void findByElementByTextsAllTest() {
126+
Assert.assertTrue(textsAll.size() > 1);
127+
}
128+
129+
@Test
130+
public void findByElementByCalssTest() {
131+
Assert.assertNotEquals(null, textClass.getAttribute("text"));
132+
}
133+
134+
@Test
135+
public void findByElementByTagTest() {
136+
Assert.assertNotEquals(null, textTag.getAttribute("text"));
137+
}
138+
@Test
139+
public void findBySelendroidAnnotationOnlyTest() {
140+
Assert.assertNotEquals(null, textSelendroidId.getAttribute("text"));
141+
}
142+
143+
}

0 commit comments

Comments
 (0)