Skip to content

Commit 29336b8

Browse files
#565: The feature was implemented
but it needs to be covered with tests
1 parent 6c85323 commit 29336b8

20 files changed

+163
-107
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
AndroidFindBy[] value();
4141

4242
/**
43-
* @return priority of the searching
43+
* @return priority of the searching. Higher number means lower priority.
4444
*/
4545
int priority() default 0;
4646
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
String xpath() default "";
7171

7272
/**
73-
* @return priority of the searching
73+
* @return priority of the searching. Higher number means lower priority.
7474
*/
7575
int priority() default 0;
7676
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
AndroidFindBy[] value();
3939

4040
/**
41-
* @return priority of the searching
41+
* @return priority of the searching. Higher number means lower priority.
4242
*/
4343
int priority() default 0;
4444
}

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

Lines changed: 71 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919
import io.appium.java_client.pagefactory.bys.ContentMappedBy;
2020
import io.appium.java_client.pagefactory.bys.ContentType;
2121
import io.appium.java_client.pagefactory.bys.builder.AppiumByBuilder;
22+
import io.appium.java_client.pagefactory.bys.builder.ByChained;
2223
import io.appium.java_client.pagefactory.bys.builder.HowToUseSelectors;
2324
import org.openqa.selenium.By;
2425
import org.openqa.selenium.support.ByIdOrName;
2526
import org.openqa.selenium.support.CacheLookup;
2627
import org.openqa.selenium.support.FindAll;
2728
import org.openqa.selenium.support.FindBy;
2829
import org.openqa.selenium.support.FindBys;
30+
import org.openqa.selenium.support.pagefactory.ByAll;
2931

3032
import java.lang.annotation.Annotation;
3133
import java.lang.reflect.AnnotatedElement;
@@ -35,7 +37,11 @@
3537
import java.util.Comparator;
3638
import java.util.HashMap;
3739
import java.util.Map;
38-
import java.util.Optional;
40+
41+
import static java.util.Arrays.sort;
42+
import static java.util.Optional.ofNullable;
43+
import static org.apache.commons.lang3.ArrayUtils.add;
44+
import static org.apache.commons.lang3.ArrayUtils.addAll;
3945

4046
public class DefaultElementByBuilder extends AppiumByBuilder {
4147

@@ -58,17 +64,16 @@ private static void checkDisallowedAnnotationPairs(Annotation a1, Annotation a2)
5864
}
5965
}
6066

61-
private static By buildMobileBy(LocatorGroupStrategy locatorGroupStrategy, Annotation[] annotations) {
62-
if (annotations.length == 1) {
63-
return createBy(new Annotation[] {annotations[0]}, HowToUseSelectors.USE_ONE);
64-
} else {
65-
LocatorGroupStrategy strategy = Optional.ofNullable(locatorGroupStrategy)
66-
.orElse(LocatorGroupStrategy.CHAIN);
67-
if (strategy.equals(LocatorGroupStrategy.ALL_POSSIBLE)) {
68-
return createBy(annotations, HowToUseSelectors.USE_ANY);
69-
}
70-
return createBy(annotations, HowToUseSelectors.BUILD_CHAINED);
67+
private static By buildMobileBy(LocatorGroupStrategy locatorGroupStrategy, By[] bys) {
68+
if (bys.length == 0) {
69+
return null;
7170
}
71+
LocatorGroupStrategy strategy = ofNullable(locatorGroupStrategy)
72+
.orElse(LocatorGroupStrategy.CHAIN);
73+
if (strategy.equals(LocatorGroupStrategy.ALL_POSSIBLE)) {
74+
return new ByAll(bys);
75+
}
76+
return new ByChained(bys);
7277
}
7378

7479
@Override protected void assertValidAnnotations() {
@@ -105,105 +110,80 @@ private static By buildMobileBy(LocatorGroupStrategy locatorGroupStrategy, Annot
105110
return defaultBy;
106111
}
107112

108-
@Override protected By buildMobileNativeBy() {
113+
private By[] getBys(Class<? extends Annotation> singleLocator, Class<? extends Annotation> chainedLocator,
114+
Class<? extends Annotation> allLocator) {
115+
AnnotationComparator comparator = new AnnotationComparator();
109116
AnnotatedElement annotatedElement = annotatedElementContainer.getAnnotated();
110-
HowToUseLocators howToUseLocators = annotatedElement.getAnnotation(HowToUseLocators.class);
111117

112-
if (isSelendroidAutomation()) {
113-
SelendroidFindBy[] selendroidFindByArray =
114-
annotatedElement.getAnnotationsByType(SelendroidFindBy.class);
115-
//should be kept for some time
116-
SelendroidFindBys selendroidFindBys =
117-
annotatedElement.getAnnotation(SelendroidFindBys.class);
118-
SelendroidFindAll selendroidFindByAll =
119-
annotatedElement.getAnnotation(SelendroidFindAll.class);
120-
121-
if (selendroidFindByArray != null && selendroidFindByArray.length == 1) {
122-
return createBy(new Annotation[] {selendroidFindByArray[0]}, HowToUseSelectors.USE_ONE);
123-
}
118+
Annotation[] annotations = annotatedElement.getAnnotationsByType(singleLocator);
119+
annotations = addAll(annotations, annotatedElement.getAnnotationsByType(chainedLocator));
120+
annotations = addAll(annotations, annotatedElement.getAnnotationsByType(allLocator));
124121

125-
if (selendroidFindBys != null) {
126-
return createBy(selendroidFindBys.value(), HowToUseSelectors.BUILD_CHAINED);
127-
}
122+
sort(annotations, comparator);
123+
By[] result = new By[] {};
128124

129-
if (selendroidFindByAll != null) {
130-
return createBy(selendroidFindByAll.value(), HowToUseSelectors.USE_ANY);
125+
for (Annotation a: annotations) {
126+
Class<?> annotationClass = a.getClass().getInterfaces()[0];
127+
if (singleLocator.equals(annotationClass)) {
128+
result = add(result, createBy(new Annotation[] {a}, HowToUseSelectors.USE_ONE));
129+
continue;
131130
}
132-
///////////////////////////////////////
133-
//code that supposed to be supported
134-
if (selendroidFindByArray != null && selendroidFindByArray.length > 0) {
135-
return buildMobileBy(howToUseLocators != null ? howToUseLocators.selendroidAutomation() : null,
136-
selendroidFindByArray);
137-
}
138-
}
139131

140-
if (isAndroid()) {
141-
AndroidFindBy[] androidFindByArray = annotatedElement.getAnnotationsByType(AndroidFindBy.class);
142-
//should be kept for some time
143-
AndroidFindBys androidFindBys = annotatedElement.getAnnotation(AndroidFindBys.class);
144-
AndroidFindAll androidFindAll = annotatedElement.getAnnotation(AndroidFindAll.class);
145-
146-
if (androidFindByArray != null && androidFindByArray.length == 1) {
147-
return createBy(new Annotation[] {androidFindByArray[0]}, HowToUseSelectors.USE_ONE);
132+
Method value;
133+
Annotation[] set;
134+
try {
135+
value = annotationClass.getMethod(VALUE, ANNOTATION_ARGUMENTS);
136+
set = (Annotation[]) value.invoke(a, ANNOTATION_PARAMETERS);
137+
} catch (NoSuchMethodException| IllegalAccessException| InvocationTargetException e) {
138+
throw new ClassCastException(String.format("The annotation '%s' has no convenient '%s' method which " +
139+
"returns array of annotations", annotationClass.getName(), VALUE));
148140
}
149141

150-
if (androidFindBys != null) {
151-
return createBy(androidFindBys.value(), HowToUseSelectors.BUILD_CHAINED);
142+
sort(set, comparator);
143+
if (chainedLocator.equals(annotationClass)) {
144+
result = add(result, createBy(set, HowToUseSelectors.BUILD_CHAINED));
145+
continue;
152146
}
153147

154-
if (androidFindAll != null) {
155-
return createBy(androidFindAll.value(), HowToUseSelectors.USE_ANY);
156-
}
157-
///////////////////////////////////////
158-
//code that supposed to be supported
159-
if (androidFindByArray != null && androidFindByArray.length > 0) {
160-
return buildMobileBy(howToUseLocators != null ? howToUseLocators.androidAutomation() : null,
161-
androidFindByArray);
148+
if (allLocator.equals(annotationClass)) {
149+
result = add(result, createBy(set, HowToUseSelectors.USE_ANY));
162150
}
163151
}
164152

165-
if (isIOSXcuit()) {
166-
iOSXCUITFindBy[] xCuitFindByArray = annotatedElement.getAnnotationsByType(iOSXCUITFindBy.class);
167-
if (xCuitFindByArray != null && xCuitFindByArray.length > 0) {
168-
return buildMobileBy(howToUseLocators != null ? howToUseLocators.iOSXCUITAutomation() : null,
169-
xCuitFindByArray);
170-
}
171-
}
153+
return result;
154+
}
172155

173-
if (isIOS()) {
174-
iOSFindBy[] iOSFindByArray = annotatedElement.getAnnotationsByType(iOSFindBy.class);
175-
//should be kept for some time
176-
iOSFindBys iOSFindBys = annotatedElement.getAnnotation(iOSFindBys.class);
177-
iOSFindAll iOSFindAll = annotatedElement.getAnnotation(iOSFindAll.class);
156+
@Override protected By buildMobileNativeBy() {
157+
AnnotatedElement annotatedElement = annotatedElementContainer.getAnnotated();
158+
HowToUseLocators howToUseLocators = annotatedElement.getAnnotation(HowToUseLocators.class);
178159

179-
if (iOSFindByArray != null && iOSFindByArray.length == 1) {
180-
return createBy(new Annotation[] {iOSFindByArray[0]}, HowToUseSelectors.USE_ONE);
181-
}
160+
By result = null;
161+
if (isSelendroidAutomation()) {
162+
result = buildMobileBy(howToUseLocators != null ? howToUseLocators.selendroidAutomation() : null,
163+
getBys(SelendroidFindBy.class, SelendroidFindBys.class, SelendroidFindAll.class));
164+
}
182165

183-
if (iOSFindBys != null) {
184-
return createBy(iOSFindBys.value(), HowToUseSelectors.BUILD_CHAINED);
185-
}
166+
if (isAndroid() && result == null) {
167+
result = buildMobileBy(howToUseLocators != null ? howToUseLocators.androidAutomation() : null,
168+
getBys(AndroidFindBy.class, AndroidFindBys.class, AndroidFindAll.class));
169+
}
186170

187-
if (iOSFindAll != null) {
188-
return createBy(iOSFindAll.value(), HowToUseSelectors.USE_ANY);
189-
}
190-
///////////////////////////////////////
191-
//code that supposed to be supported
192-
if (iOSFindByArray != null && iOSFindByArray.length > 0) {
193-
return buildMobileBy(howToUseLocators != null ? howToUseLocators.iOSAutomation() : null,
194-
iOSFindByArray);
195-
}
171+
if (isIOSXcuit() && result == null) {
172+
result = buildMobileBy(howToUseLocators != null ? howToUseLocators.iOSXCUITAutomation() : null,
173+
getBys(iOSXCUITFindBy.class, iOSXCUITFindBys.class, iOSXCUITFindAll.class));
196174
}
197175

198-
if (isWindows()) {
199-
WindowsFindBy[] windowsFindByArray = annotatedElement.getAnnotationsByType(WindowsFindBy.class);
200-
if (windowsFindByArray != null && windowsFindByArray.length > 0) {
201-
return buildMobileBy(howToUseLocators != null ? howToUseLocators.windowsAutomation() : null,
202-
windowsFindByArray);
203-
}
176+
if (isIOS() && result == null) {
177+
result = buildMobileBy(howToUseLocators != null ? howToUseLocators.iOSAutomation() : null,
178+
getBys(iOSFindBy.class, iOSFindBys.class, iOSFindAll.class));
179+
}
180+
181+
if (isWindows() && result == null) {
182+
result = buildMobileBy(howToUseLocators != null ? howToUseLocators.windowsAutomation() : null,
183+
getBys(WindowsFindBy.class, WindowsFindBys.class, WindowsFindAll.class));
204184
}
205185

206-
return null;
186+
return ofNullable(result).orElse(null);
207187
}
208188

209189
@Override public boolean isLookupCached() {
@@ -255,8 +235,8 @@ public int compare(Annotation o1, Annotation o2) {
255235
int priority2;
256236
Method priority;
257237

258-
Class<? extends Annotation> c1 = o1.getClass();
259-
Class<? extends Annotation> c2 = o2.getClass();
238+
Class<?> c1 = o1.getClass().getInterfaces()[0];
239+
Class<?> c2 = o2.getClass().getInterfaces()[0];
260240

261241
if (!c1.equals(c2)) {
262242
throw new ClassCastException(String.format("Given annotations have different classes (%s, %s). " +

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
SelendroidFindBy[] value();
3737

3838
/**
39-
* @return priority of the searching
39+
* @return priority of the searching. Higher number means lower priority.
4040
*/
4141
int priority() default 0;
4242
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
String partialLinkText() default "";
7070

7171
/**
72-
* @return priority of the searching
72+
* @return priority of the searching. Higher number means lower priority.
7373
*/
7474
int priority() default 0;
7575
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.pagefactory;
18+
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
24+
/**
25+
* Defines set of chained/possible locators. Each one locator
26+
* should be defined with {@link SelendroidFindAll}
27+
*/
28+
@Target(value = {ElementType.TYPE, ElementType.FIELD})
29+
@Retention(value = RetentionPolicy.RUNTIME)
30+
public @interface SelendroidFindByAllSet {
31+
/**
32+
* @return an array of {@link SelendroidFindAll} which builds a sequence of
33+
* the chained searching for elements or a set of possible locators
34+
*/
35+
SelendroidFindAll[] value();
36+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.pagefactory;
18+
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
24+
/**
25+
* Defines set of chained/possible locators. Each one locator
26+
* should be defined with {@link SelendroidFindBys}
27+
*/
28+
@Target(value = {ElementType.TYPE, ElementType.FIELD})
29+
@Retention(value = RetentionPolicy.RUNTIME)
30+
public @interface SelendroidFindByChainSet {
31+
/**
32+
* @return an array of {@link SelendroidFindBys} which builds a sequence of
33+
* the chained searching for elements or a set of possible locators
34+
*/
35+
SelendroidFindBys[] value();
36+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
SelendroidFindBy[] value();
3535

3636
/**
37-
* @return priority of the searching
37+
* @return priority of the searching. Higher number means lower priority.
3838
*/
3939
int priority() default 0;
4040
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
WindowsFindBy[] value();
4141

4242
/**
43-
* @return priority of the searching
43+
* @return priority of the searching. Higher number means lower priority.
4444
*/
4545
int priority() default 0;
4646
}

0 commit comments

Comments
 (0)