Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#228 fix: The extension of the Page Object design pattern #267

Merged
merged 16 commits into from
Nov 21, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
#228 fix: Drafts of a new framework
  • Loading branch information
TikhomirovSergey committed Nov 1, 2015
commit 34b9bdf4a48a33cf7806595be2404d445a2c8fd3
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,19 @@
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;

class AppiumElementLocatorFactory implements ElementLocatorFactory {
protected final SearchContext searchContext;
protected final TimeOutDuration timeOutDuration;
protected final String platform;
protected final String automation;
protected final WebDriver originalWebDriver;
protected final AppiumByBuilder byBuilder;
private final SearchContext searchContext;
private final TimeOutDuration timeOutDuration;
private final WebDriver originalWebDriver;
private final AppiumByBuilder builder;

public AppiumElementLocatorFactory(SearchContext searchContext,
String platform, String automation,
TimeOutDuration timeOutDuration,
WebDriver originalWebDriver) {
WebDriver originalWebDriver,
AppiumByBuilder builder) {
this.searchContext = searchContext;
this.originalWebDriver = originalWebDriver;
this.timeOutDuration = timeOutDuration;
this.platform = platform;
this.automation = automation;
byBuilder = new DefaultElementByBuilder(platform, automation);
this.builder = builder;
}

public ElementLocator createLocator(Field field) {
Expand All @@ -53,10 +49,12 @@ public ElementLocator createLocator(Field field) {
}
else
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if/else should always use braces, even for single lines. This is a style suggestion.

customDuration = timeOutDuration;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spacing is weird, your IDE should be able to auto format the Java files

byBuilder.setAnnotated(field);
By by = byBuilder.buildBy();
builder.setAnnotated(field);
By by = builder.buildBy();
if (by != null)
return new AppiumElementLocator(searchContext, by, byBuilder.isLookupCached(), customDuration, originalWebDriver);
return new AppiumElementLocator(searchContext, by, builder.isLookupCached(), customDuration, originalWebDriver);
return null;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ public class AppiumFieldDecorator implements FieldDecorator{
}
};

private final SearchContext context;
private final WebDriver originalDriver;
private final DefaultFieldDecorator elementFieldDecoracor;
private final DefaultFieldDecorator defaultElementFieldDecoracor;
private final AppiumElementLocatorFactory widgetLocatorFactory;
public static long DEFAULT_IMPLICITLY_WAIT_TIMEOUT = 1;
public static TimeUnit DEFAULT_TIMEUNIT = TimeUnit.SECONDS;

Expand Down Expand Up @@ -113,13 +113,13 @@ public AppiumFieldDecorator(SearchContext context, long implicitlyWaitTimeOut, T
}

public AppiumFieldDecorator(SearchContext context, TimeOutDuration timeOutDuration) {
this.context = context;
this.originalDriver = unpackWebDriverFromSearchContext(this.context);
this.originalDriver = unpackWebDriverFromSearchContext(context);
String platform = getPlatform(originalDriver);
String automation = getAutomation(originalDriver);

elementFieldDecoracor = new DefaultFieldDecorator(
new AppiumElementLocatorFactory(context, platform, automation, timeOutDuration, originalDriver)){
defaultElementFieldDecoracor = new DefaultFieldDecorator(
new AppiumElementLocatorFactory(context, timeOutDuration, originalDriver,
new DefaultElementByBuilder(platform, automation))){
@Override
protected WebElement proxyForLocator(ClassLoader ignored, ElementLocator locator){
return getAProxOfASingleElement(locator);
Expand All @@ -135,25 +135,61 @@ protected boolean isDecoratableList(Field field) {
return isAvailableWebElementListField(field);
}
};
widgetLocatorFactory = new AppiumElementLocatorFactory(context, timeOutDuration, originalDriver,
new WidgetByBuilder(platform, automation));
}

public AppiumFieldDecorator(SearchContext context) {
this(context, DEFAULT_IMPLICITLY_WAIT_TIMEOUT, DEFAULT_TIMEUNIT);
}

public Object decorate(ClassLoader ignored, Field field) {
//TODO add logic of page object decoration
return elementFieldDecoracor.decorate(ignored, field);
Object result = defaultElementFieldDecoracor.decorate(ignored, field);
if (result != null)
return result;

return decorateWidget(field);
}

@SuppressWarnings("unchecked")
private Object decorateWidget(Field field){
Class<?> type = field.getType();
if (!Widget.class.isAssignableFrom(type) && !List.class.isAssignableFrom(type))
return null;

Class<? extends Widget> widgetType;
boolean isAlist = false;
if (List.class.isAssignableFrom(type)){
isAlist = true;
Type genericType = field.getGenericType();
if (!(genericType instanceof ParameterizedType)) {
return null;
}

Type listType = ((ParameterizedType) genericType).getActualTypeArguments()[0];

if (ParameterizedType.class.isAssignableFrom(listType.getClass()))
listType = ((ParameterizedType) listType).getRawType();

if (!Widget.class.isAssignableFrom((Class) listType))
return null;

widgetType = Class.class.cast(listType);
}
else
widgetType = (Class<? extends Widget>) field.getType();

ElementLocator locator = widgetLocatorFactory.createLocator(field);
//TODO
return null;
}

private Class<?> getTypeForProxy(){
Class<? extends SearchContext> driverClass = originalDriver.getClass();
Iterable<Map.Entry<Class<? extends SearchContext>, Class<? extends WebElement>>> rules = elementRuleMap.entrySet();
Iterator<Map.Entry<Class<? extends SearchContext>, Class<? extends WebElement>>> iterator = rules.iterator();
while (iterator.hasNext()){ //it will return MobileElement subclass when here is something
//it will return MobileElement subclass when here is something
for (Map.Entry<Class<? extends SearchContext>, Class<? extends WebElement>> e : rules) {
//that extends AppiumDriver or MobileElement
Map.Entry<Class<? extends SearchContext>, Class<? extends WebElement>> e = iterator.next();
if (e.getKey().isAssignableFrom(driverClass))
return e.getValue();
} //it is compatible with desktop browser. So at this case it returns RemoteWebElement.class
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.appium.java_client.pagefactory;


import io.appium.java_client.pagefactory.bys.ContentType;

import java.lang.reflect.AnnotatedElement;
import java.util.Map;

class OverrideWidgetReader {

Map<ContentType, Class<? extends Widget>> read(Class<? extends Widget> declaredClass, AnnotatedElement annotatedElement,
String platform, String automation){
//TODO
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
import org.openqa.selenium.WebElement;
import org.openqa.selenium.internal.WrapsDriver;
import org.openqa.selenium.internal.WrapsElement;
import org.openqa.selenium.support.PageFactory;

import java.util.List;

import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.unpackWebDriverFromSearchContext;
Expand All @@ -40,9 +38,8 @@ public abstract class Widget implements SearchContext, WrapsDriver, WrapsElement

private final WebElement element;

protected Widget(WebElement element, TimeOutDuration timeOutDuration) {
protected Widget(WebElement element) {
this.element = element;
PageFactory.initElements(new AppiumFieldDecorator(this, timeOutDuration), this);
}

@Override
Expand Down
101 changes: 101 additions & 0 deletions src/main/java/io/appium/java_client/pagefactory/WidgetByBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appium.java_client.pagefactory;

import org.openqa.selenium.By;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

class WidgetByBuilder extends DefaultElementByBuilder {

private enum WhatIsNeeded {
DEFAULT_OR_HTML,
MOBILE_NATIVE
}

protected WidgetByBuilder(String platform, String automation) {
super(platform, automation);
}

private static Class<?> getClassFromAListField(Field field){
Type genericType = field.getGenericType();
if (!(genericType instanceof ParameterizedType)) {
return null;
}

Type listType = ((ParameterizedType) genericType).getActualTypeArguments()[0];
if (ParameterizedType.class.isAssignableFrom(listType.getClass()))
listType = ((ParameterizedType) listType).getRawType();

return (Class<?>) listType;
}

private By getByFromDeclaredClass(WhatIsNeeded whatIsNeeded){
AnnotatedElement annotatedElement = annotatedElementContainer.getAnnotated();
By result = null;
Field field = null;
try {
Class<?> declaredClass;
if (Field.class.isAssignableFrom(annotatedElement.getClass())) {
field = Field.class.cast(annotatedElement);
if (List.class.isAssignableFrom(field.getType()))
declaredClass = getClassFromAListField(field);
else
declaredClass = field.getType();
} else {
declaredClass = Class.class.cast(annotatedElement);
}

while (result == null && !declaredClass.equals(Object.class)) {
setAnnotated(declaredClass);
if (whatIsNeeded.equals(WhatIsNeeded.DEFAULT_OR_HTML))
result = super.buildDefaultBy();
else
result = super.buildMobileNativeBy();
declaredClass = declaredClass.getSuperclass();
}
return result;
}
finally {
if (field != null)
setAnnotated(field);
}
}

@Override
protected By buildDefaultBy() {
By defaultBy = super.buildDefaultBy();

if (defaultBy != null)
return defaultBy;
else
return getByFromDeclaredClass(WhatIsNeeded.DEFAULT_OR_HTML);
}

@Override
protected By buildMobileNativeBy() {
By mobileBy = super.buildMobileNativeBy();

if (mobileBy != null)
return mobileBy;
else
return getByFromDeclaredClass(WhatIsNeeded.MOBILE_NATIVE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.appium.java_client.pagefactory;

import io.appium.java_client.pagefactory.bys.ContentType;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.openqa.selenium.WebElement;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

class WidgetInterceptor implements MethodInterceptor{

private final WebElement element;
private final Map<ContentType, Class<? extends Widget>> instantiationMap;
private final Map<ContentType, Widget> cachedInstances = new HashMap<>();
private final AppiumFieldDecorator decorator;

WidgetInterceptor(WebElement element, Map<ContentType, Class<? extends Widget>> instantiationMap,
AppiumFieldDecorator decorator) {
this.element = element;
this.instantiationMap = instantiationMap;
this.decorator = decorator;
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//TODO
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.appium.java_client.pagefactory;

import io.appium.java_client.pagefactory.bys.ContentType;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.openqa.selenium.WebElement;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

class WidgetListInterceptor implements MethodInterceptor{

private final List<WebElement> elements;
private final Map<ContentType, Class<? extends Widget>> instantiationMap;
private final List<WebElement> cachedElements = new ArrayList<>();
private final List<Widget> cachedWidgets = new ArrayList<>();
private final Class<? extends Widget> declaredType;
private final AppiumFieldDecorator decorator;

WidgetListInterceptor(List<WebElement> elements, Map<ContentType, Class<? extends Widget>> instantiationMap,
Class<? extends Widget> declaredType, AppiumFieldDecorator decorator) {
this.elements = elements;
this.instantiationMap = instantiationMap;
this.declaredType = declaredType;
this.decorator = decorator;
}


@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//TODO
return null;
}
}