Skip to content

Commit

Permalink
refactor: Add more nice functional stuff into pagefactory helpers (#1584
Browse files Browse the repository at this point in the history
)
  • Loading branch information
mykola-mokhnach authored Nov 5, 2021
1 parent 21d92f0 commit 3550ba7
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public class DefaultElementByBuilder extends AppiumByBuilder {

private static final String PRIORITY = "priority";
private static final String VALUE = "value";
private static final Class[] ANNOTATION_ARGUMENTS = new Class[]{};
private static final Class<?>[] ANNOTATION_ARGUMENTS = new Class[]{};
private static final Object[] ANNOTATION_PARAMETERS = new Object[]{};

public DefaultElementByBuilder(String platform, String automation) {
Expand Down Expand Up @@ -155,7 +155,7 @@ private By[] getBys(Class<? extends Annotation> singleLocator, Class<? extends A
}
}

return result.toArray(new By[result.size()]);
return result.toArray(new By[0]);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@
package io.appium.java_client.pagefactory.bys;

public enum ContentType {
HTML_OR_DEFAULT,
NATIVE_MOBILE_SPECIFIC;
HTML_OR_DEFAULT, NATIVE_MOBILE_SPECIFIC
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.openqa.selenium.By;
import org.openqa.selenium.support.pagefactory.AbstractAnnotations;

import javax.annotation.Nullable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
Expand All @@ -33,6 +34,7 @@
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
Expand All @@ -42,18 +44,19 @@
* - https://code.google.com/p/selenium/wiki/PageFactory
*/
public abstract class AppiumByBuilder extends AbstractAnnotations {
protected static final Class<?>[] DEFAULT_ANNOTATION_METHOD_ARGUMENTS = new Class<?>[] {};
protected static final Class<?>[] DEFAULT_ANNOTATION_METHOD_ARGUMENTS = new Class<?>[]{};

private static final List<String> METHODS_TO_BE_EXCLUDED_WHEN_ANNOTATION_IS_READ =
new ArrayList<String>() {
private static final long serialVersionUID = 1L; {
Stream.of(Object.class, Annotation.class, Proxy.class)
private static final List<String> METHODS_TO_BE_EXCLUDED_WHEN_ANNOTATION_IS_READ = new ArrayList<String>() {
private static final long serialVersionUID = 1L;

{
Stream.of(Object.class, Annotation.class, Proxy.class)
.map(Class::getDeclaredMethods)
.map(AppiumByBuilder::getMethodNames)
.flatMap(List::stream)
.forEach(this::add);
}
};
}
};
protected final AnnotatedElementContainer annotatedElementContainer;
protected final String platform;
protected final String automation;
Expand All @@ -65,79 +68,68 @@ protected AppiumByBuilder(String platform, String automation) {
}

private static List<String> getMethodNames(Method[] methods) {
List<String> names = new ArrayList<>();
for (Method m : methods) {
names.add(m.getName());
}
return names;
return Stream.of(methods).map(Method::getName).collect(Collectors.toList());
}

private static Method[] prepareAnnotationMethods(Class<? extends Annotation> annotation) {
List<String> targetAnnotationMethodNamesList =
getMethodNames(annotation.getDeclaredMethods());
List<String> targetAnnotationMethodNamesList = getMethodNames(annotation.getDeclaredMethods());
targetAnnotationMethodNamesList.removeAll(METHODS_TO_BE_EXCLUDED_WHEN_ANNOTATION_IS_READ);
Method[] result = new Method[targetAnnotationMethodNamesList.size()];
for (String methodName : targetAnnotationMethodNamesList) {
try {
result[targetAnnotationMethodNamesList.indexOf(methodName)] =
annotation.getMethod(methodName, DEFAULT_ANNOTATION_METHOD_ARGUMENTS);
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}
return result;
return targetAnnotationMethodNamesList.stream()
.map((methodName) -> {
try {
return annotation.getMethod(methodName, DEFAULT_ANNOTATION_METHOD_ARGUMENTS);
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}).toArray(Method[]::new);
}

private static String getFilledValue(Annotation mobileBy) {
Method[] values = prepareAnnotationMethods(mobileBy.getClass());
for (Method value : values) {
if (!String.class.equals(value.getReturnType())) {
continue;
}

try {
String strategyParameter = value.invoke(mobileBy).toString();
if (!strategyParameter.isEmpty()) {
return value.getName();
}
} catch (IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
}
throw new IllegalArgumentException(
"@" + mobileBy.getClass().getSimpleName() + ": one of " + Strategies.strategiesNames()
.toString() + " should be filled");
return Stream.of(prepareAnnotationMethods(mobileBy.getClass()))
.filter((method) -> String.class == method.getReturnType())
.filter((method) -> {
try {
Object strategyParameter = method.invoke(mobileBy);
return strategyParameter != null && !String.valueOf(strategyParameter).isEmpty();
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
})
.findFirst()
.map(Method::getName)
.orElseThrow(() -> new IllegalArgumentException(
String.format("@%s: one of %s should be filled",
mobileBy.getClass().getSimpleName(), Strategies.strategiesNames())
));
}

private static By getMobileBy(Annotation annotation, String valueName) {
Strategies[] strategies = Strategies.values();
for (Strategies strategy : strategies) {
if (strategy.returnValueName().equals(valueName)) {
return strategy.getBy(annotation);
}
}
throw new IllegalArgumentException(
"@" + annotation.getClass().getSimpleName() + ": There is an unknown strategy "
+ valueName);
return Stream.of(Strategies.values())
.filter((strategy) -> strategy.returnValueName().equals(valueName))
.findFirst()
.map((strategy) -> strategy.getBy(annotation))
.orElseThrow(() -> new IllegalArgumentException(
String.format("@%s: There is an unknown strategy %s",
annotation.getClass().getSimpleName(), valueName)
));
}

private static <T extends By> T getComplexMobileBy(Annotation[] annotations,
Class<T> requiredByClass) {
By[] byArray = new By[annotations.length];
for (int i = 0; i < annotations.length; i++) {
byArray[i] = getMobileBy(annotations[i], getFilledValue(annotations[i]));
}
private static <T extends By> T getComplexMobileBy(Annotation[] annotations, Class<T> requiredByClass) {
By[] byArray = Stream.of(annotations)
.map((annotation) -> getMobileBy(annotation, getFilledValue(annotation)))
.toArray(By[]::new);
try {
Constructor<T> c = requiredByClass.getConstructor(By[].class);
Object[] values = new Object[] {byArray};
Object[] values = new Object[]{byArray};
return c.newInstance(values);
} catch (Exception e) {
} catch (InvocationTargetException | NoSuchMethodException | InstantiationException
| IllegalAccessException e) {
throw new RuntimeException(e);
}
}

@Nullable
protected static By createBy(Annotation[] annotations, HowToUseSelectors howToUseLocators) {
if (annotations == null || annotations.length == 0) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public class ByChained extends org.openqa.selenium.support.pagefactory.ByChained
private static AppiumFunction<SearchContext, WebElement> getSearchingFunction(By by) {
return input -> {
try {
if (input == null) {
return null;
}
return input.findElement(by);
} catch (NoSuchElementException e) {
return null;
Expand Down Expand Up @@ -71,7 +74,7 @@ public WebElement findElement(SearchContext context) {
checkNotNull(searchingFunction);
return waiting.until(searchingFunction);
} catch (TimeoutException e) {
throw new NoSuchElementException("Cannot locate an element using " + toString());
throw new NoSuchElementException("Cannot locate an element using " + this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,5 @@
package io.appium.java_client.pagefactory.bys.builder;

public enum HowToUseSelectors {
USE_ONE,
BUILD_CHAINED,
USE_ANY;
USE_ONE, BUILD_CHAINED, USE_ANY
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.openqa.selenium.By;

Expand Down Expand Up @@ -127,12 +128,7 @@ enum Strategies {
}

static List<String> strategiesNames() {
Strategies[] strategies = values();
List<String> result = new ArrayList<>();
for (Strategies strategy : strategies) {
result.add(strategy.valueName);
}
return result;
return Stream.of(values()).map((s) -> s.valueName).collect(Collectors.toList());
}

private static String getValue(Annotation annotation, Strategies strategy) {
Expand Down

0 comments on commit 3550ba7

Please sign in to comment.