diff --git a/README.md b/README.md index 07dd0e557..9c92cacbc 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,6 @@ More can be found in the docs, but here's a quick list of features which this pr - lockScreen() - isLocked() - shake() -- complexFind() - scrollTo() - scrollToExact() - openNotifications() @@ -109,6 +108,16 @@ If you are working on this project and use Intellij Idea, you need to change the If you are using the Eclipse IDE, make sure you are using verison Luna or later. ##Changelog## +*3.3.0 (still not released)* +- updated the dependency on Selenium to version 2.48.2 +- bug fix and enhancements of io.appium.java_client.service.local.AppiumDriverLocalService + - FIXED bug which was found and reproduced with Eclipse for Mac OS X. Please read about details here: [#252](https://github.com/appium/java-client/issues/252) + Thanks to [saikrishna321](https://github.com/saikrishna321) for the bug report + - The ability to set additional output streams was provided +- The additional __startActivity()__ method was added to AndroidDriver. It allows to start activities without the stopping of a target app +Thanks to [deadmoto](https://github.com/deadmoto) for the contribution +- The additional extension of the Page Object design pattern was designed. Please read about details here: [#267](https://github.com/appium/java-client/pull/267) + *3.2.0* - updated the dependency on Selenium to version 2.47.1 - the new dependency on commons-validator v1.4.1 diff --git a/pom.xml b/pom.xml index 466e276e7..9263dafd7 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ org.seleniumhq.selenium selenium-java - 2.47.1 + 2.48.2 junit diff --git a/src/main/java/io/appium/java_client/pagefactory/AppiumAnnotations.java b/src/main/java/io/appium/java_client/pagefactory/AppiumAnnotations.java deleted file mode 100644 index df630ea38..000000000 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumAnnotations.java +++ /dev/null @@ -1,392 +0,0 @@ -/* - * 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 static io.appium.java_client.remote.MobilePlatform.*; -import static io.appium.java_client.remote.AutomationName.*; -import io.appium.java_client.MobileBy; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.openqa.selenium.By; -import org.openqa.selenium.support.pagefactory.Annotations; -import org.openqa.selenium.support.pagefactory.ByAll; -import org.openqa.selenium.support.pagefactory.ByChained; - -class AppiumAnnotations extends Annotations { - - private final static List METHODS_TO_BE_EXCLUDED_WHEN_ANNOTATION_IS_READ = new ArrayList() { - private static final long serialVersionUID = 1L; - { - List objectClassMethodNames = getMethodNames(Object.class - .getDeclaredMethods()); - addAll(objectClassMethodNames); - List annotationClassMethodNames = getMethodNames(Annotation.class - .getDeclaredMethods()); - annotationClassMethodNames.removeAll(objectClassMethodNames); - addAll(annotationClassMethodNames); - } - }; - private final static Class[] DEFAULT_ANNOTATION_METHOD_ARGUMENTS = new Class[] {}; - - private static List getMethodNames(Method[] methods) { - List names = new ArrayList(); - for (Method m : methods) { - names.add(m.getName()); - } - return names; - } - - private static enum Strategies { - BYUIAUTOMATOR("uiAutomator") { - @Override - By getBy(Annotation annotation) { - String value = getValue(annotation, this); - if (annotation.annotationType().equals(AndroidFindBy.class)) { - return MobileBy.AndroidUIAutomator(value); - } - if (annotation.annotationType().equals(iOSFindBy.class)) { - return MobileBy.IosUIAutomation(value); - } - return super.getBy(annotation); - } - }, - BYACCESSABILITY("accessibility") { - @Override - By getBy(Annotation annotation) { - return MobileBy.AccessibilityId(getValue(annotation, this)); - } - }, - BYCLASSNAME("className") { - @Override - By getBy(Annotation annotation) { - return By.className(getValue(annotation, this)); - } - }, - BYID("id") { - @Override - By getBy(Annotation annotation) { - return By.id(getValue(annotation, this)); - } - }, - BYTAG("tagName") { - @Override - By getBy(Annotation annotation) { - return By.tagName(getValue(annotation, this)); - } - }, - BYNAME("name") { - @Override - By getBy(Annotation annotation) { - return By.name(getValue(annotation, this)); - } - }, - BYXPATH("xpath") { - @Override - By getBy(Annotation annotation) { - return By.xpath(getValue(annotation, this)); - } - }, - BYLINKTEXT("linkText") { - @Override - By getBy(Annotation annotation) { - return By.linkText(getValue(annotation, this)); - } - }, - BYPARTIALLINKTEXT("partialLinkText") { - @Override - By getBy(Annotation annotation) { - return By.partialLinkText(getValue(annotation, this)); - } - }; - - private final String valueName; - - private String returnValueName() { - return valueName; - } - - private Strategies(String valueName) { - this.valueName = valueName; - } - - private static String[] strategiesNames() { - Strategies[] strategies = values(); - String[] result = new String[strategies.length]; - int i = 0; - for (Strategies strategy : values()) { - result[i] = strategy.valueName; - i++; - } - return result; - } - - private static String getValue(Annotation annotation, - Strategies strategy) { - try { - Method m = annotation.getClass().getMethod(strategy.valueName, - DEFAULT_ANNOTATION_METHOD_ARGUMENTS); - return m.invoke(annotation, new Object[] {}).toString(); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } catch (SecurityException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - By getBy(Annotation annotation) { - return null; - } - } - - private final Field mobileField; - private final String platform; - private final String automation; - - AppiumAnnotations(Field field, String platform, String automation) { - super(field); - mobileField = field; - this.platform = String.valueOf(platform).toUpperCase().trim(); - this.automation = String.valueOf(automation).toUpperCase().trim(); - } - - private static void checkDisallowedAnnotationPairs(Annotation a1, - Annotation a2) throws IllegalArgumentException { - if (a1 != null && a2 != null) { - throw new IllegalArgumentException("If you use a '@" - + a1.getClass().getSimpleName() + "' annotation, " - + "you must not also use a '@" - + a2.getClass().getSimpleName() + "' annotation"); - } - } - - @Override - protected void assertValidAnnotations() { - AndroidFindBy androidBy = mobileField - .getAnnotation(AndroidFindBy.class); - AndroidFindBys androidBys = mobileField - .getAnnotation(AndroidFindBys.class); - AndroidFindAll androidFindAll = mobileField - .getAnnotation(AndroidFindAll.class); - - SelendroidFindBy selendroidBy = mobileField - .getAnnotation(SelendroidFindBy.class); - SelendroidFindBys selendroidBys = mobileField - .getAnnotation(SelendroidFindBys.class); - SelendroidFindAll selendroidFindAll = mobileField - .getAnnotation(SelendroidFindAll.class); - - iOSFindBy iOSBy = mobileField.getAnnotation(iOSFindBy.class); - iOSFindBys iOSBys = mobileField.getAnnotation(iOSFindBys.class); - iOSFindAll iOSFindAll = mobileField.getAnnotation(iOSFindAll.class); - - checkDisallowedAnnotationPairs(androidBy, androidBys); - checkDisallowedAnnotationPairs(androidBy, androidFindAll); - checkDisallowedAnnotationPairs(androidBys, androidFindAll); - - checkDisallowedAnnotationPairs(selendroidBy, selendroidBys); - checkDisallowedAnnotationPairs(selendroidBy, selendroidFindAll); - checkDisallowedAnnotationPairs(selendroidBys, selendroidFindAll); - - checkDisallowedAnnotationPairs(iOSBy, iOSBys); - checkDisallowedAnnotationPairs(iOSBy, iOSFindAll); - checkDisallowedAnnotationPairs(iOSBys, iOSFindAll); - super.assertValidAnnotations(); - } - - private static Method[] prepareAnnotationMethods( - Class annotation) { - List targeAnnotationMethodNamesList = getMethodNames(annotation - .getDeclaredMethods()); - targeAnnotationMethodNamesList - .removeAll(METHODS_TO_BE_EXCLUDED_WHEN_ANNOTATION_IS_READ); - Method[] result = new Method[targeAnnotationMethodNamesList.size()]; - for (String methodName : targeAnnotationMethodNamesList) { - try { - result[targeAnnotationMethodNamesList.indexOf(methodName)] = annotation - .getMethod(methodName, - DEFAULT_ANNOTATION_METHOD_ARGUMENTS); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } catch (SecurityException e) { - throw new RuntimeException(e); - } - } - return result; - } - - // I suppose that only @AndroidFindBy and @iOSFindBy will be here - private static String getFilledValue(Annotation mobileBy) { - Method[] values = prepareAnnotationMethods(mobileBy.getClass()); - for (Method value : values) { - try { - String strategyParameter = value.invoke(mobileBy, - new Object[] {}).toString(); - if (!"".equals(strategyParameter)) { - return value.getName(); - } - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } - throw new IllegalArgumentException("@" - + mobileBy.getClass().getSimpleName() + ": one of " - + Strategies.strategiesNames().toString() + " should be filled"); - } - - private 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); - } - - @SuppressWarnings("unchecked") - private T getComplexMobileBy(Annotation[] annotations, - Class requiredByClass) { - ; - By[] byArray = new By[annotations.length]; - for (int i = 0; i < annotations.length; i++) { - byArray[i] = getMobileBy(annotations[i], - getFilledValue(annotations[i])); - } - try { - Constructor c = requiredByClass.getConstructor(By[].class); - Object[] values = new Object[] { byArray }; - return (T) c.newInstance(values); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private ContentMappedBy setByForTheNativeContentAndReturn(By by, - Map contentMap) { - if (by != null) - contentMap.put(ContentType.NATIVE, by); - return new ContentMappedBy(contentMap); - } - - @Override - public By buildBy() { - - Map contentMap = new HashMap(); - - By defaultBy = super.buildBy(); - contentMap.put(ContentType.HTML, defaultBy); - contentMap.put(ContentType.NATIVE, defaultBy); - - assertValidAnnotations(); - - SelendroidFindBy selendroidBy = mobileField - .getAnnotation(SelendroidFindBy.class); - if (selendroidBy != null && ANDROID.toUpperCase().equals(platform) - && SELENDROID.toUpperCase().equals(automation)) { - return setByForTheNativeContentAndReturn( - getMobileBy(selendroidBy, getFilledValue(selendroidBy)), - contentMap); - } - - SelendroidFindBys selendroidBys = mobileField - .getAnnotation(SelendroidFindBys.class); - if (selendroidBys != null && ANDROID.toUpperCase().equals(platform) - && SELENDROID.toUpperCase().equals(automation)) { - return setByForTheNativeContentAndReturn( - getComplexMobileBy(selendroidBys.value(), ByChained.class), - contentMap); - } - - SelendroidFindAll selendroidAll = mobileField - .getAnnotation(SelendroidFindAll.class); - if (selendroidAll != null && ANDROID.toUpperCase().equals(platform) - && SELENDROID.toUpperCase().equals(automation)) { - return setByForTheNativeContentAndReturn( - getComplexMobileBy(selendroidAll.value(), ByAll.class), - contentMap); - } - - AndroidFindBy androidBy = mobileField - .getAnnotation(AndroidFindBy.class); - if (androidBy != null && ANDROID.toUpperCase().equals(platform)) { - return setByForTheNativeContentAndReturn( - getMobileBy(androidBy, getFilledValue(androidBy)), - contentMap); - } - - AndroidFindBys androidBys = mobileField - .getAnnotation(AndroidFindBys.class); - if (androidBys != null && ANDROID.toUpperCase().equals(platform)) { - return setByForTheNativeContentAndReturn( - getComplexMobileBy(androidBys.value(), ByChained.class), - contentMap); - } - - AndroidFindAll androidFindAll = mobileField - .getAnnotation(AndroidFindAll.class); - if (androidFindAll != null && ANDROID.toUpperCase().equals(platform)) { - return setByForTheNativeContentAndReturn( - getComplexMobileBy(androidFindAll.value(), ByAll.class), - contentMap); - } - - iOSFindBy iOSBy = mobileField.getAnnotation(iOSFindBy.class); - if (iOSBy != null && IOS.toUpperCase().equals(platform)) { - return setByForTheNativeContentAndReturn( - getMobileBy(iOSBy, getFilledValue(iOSBy)), - contentMap); - } - - iOSFindBys iOSBys = mobileField.getAnnotation(iOSFindBys.class); - if (iOSBys != null && IOS.toUpperCase().equals(platform)) { - return setByForTheNativeContentAndReturn( - getComplexMobileBy(iOSBys.value(), ByChained.class), - contentMap); - } - - iOSFindAll iOSFindAll = mobileField.getAnnotation(iOSFindAll.class); - if (iOSFindAll != null && IOS.toUpperCase().equals(platform)) { - return setByForTheNativeContentAndReturn( - getComplexMobileBy(iOSFindAll.value(), ByAll.class), - contentMap); - } - - return new ContentMappedBy(contentMap); - } - -} diff --git a/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java b/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java index 22153c28a..60b61ccf0 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java @@ -16,191 +16,163 @@ package io.appium.java_client.pagefactory; -import io.appium.java_client.android.AndroidDriver; -import io.appium.java_client.ios.IOSDriver; -import io.appium.java_client.remote.MobileCapabilityType; - -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import io.appium.java_client.remote.MobilePlatform; +import io.appium.java_client.pagefactory.locator.CacheableLocator; import org.openqa.selenium.*; -import org.openqa.selenium.support.pagefactory.ElementLocator; import org.openqa.selenium.support.ui.FluentWait; - import com.google.common.base.Function; -class AppiumElementLocator implements ElementLocator { - - // This function waits for not empty element list using all defined by - private static class WaitingFunction implements - Function> { - private final SearchContext searchContext; - private final static String INVALID_SELECTOR_PATTERN = "Invalid locator strategy:"; - - private WaitingFunction(SearchContext searchContext) { - this.searchContext = searchContext; - } - - public List apply(By by) { - List result = new ArrayList(); - try { - result.addAll(searchContext.findElements(by)); - } catch (StaleElementReferenceException ignored) { - } - catch (RuntimeException e){ - if (!isInvalidSelectorRootCause(e)) - throw e; - } - if (result.size() > 0) { - return result; - } else { - return null; - } - } +import static io.appium.java_client.pagefactory.ThrowableUtil.extractReadableException; +import static io.appium.java_client.pagefactory.ThrowableUtil.isInvalidSelectorRootCause; +import static io.appium.java_client.pagefactory.ThrowableUtil.isStaleElementReferenceException; - private static boolean isInvalidSelectorRootCause(Throwable e){ - if (e == null) - return false; +class AppiumElementLocator implements CacheableLocator { - if (String.valueOf(e.getMessage()).contains(INVALID_SELECTOR_PATTERN)) - return true; + // This function waits for not empty element list using all defined by + private static class WaitingFunction implements + Function> { + private final SearchContext searchContext; + Throwable foundStaleElementReferenceException; - return isInvalidSelectorRootCause(e.getCause()); + private WaitingFunction(SearchContext searchContext) { + this.searchContext = searchContext; } - } - - private final SearchContext searchContext; - private final boolean shouldCache; - private final By by; - private WebElement cachedElement; - private List cachedElementList; - - private final TimeOutDuration timeOutDuration; - - /** - * Creates a new mobile element locator. It instantiates {@link WebElement} - * using @AndroidFindBy (-s), @iOSFindBy (-s) and @FindBy (-s) annotation - * sets - * - * @param searchContext - * The context to use when finding the element - * @param field - * The field on the Page Object that will hold the located value - */ - AppiumElementLocator(SearchContext searchContext, Field field, - TimeOutDuration timeOutDuration) { - this.searchContext = searchContext; - - String platform = getPlatform(); - String automation = getAutomation(); - - AppiumAnnotations annotations = new AppiumAnnotations(field, platform, - automation); - if (field.isAnnotationPresent(WithTimeout.class)){ - WithTimeout withTimeout = field.getAnnotation(WithTimeout.class); - this.timeOutDuration = new TimeOutDuration(withTimeout.time(), withTimeout.unit()); + + public List apply(By by) { + List result = new ArrayList<>(); + Throwable shouldBeThrown = null; + boolean isRootCauseInvalidSelector; + boolean isRootCauseStaleElementReferenceException = false; + foundStaleElementReferenceException = null; + + try { + result.addAll(searchContext.findElements(by)); + } catch (Throwable e) { + + isRootCauseInvalidSelector = isInvalidSelectorRootCause(e); + if (!isRootCauseInvalidSelector) { + isRootCauseStaleElementReferenceException = isStaleElementReferenceException(e); + } + + if (isRootCauseStaleElementReferenceException) { + foundStaleElementReferenceException = extractReadableException(e); + } + + if (!isRootCauseInvalidSelector & !isRootCauseStaleElementReferenceException) { + shouldBeThrown = extractReadableException(e); + } + } + + if (shouldBeThrown != null) { + if (RuntimeException.class.isAssignableFrom(shouldBeThrown.getClass())) { + throw (RuntimeException) shouldBeThrown; + } + throw new RuntimeException(shouldBeThrown); + } + + if (result.size() > 0) { + return result; + } else { + return null; + } } - else - this.timeOutDuration = timeOutDuration; - shouldCache = annotations.isLookupCached(); - by = annotations.buildBy(); - } - - private String getPlatform(){ - WebDriver d = WebDriverUnpackUtility. - unpackWebDriverFromSearchContext(this.searchContext); - if (d == null) - return null; - - Class driverClass = d.getClass(); - if (AndroidDriver.class.isAssignableFrom(driverClass)) - return MobilePlatform.ANDROID; - - if (IOSDriver.class.isAssignableFrom(driverClass)) - return MobilePlatform.IOS; - - //it is possible that somebody uses RemoteWebDriver or their - //own WebDriver implementation. At this case capabilities are used - //to detect platform - if (HasCapabilities.class.isAssignableFrom(driverClass)) - return String.valueOf(((HasCapabilities) d).getCapabilities(). - getCapability(MobileCapabilityType.PLATFORM_NAME)); - - return null; } - private String getAutomation(){ - WebDriver d = WebDriverUnpackUtility. - unpackWebDriverFromSearchContext(this.searchContext); - if (d == null) - return null; + private final SearchContext searchContext; + final boolean shouldCache; + final By by; + private WebElement cachedElement; + private List cachedElementList; + final TimeOutDuration timeOutDuration; + final WebDriver originalWebDriver; + private final WaitingFunction waitingFunction; + + /** + * Creates a new mobile element locator. It instantiates {@link WebElement} + * using @AndroidFindBy (-s), @iOSFindBy (-s) and @FindBy (-s) annotation + * sets + * @param searchContext + * The context to use when finding the element + * @param by a By locator strategy + * @param shouldCache is the flag that signalizes that elements which are found once should be cached + * @param duration is a POJO which contains timeout parameters + * @param originalWebDriver + */ + public AppiumElementLocator(SearchContext searchContext, By by, boolean shouldCache, TimeOutDuration duration, WebDriver originalWebDriver) { + this.searchContext = searchContext; + this.shouldCache = shouldCache; + this.timeOutDuration = duration; + this.by = by; + this.originalWebDriver = originalWebDriver; + waitingFunction = new WaitingFunction(this.searchContext); + } - if (HasCapabilities.class.isAssignableFrom(d.getClass())) - return String.valueOf(((HasCapabilities) d).getCapabilities(). - getCapability(MobileCapabilityType.AUTOMATION_NAME)); + private void changeImplicitlyWaitTimeOut(long newTimeOut, + TimeUnit newTimeUnit) { + originalWebDriver.manage().timeouts().implicitlyWait(newTimeOut, newTimeUnit); + } - return null; + // This method waits for not empty element list using all defined by + private List waitFor() { + // When we use complex By strategies (like ChainedBy or ByAll) + // there are some problems (StaleElementReferenceException, implicitly + // wait time out + // for each chain By section, etc) + try { + changeImplicitlyWaitTimeOut(0, TimeUnit.SECONDS); + FluentWait wait = new FluentWait<>(by); + wait.withTimeout(timeOutDuration.getTime(), + timeOutDuration.getTimeUnit()); + return wait.until(waitingFunction); + } catch (TimeoutException e) { + return new ArrayList<>(); + } finally { + changeImplicitlyWaitTimeOut(timeOutDuration.getTime(), + timeOutDuration.getTimeUnit()); + } } - private void changeImplicitlyWaitTimeOut(long newTimeOut, - TimeUnit newTimeUnit) { - WebDriverUnpackUtility.unpackWebDriverFromSearchContext(searchContext) - .manage().timeouts().implicitlyWait(newTimeOut, newTimeUnit); - } - - // This method waits for not empty element list using all defined by - private List waitFor() { - // When we use complex By strategies (like ChainedBy or ByAll) - // there are some problems (StaleElementReferenceException, implicitly - // wait time out - // for each chain By section, etc) - try { - changeImplicitlyWaitTimeOut(0, TimeUnit.SECONDS); - FluentWait wait = new FluentWait(by); - wait.withTimeout(timeOutDuration.getTime(), - timeOutDuration.getTimeUnit()); - return wait.until(new WaitingFunction(searchContext)); - } catch (TimeoutException e) { - return new ArrayList(); - } finally { - changeImplicitlyWaitTimeOut(timeOutDuration.getTime(), - timeOutDuration.getTimeUnit()); - } - } - - /** - * Find the element. - */ - public WebElement findElement() { - if (cachedElement != null && shouldCache) { - return cachedElement; - } - List result = waitFor(); - if (result.size() == 0) { - String message = "Can't locate an element by this strategy: " - + by.toString(); - throw new NoSuchElementException(message); - } - if (shouldCache) { - cachedElement = result.get(0); - } - return result.get(0); - } - - /** - * Find the element list. - */ - public List findElements() { - if (cachedElementList != null && shouldCache) { - return cachedElementList; - } - List result = waitFor(); - if (shouldCache) { - cachedElementList = result; - } - return result; - } + /** + * Find the element. + */ + public WebElement findElement() { + if (cachedElement != null && shouldCache) { + return cachedElement; + } + List result = waitFor(); + if (result.size() == 0) { + String message = "Can't locate an element by this strategy: " + + by.toString(); + if (waitingFunction.foundStaleElementReferenceException != null) { + throw new NoSuchElementException(message, waitingFunction.foundStaleElementReferenceException); + } + throw new NoSuchElementException(message); + } + if (shouldCache) { + cachedElement = result.get(0); + } + return result.get(0); + } + + /** + * Find the element list. + */ + public List findElements() { + if (cachedElementList != null && shouldCache) { + return cachedElementList; + } + List result = waitFor(); + if (shouldCache) { + cachedElementList = result; + } + return result; + } + + @Override + public boolean isLookUpCached() { + return shouldCache; + } } diff --git a/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocatorFactory.java b/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocatorFactory.java index e5265f4d6..ece053753 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocatorFactory.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocatorFactory.java @@ -16,28 +16,53 @@ package io.appium.java_client.pagefactory; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; +import io.appium.java_client.pagefactory.bys.builder.AppiumByBuilder; +import io.appium.java_client.pagefactory.locator.CacheableElementLocatorFactory; +import io.appium.java_client.pagefactory.locator.CacheableLocator; +import org.openqa.selenium.By; import org.openqa.selenium.SearchContext; -import org.openqa.selenium.support.pagefactory.ElementLocator; -import org.openqa.selenium.support.pagefactory.ElementLocatorFactory; +import org.openqa.selenium.WebDriver; -class AppiumElementLocatorFactory implements ElementLocatorFactory { +class AppiumElementLocatorFactory implements CacheableElementLocatorFactory { private final SearchContext searchContext; - private final TimeOutDuration timeOutDuration; - - public AppiumElementLocatorFactory(SearchContext searchContext, - TimeOutDuration timeOutDuration) { - this.searchContext = searchContext; - this.timeOutDuration = timeOutDuration; - } - - public AppiumElementLocatorFactory(SearchContext searchContext) { - this(searchContext, new TimeOutDuration(AppiumFieldDecorator.DEFAULT_IMPLICITLY_WAIT_TIMEOUT, - AppiumFieldDecorator.DEFAULT_TIMEUNIT)); - } - - public ElementLocator createLocator(Field field) { - return new AppiumElementLocator(searchContext, field, timeOutDuration); - } + private final TimeOutDuration timeOutDuration; + private final WebDriver originalWebDriver; + private final AppiumByBuilder builder; + + public AppiumElementLocatorFactory(SearchContext searchContext, + TimeOutDuration timeOutDuration, + WebDriver originalWebDriver, + AppiumByBuilder builder) { + this.searchContext = searchContext; + this.originalWebDriver = originalWebDriver; + this.timeOutDuration = timeOutDuration; + this.builder = builder; + } + + public CacheableLocator createLocator(Field field) { + return this.createLocator((AnnotatedElement) field); + } + + @Override + public CacheableLocator createLocator(AnnotatedElement annotatedElement) { + TimeOutDuration customDuration; + if (annotatedElement.isAnnotationPresent(WithTimeout.class)) { + WithTimeout withTimeout = annotatedElement.getAnnotation(WithTimeout.class); + customDuration = new TimeOutDuration(withTimeout.time(), withTimeout.unit()); + } + else { + customDuration = timeOutDuration; + } + + builder.setAnnotated(annotatedElement); + By by = builder.buildBy(); + if (by != null) + return new AppiumElementLocator(searchContext, by, builder.isLookupCached(), customDuration, originalWebDriver); + return null; + } + + } diff --git a/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java b/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java index 72cc4282b..e75901de9 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java @@ -23,151 +23,205 @@ import io.appium.java_client.ios.IOSDriver; import io.appium.java_client.ios.IOSElement; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.*; import java.util.concurrent.TimeUnit; +import io.appium.java_client.pagefactory.bys.ContentType; +import io.appium.java_client.pagefactory.locator.CacheableLocator; import org.openqa.selenium.SearchContext; +import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.RemoteWebElement; +import org.openqa.selenium.support.pagefactory.DefaultFieldDecorator; import org.openqa.selenium.support.pagefactory.ElementLocator; import org.openqa.selenium.support.pagefactory.FieldDecorator; +import static io.appium.java_client.pagefactory.utils.ProxyFactory.getEnhancedProxy; +import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.getAutomation; +import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.getPlatform; +import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.unpackWebDriverFromSearchContext; + /** * Default decorator for use with PageFactory. Will decorate 1) all of the * WebElement fields and 2) List fields that have * {@literal @AndroidFindBy}, {@literal @AndroidFindBys}, or * {@literal @iOSFindBy/@iOSFindBys} annotation with a proxy that locates the * elements using the passed in ElementLocatorFactory. - * + * * Please pay attention: fields of {@link WebElement}, {@link RemoteWebElement}, * {@link MobileElement}, {@link AndroidElement} and {@link IOSElement} are allowed * to use with this decorator */ -public class AppiumFieldDecorator implements FieldDecorator { - - private static final List> availableElementClasses = - new ArrayList>(){ - private static final long serialVersionUID = 1L; - { - add(WebElement.class); - add(RemoteWebElement.class); - add(MobileElement.class); +public class AppiumFieldDecorator implements FieldDecorator{ + + private static final List> availableElementClasses = + new ArrayList>() { + private static final long serialVersionUID = 1L; + { + add(WebElement.class); + add(RemoteWebElement.class); + add(MobileElement.class); add(TouchableElement.class); - add(AndroidElement.class); - add(IOSElement.class); - } - - }; + add(AndroidElement.class); + add(IOSElement.class); + } + + }; private final static Map, Class> elementRuleMap = - new HashMap, Class>(){ + new HashMap, Class>() { private static final long serialVersionUID = 1L; { put(AndroidDriver.class, AndroidElement.class); - put(AndroidElement.class, AndroidElement.class); put(IOSDriver.class, IOSElement.class); - put(IOSElement.class, IOSElement.class); } }; - - private final AppiumElementLocatorFactory factory; - private final SearchContext context; - public static long DEFAULT_IMPLICITLY_WAIT_TIMEOUT = 1; - public static TimeUnit DEFAULT_TIMEUNIT = TimeUnit.SECONDS; + private final WebDriver originalDriver; + private final DefaultFieldDecorator defaultElementFieldDecoracor; + private final AppiumElementLocatorFactory widgetLocatorFactory; + private final String platform; + private final String automation; + private final TimeOutDuration timeOutDuration; + + public static long DEFAULT_IMPLICITLY_WAIT_TIMEOUT = 1; + public static TimeUnit DEFAULT_TIMEUNIT = TimeUnit.SECONDS; - public AppiumFieldDecorator(SearchContext context, long implicitlyWaitTimeOut, TimeUnit timeUnit) { - this.context = context; - factory = new AppiumElementLocatorFactory(this.context, new TimeOutDuration(implicitlyWaitTimeOut, timeUnit)); - } + public AppiumFieldDecorator(SearchContext context, long implicitlyWaitTimeOut, TimeUnit timeUnit) { + this(context, new TimeOutDuration(implicitlyWaitTimeOut, timeUnit)); + } public AppiumFieldDecorator(SearchContext context, TimeOutDuration timeOutDuration) { - this.context = context; - factory = new AppiumElementLocatorFactory(this.context, timeOutDuration); + this.originalDriver = unpackWebDriverFromSearchContext(context); + platform = getPlatform(originalDriver); + automation = getAutomation(originalDriver); + this.timeOutDuration = timeOutDuration; + + defaultElementFieldDecoracor = new DefaultFieldDecorator( + new AppiumElementLocatorFactory(context, timeOutDuration, originalDriver, + new DefaultElementByBuilder(platform, automation))) { + @Override + protected WebElement proxyForLocator(ClassLoader ignored, ElementLocator locator) { + return proxyForAnElement(locator); + } + + @Override + @SuppressWarnings("unchecked") + protected List proxyForListLocator(ClassLoader ignored, ElementLocator locator) { + ElementListInterceptor elementInterceptor = new ElementListInterceptor(locator); + return getEnhancedProxy(ArrayList.class, + elementInterceptor); + } + + @Override + protected boolean isDecoratableList(Field field) { + if (!List.class.isAssignableFrom(field.getType())) { + return false; + } + + Type genericType = field.getGenericType(); + if (!(genericType instanceof ParameterizedType)) { + return false; + } + + Type listType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; + + boolean result = false; + for (Class webElementClass: + availableElementClasses) { + if (!webElementClass.equals(listType)) { + continue; + } + result = true; + break; + } + return result; + } + }; + + widgetLocatorFactory = new AppiumElementLocatorFactory(context, timeOutDuration, originalDriver, + new WidgetByBuilder(platform, automation)); } - - public AppiumFieldDecorator(SearchContext context) { - this.context = context; - factory = new AppiumElementLocatorFactory(this.context); - } - - public Object decorate(ClassLoader ignored, Field field) { - if (!(availableElementClasses.contains(field.getType()) || isDecoratableList(field))) { - return null; - } - - ElementLocator locator = factory.createLocator(field); - if (locator == null) { - return null; - } - - if (WebElement.class.isAssignableFrom(field.getType())) { - return proxyForLocator(locator); - } else if (List.class.isAssignableFrom(field.getType())) { - return proxyForListLocator(locator); - } else { - return null; - } - } - - private static boolean isAvailableElementClass(Type type){ - boolean result = false; - for (Class webElementClass: - availableElementClasses){ - if (!webElementClass.equals(type)){ - continue; - } - result = true; - break; - } - return result; - } - - private boolean isDecoratableList(Field field) { - if (!List.class.isAssignableFrom(field.getType())) { - return false; - } - - // Type erasure in Java isn't complete. Attempt to discover the generic - // type of the list. - Type genericType = field.getGenericType(); - if (!(genericType instanceof ParameterizedType)) { - return false; - } - - Type listType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; - return isAvailableElementClass(listType); - //if there is no annotation list is supposed to be found by org.openqa.selenium.support.ByIdOrName - //DefaultElementLocator has an issue :) - } - - private Class getTypeForProxy(){ - Class contextClass = context.getClass(); + + public AppiumFieldDecorator(SearchContext context) { + this(context, DEFAULT_IMPLICITLY_WAIT_TIMEOUT, DEFAULT_TIMEUNIT); + } + + public Object decorate(ClassLoader ignored, Field 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 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) field.getType(); + } + + CacheableLocator locator = widgetLocatorFactory.createLocator(field); + Map> map = + OverrideWidgetReader.read(widgetType, field, platform, automation); + + if (isAlist) { + return getEnhancedProxy(ArrayList.class, + new WidgetListInterceptor(locator, originalDriver, map, widgetType, timeOutDuration)); + } + + Constructor constructor = WidgetConstructorUtil.findConvenientConstructor(widgetType); + return getEnhancedProxy(widgetType, new Class[] {constructor.getParameterTypes()[0]}, + new Object[] {proxyForAnElement(locator)}, new WidgetInterceptor(locator, originalDriver, null, map, + timeOutDuration)); + } + + private Class getTypeForProxy() { + Class driverClass = originalDriver.getClass(); Iterable, Class>> rules = elementRuleMap.entrySet(); - Iterator, Class>> 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> e : rules) { //that extends AppiumDriver or MobileElement - Map.Entry, Class> e = iterator.next(); - if (e.getKey().isAssignableFrom(contextClass)) + if (e.getKey().isAssignableFrom(driverClass)) { return e.getValue(); + } } //it is compatible with desktop browser. So at this case it returns RemoteWebElement.class return RemoteWebElement.class; } - private Object proxyForLocator(ElementLocator locator) { - ElementInterceptor elementInterceptor = new ElementInterceptor(locator); - return ProxyFactory.getEnhancedProxy(getTypeForProxy(), - elementInterceptor); - } - - @SuppressWarnings("unchecked") - private List proxyForListLocator( - ElementLocator locator) { - ElementListInterceptor elementInterceptor = new ElementListInterceptor(locator); - return ProxyFactory.getEnhancedProxy(ArrayList.class, - elementInterceptor); - } + private WebElement proxyForAnElement(ElementLocator locator) { + ElementInterceptor elementInterceptor = new ElementInterceptor(locator, originalDriver); + return (WebElement) getEnhancedProxy(getTypeForProxy(), elementInterceptor); + } } diff --git a/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java b/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java deleted file mode 100644 index 1ffab3df8..000000000 --- a/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 java.util.List; -import java.util.Map; - -import org.openqa.selenium.By; -import org.openqa.selenium.ContextAware; -import org.openqa.selenium.SearchContext; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; - -class ContentMappedBy extends By { - private final Map map; - private final static String NATIVE_APP_PATTERN = "NATIVE_APP"; - - public ContentMappedBy(Map map) { - this.map = map; - } - - private By returnRelevantBy(SearchContext context){ - WebDriver driver = WebDriverUnpackUtility.unpackWebDriverFromSearchContext(context); - if (!ContextAware.class.isAssignableFrom(driver.getClass())){ //it is desktop browser - return map.get(ContentType.HTML); - } - - ContextAware contextAware = ContextAware.class.cast(driver); - String currentContext = contextAware.getContext(); - if (currentContext.contains(NATIVE_APP_PATTERN)) - return map.get(ContentType.NATIVE); - return map.get(ContentType.HTML); - } - - @Override - public List findElements(SearchContext context) { - return context.findElements(returnRelevantBy(context)); - } - - @Override - public String toString(){ - By defaultBy = map.get(ContentType.HTML); - By nativeBy = map.get(ContentType.NATIVE); - - if (defaultBy.equals(nativeBy)) - return defaultBy.toString(); - - return "Locator map: " + "\n" + - "- native content: \"" + nativeBy.toString() + "\" \n" + - "- html content: \"" + defaultBy.toString() + "\""; - } - -} diff --git a/src/main/java/io/appium/java_client/pagefactory/DefaultElementByBuilder.java b/src/main/java/io/appium/java_client/pagefactory/DefaultElementByBuilder.java new file mode 100644 index 000000000..a98259028 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/DefaultElementByBuilder.java @@ -0,0 +1,205 @@ +/* + * 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 static io.appium.java_client.remote.MobilePlatform.*; +import static io.appium.java_client.remote.AutomationName.*; + +import java.lang.annotation.Annotation; +import java.lang.reflect.*; +import java.util.HashMap; +import java.util.Map; + +import io.appium.java_client.pagefactory.bys.ContentMappedBy; +import io.appium.java_client.pagefactory.bys.ContentType; +import io.appium.java_client.pagefactory.bys.builder.AppiumByBuilder; +import io.appium.java_client.pagefactory.bys.builder.HowToUseSelectors; +import org.openqa.selenium.By; +import org.openqa.selenium.support.*; + +class DefaultElementByBuilder extends AppiumByBuilder { + + private static void checkDisallowedAnnotationPairs(Annotation a1, + Annotation a2) throws IllegalArgumentException { + if (a1 != null && a2 != null) { + throw new IllegalArgumentException("If you use a '@" + + a1.getClass().getSimpleName() + "' annotation, " + + "you must not also use a '@" + + a2.getClass().getSimpleName() + "' annotation"); + } + } + + @Override + protected void assertValidAnnotations() { + AnnotatedElement annotatedElement = annotatedElementContainer.getAnnotated(); + AndroidFindBy androidBy = annotatedElement + .getAnnotation(AndroidFindBy.class); + AndroidFindBys androidBys = annotatedElement + .getAnnotation(AndroidFindBys.class); + AndroidFindAll androidFindAll = annotatedElement + .getAnnotation(AndroidFindAll.class); + + SelendroidFindBy selendroidBy = annotatedElement + .getAnnotation(SelendroidFindBy.class); + SelendroidFindBys selendroidBys = annotatedElement + .getAnnotation(SelendroidFindBys.class); + SelendroidFindAll selendroidFindAll = annotatedElement + .getAnnotation(SelendroidFindAll.class); + + iOSFindBy iOSBy = annotatedElement.getAnnotation(iOSFindBy.class); + iOSFindBys iOSBys = annotatedElement.getAnnotation(iOSFindBys.class); + iOSFindAll iOSFindAll = annotatedElement.getAnnotation(iOSFindAll.class); + + FindBys findBys = annotatedElement.getAnnotation(FindBys.class); + FindAll findAll = annotatedElement.getAnnotation(FindAll.class); + FindBy findBy = annotatedElement.getAnnotation(FindBy.class); + + checkDisallowedAnnotationPairs(androidBy, androidBys); + checkDisallowedAnnotationPairs(androidBy, androidFindAll); + checkDisallowedAnnotationPairs(androidBys, androidFindAll); + + checkDisallowedAnnotationPairs(selendroidBy, selendroidBys); + checkDisallowedAnnotationPairs(selendroidBy, selendroidFindAll); + checkDisallowedAnnotationPairs(selendroidBys, selendroidFindAll); + + checkDisallowedAnnotationPairs(iOSBy, iOSBys); + checkDisallowedAnnotationPairs(iOSBy, iOSFindAll); + checkDisallowedAnnotationPairs(iOSBys, iOSFindAll); + + checkDisallowedAnnotationPairs(findBy, findBys); + checkDisallowedAnnotationPairs(findBy, findAll); + checkDisallowedAnnotationPairs(findBys, findAll); + } + + protected DefaultElementByBuilder(String platform, String automation) { + super(platform, automation); + } + + @Override + protected By buildDefaultBy() { + AnnotatedElement annotatedElement = annotatedElementContainer.getAnnotated(); + By defaultBy = null; + FindBy findBy = annotatedElement.getAnnotation(FindBy.class); + if (findBy != null) { + defaultBy = super.buildByFromFindBy(findBy); + } + + if (defaultBy == null) { + FindBys findBys = annotatedElement.getAnnotation(FindBys.class); + if (findBys != null) { + defaultBy = super.buildByFromFindBys(findBys); + } + } + + if (defaultBy == null) { + FindAll findAll = annotatedElement.getAnnotation(FindAll.class); + if (findAll != null) { + defaultBy = super.buildBysFromFindByOneOf(findAll); + } + } + return defaultBy; + } + + @Override + protected By buildMobileNativeBy() { + AnnotatedElement annotatedElement = annotatedElementContainer.getAnnotated(); + if (ANDROID.toUpperCase().equals(platform) + && SELENDROID.toUpperCase().equals(automation)) { + SelendroidFindBy selendroidFindBy = annotatedElement.getAnnotation(SelendroidFindBy.class); + SelendroidFindBys selendroidFindBys = annotatedElement.getAnnotation(SelendroidFindBys.class); + SelendroidFindAll selendroidFindByAll = annotatedElement.getAnnotation(SelendroidFindAll.class); + + if (selendroidFindBy != null) { + return createBy(new Annotation[]{selendroidFindBy}, HowToUseSelectors.USE_ONE); + } + + if (selendroidFindBys != null) { + return createBy(selendroidFindBys.value(), HowToUseSelectors.BUILD_CHAINED); + } + + if (selendroidFindByAll != null) { + return createBy(selendroidFindByAll.value(), HowToUseSelectors.USE_ANY); + } + } + + if (ANDROID.toUpperCase().equals(platform)) { + AndroidFindBy androidFindBy = annotatedElement.getAnnotation(AndroidFindBy.class); + AndroidFindBys androidFindBys= annotatedElement.getAnnotation(AndroidFindBys.class); + AndroidFindAll androidFindAll = annotatedElement.getAnnotation(AndroidFindAll.class); + + if (androidFindBy != null) { + return createBy(new Annotation[]{androidFindBy}, HowToUseSelectors.USE_ONE); + } + + if (androidFindBys != null) { + return createBy(androidFindBys.value(), HowToUseSelectors.BUILD_CHAINED); + } + + if (androidFindAll != null) { + return createBy(androidFindAll.value(), HowToUseSelectors.USE_ANY); + } + } + + if (IOS.toUpperCase().equals(platform)) { + iOSFindBy iOSFindBy = annotatedElement.getAnnotation(iOSFindBy.class); + iOSFindBys iOSFindBys= annotatedElement.getAnnotation(iOSFindBys.class); + iOSFindAll iOSFindAll = annotatedElement.getAnnotation(iOSFindAll.class); + + if (iOSFindBy != null) { + return createBy(new Annotation[]{iOSFindBy}, HowToUseSelectors.USE_ONE); + } + + if (iOSFindBys != null) { + return createBy(iOSFindBys.value(), HowToUseSelectors.BUILD_CHAINED); + } + + if (iOSFindAll != null) { + return createBy(iOSFindAll.value(), HowToUseSelectors.USE_ANY); + } + } + + return null; + } + + @Override + public boolean isLookupCached() { + AnnotatedElement annotatedElement = annotatedElementContainer.getAnnotated(); + return (annotatedElement.getAnnotation(CacheLookup.class) != null); + } + + @Override + public By buildBy() { + assertValidAnnotations(); + + By defaultBy = buildDefaultBy(); + By mobileNativeBy = buildMobileNativeBy(); + + if (defaultBy == null) { + defaultBy = new ByIdOrName(((Field) annotatedElementContainer.getAnnotated()).getName()); + } + + + if (mobileNativeBy == null) { + mobileNativeBy = defaultBy; + } + + Map contentMap = new HashMap<>(); + contentMap.put(ContentType.HTML_OR_DEFAULT, defaultBy); + contentMap.put(ContentType.NATIVE_MOBILE_SPECIFIC, mobileNativeBy); + return new ContentMappedBy(contentMap); + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/ElementInterceptor.java b/src/main/java/io/appium/java_client/pagefactory/ElementInterceptor.java index 611c0c19c..f5a9c4f46 100644 --- a/src/main/java/io/appium/java_client/pagefactory/ElementInterceptor.java +++ b/src/main/java/io/appium/java_client/pagefactory/ElementInterceptor.java @@ -17,34 +17,29 @@ package io.appium.java_client.pagefactory; import io.appium.java_client.MobileElement; - import java.lang.reflect.Method; - +import io.appium.java_client.pagefactory.interceptors.InterceptorOfASingleElement; +import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.pagefactory.ElementLocator; -import net.sf.cglib.proxy.MethodInterceptor; -import net.sf.cglib.proxy.MethodProxy; - /** * Intercepts requests to {@link MobileElement} * */ -class ElementInterceptor implements MethodInterceptor { - private final ElementLocator locator; +class ElementInterceptor extends InterceptorOfASingleElement { - ElementInterceptor(ElementLocator locator) { - this.locator = locator; + ElementInterceptor(ElementLocator locator, WebDriver driver) { + super(locator, driver); } - - public Object intercept(Object obj, Method method, Object[] args, - MethodProxy proxy) throws Throwable { - if(Object.class.getDeclaredMethod("finalize").equals(method)){ - return proxy.invokeSuper(obj, args); //invokes .finalize of the proxy-object - } - WebElement realElement = locator.findElement(); - return method.invoke(realElement, args); + @Override + protected Object getObject(WebElement element, Method method, Object[] args) throws Throwable { + try { + return method.invoke(element, args); + } + catch (Throwable t){ + throw ThrowableUtil.extractReadableException(t); + } } - } diff --git a/src/main/java/io/appium/java_client/pagefactory/ElementListInterceptor.java b/src/main/java/io/appium/java_client/pagefactory/ElementListInterceptor.java index fc82f0b3e..35bb4dfd6 100644 --- a/src/main/java/io/appium/java_client/pagefactory/ElementListInterceptor.java +++ b/src/main/java/io/appium/java_client/pagefactory/ElementListInterceptor.java @@ -19,36 +19,30 @@ import io.appium.java_client.MobileElement; import java.lang.reflect.Method; -import java.util.ArrayList; - +import java.util.List; +import io.appium.java_client.pagefactory.interceptors.InterceptorOfAListOfElements; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.pagefactory.ElementLocator; -import net.sf.cglib.proxy.MethodInterceptor; -import net.sf.cglib.proxy.MethodProxy; - /** * * Intercepts requests to the list of {@link MobileElement} * */ -class ElementListInterceptor implements MethodInterceptor{ +class ElementListInterceptor extends InterceptorOfAListOfElements{ - private final ElementLocator locator; - ElementListInterceptor(ElementLocator locator){ - this.locator = locator; + super(locator); } - public Object intercept(Object obj, Method method, Object[] args, - MethodProxy proxy) throws Throwable { - if(Object.class.getDeclaredMethod("finalize").equals(method)){ - return proxy.invokeSuper(obj, args); //invokes .finalize of the proxy-object - } - - ArrayList realElements = new ArrayList(); - realElements.addAll(locator.findElements()); - return method.invoke(realElements, args); + @Override + protected Object getObject(List elements, Method method, Object[] args) throws Throwable { + try { + return method.invoke(elements, args); + } + catch (Throwable t){ + throw ThrowableUtil.extractReadableException(t); + } } } diff --git a/src/main/java/io/appium/java_client/pagefactory/OverrideWidget.java b/src/main/java/io/appium/java_client/pagefactory/OverrideWidget.java new file mode 100644 index 000000000..db48fbf60 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/OverrideWidget.java @@ -0,0 +1,65 @@ +/* + * 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 java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation should mark an abstract/certain Widget object class + * or field that declares some abstract/certain Widget object class. + * + * io.appium.java_client.pagefactory.Widget is the Appium-specific extension of the + * Page Object design pattern. + * + * About the Page Object design pattern please read these documents: + * - https://code.google.com/p/selenium/wiki/PageObjects + * - https://code.google.com/p/selenium/wiki/PageFactory +*/ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface OverrideWidget { + /** + * This property is designed for HTML (browser or web view) content. + * + * A declared class should not be abstract. Declared class also should be a subclass + * of an annotated class/class which is declared by an annotated field. + */ + Class html() default Widget.class; + /** + * This property is designed for Android native content. + * + * A declared class should not be abstract. Declared class also should be a subclass + * of an annotated class/class which is declared by an annotated field. + */ + Class androidUIAutomator() default Widget.class; + /** + * This property is designed for iOS native content. + * + * A declared class should not be abstract. Declared class also should be a subclass + * of an annotated class/class which is declared by an annotated field. + */ + Class iOSUIAutomation() default Widget.class; + /** + * This property is designed for Android native content when Selendroid automation is used. + * + * A declared class should not be abstract. Declared class also should be a subclass + * of an annotated class/class which is declared by an annotated field. + */ + Class selendroid() default Widget.class; +} diff --git a/src/main/java/io/appium/java_client/pagefactory/OverrideWidgetReader.java b/src/main/java/io/appium/java_client/pagefactory/OverrideWidgetReader.java new file mode 100644 index 000000000..c03d66e27 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/OverrideWidgetReader.java @@ -0,0 +1,118 @@ +/* + * 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.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +import io.appium.java_client.remote.AutomationName; + +import static io.appium.java_client.pagefactory.WidgetConstructorUtil.findConvenientConstructor; +import static io.appium.java_client.remote.MobilePlatform.ANDROID; +import static io.appium.java_client.remote.MobilePlatform.IOS; + +class OverrideWidgetReader { + private static final Class EMPTY = Widget.class; + private static final String HTML = "html"; + private static final String ANDROID_UI_AUTOMATOR = "androidUIAutomator"; + private static final String IOS_UI_AUTOMATION = "iOSUIAutomation"; + private static final String SELENDROID = "selendroid"; + + @SuppressWarnings("unchecked") + private static Class getConvenientClass(Class declaredClass, + AnnotatedElement annotatedElement, String method) { + Class convenientClass; + OverrideWidget overrideWidget = annotatedElement.getAnnotation(OverrideWidget.class); + + try { + if (overrideWidget == null || (convenientClass = (Class) OverrideWidget. + class.getDeclaredMethod(method, new Class[]{}).invoke(overrideWidget)).equals(EMPTY)) + convenientClass = declaredClass; + } catch (NoSuchMethodException|InvocationTargetException|IllegalAccessException e) { + throw new RuntimeException(e); + } + + if (!declaredClass.isAssignableFrom(convenientClass)) { + throw new IllegalArgumentException(new InstantiationException(declaredClass.getName() + " is not assignable from " + + convenientClass.getName())); + } + + int modifiers = convenientClass.getModifiers(); + if (Modifier.isAbstract(modifiers)) { + throw new IllegalArgumentException(new InstantiationException(convenientClass.getName() + " is abstract so " + + "it can't be instantiated")); + } + + return convenientClass; + + } + + static Class getDefaultOrHTMLWidgetClass(Class declaredClass, + AnnotatedElement annotatedElement) { + return getConvenientClass(declaredClass, annotatedElement, HTML); + } + + static Class getMobileNativeWidgetClass(Class declaredClass, + AnnotatedElement annotatedElement, String platform, + String automation) { + String transformedPlatform = platform.toUpperCase().trim(); + String transformedAutomation = automation.toUpperCase().trim(); + + if (ANDROID.toUpperCase().equals(transformedPlatform) + && AutomationName.SELENDROID.toUpperCase().equals(transformedAutomation)) { + return getConvenientClass(declaredClass, annotatedElement, SELENDROID); + } + + if (ANDROID.toUpperCase().equals(transformedPlatform)) { + return getConvenientClass(declaredClass, annotatedElement, ANDROID_UI_AUTOMATOR); + } + + if (IOS.toUpperCase().equals(transformedPlatform)) { + return getConvenientClass(declaredClass, annotatedElement, IOS_UI_AUTOMATION); + } + + return getDefaultOrHTMLWidgetClass(declaredClass, annotatedElement); + } + + private static Constructor getConstructorOfADefaultOrHTMLWidget(Class declaredClass, + AnnotatedElement annotatedElement) { + Class clazz = getDefaultOrHTMLWidgetClass(declaredClass, annotatedElement); + return findConvenientConstructor(clazz); + } + + private static Constructor getConstructorOfAMobileNativeWidgets(Class declaredClass, + AnnotatedElement annotatedElement, + String platform, String automation) { + Class clazz = getMobileNativeWidgetClass(declaredClass, annotatedElement, platform, automation); + return findConvenientConstructor(clazz); + } + + static Map> read(Class declaredClass, + AnnotatedElement annotatedElement, + String platform, String automation) { + Map> result = new HashMap<>(); + result.put(ContentType.HTML_OR_DEFAULT, getConstructorOfADefaultOrHTMLWidget(declaredClass, annotatedElement)); + result.put(ContentType.NATIVE_MOBILE_SPECIFIC, + getConstructorOfAMobileNativeWidgets(declaredClass, annotatedElement, platform, automation)); + return result; + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/ThrowableUtil.java b/src/main/java/io/appium/java_client/pagefactory/ThrowableUtil.java new file mode 100644 index 000000000..b7c71fe96 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/ThrowableUtil.java @@ -0,0 +1,57 @@ +/* + * 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.StaleElementReferenceException; + +import java.lang.reflect.InvocationTargetException; + +class ThrowableUtil { + private final static String INVALID_SELECTOR_PATTERN = "Invalid locator strategy:"; + + static boolean isInvalidSelectorRootCause(Throwable e) { + if (e == null) { + return false; + } + + if (String.valueOf(e.getMessage()).contains(INVALID_SELECTOR_PATTERN)) { + return true; + } + + return isInvalidSelectorRootCause(e.getCause()); + } + + static boolean isStaleElementReferenceException(Throwable e) { + if (e == null) { + return false; + } + + if (StaleElementReferenceException.class.isAssignableFrom(e.getClass())) { + return true; + } + + return isStaleElementReferenceException(e.getCause()); + } + + static Throwable extractReadableException(Throwable e) { + if (!RuntimeException.class.equals(e.getClass()) && !InvocationTargetException.class.equals(e.getClass())) { + return e; + } + + return extractReadableException(e.getCause()); + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/WebDriverUnpackUtility.java b/src/main/java/io/appium/java_client/pagefactory/WebDriverUnpackUtility.java deleted file mode 100644 index 95c3ba016..000000000 --- a/src/main/java/io/appium/java_client/pagefactory/WebDriverUnpackUtility.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.SearchContext; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.internal.WrapsDriver; -import org.openqa.selenium.internal.WrapsElement; - -final class WebDriverUnpackUtility { - - static WebDriver unpackWebDriverFromSearchContext( - SearchContext searchContext) { - WebDriver driver = null; - if (searchContext instanceof WebDriver) - return (WebDriver) searchContext; - - // Search context it is not only Webdriver. Webelement is search context - // too. - // RemoteWebElement and MobileElement implement WrapsDriver - if (searchContext instanceof WrapsElement) - return unpackWebDriverFromSearchContext(((WrapsElement) searchContext) - .getWrappedElement()); - - if (searchContext instanceof WrapsDriver) - return unpackWebDriverFromSearchContext(((WrapsDriver) searchContext) - .getWrappedDriver()); - - return driver; - } -} diff --git a/src/main/java/io/appium/java_client/pagefactory/Widget.java b/src/main/java/io/appium/java_client/pagefactory/Widget.java new file mode 100644 index 000000000..2e6b7f681 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/Widget.java @@ -0,0 +1,64 @@ +/* + * 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 org.openqa.selenium.SearchContext; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.internal.WrapsDriver; +import org.openqa.selenium.internal.WrapsElement; +import java.util.List; + +import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.unpackWebDriverFromSearchContext; + +/** + * It is the Appium-specific extension of the Page Object design pattern. It allows user + * to create objects which typify some element with nested sub-elements. Also it allows to describe and encapsulate + * logic of interaction/behavior within. + * + * About the Page Object design pattern please read these documents: + * - https://code.google.com/p/selenium/wiki/PageObjects + * - https://code.google.com/p/selenium/wiki/PageFactory + */ +public abstract class Widget implements SearchContext, WrapsDriver, WrapsElement { + + private final SearchContext element; + + protected Widget(WebElement element) { + this.element = element; + } + + @Override + public List findElements(By by) { + return element.findElements(by); + } + + @Override + public WebElement findElement(By by) { + return element.findElement(by); + } + + @Override + public WebDriver getWrappedDriver() { + return unpackWebDriverFromSearchContext(element); + } + + @Override + public WebElement getWrappedElement() { + return (WebElement) element; + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/WidgetByBuilder.java b/src/main/java/io/appium/java_client/pagefactory/WidgetByBuilder.java new file mode 100644 index 000000000..ace46cd91 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/WidgetByBuilder.java @@ -0,0 +1,121 @@ +/* + * 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; + +import static io.appium.java_client.pagefactory.OverrideWidgetReader.getDefaultOrHTMLWidgetClass; +import static io.appium.java_client.pagefactory.OverrideWidgetReader.getMobileNativeWidgetClass; + +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; + } + + @SuppressWarnings("unchecked") + private By getByFromDeclaredClass(WhatIsNeeded whatIsNeeded) { + AnnotatedElement annotatedElement = annotatedElementContainer.getAnnotated(); + Field field = Field.class.cast(annotatedElement); + Class declaredClass; + + By result = null; + try { + if (List.class.isAssignableFrom(field.getType())) { + declaredClass = getClassFromAListField(field); + } + else { + declaredClass = field.getType(); + } + + Class convenientClass; + + if (whatIsNeeded.equals(WhatIsNeeded.DEFAULT_OR_HTML)) { + convenientClass = getDefaultOrHTMLWidgetClass((Class) declaredClass, field); + } + else { + convenientClass = getMobileNativeWidgetClass((Class) declaredClass, field, platform, automation); + } + + while (result == null && !convenientClass.equals(Object.class)) { + setAnnotated(convenientClass); + if (whatIsNeeded.equals(WhatIsNeeded.DEFAULT_OR_HTML)) { + result = super.buildDefaultBy(); + } + else { + result = super.buildMobileNativeBy(); + } + + convenientClass = convenientClass.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); + } + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/WidgetConstructorUtil.java b/src/main/java/io/appium/java_client/pagefactory/WidgetConstructorUtil.java new file mode 100644 index 000000000..e5b90a80e --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/WidgetConstructorUtil.java @@ -0,0 +1,49 @@ +/* + * 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.WebElement; + +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.List; + +class WidgetConstructorUtil { + private WidgetConstructorUtil() { + super(); + } + + @SuppressWarnings("unchecked") + static Constructor findConvenientConstructor(Class clazz) { + Constructor[] constructors = clazz.getDeclaredConstructors(); + for (Constructor constructor: constructors) { + Class[] params = constructor.getParameterTypes(); + if (constructor.getParameterTypes().length !=1) { + continue; + } + + Class param = params[0]; + if (WebElement.class.isAssignableFrom(param)) { + constructor.setAccessible(true); + return (Constructor) constructor; + } + } + List> declared = Arrays.asList(clazz.getDeclaredConstructors()); + throw new NoSuchMethodError(clazz.getName() + " has no convenient constructor which could pass a " + + WebElement.class.getName() + " instance as a parameter. The actual list of constructors: " + + declared.toString()); + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/WidgetInterceptor.java b/src/main/java/io/appium/java_client/pagefactory/WidgetInterceptor.java new file mode 100644 index 000000000..d1846c722 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/WidgetInterceptor.java @@ -0,0 +1,75 @@ +/* + * 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 io.appium.java_client.pagefactory.interceptors.InterceptorOfASingleElement; +import io.appium.java_client.pagefactory.locator.CacheableLocator; +import net.sf.cglib.proxy.MethodProxy; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.PageFactory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.getCurrentContentType; + +class WidgetInterceptor extends InterceptorOfASingleElement{ + + private WebElement cachedElement; + private final Map> instantiationMap; + private final Map cachedInstances = new HashMap<>(); + private final TimeOutDuration duration; + + WidgetInterceptor(CacheableLocator locator, WebDriver driver, WebElement cachedElement, + Map> instantiationMap, + TimeOutDuration duration) { + super(locator, driver); + this.cachedElement = cachedElement; + this.instantiationMap = instantiationMap; + this.duration = duration; + } + + + @Override + protected Object getObject(WebElement element, Method method, Object[] args) throws Throwable { + ContentType type = getCurrentContentType(element); + if (cachedElement == null || (locator !=null && !((CacheableLocator) locator).isLookUpCached()) || + cachedInstances.size() == 0) { + cachedElement = element; + Widget widget = instantiationMap.get(type).newInstance(cachedElement); + cachedInstances.put(type, widget); + PageFactory.initElements(new AppiumFieldDecorator(widget, duration), widget); + } + try { + return method.invoke(cachedInstances.get(type), args); + } + catch (Throwable t) { + throw ThrowableUtil.extractReadableException(t); + } + } + + public Object intercept(Object obj, Method method, Object[] args, + MethodProxy proxy) throws Throwable { + if (locator != null) { + return super.intercept(obj, method, args, proxy); + } + return getObject(cachedElement, method, args); + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/WidgetListInterceptor.java b/src/main/java/io/appium/java_client/pagefactory/WidgetListInterceptor.java new file mode 100644 index 000000000..833956a45 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/WidgetListInterceptor.java @@ -0,0 +1,71 @@ +/* + * 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 io.appium.java_client.pagefactory.interceptors.InterceptorOfAListOfElements; +import io.appium.java_client.pagefactory.locator.CacheableLocator; +import io.appium.java_client.pagefactory.utils.ProxyFactory; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.getCurrentContentType; + +class WidgetListInterceptor extends InterceptorOfAListOfElements{ + + private final Map> instantiationMap; + private List cachedElements; + private final List cachedWidgets = new ArrayList<>(); + private final Class declaredType; + private final TimeOutDuration duration; + private final WebDriver driver; + + WidgetListInterceptor(CacheableLocator locator, WebDriver driver, Map> instantiationMap, + Class declaredType, TimeOutDuration duration) { + super(locator); + this.instantiationMap = instantiationMap; + this.declaredType = declaredType; + this.duration = duration; + this.driver = driver; + } + + + @Override + protected Object getObject(List elements, Method method, Object[] args) throws Throwable { + if (cachedElements == null || (locator !=null && !((CacheableLocator) locator).isLookUpCached())) { + cachedElements = elements; + cachedWidgets.clear(); + + for (WebElement element: cachedElements) { + ContentType type = getCurrentContentType(element); + Class[] params = new Class[] {instantiationMap.get(type).getParameterTypes()[0]}; + cachedWidgets.add(ProxyFactory.getEnhancedProxy(declaredType, params, new Object[]{element}, + new WidgetInterceptor(null, driver, element, instantiationMap, duration))); + } + } + try { + return method.invoke(cachedWidgets, args); + } + catch (Throwable t) { + throw ThrowableUtil.extractReadableException(t); + } + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/WithTimeout.java b/src/main/java/io/appium/java_client/pagefactory/WithTimeout.java index 830dac439..6f1995564 100644 --- a/src/main/java/io/appium/java_client/pagefactory/WithTimeout.java +++ b/src/main/java/io/appium/java_client/pagefactory/WithTimeout.java @@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit; @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.TYPE}) +@Target({ElementType.FIELD}) /** This annotation is used when some element waits for time that differs from defined by default diff --git a/src/main/java/io/appium/java_client/pagefactory/bys/ContentMappedBy.java b/src/main/java/io/appium/java_client/pagefactory/bys/ContentMappedBy.java new file mode 100644 index 000000000..9eaa2bae1 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/bys/ContentMappedBy.java @@ -0,0 +1,52 @@ +/* +* 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.bys; + +import java.util.List; + import java.util.Map; + + import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.*; + import org.openqa.selenium.By; + import org.openqa.selenium.SearchContext; + import org.openqa.selenium.WebElement; + +public class ContentMappedBy extends By { + private final Map map; + private final static String NATIVE_APP_PATTERN = "NATIVE_APP"; + + public ContentMappedBy(Map map) { + this.map = map; + } + + @Override + public List findElements(SearchContext context) { + return context.findElements(map.get(getCurrentContentType(context))); + } + + @Override + public String toString(){ + By defaultBy = map.get(ContentType.HTML_OR_DEFAULT); + By nativeBy = map.get(ContentType.NATIVE_MOBILE_SPECIFIC); + + if (defaultBy.equals(nativeBy)) + return defaultBy.toString(); + + return "Locator map: " + "\n" + + "- native content: \"" + nativeBy.toString() + "\" \n" + + "- html content: \"" + defaultBy.toString() + "\""; + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/ContentType.java b/src/main/java/io/appium/java_client/pagefactory/bys/ContentType.java similarity index 85% rename from src/main/java/io/appium/java_client/pagefactory/ContentType.java rename to src/main/java/io/appium/java_client/pagefactory/bys/ContentType.java index 6f3ddc590..68a5371cb 100644 --- a/src/main/java/io/appium/java_client/pagefactory/ContentType.java +++ b/src/main/java/io/appium/java_client/pagefactory/bys/ContentType.java @@ -1,22 +1,22 @@ -/* - * 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; - -enum ContentType { - HTML, - NATIVE; -} +/* + * 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.bys; + +public enum ContentType { + HTML_OR_DEFAULT, + NATIVE_MOBILE_SPECIFIC; +} diff --git a/src/main/java/io/appium/java_client/pagefactory/ProxyFactory.java b/src/main/java/io/appium/java_client/pagefactory/bys/builder/AnnotatedElementContainer.java similarity index 54% rename from src/main/java/io/appium/java_client/pagefactory/ProxyFactory.java rename to src/main/java/io/appium/java_client/pagefactory/bys/builder/AnnotatedElementContainer.java index b22f1e546..a89736a11 100644 --- a/src/main/java/io/appium/java_client/pagefactory/ProxyFactory.java +++ b/src/main/java/io/appium/java_client/pagefactory/bys/builder/AnnotatedElementContainer.java @@ -13,29 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package io.appium.java_client.pagefactory.bys.builder; -package io.appium.java_client.pagefactory; - - -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.MethodInterceptor; +import java.lang.reflect.AnnotatedElement; /** - * Original class is a super class of a - * proxy object here + * This is the POJO for the setting/getting of an AnnotatedElement instances */ -abstract class ProxyFactory { +public class AnnotatedElementContainer { + private AnnotatedElement annotated; + - private ProxyFactory() { - super(); - } - - @SuppressWarnings("unchecked") - static T getEnhancedProxy(Class requiredClazz, MethodInterceptor interceptor){ - Enhancer enhancer = new Enhancer(); - enhancer.setSuperclass(requiredClazz); - enhancer.setCallback(interceptor); - return (T) enhancer.create(new Class[] {}, new Object[] {}); - } + public AnnotatedElement getAnnotated() { + return annotated; + } + void setAnnotated(AnnotatedElement annotated) { + this.annotated = annotated; + } } diff --git a/src/main/java/io/appium/java_client/pagefactory/bys/builder/AppiumByBuilder.java b/src/main/java/io/appium/java_client/pagefactory/bys/builder/AppiumByBuilder.java new file mode 100644 index 000000000..6e7b3ffa2 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/bys/builder/AppiumByBuilder.java @@ -0,0 +1,180 @@ +/* + * 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.bys.builder; +import org.openqa.selenium.By; +import org.openqa.selenium.support.pagefactory.AbstractAnnotations; +import org.openqa.selenium.support.pagefactory.ByAll; +import org.openqa.selenium.support.pagefactory.ByChained; + +import java.lang.annotation.Annotation; +import java.lang.reflect.*; +import java.util.ArrayList; +import java.util.List; + +/** + * It is the basic handler of Appium-specific page object annotations + * About the Page Object design pattern please read these documents: + * - https://code.google.com/p/selenium/wiki/PageObjects + * - https://code.google.com/p/selenium/wiki/PageFactory + */ +public abstract class AppiumByBuilder extends AbstractAnnotations { + final static Class[] DEFAULT_ANNOTATION_METHOD_ARGUMENTS = new Class[] {}; + + private final static List METHODS_TO_BE_EXCLUDED_WHEN_ANNOTATION_IS_READ = new ArrayList() { + private static final long serialVersionUID = 1L; + { + List objectClassMethodNames = getMethodNames(Object.class + .getDeclaredMethods()); + addAll(objectClassMethodNames); + List annotationClassMethodNames = getMethodNames(Annotation.class + .getDeclaredMethods()); + annotationClassMethodNames.removeAll(objectClassMethodNames); + addAll(annotationClassMethodNames); + } + }; + + private static List getMethodNames(Method[] methods) { + List names = new ArrayList<>(); + for (Method m : methods) { + names.add(m.getName()); + } + return names; + } + + private static Method[] prepareAnnotationMethods( + Class annotation) { + List targeAnnotationMethodNamesList = getMethodNames(annotation + .getDeclaredMethods()); + targeAnnotationMethodNamesList + .removeAll(METHODS_TO_BE_EXCLUDED_WHEN_ANNOTATION_IS_READ); + Method[] result = new Method[targeAnnotationMethodNamesList.size()]; + for (String methodName : targeAnnotationMethodNamesList) { + try { + result[targeAnnotationMethodNamesList.indexOf(methodName)] = annotation + .getMethod(methodName, + DEFAULT_ANNOTATION_METHOD_ARGUMENTS); + } catch (NoSuchMethodException | SecurityException e) { + throw new RuntimeException(e); + } + } + return result; + } + + private static String getFilledValue(Annotation mobileBy) { + Method[] values = prepareAnnotationMethods(mobileBy.getClass()); + for (Method value : values) { + try { + String strategyParameter = value.invoke(mobileBy, + new Object[] {}).toString(); + if (!"".equals(strategyParameter)) { + 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"); + } + + 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); + } + + @SuppressWarnings("unchecked") + private static T getComplexMobileBy(Annotation[] annotations, + Class requiredByClass) { + By[] byArray = new By[annotations.length]; + for (int i = 0; i < annotations.length; i++) { + byArray[i] = getMobileBy(annotations[i], + getFilledValue(annotations[i])); + } + try { + Constructor c = requiredByClass.getConstructor(By[].class); + Object[] values = new Object[] { byArray }; + return (T) c.newInstance(values); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected static By createBy(Annotation[] annotations, HowToUseSelectors howToUseLocators) { + if (annotations == null || annotations.length == 0) { + return null; + } + + switch (howToUseLocators ) { + case USE_ONE: { + return getMobileBy(annotations[0], getFilledValue(annotations[0])); + } + case BUILD_CHAINED: { + return getComplexMobileBy(annotations, ByChained.class); + } + case USE_ANY: { + return getComplexMobileBy(annotations, ByAll.class); + } + default: { + return null; + } + } + } + + protected final AnnotatedElementContainer annotatedElementContainer; + protected final String platform; + protected final String automation; + + protected AppiumByBuilder(String platform, String automation) { + this.annotatedElementContainer = new AnnotatedElementContainer(); + this.platform = String.valueOf(platform).toUpperCase().trim(); + this.automation = String.valueOf(automation).toUpperCase().trim(); + } + + /** + * This method should be used for the setting up of + * AnnotatedElement instances before the building of + * By-locator strategies + */ + public void setAnnotated(AnnotatedElement annotated) { + this.annotatedElementContainer.setAnnotated(annotated); + } + + /** + * Defines how to transform given object (field, class, etc) + * into {@link org.openqa.selenium.By} class used by webdriver to locate elements. + */ + public abstract By buildBy(); + + /** + * Defines whether or not given element + * should be returned from cache on further calls. + */ + public abstract boolean isLookupCached(); + + protected abstract By buildDefaultBy(); + + protected abstract By buildMobileNativeBy(); + + protected abstract void assertValidAnnotations(); +} diff --git a/src/main/java/io/appium/java_client/pagefactory/bys/builder/HowToUseSelectors.java b/src/main/java/io/appium/java_client/pagefactory/bys/builder/HowToUseSelectors.java new file mode 100644 index 000000000..35b6c4142 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/bys/builder/HowToUseSelectors.java @@ -0,0 +1,22 @@ +/* + * 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.bys.builder; + +public enum HowToUseSelectors { + USE_ONE, + BUILD_CHAINED, + USE_ANY; +} diff --git a/src/main/java/io/appium/java_client/pagefactory/bys/builder/Strategies.java b/src/main/java/io/appium/java_client/pagefactory/bys/builder/Strategies.java new file mode 100644 index 000000000..ca3a80dde --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/bys/builder/Strategies.java @@ -0,0 +1,127 @@ +package io.appium.java_client.pagefactory.bys.builder; + +import io.appium.java_client.MobileBy; +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory.iOSFindBy; +import org.openqa.selenium.By; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/* + * 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. + */ +enum Strategies { + BYUIAUTOMATOR("uiAutomator") { + @Override + By getBy(Annotation annotation) { + String value = getValue(annotation, this); + if (annotation.annotationType().equals(AndroidFindBy.class)) { + return MobileBy.AndroidUIAutomator(value); + } + if (annotation.annotationType().equals(iOSFindBy.class)) { + return MobileBy.IosUIAutomation(value); + } + return super.getBy(annotation); + } + }, + BYACCESSABILITY("accessibility") { + @Override + By getBy(Annotation annotation) { + return MobileBy.AccessibilityId(getValue(annotation, this)); + } + }, + BYCLASSNAME("className") { + @Override + By getBy(Annotation annotation) { + return By.className(getValue(annotation, this)); + } + }, + BYID("id") { + @Override + By getBy(Annotation annotation) { + return By.id(getValue(annotation, this)); + } + }, + BYTAG("tagName") { + @Override + By getBy(Annotation annotation) { + return By.tagName(getValue(annotation, this)); + } + }, + BYNAME("name") { + @Override + By getBy(Annotation annotation) { + return By.name(getValue(annotation, this)); + } + }, + BYXPATH("xpath") { + @Override + By getBy(Annotation annotation) { + return By.xpath(getValue(annotation, this)); + } + }, + BYLINKTEXT("linkText") { + @Override + By getBy(Annotation annotation) { + return By.linkText(getValue(annotation, this)); + } + }, + BYPARTIALLINKTEXT("partialLinkText") { + @Override + By getBy(Annotation annotation) { + return By.partialLinkText(getValue(annotation, this)); + } + }; + + private final String valueName; + + String returnValueName() { + return valueName; + } + + Strategies(String valueName) { + this.valueName = valueName; + } + + static List strategiesNames() { + Strategies[] strategies = values(); + List result = new ArrayList<>(); + int i = 0; + for (Strategies strategy : strategies) { + result.add(strategy.valueName); + i++; + } + return result; + } + + private static String getValue(Annotation annotation, + Strategies strategy) { + try { + Method m = annotation.getClass().getMethod(strategy.valueName, + AppiumByBuilder.DEFAULT_ANNOTATION_METHOD_ARGUMENTS); + return m.invoke(annotation, new Object[] {}).toString(); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + By getBy(Annotation annotation) { + return null; + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfAListOfElements.java b/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfAListOfElements.java new file mode 100644 index 000000000..1fc25a09e --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfAListOfElements.java @@ -0,0 +1,47 @@ +/* + * 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.interceptors; + +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +public abstract class InterceptorOfAListOfElements implements MethodInterceptor { + protected final ElementLocator locator; + + public InterceptorOfAListOfElements(ElementLocator locator) { + this.locator = locator; + } + + protected abstract Object getObject(List elements, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException, Throwable; + + public Object intercept(Object obj, Method method, Object[] args, + MethodProxy proxy) throws Throwable { + if(Object.class.equals(method.getDeclaringClass())) { + return proxy.invokeSuper(obj, args); + } + + ArrayList realElements = new ArrayList(); + realElements.addAll(locator.findElements()); + return getObject(realElements, method, args); + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfASingleElement.java b/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfASingleElement.java new file mode 100644 index 000000000..1791a0b57 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/interceptors/InterceptorOfASingleElement.java @@ -0,0 +1,54 @@ +/* + * 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.interceptors; + +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.internal.WrapsDriver; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +public abstract class InterceptorOfASingleElement implements MethodInterceptor { + protected final ElementLocator locator; + protected final WebDriver driver; + + public InterceptorOfASingleElement(ElementLocator locator, WebDriver driver) { + this.locator = locator; + this.driver = driver; + } + + protected abstract Object getObject(WebElement element, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException, Throwable; + + public Object intercept(Object obj, Method method, Object[] args, + MethodProxy proxy) throws Throwable { + if(Object.class.equals(method.getDeclaringClass())) { + return proxy.invokeSuper(obj, args); + } + + if (WrapsDriver.class.isAssignableFrom(method.getDeclaringClass()) && + method.getName().equals("getWrappedDriver")) { + return driver; + } + + WebElement realElement = locator.findElement(); + return getObject(realElement, method, args); + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/locator/CacheableElementLocatorFactory.java b/src/main/java/io/appium/java_client/pagefactory/locator/CacheableElementLocatorFactory.java new file mode 100644 index 000000000..9202a65e5 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/locator/CacheableElementLocatorFactory.java @@ -0,0 +1,28 @@ +/* + * 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.locator; + +import org.openqa.selenium.support.pagefactory.ElementLocatorFactory; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; + +public interface CacheableElementLocatorFactory extends ElementLocatorFactory { + + public CacheableLocator createLocator(Field field); + + public CacheableLocator createLocator(AnnotatedElement annotatedElement); +} diff --git a/src/main/java/io/appium/java_client/pagefactory/locator/CacheableLocator.java b/src/main/java/io/appium/java_client/pagefactory/locator/CacheableLocator.java new file mode 100644 index 000000000..614355ff2 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/locator/CacheableLocator.java @@ -0,0 +1,22 @@ +/* + * 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.locator; + +import org.openqa.selenium.support.pagefactory.ElementLocator; + +public interface CacheableLocator extends ElementLocator { + public boolean isLookUpCached(); +} diff --git a/src/main/java/io/appium/java_client/pagefactory/utils/ProxyFactory.java b/src/main/java/io/appium/java_client/pagefactory/utils/ProxyFactory.java new file mode 100644 index 000000000..450d01432 --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/utils/ProxyFactory.java @@ -0,0 +1,45 @@ +/* + * 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.utils; + + +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.MethodInterceptor; + +/** + * Original class is a super class of a + * proxy object here + */ +public final class ProxyFactory { + + private ProxyFactory() { + super(); + } + + public static T getEnhancedProxy(Class requiredClazz, MethodInterceptor interceptor){ + return getEnhancedProxy(requiredClazz, new Class[] {}, new Object[] {}, interceptor); + } + + @SuppressWarnings("unchecked") + public static T getEnhancedProxy(Class requiredClazz, Class[] params, Object[] values, + MethodInterceptor interceptor){ + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(requiredClazz); + enhancer.setCallback(interceptor); + return (T) enhancer.create(params, values); + } +} diff --git a/src/main/java/io/appium/java_client/pagefactory/utils/WebDriverUnpackUtility.java b/src/main/java/io/appium/java_client/pagefactory/utils/WebDriverUnpackUtility.java new file mode 100644 index 000000000..c43aa880e --- /dev/null +++ b/src/main/java/io/appium/java_client/pagefactory/utils/WebDriverUnpackUtility.java @@ -0,0 +1,99 @@ +/* + * 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.utils; + +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.ios.IOSDriver; +import io.appium.java_client.pagefactory.bys.ContentType; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.remote.MobilePlatform; +import org.openqa.selenium.ContextAware; +import org.openqa.selenium.HasCapabilities; +import org.openqa.selenium.SearchContext; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.internal.WrapsDriver; +import org.openqa.selenium.internal.WrapsElement; + +public final class WebDriverUnpackUtility { + private final static String NATIVE_APP_PATTERN = "NATIVE_APP"; + + public static WebDriver unpackWebDriverFromSearchContext( + SearchContext searchContext) { + WebDriver driver = null; + if (searchContext instanceof WebDriver) + return (WebDriver) searchContext; + + if (searchContext instanceof WrapsDriver) + return unpackWebDriverFromSearchContext(((WrapsDriver) searchContext) + .getWrappedDriver()); + + // Search context it is not only Webdriver. Webelement is search context + // too. + // RemoteWebElement and MobileElement implement WrapsDriver + if (searchContext instanceof WrapsElement) + return unpackWebDriverFromSearchContext(((WrapsElement) searchContext) + .getWrappedElement()); + + return driver; + } + + public static String getPlatform(WebDriver driver){ + if (driver == null) + return null; + + Class driverClass = driver.getClass(); + if (AndroidDriver.class.isAssignableFrom(driverClass)) + return MobilePlatform.ANDROID; + + if (IOSDriver.class.isAssignableFrom(driverClass)) + return MobilePlatform.IOS; + + //it is possible that somebody uses RemoteWebDriver or their + //own WebDriver implementation. At this case capabilities are used + //to detect platform + if (HasCapabilities.class.isAssignableFrom(driverClass)) + return String.valueOf(((HasCapabilities) driver).getCapabilities(). + getCapability(MobileCapabilityType.PLATFORM_NAME)); + + return null; + } + + public static String getAutomation(WebDriver driver){ + if (driver == null) + return null; + + if (HasCapabilities.class.isAssignableFrom(driver.getClass())) + return String.valueOf(((HasCapabilities) driver).getCapabilities(). + getCapability(MobileCapabilityType.AUTOMATION_NAME)); + + return null; + } + + public static ContentType getCurrentContentType(SearchContext context){ + WebDriver driver = WebDriverUnpackUtility.unpackWebDriverFromSearchContext(context); + if (!ContextAware.class.isAssignableFrom(driver.getClass())){ //it is desktop browser + return ContentType.HTML_OR_DEFAULT; + } + + ContextAware contextAware = ContextAware.class.cast(driver); + String currentContext = contextAware.getContext(); + if (currentContext.contains(NATIVE_APP_PATTERN)) + return ContentType.NATIVE_MOBILE_SPECIFIC; + + return ContentType.HTML_OR_DEFAULT; + } +} diff --git a/src/test/java/io/appium/java_client/RottenTomatoes.zip b/src/test/java/io/appium/java_client/RottenTomatoes.zip new file mode 100644 index 000000000..e215950e1 Binary files /dev/null and b/src/test/java/io/appium/java_client/RottenTomatoes.zip differ diff --git a/src/test/java/io/appium/java_client/RottenTomatoesPageSnapshot.html b/src/test/java/io/appium/java_client/RottenTomatoesPageSnapshot.html new file mode 100644 index 000000000..235d1a382 --- /dev/null +++ b/src/test/java/io/appium/java_client/RottenTomatoesPageSnapshot.html @@ -0,0 +1,2084 @@ + + + Spectre (2015) - Rotten Tomatoes

Spectre (2015)

TOMATOMETER

AUDIENCE SCORE

Critic Consensus: Spectre nudges Daniel Craig's rebooted Bond closer to the glorious, action-driven spectacle of earlier entries, although it's admittedly reliant on established 007 formula.

Tickets & Showtimes

There are no showtimes for this theater.

Enter your location to see where Spectre is playing near you

Movie Info

A cryptic message from Bond's past sends him on a trail to uncover a sinister organisation. While M battles political forces to keep the secret service alive, Bond peels back the layers of deceit to reveal the terrible truth behind Spectre.
Rating: PG-13 (for intense sequences of action and violence, some disturbing images, sensuality and language)
Genre: Action & Adventure
Directed By:
Written By: Ian Fleming, John Logan, Neal Purvis, Robert Wade
Runtime:
Sony Pictures - Official Site

Cast

Daniel Craig
as James Bond
Christoph Waltz
as Franz Oberhauser
Léa Seydoux
as Madeleine Swann
Naomie Harris
as Eve Moneypenney
Dave Bautista
as Mr. Hinx
Monica Bellucci
as Lucia Sciarra
Andrew Scott
as Denbigh
Rory Kinnear
as Bill Tanner
Alessandro Cremona
as Marco Sciarra
Tenoch Huerta Mejía
as Mexican Man in Lift
Adriana Paz
as Mexican Woman in Lif...
Marco Zingaro
as Gallo's Accomplice
Stefano Elfi DiClaud...
as Gallo's Accomplice
Ian Bonar
as Q's Assistant
Stefano Elfi DiClaud...
as Gallo's Accomplice
Tam Williams
as Moneypenny's Boyfrie...
Richard Banham
as Oberhauser's London ...
Pip Carter
as SC019 Police Officer
Marc Zinga
as Moreau
Gediminas Adomaitis
as Oberhauser's Right H...
Francesco Arca
as Francesco
Emilio Aniba
as Palazzo Security
Dai Tabuchi
as Businessman
George Lasha
as Businessman
Sargon Yelda
as Businessman
Erick Hayden
as Marshall
Andy Cheung
as Businessman
Antonio Salines
as Fiat Driver
Nigel Barber
as Heads of Nations
Patrice Naiambana
as Heads of Nations
Stephane Cornicard
as Heads of Nations
Gary Fannin
as Heads of Nations
Sadao Ueda
as Heads of Nations
Wai Wong
as Heads of Nations
Joseph Balderrama
as Heads of Nations
Phillip Law
as Heads of Nations
Victor Schefe
as Clinic Barman
Harald Windisch
as Clinic Security Guar...
Tristan Matthiae
as Clinic Security Guar...
Detlef Bothe
as Cable Car Heavies
Bodo Friesecke
as Cable Car Heavies
Wilhem Iben
as Syringe Heavy
Marlon Boess
as Snowboarder
Lili Epply
as Snowboarder
Konstantin Gerlach
as Snowboarder
Lara Parmiani
as L'American Manager
Noemi Krausz
as Snowboarder
Umit Ulgen
as Train Guard
Noah Saavedra
as Snowboarder
Amra Mallassi
as Train Waiter
Francis Attakpah
as Snowboarder
Ziad Abaza
as Train Barman
Walid Mumuni
as Oberhauser's Chauffe...
Marie Wohlmuth
as Snowboarder
Michael White
as Oberhauser's Butler
Derek Horsham
as Oberhauser's Guard
Adam McGrady
as Oberhauser's Waiter
Nader Dernaika
as Oberhauser's Analyst
Pezhmaan Alinia
as Oberhauser's Chief A...
Nari Blair Mangat
as Oberhauser's Guard
Show More Cast

Critic Reviews for Spectre

All Critics (210) | Top Critics (42)

Dazzles early and fizzles late...Bond is not Batman; he does not need an origin story.

Full Review… | November 7, 2015
The Atlantic
Top Critic

The franchise is still doing well after 53 years of following the same formula.

Full Review… | November 7, 2015
Ozus' World Movie Reviews

Beautifully filmed with plenty of action, but missing a sense of fun.

Full Review… | November 7, 2015
Three Movie Buffs

His name is Bond, but it may as well be Bland considering how lethargic Daniel Craig is in going through the motions of playing a character he's obviously tired of playing.

Full Review… | November 7, 2015
The Patriot Ledger

The film is run into the ground for a wince-inducing final half-hour of unfortunate developments. It's too bad, because what SPECTRE provides during its initial two hours is sound -- and potent enough to still earn this a shaky recommendation.

Full Review… | November 7, 2015
Creative Loafing

SPECTRE is like a strand of pearls. They're shiny, but they're artificial.

Full Review… | November 7, 2015
tonymacklin.net

Audience Reviews for Spectre

½
Read More
romy861
Nicki Marie

Super Reviewer

½
Read More
jlewis07
Josh Lewis

Super Reviewer

Read More
KJ Proulx
KJ Proulx

Super Reviewer

Spectre Quotes

– Submitted by Matthew B (8 days ago)
– Submitted by Geraldine D (10 days ago)
– Submitted by Stevie A (11 days ago)
– Submitted by Mat P (12 days ago)

Discussion Forum

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TOPICLAST POST
I'm very worried this may not be good. + + + + + + + + + + + + + + + + +Nov 4, 2015 11:51 AM
So Craig will be back for at least one more? + + + + + + + + + + + + + + + + +Nov 4, 2015 11:40 AM
Box Office Prediction + + + + + + + + + + + + + + + + +Nov 3, 2015 6:50 PM
Same Score as Quantum of Solace + + + + + + + + + + + + + + + + +Nov 3, 2015 4:59 PM
I am deeply worried about this film + + + + + + + + + + + + + + + + +Nov 3, 2015 11:11 AM
+ +
+ + + + + +
+ + + + + + + +
+ + + + + +
+ +
+ +
4.4.7Status:Inactive
\ No newline at end of file diff --git a/src/test/java/io/appium/java_client/RottenTomatoesSnapshot.html b/src/test/java/io/appium/java_client/RottenTomatoesSnapshot.html new file mode 100644 index 000000000..8eecabc75 --- /dev/null +++ b/src/test/java/io/appium/java_client/RottenTomatoesSnapshot.html @@ -0,0 +1,2192 @@ + + + Top Movies - Opening in Theaters | Rotten Tomatoes
+
+
+
+
+ +
+
+ Spotlight +
+ + + 96% + + + + + + + 93% + +
+
+ + Mark Ruffalo, + + Michael Keaton + +
+ +
R, 2 hr. 7 min.
+ +
+
+
+ + + Critics Consensus: + + + Spotlight gracefully handles the lurid details of its fact-based story while resisting the temptation to lionize its heroes, resulting in a drama that + + ... + + honors the audience as well as its real-life subjects. + + More... + +
+ +
+
+ + + + + +
+ + + + + +
+ +
+ +
4.4.7Status:Active
\ No newline at end of file diff --git a/src/test/java/io/appium/java_client/android-rottentomatoes-demo-debug.apk b/src/test/java/io/appium/java_client/android-rottentomatoes-demo-debug.apk new file mode 100644 index 000000000..499e89e90 Binary files /dev/null and b/src/test/java/io/appium/java_client/android-rottentomatoes-demo-debug.apk differ diff --git a/src/test/java/io/appium/java_client/hello appium - saved page.htm b/src/test/java/io/appium/java_client/hello appium - saved page.htm new file mode 100644 index 000000000..5411519a0 --- /dev/null +++ b/src/test/java/io/appium/java_client/hello appium - saved page.htm @@ -0,0 +1,363 @@ + + + + + + + + +hello appium - Rezultate căutare Yahoo Romania + + + + +
+

Yahoo Romania Căutare pe Web

+
+ + +
+
+
+ + + + +
+
+
+
+
+ + +
+ +
+ + +
+
+
+
+
+
+ + +
    +
  1. +
    +
    +

    github.com

    +
    github.com/.../src/io/appium/android/apis/app/HelloWorld.java
    +
    +

    We would like to show you a description here but the site + won’t allow us.

  2. +
  3. +
    +
    +

    Newest ' appium' + Questions - Stack Overflow

    +
    stackoverflow.com/questions/tagged/appiumÃŽn cache
    +
    +

    Q&A for professional and enthusiast programmers ... + Tour Start here for a quick overview of the site

  4. +
  5. +
    +
    +

    Hello All, I have done + setup for appium on Mac...

    +
    www.linkedin.com/grp/post/5190708-5857830099017019392ÃŽn cache
    +
    +

    Apr 03, 2014 · Gaurav Tiwari + QA Automation Engineer at S&P Capital IQ. April 4, 2014. Hello All, + I have done setup for appium on Mac for iOS, but when i launch the + appium ...

  6. +
  7. +
    + + +
    +

    Hello Appium, I have a Selenium grid setup (machine + A) with multiple desktop nodes (Windows, Linux, Mac,...) (machines B, C & + D). From a heavily firewalled machine in ...

  8. +
  9. +
    + + +
    +

    Hi There, I am trying to make a simple Drag & Drop + gesture using TouchAction in Appium 1.2.2 .I am testing this on the + site Touch Punch Unfortunately I cannot get it ...

  10. +
  11. +
    + +
    +

    这个问题我准备写个hello world. (ADT + Appium + 在åŒä¸€å°æœºå™¨ä¸Š). 需è¦å‡†å¤‡çš„知识: 1. èƒ½çœ‹æ‡‚ä»£ç  2. 略懂 webdriver

  12. +
  13. +
    + + +
    +

    Hello: "appium --version" returns 1.3.7 as my + versiohn of Appium. my command line on a MAC is : + /Applications/Appium.app/Contents/Resources/node_modules/appium/bin + ...

  14. +
  15. +
    +
    +

    android - Activity used to start app + doesn't...

    +
    stackoverflow.com/questions/20335305/activity-used-to-start-app...ÃŽn cache
    +
    +

    When I try using Appium to test my Hello + World Android app, I get Activity used to start app doesn't exist! Make sure + it exists I have an apk with a package in it ...

  16. +
  17. +
    +
    +

    Applitools | Can you please provide + an example of...

    +
    support.applitools.com/customer/portal/questions/6091174-can-you...ÃŽn cache
    +
    +

    Can you please provide an example of an Appium test + for Android with Applitools Eyes

  18. +
  19. +
    +
    +

    Appium for Automating + Native iOS Apps | Assert...

    +
    www.assertselenium.com/appium/appium-for-automating-native-ios-appsÃŽn cache
    +
    +

    Hello, I am a newbie to mobile automation but + appium got me very curious. Is there a way to come up with a screen + capture video on how to setup and run a basic test ... +

+
    +
  1. +
+
+
+
+
+
+ + diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/AndroidPageObjectTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/AndroidPageObjectTest.java index dd0741d68..41e25bfcc 100644 --- a/src/test/java/io/appium/java_client/pagefactory_tests/AndroidPageObjectTest.java +++ b/src/test/java/io/appium/java_client/pagefactory_tests/AndroidPageObjectTest.java @@ -20,155 +20,157 @@ import io.appium.java_client.TouchableElement; import io.appium.java_client.android.AndroidDriver; import io.appium.java_client.android.AndroidElement; -import io.appium.java_client.pagefactory.AndroidFindAll; -import io.appium.java_client.pagefactory.AndroidFindBy; -import io.appium.java_client.pagefactory.AndroidFindBys; -import io.appium.java_client.pagefactory.AppiumFieldDecorator; -import io.appium.java_client.pagefactory.SelendroidFindBy; -import io.appium.java_client.pagefactory.iOSFindBy; -import io.appium.java_client.pagefactory.iOSFindBys; +import io.appium.java_client.pagefactory.*; import io.appium.java_client.remote.MobileCapabilityType; - -import java.io.File; -import java.util.List; -import java.util.concurrent.TimeUnit; - import io.appium.java_client.service.local.AppiumDriverLocalService; -import org.junit.*; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.openqa.selenium.internal.WrapsDriver; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static junit.framework.TestCase.assertNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + public class AndroidPageObjectTest { - private static WebDriver driver; + private static WebDriver driver; private static AppiumDriverLocalService service; private boolean populated = false; - @FindBy(className = "android.widget.TextView") - private List textVieWs; + @FindBy(className = "android.widget.TextView") + private List textVieWs; - @AndroidFindBy(className = "android.widget.TextView") - private List androidTextViews; + @AndroidFindBy(className = "android.widget.TextView") + private List androidTextViews; - @iOSFindBy(uiAutomator = ".elements()[0]") - private List iosTextViews; + @iOSFindBy(uiAutomator = ".elements()[0]") + private List iosTextViews; - @iOSFindBy(uiAutomator = ".elements()[0]") - @AndroidFindBy(className = "android.widget.TextView") - private List androidOriOsTextViews; + @iOSFindBy(uiAutomator = ".elements()[0]") + @AndroidFindBy(className = "android.widget.TextView") + private List androidOriOsTextViews; - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") - private List androidUIAutomatorViews; + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") + private List androidUIAutomatorViews; - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") - private List mobileElementViews; + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") + private List mobileElementViews; - @FindBy(className = "android.widget.TextView") - private List mobiletextVieWs; + @FindBy(className = "android.widget.TextView") + private List mobiletextVieWs; - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") - private List remoteElementViews; + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") + private List remoteElementViews; - @AndroidFindBys({ - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), - @AndroidFindBy(className = "android.widget.TextView") - }) - private List chainElementViews; + @AndroidFindBys({ + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), + @AndroidFindBy(className = "android.widget.TextView") + }) + private List chainElementViews; - @iOSFindBys({@iOSFindBy(uiAutomator = ".elements()[0]"), - @iOSFindBy(xpath = "//someElement")}) - private List iosChainTextViews; + @iOSFindBys({@iOSFindBy(uiAutomator = ".elements()[0]"), + @iOSFindBy(xpath = "//someElement")}) + private List iosChainTextViews; - @AndroidFindBys({ - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/content\")"), - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), - @AndroidFindBy(id = "android:id/text1") - }) - @iOSFindBys({@iOSFindBy(uiAutomator = ".elements()[0]"), - @iOSFindBy(xpath = "//someElement")}) - private List chainAndroidOrIOSUIAutomatorViews; + @AndroidFindBys({ + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/content\")"), + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), + @AndroidFindBy(id = "android:id/text1") + }) + @iOSFindBys({@iOSFindBy(uiAutomator = ".elements()[0]"), + @iOSFindBy(xpath = "//someElement")}) + private List chainAndroidOrIOSUIAutomatorViews; @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") private List touchabletextVieWs; - @FindBy(id = "android:id/text1") - private WebElement textView; - - @AndroidFindBy(className = "android.widget.TextView") - private WebElement androidTextView; - - @iOSFindBy(uiAutomator = ".elements()[0]") - private WebElement iosTextView; - - @AndroidFindBy(className = "android.widget.TextView") - @iOSFindBy(uiAutomator = ".elements()[0]") - private WebElement androidOriOsTextView; - - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") - private WebElement androidUIAutomatorView; - - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") - private MobileElement mobileElementView; - - @FindBy(className = "android.widget.TextView") - private MobileElement mobiletextVieW; - - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") - private RemoteWebElement remotetextVieW; - - @AndroidFindBys({ - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), - @AndroidFindBy(className = "android.widget.TextView") - }) - private WebElement chainElementView; - - @iOSFindBys({@iOSFindBy(uiAutomator = ".elements()[0]"), - @iOSFindBy(xpath = "//someElement")}) - private WebElement iosChainTextView; - - @AndroidFindBys({ - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/content\")"), - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), - @AndroidFindBy(id = "android:id/text1") - }) - @iOSFindBys({@iOSFindBy(uiAutomator = ".elements()[0]"), - @iOSFindBy(xpath = "//someElement")}) - private WebElement chainAndroidOrIOSUIAutomatorView; - - @AndroidFindBys({ - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/content\")"), - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), - @AndroidFindBy(id = "android:id/text1") - }) - private AndroidElement androidElementView; - - @AndroidFindBys({ - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/content\")"), - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), - @AndroidFindBy(id = "android:id/text1") - }) - private List androidElementViews; - - @AndroidFindAll({ - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/Fakecontent\")"), - @AndroidFindBy(id = "android:id/Faketext1"), - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), //by this locator element is found - @AndroidFindBy(id = "android:id/FakeId") - }) - private List findAllElementViews; - - @AndroidFindAll({ - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/Fakecontent\")"), - @AndroidFindBy(id = "android:id/Faketext1"), - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), //by this locator element is found - @AndroidFindBy(id = "android:id/FakeId") - }) - private WebElement findAllElementView; + @FindBy(id = "android:id/text1") + private WebElement textView; + + @AndroidFindBy(className = "android.widget.TextView") + private WebElement androidTextView; + + @iOSFindBy(uiAutomator = ".elements()[0]") + private WebElement iosTextView; + + @AndroidFindBy(className = "android.widget.TextView") + @iOSFindBy(uiAutomator = ".elements()[0]") + private WebElement androidOriOsTextView; + + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") + private WebElement androidUIAutomatorView; + + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") + private MobileElement mobileElementView; + + @FindBy(className = "android.widget.TextView") + private MobileElement mobiletextVieW; + + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") + private RemoteWebElement remotetextVieW; + + @AndroidFindBys({ + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), + @AndroidFindBy(className = "android.widget.TextView") + }) + private WebElement chainElementView; + + @iOSFindBys({@iOSFindBy(uiAutomator = ".elements()[0]"), + @iOSFindBy(xpath = "//someElement")}) + private WebElement iosChainTextView; + + @AndroidFindBys({ + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/content\")"), + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), + @AndroidFindBy(id = "android:id/text1") + }) + @iOSFindBys({@iOSFindBy(uiAutomator = ".elements()[0]"), + @iOSFindBy(xpath = "//someElement")}) + private WebElement chainAndroidOrIOSUIAutomatorView; + + @AndroidFindBys({ + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/content\")"), + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), + @AndroidFindBy(id = "android:id/text1") + }) + private AndroidElement androidElementView; + + @AndroidFindBys({ + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/content\")"), + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), + @AndroidFindBy(id = "android:id/text1") + }) + private List androidElementViews; + + @AndroidFindAll({ + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/Fakecontent\")"), + @AndroidFindBy(id = "android:id/Faketext1"), + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), //by this locator element is found + @AndroidFindBy(id = "android:id/FakeId") + }) + private List findAllElementViews; + + @AndroidFindAll({ + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/Fakecontent\")"), + @AndroidFindBy(id = "android:id/Faketext1"), + @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/list\")"), //by this locator element is found + @AndroidFindBy(id = "android:id/FakeId") + }) + private WebElement findAllElementView; @AndroidFindBy(id = "android:id/text1") @SelendroidFindBy(id = "Invalid Identifier") @@ -184,20 +186,26 @@ public class AndroidPageObjectTest { @iOSFindBy(uiAutomator = ".elements()[0]") @FindBy(css = "e.e1.e2") private WebElement elementWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy; - - @SuppressWarnings("rawtypes") - @BeforeClass - public static void beforeClass() throws Exception { + + @FindBy(id = "fakeId") + private WebElement fakeElement; + + @FindBy(id = "fakeId") + private List fakeElements; + + @SuppressWarnings("rawtypes") + @BeforeClass + public static void beforeClass() throws Exception { service = AppiumDriverLocalService.buildDefaultService(); service.start(); - File appDir = new File("src/test/java/io/appium/java_client"); - File app = new File(appDir, "ApiDemos-debug.apk"); - DesiredCapabilities capabilities = new DesiredCapabilities(); - capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); - capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); - driver = new AndroidDriver(service.getUrl(), capabilities); - } + File appDir = new File("src/test/java/io/appium/java_client"); + File app = new File(appDir, "ApiDemos-debug.apk"); + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + driver = new AndroidDriver(service.getUrl(), capabilities); + } @Before public void setUp() throws Exception { @@ -208,179 +216,179 @@ public void setUp() throws Exception { populated = true; } - @AfterClass - public static void afterClass() throws Exception { + @AfterClass + public static void afterClass() throws Exception { if (driver != null) - driver.quit(); + driver.quit(); if (service != null) service.stop(); - } - - @Test - public void findByElementsTest() { - Assert.assertNotEquals(0, textVieWs.size()); - } - - @Test - public void findByElementTest() { - Assert.assertNotEquals(null, textView.getAttribute("text")); - } - - - @Test - public void androidFindByElementsTest(){ - Assert.assertNotEquals(0, androidTextViews.size()); - } - - @Test - public void androidFindByElementTest(){ - Assert.assertNotEquals(null, androidTextView.getAttribute("text")); - } - - @Test - public void checkThatElementsWereNotFoundByIOSUIAutomator(){ - Assert.assertEquals(0, iosTextViews.size()); - } - - @Test - public void checkThatElementWasNotFoundByIOSUIAutomator(){ - NoSuchElementException nsee = null; - try{ - iosTextView.getAttribute("text"); - } - catch (Exception e){ - nsee = (NoSuchElementException) e; - } - Assert.assertNotNull(nsee); - } - - @Test - public void androidOrIOSFindByElementsTest(){ - Assert.assertNotEquals(0, androidOriOsTextViews.size()); - } - - @Test - public void androidOrIOSFindByElementTest(){ - Assert.assertNotEquals(null, androidOriOsTextView.getAttribute("text")); - } - - @Test - public void androidFindByUIAutomatorElementsTest(){ - Assert.assertNotEquals(0, androidUIAutomatorViews.size()); - } - - @Test - public void androidFindByUIAutomatorElementTest(){ - Assert.assertNotEquals(null, androidUIAutomatorView.getAttribute("text")); - } - - @Test - public void areMobileElementsTest(){ - Assert.assertNotEquals(0, mobileElementViews.size()); - } - - @Test - public void isMobileElementTest(){ - Assert.assertNotEquals(null, mobileElementView.getAttribute("text")); - } - - @Test - public void areMobileElements_FindByTest(){ - Assert.assertNotEquals(0, mobiletextVieWs.size()); - } - - @Test - public void isMobileElement_FindByTest(){ - Assert.assertNotEquals(null, mobiletextVieW.getAttribute("text")); - } - - @Test - public void areRemoteElementsTest(){ - Assert.assertNotEquals(0, remoteElementViews.size()); - } - - @Test - public void isRemoteElementTest(){ - Assert.assertNotEquals(null, remotetextVieW.getAttribute("text")); - } - - @Test - public void androidChainSearchElementsTest(){ - Assert.assertNotEquals(0, chainElementViews.size()); - } - - @Test - public void androidChainSearchElementTest(){ - Assert.assertNotEquals(null, chainElementView.getAttribute("text")); - } - - @Test - public void checkThatElementsWereNotFoundByIOSUIAutomator_Chain(){ - Assert.assertEquals(0, iosChainTextViews.size()); - } - - @Test - public void checkThatElementWasNotFoundByIOSUIAutomator_Chain(){ - NoSuchElementException nsee = null; - try{ - iosChainTextView.getAttribute("text"); - } - catch (Exception e){ - nsee = (NoSuchElementException) e; - } - Assert.assertNotNull(nsee); - } - - @Test - public void androidOrIOSFindByElementsTest_ChainSearches(){ - Assert.assertNotEquals(0, chainAndroidOrIOSUIAutomatorViews.size()); - } - - @Test - public void androidOrIOSFindByElementTest_ChainSearches(){ - Assert.assertNotEquals(null, chainAndroidOrIOSUIAutomatorView.getAttribute("text")); - } - - @Test - public void isAndroidElementTest(){ - Assert.assertNotEquals(null, androidElementView.getAttribute("text")); - } - - @Test - public void areAndroidElementsTest(){ - Assert.assertNotEquals(0, androidElementViews.size()); - } - - @Test - public void findAllElementTest(){ - Assert.assertNotEquals(null, findAllElementView.getAttribute("text")); - } - - @Test - public void findAllElementsTest(){ - Assert.assertNotEquals(0, findAllElementViews.size()); - } + } + + @Test + public void findByElementsTest() { + assertNotEquals(0, textVieWs.size()); + } + + @Test + public void findByElementTest() { + assertNotEquals(null, textView.getAttribute("text")); + } + + + @Test + public void androidFindByElementsTest(){ + assertNotEquals(0, androidTextViews.size()); + } + + @Test + public void androidFindByElementTest(){ + assertNotEquals(null, androidTextView.getAttribute("text")); + } + + @Test + public void checkThatElementsWereNotFoundByIOSUIAutomator(){ + assertEquals(0, iosTextViews.size()); + } + + @Test + public void checkThatElementWasNotFoundByIOSUIAutomator(){ + NoSuchElementException nsee = null; + try{ + iosTextView.getAttribute("text"); + } + catch (Exception e){ + nsee = (NoSuchElementException) e; + } + assertNotNull(nsee); + } + + @Test + public void androidOrIOSFindByElementsTest(){ + assertNotEquals(0, androidOriOsTextViews.size()); + } + + @Test + public void androidOrIOSFindByElementTest(){ + assertNotEquals(null, androidOriOsTextView.getAttribute("text")); + } + + @Test + public void androidFindByUIAutomatorElementsTest(){ + assertNotEquals(0, androidUIAutomatorViews.size()); + } + + @Test + public void androidFindByUIAutomatorElementTest(){ + assertNotEquals(null, androidUIAutomatorView.getAttribute("text")); + } + + @Test + public void areMobileElementsTest(){ + assertNotEquals(0, mobileElementViews.size()); + } + + @Test + public void isMobileElementTest(){ + assertNotEquals(null, mobileElementView.getAttribute("text")); + } + + @Test + public void areMobileElements_FindByTest(){ + assertNotEquals(0, mobiletextVieWs.size()); + } + + @Test + public void isMobileElement_FindByTest(){ + assertNotEquals(null, mobiletextVieW.getAttribute("text")); + } + + @Test + public void areRemoteElementsTest(){ + assertNotEquals(0, remoteElementViews.size()); + } + + @Test + public void isRemoteElementTest(){ + assertNotEquals(null, remotetextVieW.getAttribute("text")); + } + + @Test + public void androidChainSearchElementsTest(){ + assertNotEquals(0, chainElementViews.size()); + } + + @Test + public void androidChainSearchElementTest(){ + assertNotEquals(null, chainElementView.getAttribute("text")); + } + + @Test + public void checkThatElementsWereNotFoundByIOSUIAutomator_Chain(){ + assertEquals(0, iosChainTextViews.size()); + } + + @Test + public void checkThatElementWasNotFoundByIOSUIAutomator_Chain(){ + NoSuchElementException nsee = null; + try{ + iosChainTextView.getAttribute("text"); + } + catch (Exception e){ + nsee = (NoSuchElementException) e; + } + assertNotNull(nsee); + } + + @Test + public void androidOrIOSFindByElementsTest_ChainSearches(){ + assertNotEquals(0, chainAndroidOrIOSUIAutomatorViews.size()); + } + + @Test + public void androidOrIOSFindByElementTest_ChainSearches(){ + assertNotEquals(null, chainAndroidOrIOSUIAutomatorView.getAttribute("text")); + } + + @Test + public void isAndroidElementTest(){ + assertNotEquals(null, androidElementView.getAttribute("text")); + } + + @Test + public void areAndroidElementsTest(){ + assertNotEquals(0, androidElementViews.size()); + } + + @Test + public void findAllElementTest(){ + assertNotEquals(null, findAllElementView.getAttribute("text")); + } + + @Test + public void findAllElementsTest(){ + assertNotEquals(0, findAllElementViews.size()); + } - @Test - public void findByAndroidAnnotationOnlyTest(){ - Assert.assertNotEquals(null, textAndroidId.getAttribute("text")); - } + @Test + public void findByAndroidAnnotationOnlyTest(){ + assertNotEquals(null, textAndroidId.getAttribute("text")); + } @Test public void isTouchableElement(){ - Assert.assertNotEquals(null, touchabletextVieW.getAttribute("text")); + assertNotEquals(null, touchabletextVieW.getAttribute("text")); } @Test public void areTouchableElements(){ - Assert.assertNotEquals(0, touchabletextVieWs.size()); + assertNotEquals(0, touchabletextVieWs.size()); } @Test public void isTheFieldAndroidElement(){ @SuppressWarnings("unused") - AndroidElement androidElement = (AndroidElement) mobiletextVieW; //declared as MobileElement + AndroidElement androidElement = (AndroidElement) mobiletextVieW; //declared as MobileElement androidElement = (AndroidElement) androidTextView; //declared as WedElement androidElement = (AndroidElement) remotetextVieW; //declared as RemoteWedElement androidElement = (AndroidElement) touchabletextVieW; //declared as TouchABLEElement @@ -389,7 +397,7 @@ public void isTheFieldAndroidElement(){ @Test public void checkThatTestWillNotBeFailedBecauseOfInvalidFindBy(){ try { - Assert.assertNotEquals(null, elementWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy.getAttribute("text")); + assertNotEquals(null, elementWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy.getAttribute("text")); } catch (NoSuchElementException ignored){ return; @@ -399,6 +407,19 @@ public void checkThatTestWillNotBeFailedBecauseOfInvalidFindBy(){ @Test public void checkThatTestWillNotBeFailedBecauseOfInvalidFindBy_List(){ - Assert.assertEquals(0, elementsWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy.size()); + assertEquals(0, elementsWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy.size()); + } + + @Test + public void checkThatClassObjectMethodsDoNotInvokeTheSearching() { + assertEquals(true, AndroidElement.class.isAssignableFrom(fakeElement.getClass())); + assertEquals(false, AndroidElement.class.equals(fakeElement.getClass())); + assertEquals(true, driver.equals(((WrapsDriver) fakeElement).getWrappedDriver())); + } + + @Test + public void checkThatClassObjectMethodsDoNotInvokeTheSearchingOfElementLest() { + assertEquals(true, List.class.isAssignableFrom(fakeElements.getClass())); + assertEquals(false, ArrayList.class.equals(fakeElements.getClass())); } } diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/DesctopBrowserCompatibilityTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/DesktopBrowserCompatibilityTest.java similarity index 69% rename from src/test/java/io/appium/java_client/pagefactory_tests/DesctopBrowserCompatibilityTest.java rename to src/test/java/io/appium/java_client/pagefactory_tests/DesktopBrowserCompatibilityTest.java index 9bb741ae6..08c3bdd50 100644 --- a/src/test/java/io/appium/java_client/pagefactory_tests/DesctopBrowserCompatibilityTest.java +++ b/src/test/java/io/appium/java_client/pagefactory_tests/DesktopBrowserCompatibilityTest.java @@ -18,19 +18,9 @@ import io.appium.java_client.android.AndroidDriver; import io.appium.java_client.pagefactory.AndroidFindBy; -import io.appium.java_client.pagefactory.AndroidFindBys; import io.appium.java_client.pagefactory.AppiumFieldDecorator; import io.appium.java_client.pagefactory.iOSFindBy; import io.appium.java_client.pagefactory.iOSFindBys; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import org.junit.Assert; import org.junit.Test; import org.openqa.selenium.Platform; import org.openqa.selenium.WebDriver; @@ -38,15 +28,22 @@ import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeDriverService; import org.openqa.selenium.firefox.FirefoxDriver; -import org.openqa.selenium.ie.InternetExplorerDriver; -import org.openqa.selenium.ie.InternetExplorerDriverService; import org.openqa.selenium.safari.SafariDriver; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.FindBys; import org.openqa.selenium.support.PageFactory; -import org.openqa.selenium.remote.RemoteWebElement; -public class DesctopBrowserCompatibilityTest { +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class DesktopBrowserCompatibilityTest { private static enum AvailableDrivers { FIREFOX(FirefoxDriver.class, new ArrayList() { @@ -56,7 +53,9 @@ private static enum AvailableDrivers { add(Platform.MAC); } - }, new HashMap(), null), CHROME(ChromeDriver.class, + }, new HashMap(), null), + + CHROME(ChromeDriver.class, new ArrayList() { private static final long serialVersionUID = 1L; { @@ -75,22 +74,8 @@ private static enum AvailableDrivers { new File( "src/test/java/io/appium/java_client/pagefactory_tests/chromedriver")); } - }, ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY), - INTERNET_EXPLORER( - InternetExplorerDriver.class, new ArrayList() { - private static final long serialVersionUID = 1L; - { - add(Platform.WINDOWS); - } - }, new HashMap() { - private static final long serialVersionUID = 1L; - - { - put(Platform.WINDOWS, - new File( - "src/test/java/io/appium/java_client/pagefactory_tests/IEDriverServer.exe")); - } - }, InternetExplorerDriverService.IE_DRIVER_EXE_PROPERTY), SAFARI( + }, ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY), + SAFARI( SafariDriver.class, new ArrayList() { private static final long serialVersionUID = 1L; { @@ -149,7 +134,7 @@ private void setSystemProperty(Platform p) { } - public void setUp(Class driverClass) { + public void setUp(Class driverClass) { AvailableDrivers availableDriver = AvailableDrivers.getAvailableDriver(driverClass, current); if (availableDriver != null){ availableDriver.setSystemProperty(current); @@ -159,48 +144,29 @@ public void setUp(Class driverClass) { private final Platform current = Platform.getCurrent(); private final long IMPLICITLY_WAIT = 15; - @FindBy(name = "q") - @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/someId\")") - @iOSFindBy(uiAutomator = ".elements()[0]") - private WebElement searchTextField; - - @AndroidFindBys({ - @AndroidFindBy(className = "someClass"), - @AndroidFindBy(xpath = "//someTag")}) - @iOSFindBys({ - @iOSFindBy(xpath = "//selector[1]"), - @iOSFindBy(xpath = "//someTag")}) - @FindBy(name="btnG") - private RemoteWebElement searchButton; + + - @AndroidFindBy(className = "someClass") + @AndroidFindBy(className = "someClass") @iOSFindBys({ @iOSFindBy(xpath = "//selector[1]"), @iOSFindBy(xpath = "//someTag")}) - @FindBys({@FindBy(className = "r"), @FindBy(tagName = "a")}) + @FindBys({@FindBy(id="main"), @FindBy(tagName = "p")}) private List foundLinks; - private List ires; //this list is located by id="ires" - private WebElement btnG; //this element is found by name="btnG" + private List main; //this list is located by id="main" + private WebDriver trap1; private List> trap2; private void test(WebDriver driver){ try { PageFactory.initElements(new AppiumFieldDecorator(driver, IMPLICITLY_WAIT, TimeUnit.SECONDS), this); - driver.get("https://www.google.com"); - - searchTextField.sendKeys("Hello"); - searchButton.click(); - Assert.assertNotEquals(0, foundLinks.size()); - searchTextField.clear(); - searchTextField.sendKeys("Hello, Appium!"); - searchButton.click(); - Assert.assertNotEquals(0, foundLinks.size()); - Assert.assertNotEquals(0, ires.size()); - Assert.assertEquals(null, trap1); - Assert.assertEquals(null, trap2); - btnG.click(); + driver.get("file:///" + new File("src/test/java/io/appium/java_client/hello appium - saved page.htm").getAbsolutePath()); + assertNotEquals(0, foundLinks.size()); + assertNotEquals(0, main.size()); + assertEquals(null, trap1); + assertEquals(null, trap2); } finally { driver.quit(); } @@ -221,15 +187,7 @@ public void chromeTest() { setUp(ChromeDriver.class); test(new ChromeDriver()); } - } - - @Test - public void ieTest() { - if (AvailableDrivers.getAvailableDriver(InternetExplorerDriver.class, current)!=null){ - setUp(InternetExplorerDriver.class); - test(new InternetExplorerDriver()); - } - } + } @Test public void safariTest() { diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/IEDriverServer.exe b/src/test/java/io/appium/java_client/pagefactory_tests/IEDriverServer.exe deleted file mode 100644 index 415561f9b..000000000 Binary files a/src/test/java/io/appium/java_client/pagefactory_tests/IEDriverServer.exe and /dev/null differ diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/IOSfMobileBrowserCompatibilityTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/IOSfMobileBrowserCompatibilityTest.java index 4be065695..68fa27945 100644 --- a/src/test/java/io/appium/java_client/pagefactory_tests/IOSfMobileBrowserCompatibilityTest.java +++ b/src/test/java/io/appium/java_client/pagefactory_tests/IOSfMobileBrowserCompatibilityTest.java @@ -23,9 +23,6 @@ import io.appium.java_client.pagefactory.iOSFindBy; import io.appium.java_client.remote.MobileBrowserType; import io.appium.java_client.remote.MobileCapabilityType; -import java.util.List; -import java.util.concurrent.TimeUnit; - import io.appium.java_client.service.local.AppiumDriverLocalService; import org.junit.After; import org.junit.Assert; @@ -39,6 +36,9 @@ import org.openqa.selenium.support.FindBys; import org.openqa.selenium.support.PageFactory; +import java.util.List; +import java.util.concurrent.TimeUnit; + public class IOSfMobileBrowserCompatibilityTest { private WebDriver driver; @@ -56,7 +56,7 @@ public class IOSfMobileBrowserCompatibilityTest { @FindBy(name="btnG") private RemoteWebElement searchButton; - @AndroidFindBy(className = "someClass") + @AndroidFindBy(className = "someClass") @FindBys({@FindBy(className = "r"), @FindBy(tagName = "a")}) @iOSFindBy(className = "someClass") private List foundLinks; diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/MobileBrowserCompatibilityTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/MobileBrowserCompatibilityTest.java index 17e3480d8..dee99a560 100644 --- a/src/test/java/io/appium/java_client/pagefactory_tests/MobileBrowserCompatibilityTest.java +++ b/src/test/java/io/appium/java_client/pagefactory_tests/MobileBrowserCompatibilityTest.java @@ -22,10 +22,6 @@ import io.appium.java_client.pagefactory.AppiumFieldDecorator; import io.appium.java_client.remote.MobileBrowserType; import io.appium.java_client.remote.MobileCapabilityType; - -import java.util.List; -import java.util.concurrent.TimeUnit; - import io.appium.java_client.service.local.AppiumDriverLocalService; import org.junit.After; import org.junit.Assert; @@ -39,6 +35,9 @@ import org.openqa.selenium.support.FindBys; import org.openqa.selenium.support.PageFactory; +import java.util.List; +import java.util.concurrent.TimeUnit; + public class MobileBrowserCompatibilityTest { private WebDriver driver; @@ -50,11 +49,11 @@ public class MobileBrowserCompatibilityTest { @AndroidFindBys({ @AndroidFindBy(className = "someClass"), - @AndroidFindBy(xpath = "//someTag")}) + @AndroidFindBy(xpath = "//someTag")}) @FindBy(name="btnG") private RemoteWebElement searchButton; - @AndroidFindBy(className = "someClass") + @AndroidFindBy(className = "someClass") @FindBys({@FindBy(className = "r"), @FindBy(tagName = "a")}) private List foundLinks; @@ -87,9 +86,6 @@ public void test() { searchTextField.sendKeys("Hello"); searchButton.click(); Assert.assertNotEquals(0, foundLinks.size()); - searchTextField.clear(); - searchTextField.sendKeys("Hello, Appium!"); - searchButton.click(); } } diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/SelendroidModeTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/SelendroidModeTest.java index a02fa6882..57756c1d2 100644 --- a/src/test/java/io/appium/java_client/pagefactory_tests/SelendroidModeTest.java +++ b/src/test/java/io/appium/java_client/pagefactory_tests/SelendroidModeTest.java @@ -17,26 +17,25 @@ package io.appium.java_client.pagefactory_tests; import io.appium.java_client.android.AndroidDriver; -import io.appium.java_client.pagefactory.AndroidFindBy; -import io.appium.java_client.pagefactory.AppiumFieldDecorator; -import io.appium.java_client.pagefactory.SelendroidFindAll; -import io.appium.java_client.pagefactory.SelendroidFindBy; -import io.appium.java_client.pagefactory.SelendroidFindBys; +import io.appium.java_client.android.AndroidElement; +import io.appium.java_client.pagefactory.*; import io.appium.java_client.remote.AutomationName; import io.appium.java_client.remote.MobileCapabilityType; - import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import io.appium.java_client.service.local.flags.GeneralServerFlag; import org.junit.*; +import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; import java.io.File; import java.util.List; import java.util.concurrent.TimeUnit; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.remote.DesiredCapabilities; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; +import static org.junit.Assert.*; public class SelendroidModeTest { private static int SELENDROID_PORT = 9999; @@ -90,7 +89,8 @@ public class SelendroidModeTest { @BeforeClass public static void beforeClass() throws Exception { - service = AppiumDriverLocalService.buildDefaultService(); + AppiumServiceBuilder builder = new AppiumServiceBuilder().withArgument(GeneralServerFlag.AUTOMATION_NAME, AutomationName.SELENDROID); + service = builder.build(); service.start(); File appDir = new File("src/test/java/io/appium/java_client"); @@ -98,7 +98,6 @@ public static void beforeClass() throws Exception { DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); - capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.SELENDROID); capabilities.setCapability(MobileCapabilityType.SELENDROID_PORT, SELENDROID_PORT); driver = new AndroidDriver(service.getUrl(), capabilities); } @@ -123,73 +122,67 @@ public static void afterClass() throws Exception { @Test public void findByIdElementTest() { - Assert.assertNotEquals(null, textId.getAttribute("text")); + assertNotEquals(null, textId.getAttribute("text")); } @Test public void findBySelendroidSelectorTest() { - Assert.assertNotEquals(null, textSelendroidId.getAttribute("text")); + assertNotEquals(null, textSelendroidId.getAttribute("text")); } @Test public void findByElementByNameTest() { - Assert.assertEquals("Accessibility", textName.getText()); + assertEquals("Accessibility", textName.getText()); } @Test public void findByElementByNameAndroidTest() { - Assert.assertEquals("Accessibility", textNameAndroid.getText()); + assertEquals("Accessibility", textNameAndroid.getText()); } @Test public void findByElementByNameDefaultTest() { - Assert.assertEquals("Accessibility", textNameDefault.getText()); + assertEquals("Accessibility", textNameDefault.getText()); } @Test public void findByElementByXpathTest() { - Assert.assertEquals("Accessibility", textXpath.getText()); + assertEquals("Accessibility", textXpath.getText()); } @Test public void findByElementByIdsTest() { - Assert.assertNotNull(textIds.getText()); + assertNotNull(textIds.getText()); } @Test public void findByElementByTestAllTest() { - Assert.assertNotNull(textAll.getText()); + assertNotNull(textAll.getText()); } @Test public void findByElementByTextsAllTest() { - Assert.assertTrue(textsAll.size() > 1); + assertTrue(textsAll.size() > 1); } @Test public void findByElementByCalssTest() { - Assert.assertNotEquals(null, textClass.getAttribute("text")); + assertNotEquals(null, textClass.getAttribute("text")); } @Test public void findByElementByTagTest() { - Assert.assertNotEquals(null, textTag.getAttribute("text")); + assertNotEquals(null, textTag.getAttribute("text")); } @Test public void findBySelendroidAnnotationOnlyTest() { - Assert.assertNotEquals(null, textSelendroidId.getAttribute("text")); + assertNotEquals(null, textSelendroidId.getAttribute("text")); } @Test public void findBySelendroidLinkTextTest() { - Assert.assertEquals("Accessibility", textLink.getText()); - - } - - @Test - public void findBySelendroidPartialLinkTextTest() { - Assert.assertEquals("Accessibility", textPartialLink.getText()); + assertEquals("Accessibility", textLink.getText()); } } diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/TimeOutResetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/TimeOutResetTest.java index 1a1e8b5df..c560aa6e9 100644 --- a/src/test/java/io/appium/java_client/pagefactory_tests/TimeOutResetTest.java +++ b/src/test/java/io/appium/java_client/pagefactory_tests/TimeOutResetTest.java @@ -17,11 +17,6 @@ package io.appium.java_client.pagefactory_tests; import io.appium.java_client.pagefactory.AppiumFieldDecorator; - -import java.util.Calendar; -import java.util.List; -import java.util.concurrent.TimeUnit; - import io.appium.java_client.pagefactory.TimeOutDuration; import io.appium.java_client.pagefactory.WithTimeout; import org.junit.After; @@ -35,9 +30,13 @@ import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; +import java.util.Calendar; +import java.util.List; +import java.util.concurrent.TimeUnit; + public class TimeOutResetTest { private WebDriver driver; - private final static long ACCEPTABLE_DELTA_MILLS = 500; + private final static long ACCEPTABLE_DELTA_MILLS = 1500; @FindAll({@FindBy(className = "ClassWhichDoesNotExist"), @@ -64,17 +63,17 @@ public void tearDown() throws Exception { driver.quit(); } - private static void checkTimeDifference(long etalonTime, - TimeUnit etalonTimeUnit, long currentMillis) { - long etalonMillis = TimeUnit.MILLISECONDS.convert(etalonTime, - etalonTimeUnit); + private static void checkTimeDifference(long expectedTime, + TimeUnit expectedTimeUnit, long currentMillis) { + long expectedMillis = TimeUnit.MILLISECONDS.convert(expectedTime, + expectedTimeUnit); try{ Assert.assertEquals(true, - ((currentMillis - etalonMillis) < ACCEPTABLE_DELTA_MILLS) - && ((currentMillis - etalonMillis) >= 0)); + ((currentMillis - expectedMillis) < ACCEPTABLE_DELTA_MILLS) + && ((currentMillis - expectedMillis) >= 0)); } catch (Error e){ - String message = String.valueOf(etalonTime) + " " + etalonTimeUnit.toString() + " current duration in millis " + + String message = String.valueOf(expectedTime) + " " + expectedTimeUnit.toString() + " current duration in millis " + String.valueOf(currentMillis) + " Failed"; throw new RuntimeException(message, e); } diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/chromedriver b/src/test/java/io/appium/java_client/pagefactory_tests/chromedriver index d5eeb48ba..8c6aed5f8 100755 Binary files a/src/test/java/io/appium/java_client/pagefactory_tests/chromedriver and b/src/test/java/io/appium/java_client/pagefactory_tests/chromedriver differ diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/chromedriver.exe b/src/test/java/io/appium/java_client/pagefactory_tests/chromedriver.exe index c295cc8ae..9463a1e4b 100644 Binary files a/src/test/java/io/appium/java_client/pagefactory_tests/chromedriver.exe and b/src/test/java/io/appium/java_client/pagefactory_tests/chromedriver.exe differ diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/iOSPageObjectTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/iOSPageObjectTest.java index 40cfce79d..c9633bfd7 100644 --- a/src/test/java/io/appium/java_client/pagefactory_tests/iOSPageObjectTest.java +++ b/src/test/java/io/appium/java_client/pagefactory_tests/iOSPageObjectTest.java @@ -20,16 +20,8 @@ import io.appium.java_client.TouchableElement; import io.appium.java_client.ios.IOSDriver; import io.appium.java_client.ios.IOSElement; -import io.appium.java_client.pagefactory.AndroidFindBy; -import io.appium.java_client.pagefactory.AndroidFindBys; -import io.appium.java_client.pagefactory.AppiumFieldDecorator; -import io.appium.java_client.pagefactory.iOSFindAll; -import io.appium.java_client.pagefactory.iOSFindBy; +import io.appium.java_client.pagefactory.*; import io.appium.java_client.remote.MobileCapabilityType; - -import java.io.File; -import java.util.List; - import io.appium.java_client.service.local.AppiumDriverLocalService; import org.junit.*; import org.openqa.selenium.NoSuchElementException; @@ -40,6 +32,9 @@ import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; +import java.io.File; +import java.util.List; + public class iOSPageObjectTest { private static WebDriver driver; @@ -122,13 +117,13 @@ public class iOSPageObjectTest { private List iosButtons; @iOSFindAll({ - @iOSFindBy(xpath = "ComputeSumButton_Test"), + @iOSFindBy(xpath = "ComputeSumButton_Test"), @iOSFindBy(name = "ComputeSumButton") //it is real locator }) private WebElement findAllElement; @iOSFindAll({ - @iOSFindBy(xpath = "ComputeSumButton_Test"), + @iOSFindBy(xpath = "ComputeSumButton_Test"), @iOSFindBy(name = "ComputeSumButton") //it is real locator }) private List findAllElements; diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/AndroidOverrideWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/AndroidOverrideWidgetTest.java new file mode 100644 index 000000000..d28bed90b --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/AndroidOverrideWidgetTest.java @@ -0,0 +1,124 @@ +package io.appium.java_client.pagefactory_tests.widgets; + +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import org.apache.commons.lang3.StringUtils; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class AndroidOverrideWidgetTest implements WidgetTest{ + + private static AndroidDriver driver; + private static AppiumDriverLocalService service; + private static RottenTomatoes rottenTomatoes; + + @BeforeClass + public static void beforeClass() throws Exception { + service = AppiumDriverLocalService.buildDefaultService(); + service.start(); + + File appDir = new File("src/test/java/io/appium/java_client"); + File app = new File(appDir, "android-rottentomatoes-demo-debug.apk"); + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + driver = new AndroidDriver(service.getUrl(), capabilities); + + rottenTomatoes = new RottenTomatoes(); + PageFactory.initElements(new AppiumFieldDecorator(driver, new TimeOutDuration(5, TimeUnit.SECONDS)), rottenTomatoes); + } + + @Before + public void setUp() throws Exception { + if (driver != null) + driver.startActivity("com.codepath.example.rottentomatoes", "BoxOfficeActivity"); + } + + @AfterClass + public static void afterClass() throws Exception { + if (driver != null) + driver.quit(); + + if (service != null) + service.stop(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoes.getSimpleMovieCount() == 10); + Movie movie = rottenTomatoes.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoes.getAnnotatedMovieCount() == 10); + Movie movie = rottenTomatoes.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoes.getExtendeddMovieCount() == 10); + Movie movie = rottenTomatoes.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + try { + assertTrue(rottenTomatoes.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoes.getASimpleMovie(0).goToReview(); + + try { + rottenTomatoes.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/HtmlOverrideWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/HtmlOverrideWidgetTest.java new file mode 100644 index 000000000..392f3be28 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/HtmlOverrideWidgetTest.java @@ -0,0 +1,110 @@ +package io.appium.java_client.pagefactory_tests.widgets; + +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import org.apache.commons.lang3.StringUtils; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class HtmlOverrideWidgetTest implements WidgetTest{ + + private static FirefoxDriver driver; + private static RottenTomatoes rottenTomatoes; + + @BeforeClass + public static void beforeClass() throws Exception { + driver = new FirefoxDriver(); + driver.get("file:///" + new File("src/test/java/io/appium/java_client/RottenTomatoesSnapshot.html").getAbsolutePath()); + + rottenTomatoes = new RottenTomatoes(); + PageFactory.initElements(new AppiumFieldDecorator(driver, new TimeOutDuration(5, TimeUnit.SECONDS)), rottenTomatoes); + } + + @Before + public void setUp() throws Exception { + if (driver != null) + driver.navigate().back(); + } + + @AfterClass + public static void afterClass() throws Exception { + if (driver != null) + driver.quit(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoes.getSimpleMovieCount() == 30); + Movie movie = rottenTomatoes.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoes.getAnnotatedMovieCount() == 30); + Movie movie = rottenTomatoes.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoes.getExtendeddMovieCount() == 30); + Movie movie = rottenTomatoes.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + try { + assertTrue(rottenTomatoes.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoes.getASimpleMovie(0).goToReview(); + + try { + rottenTomatoes.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/IOSOverrideWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/IOSOverrideWidgetTest.java new file mode 100644 index 000000000..133e12735 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/IOSOverrideWidgetTest.java @@ -0,0 +1,124 @@ +package io.appium.java_client.pagefactory_tests.widgets; + +import io.appium.java_client.ios.IOSDriver; +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import org.apache.commons.lang3.StringUtils; +import org.junit.*; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class IOSOverrideWidgetTest implements WidgetTest{ + + private IOSDriver driver; + private static AppiumDriverLocalService service; + private static RottenTomatoes rottenTomatoes; + + @BeforeClass + public static void beforeClass() throws Exception { + service = AppiumDriverLocalService.buildDefaultService(); + service.start(); + } + + @Before + public void setUp() throws Exception { + File appDir = new File("src/test/java/io/appium/java_client"); + File app = new File(appDir, "RottenTomatoes.zip"); + + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, ""); + capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "7.1"); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone Simulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + driver = new IOSDriver<>(service.getUrl(), capabilities); + + rottenTomatoes = new RottenTomatoes(); + PageFactory.initElements(new AppiumFieldDecorator(driver, new TimeOutDuration(5, TimeUnit.SECONDS)), rottenTomatoes); + } + + @After + public void tearDown() { + if (driver != null) + driver.quit(); + } + + @AfterClass + public static void afterClass() throws Exception { + if (service != null) + service.stop(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoes.getSimpleMovieCount() == 10); + Movie movie = rottenTomatoes.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoes.getAnnotatedMovieCount() == 10); + Movie movie = rottenTomatoes.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoes.getExtendeddMovieCount() == 10); + Movie movie = rottenTomatoes.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + try { + assertTrue(rottenTomatoes.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoes.getASimpleMovie(0).goToReview(); + + try { + rottenTomatoes.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/Movie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/Movie.java new file mode 100644 index 000000000..b4860e45c --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/Movie.java @@ -0,0 +1,18 @@ +package io.appium.java_client.pagefactory_tests.widgets; + +import io.appium.java_client.pagefactory.Widget; +import org.openqa.selenium.WebElement; + +public abstract class Movie extends Widget{ + protected Movie(WebElement element) { + super(element); + } + + public abstract String title(); + + public abstract String score(); + + public abstract Object getPoster(); + + public abstract void goToReview(); +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/Movies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/Movies.java new file mode 100644 index 000000000..15606b1d1 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/Movies.java @@ -0,0 +1,15 @@ +package io.appium.java_client.pagefactory_tests.widgets; + +import io.appium.java_client.pagefactory.Widget; +import org.openqa.selenium.WebElement; + +public abstract class Movies extends Widget{ + + protected Movies(WebElement element) { + super(element); + } + + public abstract int getMovieCount(); + + public abstract Movie getMovie(int index); +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/Review.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/Review.java new file mode 100644 index 000000000..1f2e16f53 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/Review.java @@ -0,0 +1,19 @@ +package io.appium.java_client.pagefactory_tests.widgets; + +import io.appium.java_client.pagefactory.Widget; +import org.openqa.selenium.WebElement; + +public abstract class Review extends Widget{ + + protected Review(WebElement element) { + super(element); + } + + public abstract String title(); + + public abstract String score(); + + public abstract String info(); + + public abstract Object getPoster(); +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/RottenTomatoes.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/RottenTomatoes.java new file mode 100644 index 000000000..8e7270dbf --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/RottenTomatoes.java @@ -0,0 +1,150 @@ +package io.appium.java_client.pagefactory_tests.widgets; + + +import io.appium.java_client.pagefactory.*; +import io.appium.java_client.pagefactory_tests.widgets.android.annotated.AnnotatedAndroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.android.annotated.AnnotatedAndroidReview; +import io.appium.java_client.pagefactory_tests.widgets.android.extended.ExtendedAndroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.android.extended.ExtendedAndroidReview; +import io.appium.java_client.pagefactory_tests.widgets.android.simple.AndroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.android.simple.AndroidReview; +import io.appium.java_client.pagefactory_tests.widgets.html.annotated.AnnotatedHtmlMovies; +import io.appium.java_client.pagefactory_tests.widgets.html.annotated.AnnotatedHtmlReview; +import io.appium.java_client.pagefactory_tests.widgets.html.extended.ExtendedHtmlMovies; +import io.appium.java_client.pagefactory_tests.widgets.html.extended.ExtendedHtmlReview; +import io.appium.java_client.pagefactory_tests.widgets.html.simple.HtmlMovies; +import io.appium.java_client.pagefactory_tests.widgets.html.simple.HtmlReview; +import io.appium.java_client.pagefactory_tests.widgets.ios.annotated.AnnotatedIOSMovie; +import io.appium.java_client.pagefactory_tests.widgets.ios.annotated.AnnotatedIOSMovies; +import io.appium.java_client.pagefactory_tests.widgets.ios.annotated.AnnotatedIOSReview; +import io.appium.java_client.pagefactory_tests.widgets.ios.extended.ExtendedIOSMovies; +import io.appium.java_client.pagefactory_tests.widgets.ios.extended.ExtendedIOSReview; +import io.appium.java_client.pagefactory_tests.widgets.ios.simple.IOSMovies; +import io.appium.java_client.pagefactory_tests.widgets.ios.simple.IOSReview; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.annotated.AnnotatedSelendroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.annotated.AnnotatedSelendroidReview; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.extended.ExtendedSelendroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.extended.ExtendedSelendroidReview; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.simple.SelendroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.simple.SelendroidReview; +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.support.FindBy; + +import static junit.framework.Assert.assertTrue; + +public class RottenTomatoes { + + @FindBy(id = "movies-collection") + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/lvMovies") + @SelendroidFindBy(id = "lvMovies") + @iOSFindBy(className = "UIATableView") + @OverrideWidget(html = HtmlMovies.class, selendroid = SelendroidMovies.class, + androidUIAutomator = AndroidMovies.class, iOSUIAutomation = IOSMovies.class) + private Movies simpleMovies; + + + @AndroidFindBys({@AndroidFindBy(id = "android:id/content"), + @AndroidFindBy(className = "android.widget.RelativeLayout")}) + @FindBy(id = "main_container") + @SelendroidFindBy(className = "android.widget.RelativeLayout") + @iOSFindBy(className = "UIAWindow") + @OverrideWidget(html = HtmlReview.class, selendroid = SelendroidReview.class, + androidUIAutomator = AndroidReview.class, iOSUIAutomation = IOSReview.class) + private Review simpleReview; + + @OverrideWidget(html = AnnotatedHtmlMovies.class, selendroid = AnnotatedSelendroidMovies.class, + androidUIAutomator = AnnotatedAndroidMovies.class, iOSUIAutomation = AnnotatedIOSMovies.class) + private Movies annotatedMovies; + + @OverrideWidget(html = AnnotatedHtmlReview.class, selendroid = AnnotatedSelendroidReview.class, + androidUIAutomator = AnnotatedAndroidReview.class, iOSUIAutomation = AnnotatedIOSReview.class) + private Review annotatedReview; + + @OverrideWidget(html = ExtendedHtmlMovies.class, selendroid = ExtendedSelendroidMovies.class, + androidUIAutomator = ExtendedAndroidMovies.class, iOSUIAutomation = ExtendedIOSMovies.class) + private Movies extendedMovies; + + @OverrideWidget(html = ExtendedHtmlReview.class, selendroid = ExtendedSelendroidReview.class, + androidUIAutomator = ExtendedAndroidReview.class, iOSUIAutomation = ExtendedIOSReview.class) + private Review extendedReview; + + + @FindBy(id = "Fake_ID_For_All_Platforms") + @AndroidFindBy(id = "Fake_ID_For_All_Platforms") + @SelendroidFindBy(id = "Fake_ID_For_All_Platforms") + @iOSFindBy(id = "Fake_ID_For_All_Platforms") + @OverrideWidget(html = ExtendedHtmlMovies.class, selendroid = ExtendedSelendroidMovies.class, + androidUIAutomator = ExtendedAndroidMovies.class, iOSUIAutomation = ExtendedIOSMovies.class) + private Movies fakeMovies; + + @FindBy(id = "Fake_ID_For_All_Platforms") + @AndroidFindBy(id = "Fake_ID_For_All_Platforms") + @SelendroidFindBy(id = "Fake_ID_For_All_Platforms") + @iOSFindBy(id = "Fake_ID_For_All_Platforms") + @OverrideWidget(html = ExtendedHtmlReview.class, selendroid = ExtendedSelendroidReview.class, + androidUIAutomator = ExtendedAndroidReview.class, iOSUIAutomation = ExtendedIOSReview.class) + private Review fakeReview; + + + + public int getSimpleMovieCount(){ + return simpleMovies.getMovieCount(); + } + + public Movie getASimpleMovie(int index){ + return simpleMovies.getMovie(index); + } + + public void checkSimpleReview(){ + assertTrue(!StringUtils.isBlank(simpleReview.title())); + assertTrue(!StringUtils.isBlank(simpleReview.score())); + assertTrue(!StringUtils.isBlank(simpleReview.info())); + assertTrue(simpleReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + public int getAnnotatedMovieCount(){ + return annotatedMovies.getMovieCount(); + } + + public Movie getAnAnnotatedMovie(int index){ + return annotatedMovies.getMovie(index); + } + + public void checkAnnotatedReview(){ + assertTrue(!StringUtils.isBlank(annotatedReview.title())); + assertTrue(!StringUtils.isBlank(annotatedReview.score())); + assertTrue(!StringUtils.isBlank(annotatedReview.info())); + assertTrue(annotatedReview.getPoster() != null); + } + ///////////////////////////////////////////////////////// + + public int getExtendeddMovieCount(){ + return extendedMovies.getMovieCount(); + } + + public Movie getAnExtendedMovie(int index){ + return extendedMovies.getMovie(index); + } + + public void checkExtendedReview(){ + assertTrue(!StringUtils.isBlank(extendedReview.title())); + assertTrue(!StringUtils.isBlank(extendedReview.score())); + assertTrue(!StringUtils.isBlank(extendedReview.info())); + assertTrue(extendedReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + + public int getFakedMovieCount(){ + return fakeMovies.getMovieCount(); + } + + public void checkFakeReview(){ + assertTrue(!StringUtils.isBlank(fakeReview.title())); + assertTrue(!StringUtils.isBlank(fakeReview.score())); + assertTrue(!StringUtils.isBlank(fakeReview.info())); + assertTrue(fakeReview.getPoster() != null); + } + +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/SelendroidOverrideWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/SelendroidOverrideWidgetTest.java new file mode 100644 index 000000000..fd567b463 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/SelendroidOverrideWidgetTest.java @@ -0,0 +1,129 @@ +package io.appium.java_client.pagefactory_tests.widgets; + +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import io.appium.java_client.service.local.flags.GeneralServerFlag; +import org.apache.commons.lang3.StringUtils; +import org.junit.*; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class SelendroidOverrideWidgetTest implements WidgetTest{ + private static int SELENDROID_PORT = 9999; + + private AndroidDriver driver; + private static AppiumDriverLocalService service; + private RottenTomatoes rottenTomatoes; + private TimeOutDuration duration; + + @BeforeClass + public static void beforeClass() throws Exception { + AppiumServiceBuilder builder = new AppiumServiceBuilder().withArgument(GeneralServerFlag.AUTOMATION_NAME, AutomationName.SELENDROID); + service = builder.build(); + service.start(); + } + + @Before + public void setUp() throws Exception { + File appDir = new File("src/test/java/io/appium/java_client"); + File app = new File(appDir, "android-rottentomatoes-demo-debug.apk"); + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + capabilities.setCapability(MobileCapabilityType.SELENDROID_PORT, SELENDROID_PORT); + driver = new AndroidDriver(service.getUrl(), capabilities); + + duration = new TimeOutDuration(20, TimeUnit.SECONDS); + rottenTomatoes = new RottenTomatoes(); + PageFactory.initElements(new AppiumFieldDecorator(driver, duration), rottenTomatoes); + } + + @After + public void tearDown() throws Exception { + driver.quit(); + } + + @AfterClass + public static void afterClass() throws Exception { + if (service != null) + service.stop(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoes.getSimpleMovieCount() == 10); + Movie movie = rottenTomatoes.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + rottenTomatoes.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoes.getAnnotatedMovieCount() == 10); + Movie movie = rottenTomatoes.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + rottenTomatoes.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoes.getExtendeddMovieCount() == 10); + Movie movie = rottenTomatoes.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + rottenTomatoes.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + duration.setTime(5); + try { + assertTrue(rottenTomatoes.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoes.getASimpleMovie(0).goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + try { + rottenTomatoes.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/WidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/WidgetTest.java new file mode 100644 index 000000000..80d1c5c4e --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/WidgetTest.java @@ -0,0 +1,13 @@ +package io.appium.java_client.pagefactory_tests.widgets; + +public interface WidgetTest { + + public void checkACommonWidget(); + + public void checkAnAnnotatedWidget(); + + public void checkAnExtendedWidget(); + + public void checkTheLocatorOverridingOnAWidget(); + +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/AndroidWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/AndroidWidgetTest.java new file mode 100644 index 000000000..df1642eb5 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/AndroidWidgetTest.java @@ -0,0 +1,126 @@ +package io.appium.java_client.pagefactory_tests.widgets.android; + +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.WidgetTest; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import org.apache.commons.lang3.StringUtils; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class AndroidWidgetTest implements WidgetTest{ + + private static AndroidDriver driver; + private static AppiumDriverLocalService service; + private static RottenTomatoesApp rottenTomatoesApp; + + @BeforeClass + public static void beforeClass() throws Exception { + service = AppiumDriverLocalService.buildDefaultService(); + service.start(); + + File appDir = new File("src/test/java/io/appium/java_client"); + File app = new File(appDir, "android-rottentomatoes-demo-debug.apk"); + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + driver = new AndroidDriver(service.getUrl(), capabilities); + + rottenTomatoesApp = new RottenTomatoesApp(); + PageFactory.initElements(new AppiumFieldDecorator(driver, new TimeOutDuration(5, TimeUnit.SECONDS)), rottenTomatoesApp); + } + + @Before + public void setUp() throws Exception { + if (driver != null) + driver.startActivity("com.codepath.example.rottentomatoes", "BoxOfficeActivity"); + } + + @AfterClass + public static void afterClass() throws Exception { + if (driver != null) + driver.quit(); + + if (service != null) + service.stop(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoesApp.getSimpleMovieCount() == 10); + Movie movie = rottenTomatoesApp.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoesApp.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoesApp.getAnnotatedMovieCount() == 10); + Movie movie = rottenTomatoesApp.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoesApp.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoesApp.getExtendeddMovieCount() == 10); + Movie movie = rottenTomatoesApp.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoesApp.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + try { + assertTrue(rottenTomatoesApp.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoesApp.getASimpleMovie(0).goToReview(); + + try { + rottenTomatoesApp.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/RottenTomatoesApp.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/RottenTomatoesApp.java new file mode 100644 index 000000000..5376388e9 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/RottenTomatoesApp.java @@ -0,0 +1,103 @@ +package io.appium.java_client.pagefactory_tests.widgets.android; + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory.AndroidFindBys; +import io.appium.java_client.pagefactory_tests.widgets.android.annotated.AnnotatedAndroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.android.annotated.AnnotatedAndroidReview; +import io.appium.java_client.pagefactory_tests.widgets.android.extended.ExtendedAndroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.android.extended.ExtendedAndroidReview; +import io.appium.java_client.pagefactory_tests.widgets.android.simple.AndroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.android.simple.AndroidReview; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import org.apache.commons.lang3.StringUtils; + +import static junit.framework.Assert.assertTrue; + +/** + * This is the example of page object with declared Widgets + * instead of WebElement + */ +public class RottenTomatoesApp { + + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/lvMovies") + private AndroidMovies simpleMovies; + + @AndroidFindBys({@AndroidFindBy(id = "android:id/content"), + @AndroidFindBy(className = "android.widget.RelativeLayout")}) + private AndroidReview simpleReview; + + private AnnotatedAndroidMovies annotatedAndroidMovies; + + private AnnotatedAndroidReview annotatedAndroidReview; + + private ExtendedAndroidMovies extendedAndroidMovies; + + private ExtendedAndroidReview extendedAndroidReview; + + @AndroidFindBy(id = "fakeId") + private ExtendedAndroidMovies fakeMovies; + + @AndroidFindBy(id = "fakeId") + private ExtendedAndroidReview fakeReview; + + + public int getSimpleMovieCount(){ + return simpleMovies.getMovieCount(); + } + + public Movie getASimpleMovie(int index){ + return simpleMovies.getMovie(index); + } + + public void checkSimpleReview(){ + assertTrue(!StringUtils.isBlank(simpleReview.title())); + assertTrue(!StringUtils.isBlank(simpleReview.score())); + assertTrue(!StringUtils.isBlank(simpleReview.info())); + assertTrue(simpleReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + public int getAnnotatedMovieCount(){ + return annotatedAndroidMovies.getMovieCount(); + } + + public Movie getAnAnnotatedMovie(int index){ + return annotatedAndroidMovies.getMovie(index); + } + + public void checkAnnotatedReview(){ + assertTrue(!StringUtils.isBlank(annotatedAndroidReview.title())); + assertTrue(!StringUtils.isBlank(annotatedAndroidReview.score())); + assertTrue(!StringUtils.isBlank(annotatedAndroidReview.info())); + assertTrue(annotatedAndroidReview.getPoster() != null); + } + ///////////////////////////////////////////////////////// + + public int getExtendeddMovieCount(){ + return extendedAndroidMovies.getMovieCount(); + } + + public Movie getAnExtendedMovie(int index){ + return extendedAndroidMovies.getMovie(index); + } + + public void checkExtendedReview(){ + assertTrue(!StringUtils.isBlank(extendedAndroidReview.title())); + assertTrue(!StringUtils.isBlank(extendedAndroidReview.score())); + assertTrue(!StringUtils.isBlank(extendedAndroidReview.info())); + assertTrue(extendedAndroidReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + + public int getFakedMovieCount(){ + return fakeMovies.getMovieCount(); + } + + public void checkFakeReview(){ + assertTrue(!StringUtils.isBlank(fakeReview.title())); + assertTrue(!StringUtils.isBlank(fakeReview.score())); + assertTrue(!StringUtils.isBlank(fakeReview.info())); + assertTrue(fakeReview.getPoster() != null); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/annotated/AnnotatedAndroidMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/annotated/AnnotatedAndroidMovie.java new file mode 100644 index 000000000..f243855e8 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/annotated/AnnotatedAndroidMovie.java @@ -0,0 +1,12 @@ +package io.appium.java_client.pagefactory_tests.widgets.android.annotated; + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.android.simple.AndroidMovie; +import org.openqa.selenium.WebElement; + +@AndroidFindBy(className = "android.widget.RelativeLayout") +public class AnnotatedAndroidMovie extends AndroidMovie{ + protected AnnotatedAndroidMovie(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/annotated/AnnotatedAndroidMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/annotated/AnnotatedAndroidMovies.java new file mode 100644 index 000000000..7bfebc6f6 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/annotated/AnnotatedAndroidMovies.java @@ -0,0 +1,33 @@ +package io.appium.java_client.pagefactory_tests.widgets.android.annotated; + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.Movies; +import org.openqa.selenium.WebElement; + +import java.util.List; + +@AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/lvMovies") +public class AnnotatedAndroidMovies extends Movies{ + + List movieList; + + /* + There could be additional behavior. + But for test it is enough. + */ + + protected AnnotatedAndroidMovies(WebElement element) { + super(element); + } + + @Override + public int getMovieCount() { + return movieList.size(); + } + + @Override + public Movie getMovie(int index) { + return movieList.get(index); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/annotated/AnnotatedAndroidReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/annotated/AnnotatedAndroidReview.java new file mode 100644 index 000000000..64b60def6 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/annotated/AnnotatedAndroidReview.java @@ -0,0 +1,15 @@ +package io.appium.java_client.pagefactory_tests.widgets.android.annotated; + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory.AndroidFindBys; +import io.appium.java_client.pagefactory_tests.widgets.android.simple.AndroidReview; +import org.openqa.selenium.WebElement; + +@AndroidFindBys({@AndroidFindBy(id = "android:id/content"), + @AndroidFindBy(className = "android.widget.RelativeLayout")}) +public class AnnotatedAndroidReview extends AndroidReview{ + + protected AnnotatedAndroidReview(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/extended/ExtendedAndroidMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/extended/ExtendedAndroidMovies.java new file mode 100644 index 000000000..f1cbf77a5 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/extended/ExtendedAndroidMovies.java @@ -0,0 +1,11 @@ +package io.appium.java_client.pagefactory_tests.widgets.android.extended; + +import io.appium.java_client.pagefactory_tests.widgets.android.annotated.AnnotatedAndroidMovies; +import org.openqa.selenium.WebElement; + +public class ExtendedAndroidMovies extends AnnotatedAndroidMovies{ + + protected ExtendedAndroidMovies(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/extended/ExtendedAndroidReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/extended/ExtendedAndroidReview.java new file mode 100644 index 000000000..4eb0c1863 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/extended/ExtendedAndroidReview.java @@ -0,0 +1,11 @@ +package io.appium.java_client.pagefactory_tests.widgets.android.extended; + +import io.appium.java_client.pagefactory_tests.widgets.android.annotated.AnnotatedAndroidReview; +import org.openqa.selenium.WebElement; + +public class ExtendedAndroidReview extends AnnotatedAndroidReview{ + + protected ExtendedAndroidReview(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/simple/AndroidMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/simple/AndroidMovie.java new file mode 100644 index 000000000..cc86daf1c --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/simple/AndroidMovie.java @@ -0,0 +1,42 @@ +package io.appium.java_client.pagefactory_tests.widgets.android.simple; + +import io.appium.java_client.android.AndroidElement; +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import org.openqa.selenium.WebElement; + +public class AndroidMovie extends Movie{ + + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/tvTitle") + private AndroidElement title; + + @AndroidFindBy(uiAutomator = "resourceId(\"com.codepath.example.rottentomatoes:id/tvCriticsScore\")") + private AndroidElement score; + + @AndroidFindBy(accessibility = "poster image") + private AndroidElement poster; + + protected AndroidMovie(WebElement element) { + super(element); + } + + @Override + public String title() { + return title.getText(); + } + + @Override + public String score() { + return score.getText(); + } + + @Override + public Object getPoster() { + return poster.getSize(); + } + + @Override + public void goToReview() { + ((AndroidElement) getWrappedElement()).tap(1, 1500); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/simple/AndroidMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/simple/AndroidMovies.java new file mode 100644 index 000000000..9380cce58 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/simple/AndroidMovies.java @@ -0,0 +1,34 @@ +package io.appium.java_client.pagefactory_tests.widgets.android.simple; + + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.Movies; +import org.openqa.selenium.WebElement; + +import java.util.List; + +public class AndroidMovies extends Movies{ + + @AndroidFindBy(className = "android.widget.RelativeLayout") + List movieList; + + /* + There could be additional behavior. + But for test it is enough. + */ + + protected AndroidMovies(WebElement element) { + super(element); + } + + @Override + public int getMovieCount() { + return movieList.size(); + } + + @Override + public Movie getMovie(int index) { + return movieList.get(index); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/simple/AndroidReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/simple/AndroidReview.java new file mode 100644 index 000000000..f5365febe --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/android/simple/AndroidReview.java @@ -0,0 +1,47 @@ +package io.appium.java_client.pagefactory_tests.widgets.android.simple; + + +import io.appium.java_client.android.AndroidElement; +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Review; +import org.openqa.selenium.WebElement; + +public class AndroidReview extends Review{ + + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/tvTitle") + private AndroidElement title; + + @AndroidFindBy(uiAutomator = "resourceId(\"com.codepath.example.rottentomatoes:id/tvCriticsScore\")") + private AndroidElement score; + + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/tvSynopsis") + private AndroidElement synopsis; + + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/ivPosterImage") + private AndroidElement poster; + + + protected AndroidReview(WebElement element) { + super(element); + } + + @Override + public String title() { + return title.getText(); + } + + @Override + public String score() { + return score.getText(); + } + + @Override + public String info() { + return synopsis.getText(); + } + + @Override + public Object getPoster() { + return poster.getSize(); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/AndroidCombinedWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/AndroidCombinedWidgetTest.java new file mode 100644 index 000000000..31c7f09bf --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/AndroidCombinedWidgetTest.java @@ -0,0 +1,126 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined; + +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.WidgetTest; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import org.apache.commons.lang3.StringUtils; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class AndroidCombinedWidgetTest implements WidgetTest{ + + private static AndroidDriver driver; + private static AppiumDriverLocalService service; + private static RottenTomatoesAppWithCombinedWidgets rottenTomatoes; + + @BeforeClass + public static void beforeClass() throws Exception { + service = AppiumDriverLocalService.buildDefaultService(); + service.start(); + + File appDir = new File("src/test/java/io/appium/java_client"); + File app = new File(appDir, "android-rottentomatoes-demo-debug.apk"); + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + driver = new AndroidDriver(service.getUrl(), capabilities); + + rottenTomatoes = new RottenTomatoesAppWithCombinedWidgets(); + PageFactory.initElements(new AppiumFieldDecorator(driver, new TimeOutDuration(5, TimeUnit.SECONDS)), rottenTomatoes); + } + + @Before + public void setUp() throws Exception { + if (driver != null) + driver.startActivity("com.codepath.example.rottentomatoes", "BoxOfficeActivity"); + } + + @AfterClass + public static void afterClass() throws Exception { + if (driver != null) + driver.quit(); + + if (service != null) + service.stop(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoes.getSimpleMovieCount() == 10); + Movie movie = rottenTomatoes.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoes.getAnnotatedMovieCount() == 10); + Movie movie = rottenTomatoes.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoes.getExtendeddMovieCount() == 10); + Movie movie = rottenTomatoes.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + try { + assertTrue(rottenTomatoes.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoes.getASimpleMovie(0).goToReview(); + + try { + rottenTomatoes.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/HtmlCombinedWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/HtmlCombinedWidgetTest.java new file mode 100644 index 000000000..6cd4bb147 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/HtmlCombinedWidgetTest.java @@ -0,0 +1,112 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined; + +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.WidgetTest; +import org.apache.commons.lang3.StringUtils; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class HtmlCombinedWidgetTest implements WidgetTest{ + + private static FirefoxDriver driver; + private static RottenTomatoesAppWithCombinedWidgets rottenTomatoes; + + @BeforeClass + public static void beforeClass() throws Exception { + driver = new FirefoxDriver(); + driver.get("file:///" + new File("src/test/java/io/appium/java_client/RottenTomatoesSnapshot.html").getAbsolutePath()); + + rottenTomatoes = new RottenTomatoesAppWithCombinedWidgets(); + PageFactory.initElements(new AppiumFieldDecorator(driver, new TimeOutDuration(5, TimeUnit.SECONDS)), rottenTomatoes); + } + + @Before + public void setUp() throws Exception { + if (driver != null) + driver.navigate().back(); + } + + @AfterClass + public static void afterClass() throws Exception { + if (driver != null) + driver.quit(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoes.getSimpleMovieCount() == 30); + Movie movie = rottenTomatoes.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoes.getAnnotatedMovieCount() == 30); + Movie movie = rottenTomatoes.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoes.getExtendeddMovieCount() == 30); + Movie movie = rottenTomatoes.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoes.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + try { + assertTrue(rottenTomatoes.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoes.getASimpleMovie(0).goToReview(); + + try { + rottenTomatoes.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/RottenTomatoesAppWithCombinedWidgets.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/RottenTomatoesAppWithCombinedWidgets.java new file mode 100644 index 000000000..23ffd9d72 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/RottenTomatoesAppWithCombinedWidgets.java @@ -0,0 +1,111 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined; + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory.AndroidFindBys; +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.combined.annotated.AnnotatedCombinedMovies; +import io.appium.java_client.pagefactory_tests.widgets.combined.annotated.AnnotatedCombinedReview; +import io.appium.java_client.pagefactory_tests.widgets.combined.extended.ExtendedCombinedMovies; +import io.appium.java_client.pagefactory_tests.widgets.combined.extended.ExtendedCombinedReview; +import io.appium.java_client.pagefactory_tests.widgets.combined.simple.CombinedMovies; +import io.appium.java_client.pagefactory_tests.widgets.combined.simple.CombinedReview; +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.support.FindBy; + +import static junit.framework.Assert.assertTrue; + +/** + * This is the example of page object with declared Widgets + * instead of WebElement + */ +public class RottenTomatoesAppWithCombinedWidgets { + + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/lvMovies") + @SelendroidFindBy(id = "lvMovies") + @FindBy(id = "movies-collection") + private CombinedMovies simpleMovies; + + @AndroidFindBys({@AndroidFindBy(id = "android:id/content"), + @AndroidFindBy(className = "android.widget.RelativeLayout")}) + @FindBy(id = "main_container") + @SelendroidFindBy(className = "android.widget.RelativeLayout") + private CombinedReview simpleReview; + + private AnnotatedCombinedMovies annotatedCombinedMovies; + + private AnnotatedCombinedReview annotatedCombinedReview; + + private ExtendedCombinedMovies extendedCombinedMovies; + + private ExtendedCombinedReview extendedCombinedReview; + + @AndroidFindBy(id = "fakeId") + @FindBy(id = "fakeId") + private ExtendedCombinedMovies fakeMovies; + + @AndroidFindBy(id = "fakeId") + @FindBy(id = "fakeId") + private ExtendedCombinedReview fakeReview; + + + public int getSimpleMovieCount(){ + return simpleMovies.getMovieCount(); + } + + public Movie getASimpleMovie(int index){ + return simpleMovies.getMovie(index); + } + + public void checkSimpleReview(){ + assertTrue(!StringUtils.isBlank(simpleReview.title())); + assertTrue(!StringUtils.isBlank(simpleReview.score())); + assertTrue(!StringUtils.isBlank(simpleReview.info())); + assertTrue(simpleReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + public int getAnnotatedMovieCount(){ + return annotatedCombinedMovies.getMovieCount(); + } + + public Movie getAnAnnotatedMovie(int index){ + return annotatedCombinedMovies.getMovie(index); + } + + public void checkAnnotatedReview(){ + assertTrue(!StringUtils.isBlank(annotatedCombinedReview.title())); + assertTrue(!StringUtils.isBlank(annotatedCombinedReview.score())); + assertTrue(!StringUtils.isBlank(annotatedCombinedReview.info())); + assertTrue(annotatedCombinedReview.getPoster() != null); + } + ///////////////////////////////////////////////////////// + + public int getExtendeddMovieCount(){ + return extendedCombinedMovies.getMovieCount(); + } + + public Movie getAnExtendedMovie(int index){ + return extendedCombinedMovies.getMovie(index); + } + + public void checkExtendedReview(){ + assertTrue(!StringUtils.isBlank(extendedCombinedReview.title())); + assertTrue(!StringUtils.isBlank(extendedCombinedReview.score())); + assertTrue(!StringUtils.isBlank(extendedCombinedReview.info())); + assertTrue(extendedCombinedReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + + public int getFakedMovieCount(){ + return fakeMovies.getMovieCount(); + } + + public void checkFakeReview(){ + assertTrue(!StringUtils.isBlank(fakeReview.title())); + assertTrue(!StringUtils.isBlank(fakeReview.score())); + assertTrue(!StringUtils.isBlank(fakeReview.info())); + assertTrue(fakeReview.getPoster() != null); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/SelendroidCombinedWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/SelendroidCombinedWidgetTest.java new file mode 100644 index 000000000..7ddef4174 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/SelendroidCombinedWidgetTest.java @@ -0,0 +1,131 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined; + +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.WidgetTest; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import io.appium.java_client.service.local.flags.GeneralServerFlag; +import org.apache.commons.lang3.StringUtils; +import org.junit.*; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class SelendroidCombinedWidgetTest implements WidgetTest{ + private static int SELENDROID_PORT = 9999; + + private AndroidDriver driver; + private static AppiumDriverLocalService service; + private RottenTomatoesAppWithCombinedWidgets rottenTomatoes; + private TimeOutDuration duration; + + @BeforeClass + public static void beforeClass() throws Exception { + AppiumServiceBuilder builder = new AppiumServiceBuilder().withArgument(GeneralServerFlag.AUTOMATION_NAME, AutomationName.SELENDROID); + service = builder.build(); + service.start(); + } + + @Before + public void setUp() throws Exception { + File appDir = new File("src/test/java/io/appium/java_client"); + File app = new File(appDir, "android-rottentomatoes-demo-debug.apk"); + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + capabilities.setCapability(MobileCapabilityType.SELENDROID_PORT, SELENDROID_PORT); + driver = new AndroidDriver(service.getUrl(), capabilities); + + duration = new TimeOutDuration(20, TimeUnit.SECONDS); + rottenTomatoes = new RottenTomatoesAppWithCombinedWidgets(); + PageFactory.initElements(new AppiumFieldDecorator(driver, duration), rottenTomatoes); + } + + @After + public void tearDown() throws Exception { + driver.quit(); + } + + @AfterClass + public static void afterClass() throws Exception { + if (service != null) + service.stop(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoes.getSimpleMovieCount() == 10); + Movie movie = rottenTomatoes.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + rottenTomatoes.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoes.getAnnotatedMovieCount() == 10); + Movie movie = rottenTomatoes.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + rottenTomatoes.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoes.getExtendeddMovieCount() == 10); + Movie movie = rottenTomatoes.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + rottenTomatoes.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + duration.setTime(5); + try { + assertTrue(rottenTomatoes.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoes.getASimpleMovie(0).goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + try { + rottenTomatoes.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/annotated/AnnotatedCombinedMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/annotated/AnnotatedCombinedMovie.java new file mode 100644 index 000000000..7f379b667 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/annotated/AnnotatedCombinedMovie.java @@ -0,0 +1,14 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined.annotated; + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.combined.simple.CombinedMovie; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +@AndroidFindBy(className = "android.widget.RelativeLayout") +@FindBy(className = "mb-movie") +public class AnnotatedCombinedMovie extends CombinedMovie { + protected AnnotatedCombinedMovie(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/annotated/AnnotatedCombinedMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/annotated/AnnotatedCombinedMovies.java new file mode 100644 index 000000000..55cc5a9cf --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/annotated/AnnotatedCombinedMovies.java @@ -0,0 +1,38 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined.annotated; + + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.Movies; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +import java.util.List; + +@AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/lvMovies") +@SelendroidFindBy(id = "lvMovies") +@FindBy(id = "movies-collection") +public class AnnotatedCombinedMovies extends Movies{ + + List movieList; + + /* + There could be additional behavior. + But for test it is enough. + */ + + protected AnnotatedCombinedMovies(WebElement element) { + super(element); + } + + @Override + public int getMovieCount() { + return movieList.size(); + } + + @Override + public Movie getMovie(int index) { + return movieList.get(index); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/annotated/AnnotatedCombinedReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/annotated/AnnotatedCombinedReview.java new file mode 100644 index 000000000..201097d2e --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/annotated/AnnotatedCombinedReview.java @@ -0,0 +1,20 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined.annotated; + + + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory.AndroidFindBys; +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.combined.simple.CombinedReview; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +@FindBy(id = "main_container") +@SelendroidFindBy(className = "android.widget.RelativeLayout") +@AndroidFindBys({@AndroidFindBy(id = "android:id/content"), + @AndroidFindBy(className = "android.widget.RelativeLayout")}) +public class AnnotatedCombinedReview extends CombinedReview { + protected AnnotatedCombinedReview(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/extended/ExtendedCombinedMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/extended/ExtendedCombinedMovies.java new file mode 100644 index 000000000..63d28cac4 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/extended/ExtendedCombinedMovies.java @@ -0,0 +1,11 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined.extended; + + +import io.appium.java_client.pagefactory_tests.widgets.combined.annotated.AnnotatedCombinedMovies; +import org.openqa.selenium.WebElement; + +public class ExtendedCombinedMovies extends AnnotatedCombinedMovies { + protected ExtendedCombinedMovies(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/extended/ExtendedCombinedReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/extended/ExtendedCombinedReview.java new file mode 100644 index 000000000..ad109b5e1 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/extended/ExtendedCombinedReview.java @@ -0,0 +1,10 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined.extended; + +import io.appium.java_client.pagefactory_tests.widgets.combined.annotated.AnnotatedCombinedReview; +import org.openqa.selenium.WebElement; + +public class ExtendedCombinedReview extends AnnotatedCombinedReview{ + protected ExtendedCombinedReview(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/simple/CombinedMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/simple/CombinedMovie.java new file mode 100644 index 000000000..78ae85006 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/simple/CombinedMovie.java @@ -0,0 +1,55 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined.simple; + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebElement; +import org.openqa.selenium.support.FindBy; + +public class CombinedMovie extends Movie{ + + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/tvTitle") + @SelendroidFindBy(id = "tvTitle") + @FindBy(className = "movieTitle") + private RemoteWebElement title; + + @AndroidFindBy(uiAutomator = "resourceId(\"com.codepath.example.rottentomatoes:id/tvCriticsScore\")") + @FindBy(className = "tMeterScore") + @SelendroidFindBy(id = "tvCriticsScore") + private RemoteWebElement score; + + @AndroidFindBy(accessibility = "poster image") + @SelendroidFindBy(id = "ivPosterImage") + @FindBy(className = "poster_container") + private RemoteWebElement poster; + + @AndroidFindBy(accessibility = "poster image") + @SelendroidFindBy(id = "ivPosterImage") + @FindBy(xpath = ".//*[@class=\"movie_info\"]/a/h3") + private RemoteWebElement movieSwitcher; + + protected CombinedMovie(WebElement element) { + super(element); + } + + @Override + public String title() { + return title.getText(); + } + + @Override + public String score() { + return score.getText(); + } + + @Override + public Object getPoster() { + return poster.getSize(); + } + + @Override + public void goToReview() { + movieSwitcher.click(); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/simple/CombinedMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/simple/CombinedMovies.java new file mode 100644 index 000000000..58ba9fb7f --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/simple/CombinedMovies.java @@ -0,0 +1,37 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined.simple; + + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.Movies; +import io.appium.java_client.pagefactory_tests.widgets.android.simple.AndroidMovie; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +import java.util.List; + +public class CombinedMovies extends Movies{ + + @AndroidFindBy(className = "android.widget.RelativeLayout") + @FindBy(className = "mb-movie") + List movieList; + + /* + There could be additional behavior. + But for test it is enough. + */ + + protected CombinedMovies(WebElement element) { + super(element); + } + + @Override + public int getMovieCount() { + return movieList.size(); + } + + @Override + public Movie getMovie(int index) { + return movieList.get(index); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/simple/CombinedReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/simple/CombinedReview.java new file mode 100644 index 000000000..d4d463699 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/combined/simple/CombinedReview.java @@ -0,0 +1,56 @@ +package io.appium.java_client.pagefactory_tests.widgets.combined.simple; + + +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Review; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebElement; +import org.openqa.selenium.support.FindBy; + +public class CombinedReview extends Review{ + + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/tvTitle") + @FindBy(id = "movie-title") + @SelendroidFindBy(id = "tvTitle") + private RemoteWebElement title; + + @AndroidFindBy(uiAutomator = "resourceId(\"com.codepath.example.rottentomatoes:id/tvCriticsScore\")") + @SelendroidFindBy(id = "tvCriticsScore") + @FindBy(xpath = ".//*[@id=\"tomato_meter_link\"]//*[@itemprop=\"ratingValue\"]") + private RemoteWebElement score; + + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/tvSynopsis") + @SelendroidFindBy(id = "tvSynopsis") + private RemoteWebElement movieSynopsis; + + @AndroidFindBy(id = "com.codepath.example.rottentomatoes:id/ivPosterImage") + @SelendroidFindBy(id = "ivPosterImage") + @FindBy(className = "videoPic") + private RemoteWebElement poster; + + + protected CombinedReview(WebElement element) { + super(element); + } + + @Override + public String title() { + return title.getText(); + } + + @Override + public String score() { + return score.getText(); + } + + @Override + public String info() { + return movieSynopsis.getText(); + } + + @Override + public Object getPoster() { + return poster.getSize(); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/HtmlWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/HtmlWidgetTest.java new file mode 100644 index 000000000..efac13ec1 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/HtmlWidgetTest.java @@ -0,0 +1,112 @@ +package io.appium.java_client.pagefactory_tests.widgets.html; + +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.WidgetTest; +import org.apache.commons.lang3.StringUtils; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class HtmlWidgetTest implements WidgetTest{ + + private static FirefoxDriver driver; + private static RottenTomatoesSite rottenTomatoesSite; + + @BeforeClass + public static void beforeClass() throws Exception { + driver = new FirefoxDriver(); + driver.get("file:///" + new File("src/test/java/io/appium/java_client/RottenTomatoesSnapshot.html").getAbsolutePath()); + + rottenTomatoesSite = new RottenTomatoesSite(); + PageFactory.initElements(new AppiumFieldDecorator(driver, new TimeOutDuration(5, TimeUnit.SECONDS)), rottenTomatoesSite); + } + + @Before + public void setUp() throws Exception { + if (driver != null) + driver.navigate().back(); + } + + @AfterClass + public static void afterClass() throws Exception { + if (driver != null) + driver.quit(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoesSite.getSimpleMovieCount() == 30); + Movie movie = rottenTomatoesSite.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoesSite.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoesSite.getAnnotatedMovieCount() == 30); + Movie movie = rottenTomatoesSite.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoesSite.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoesSite.getExtendeddMovieCount() == 30); + Movie movie = rottenTomatoesSite.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoesSite.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + try { + assertTrue(rottenTomatoesSite.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoesSite.getASimpleMovie(0).goToReview(); + + try { + rottenTomatoesSite.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/RottenTomatoesSite.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/RottenTomatoesSite.java new file mode 100644 index 000000000..5f224061a --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/RottenTomatoesSite.java @@ -0,0 +1,101 @@ +package io.appium.java_client.pagefactory_tests.widgets.html; + +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.html.annotated.AnnotatedHtmlMovies; +import io.appium.java_client.pagefactory_tests.widgets.html.annotated.AnnotatedHtmlReview; +import io.appium.java_client.pagefactory_tests.widgets.html.extended.ExtendedHtmlMovies; +import io.appium.java_client.pagefactory_tests.widgets.html.extended.ExtendedHtmlReview; +import io.appium.java_client.pagefactory_tests.widgets.html.simple.HtmlMovies; +import io.appium.java_client.pagefactory_tests.widgets.html.simple.HtmlReview; +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.support.FindBy; + +import static junit.framework.Assert.assertTrue; + +/** + * This is the example of page object with declared Widgets + * instead of WebElement + */ +public class RottenTomatoesSite { + + @FindBy(id = "movies-collection") + private HtmlMovies simpleMovies; + + @FindBy(id = "main_container") + private HtmlReview simpleReview; + + private AnnotatedHtmlMovies annotatedHtmlMovies; + + private AnnotatedHtmlReview annotatedHtmlReview; + + private ExtendedHtmlMovies extendedHtmlMovies; + + private ExtendedHtmlReview extendedHtmlReview; + + @FindBy(id = "fakeId") + private ExtendedHtmlMovies fakeMovies; + + @FindBy(id = "fakeId") + private ExtendedHtmlReview fakeReview; + + + public int getSimpleMovieCount(){ + return simpleMovies.getMovieCount(); + } + + public Movie getASimpleMovie(int index){ + return simpleMovies.getMovie(index); + } + + public void checkSimpleReview(){ + assertTrue(!StringUtils.isBlank(simpleReview.title())); + assertTrue(!StringUtils.isBlank(simpleReview.score())); + assertTrue(!StringUtils.isBlank(simpleReview.info())); + assertTrue(simpleReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + public int getAnnotatedMovieCount(){ + return annotatedHtmlMovies.getMovieCount(); + } + + public Movie getAnAnnotatedMovie(int index){ + return annotatedHtmlMovies.getMovie(index); + } + + public void checkAnnotatedReview(){ + assertTrue(!StringUtils.isBlank(annotatedHtmlReview.title())); + assertTrue(!StringUtils.isBlank(annotatedHtmlReview.score())); + assertTrue(!StringUtils.isBlank(annotatedHtmlReview.info())); + assertTrue(annotatedHtmlReview.getPoster() != null); + } + ///////////////////////////////////////////////////////// + + public int getExtendeddMovieCount(){ + return extendedHtmlMovies.getMovieCount(); + } + + public Movie getAnExtendedMovie(int index){ + return extendedHtmlMovies.getMovie(index); + } + + public void checkExtendedReview(){ + assertTrue(!StringUtils.isBlank(extendedHtmlReview.title())); + assertTrue(!StringUtils.isBlank(extendedHtmlReview.score())); + assertTrue(!StringUtils.isBlank(extendedHtmlReview.info())); + assertTrue(extendedHtmlReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + + public int getFakedMovieCount(){ + return fakeMovies.getMovieCount(); + } + + public void checkFakeReview(){ + assertTrue(!StringUtils.isBlank(fakeReview.title())); + assertTrue(!StringUtils.isBlank(fakeReview.score())); + assertTrue(!StringUtils.isBlank(fakeReview.info())); + assertTrue(fakeReview.getPoster() != null); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/annotated/AnnotatedHtmlMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/annotated/AnnotatedHtmlMovie.java new file mode 100644 index 000000000..63aac8e51 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/annotated/AnnotatedHtmlMovie.java @@ -0,0 +1,13 @@ +package io.appium.java_client.pagefactory_tests.widgets.html.annotated; + +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.html.simple.HtmlMovie; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +@FindBy(className = "mb-movie") +public class AnnotatedHtmlMovie extends HtmlMovie { + protected AnnotatedHtmlMovie(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/annotated/AnnotatedHtmlMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/annotated/AnnotatedHtmlMovies.java new file mode 100644 index 000000000..f43ee6ad0 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/annotated/AnnotatedHtmlMovies.java @@ -0,0 +1,33 @@ +package io.appium.java_client.pagefactory_tests.widgets.html.annotated; + +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.Movies; +import io.appium.java_client.pagefactory_tests.widgets.html.simple.HtmlMovie; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +import java.util.List; + +@FindBy(id = "movies-collection") +public class AnnotatedHtmlMovies extends Movies{ + List movieList; + + /* + There could be additional behavior. + But for test it is enough. + */ + + protected AnnotatedHtmlMovies(WebElement element) { + super(element); + } + + @Override + public int getMovieCount() { + return movieList.size(); + } + + @Override + public Movie getMovie(int index) { + return movieList.get(index); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/annotated/AnnotatedHtmlReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/annotated/AnnotatedHtmlReview.java new file mode 100644 index 000000000..79273b292 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/annotated/AnnotatedHtmlReview.java @@ -0,0 +1,14 @@ +package io.appium.java_client.pagefactory_tests.widgets.html.annotated; + + +import io.appium.java_client.pagefactory_tests.widgets.Review; +import io.appium.java_client.pagefactory_tests.widgets.html.simple.HtmlReview; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +@FindBy(id = "main_container") +public class AnnotatedHtmlReview extends HtmlReview { + protected AnnotatedHtmlReview(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/extended/ExtendedHtmlMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/extended/ExtendedHtmlMovies.java new file mode 100644 index 000000000..f36d1a017 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/extended/ExtendedHtmlMovies.java @@ -0,0 +1,10 @@ +package io.appium.java_client.pagefactory_tests.widgets.html.extended; + +import io.appium.java_client.pagefactory_tests.widgets.html.annotated.AnnotatedHtmlMovies; +import org.openqa.selenium.WebElement; + +public class ExtendedHtmlMovies extends AnnotatedHtmlMovies { + protected ExtendedHtmlMovies(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/extended/ExtendedHtmlReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/extended/ExtendedHtmlReview.java new file mode 100644 index 000000000..ced4cb70a --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/extended/ExtendedHtmlReview.java @@ -0,0 +1,11 @@ +package io.appium.java_client.pagefactory_tests.widgets.html.extended; + + +import io.appium.java_client.pagefactory_tests.widgets.html.annotated.AnnotatedHtmlReview; +import org.openqa.selenium.WebElement; + +public class ExtendedHtmlReview extends AnnotatedHtmlReview { + protected ExtendedHtmlReview(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/simple/HtmlMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/simple/HtmlMovie.java new file mode 100644 index 000000000..ec0991e14 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/simple/HtmlMovie.java @@ -0,0 +1,46 @@ +package io.appium.java_client.pagefactory_tests.widgets.html.simple; + +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +public class HtmlMovie extends Movie{ + + @FindBy(className = "movieTitle") + private WebElement title; + + @FindBy(className = "tMeterScore") + private WebElement score; + + @FindBy(className = "poster_container") + private WebElement poster; + + @FindBy(xpath = ".//*[@class=\"movie_info\"]/a/h3") + private WebElement linkToMovie; + + + + protected HtmlMovie(WebElement element) { + super(element); + } + + @Override + public String title() { + return title.getText(); + } + + @Override + public String score() { + return score.getText(); + } + + @Override + public Object getPoster() { + return poster.getSize(); + } + + @Override + public void goToReview() { + linkToMovie.click(); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/simple/HtmlMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/simple/HtmlMovies.java new file mode 100644 index 000000000..e5dd55327 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/simple/HtmlMovies.java @@ -0,0 +1,33 @@ +package io.appium.java_client.pagefactory_tests.widgets.html.simple; + +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.Movies; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +import java.util.List; + +public class HtmlMovies extends Movies{ + + @FindBy(className = "mb-movie") + List movieList; + + /* + There could be additional behavior. + But for test it is enough. + */ + + protected HtmlMovies(WebElement element) { + super(element); + } + + @Override + public int getMovieCount() { + return movieList.size(); + } + + @Override + public Movie getMovie(int index) { + return movieList.get(index); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/simple/HtmlReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/simple/HtmlReview.java new file mode 100644 index 000000000..52da521eb --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/html/simple/HtmlReview.java @@ -0,0 +1,47 @@ +package io.appium.java_client.pagefactory_tests.widgets.html.simple; + + +import io.appium.java_client.android.AndroidElement; +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Review; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +public class HtmlReview extends Review{ + + @FindBy(id = "movie-title") + private WebElement title; + + @FindBy(xpath = ".//*[@id=\"tomato_meter_link\"]//*[@itemprop=\"ratingValue\"]") + private WebElement score; + + private WebElement movieSynopsis; + + @FindBy(className = "videoPic") + private WebElement poster; + + + protected HtmlReview(WebElement element) { + super(element); + } + + @Override + public String title() { + return title.getText(); + } + + @Override + public String score() { + return score.getText(); + } + + @Override + public String info() { + return movieSynopsis.getText(); + } + + @Override + public Object getPoster() { + return poster.getSize(); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/IOSWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/IOSWidgetTest.java new file mode 100644 index 000000000..0b6507315 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/IOSWidgetTest.java @@ -0,0 +1,126 @@ +package io.appium.java_client.pagefactory_tests.widgets.ios; + +import io.appium.java_client.ios.IOSDriver; +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.WidgetTest; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import org.apache.commons.lang3.StringUtils; +import org.junit.*; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class IOSWidgetTest implements WidgetTest{ + + private IOSDriver driver; + private static AppiumDriverLocalService service; + private RottenTomatoesIOSApp rottenTomatoesApp; + + @BeforeClass + public static void beforeClass() throws Exception { + service = AppiumDriverLocalService.buildDefaultService(); + service.start(); + } + + @Before + public void setUp() throws Exception { + File appDir = new File("src/test/java/io/appium/java_client"); + File app = new File(appDir, "RottenTomatoes.zip"); + + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, ""); + capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "7.1"); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone Simulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + driver = new IOSDriver<>(service.getUrl(), capabilities); + + rottenTomatoesApp = new RottenTomatoesIOSApp(); + PageFactory.initElements(new AppiumFieldDecorator(driver, new TimeOutDuration(5, TimeUnit.SECONDS)), rottenTomatoesApp); + } + + @After + public void tearDown() { + if (driver != null) + driver.quit(); + } + + @AfterClass + public static void afterClass() throws Exception { + if (service != null) + service.stop(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoesApp.getSimpleMovieCount() == 10); + Movie movie = rottenTomatoesApp.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoesApp.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoesApp.getAnnotatedMovieCount() == 10); + Movie movie = rottenTomatoesApp.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoesApp.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoesApp.getExtendeddMovieCount() == 10); + Movie movie = rottenTomatoesApp.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + + rottenTomatoesApp.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + try { + assertTrue(rottenTomatoesApp.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoesApp.getASimpleMovie(0).goToReview(); + + try { + rottenTomatoesApp.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/RottenTomatoesIOSApp.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/RottenTomatoesIOSApp.java new file mode 100644 index 000000000..4a5e86982 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/RottenTomatoesIOSApp.java @@ -0,0 +1,102 @@ +package io.appium.java_client.pagefactory_tests.widgets.ios; + + +import io.appium.java_client.pagefactory.iOSFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.ios.annotated.AnnotatedIOSMovies; +import io.appium.java_client.pagefactory_tests.widgets.ios.annotated.AnnotatedIOSReview; +import io.appium.java_client.pagefactory_tests.widgets.ios.extended.ExtendedIOSMovies; +import io.appium.java_client.pagefactory_tests.widgets.ios.extended.ExtendedIOSReview; +import io.appium.java_client.pagefactory_tests.widgets.ios.simple.IOSMovies; +import io.appium.java_client.pagefactory_tests.widgets.ios.simple.IOSReview; +import org.apache.commons.lang3.StringUtils; + +import static junit.framework.Assert.assertTrue; + +/** + * This is the example of page object with declared Widgets + * instead of WebElement + */ +public class RottenTomatoesIOSApp { + + @iOSFindBy(className = "UIATableView") + private IOSMovies simpleMovies; + + @iOSFindBy(className = "UIAWindow") + private IOSReview simpleReview; + + private AnnotatedIOSMovies annotatedIOSMovies; + + private AnnotatedIOSReview annotatedIOSReview; + + private ExtendedIOSMovies extendedIOSMovies; + + private ExtendedIOSReview extendedIOSReview; + + @iOSFindBy(id = "fakeId") + private ExtendedIOSMovies fakeMovies; + + @iOSFindBy(id = "fakeId") + private ExtendedIOSReview fakeReview; + + + public int getSimpleMovieCount(){ + return simpleMovies.getMovieCount(); + } + + public Movie getASimpleMovie(int index){ + return simpleMovies.getMovie(index); + } + + public void checkSimpleReview(){ + assertTrue(!StringUtils.isBlank(simpleReview.title())); + assertTrue(!StringUtils.isBlank(simpleReview.score())); + assertTrue(!StringUtils.isBlank(simpleReview.info())); + assertTrue(simpleReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + public int getAnnotatedMovieCount(){ + return annotatedIOSMovies.getMovieCount(); + } + + public Movie getAnAnnotatedMovie(int index){ + return annotatedIOSMovies.getMovie(index); + } + + public void checkAnnotatedReview(){ + assertTrue(!StringUtils.isBlank(annotatedIOSReview.title())); + assertTrue(!StringUtils.isBlank(annotatedIOSReview.score())); + assertTrue(!StringUtils.isBlank(annotatedIOSReview.info())); + assertTrue(annotatedIOSReview.getPoster() != null); + } + ///////////////////////////////////////////////////////// + + public int getExtendeddMovieCount(){ + return extendedIOSMovies.getMovieCount(); + } + + public Movie getAnExtendedMovie(int index){ + return extendedIOSMovies.getMovie(index); + } + + public void checkExtendedReview(){ + assertTrue(!StringUtils.isBlank(extendedIOSReview.title())); + assertTrue(!StringUtils.isBlank(extendedIOSReview.score())); + assertTrue(!StringUtils.isBlank(extendedIOSReview.info())); + assertTrue(extendedIOSReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + + public int getFakedMovieCount(){ + return fakeMovies.getMovieCount(); + } + + public void checkFakeReview(){ + assertTrue(!StringUtils.isBlank(fakeReview.title())); + assertTrue(!StringUtils.isBlank(fakeReview.score())); + assertTrue(!StringUtils.isBlank(fakeReview.info())); + assertTrue(fakeReview.getPoster() != null); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/annotated/AnnotatedIOSMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/annotated/AnnotatedIOSMovie.java new file mode 100644 index 000000000..9609f6f2e --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/annotated/AnnotatedIOSMovie.java @@ -0,0 +1,15 @@ +package io.appium.java_client.pagefactory_tests.widgets.ios.annotated; + +import io.appium.java_client.ios.IOSElement; +import io.appium.java_client.pagefactory.iOSFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.ios.simple.IOSMovie; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebElement; + +@iOSFindBy(className = "UIATableCell") +public class AnnotatedIOSMovie extends IOSMovie { + protected AnnotatedIOSMovie(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/annotated/AnnotatedIOSMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/annotated/AnnotatedIOSMovies.java new file mode 100644 index 000000000..c1680a13e --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/annotated/AnnotatedIOSMovies.java @@ -0,0 +1,33 @@ +package io.appium.java_client.pagefactory_tests.widgets.ios.annotated; + +import io.appium.java_client.pagefactory.iOSFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.Movies; +import org.openqa.selenium.WebElement; + +import java.util.List; + +@iOSFindBy(className = "UIATableView") +public class AnnotatedIOSMovies extends Movies{ + + List movieList; + + /* + There could be additional behavior. + But for test it is enough. + */ + + protected AnnotatedIOSMovies(WebElement element) { + super(element); + } + + @Override + public int getMovieCount() { + return movieList.size(); + } + + @Override + public Movie getMovie(int index) { + return movieList.get(index); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/annotated/AnnotatedIOSReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/annotated/AnnotatedIOSReview.java new file mode 100644 index 000000000..bd619ae92 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/annotated/AnnotatedIOSReview.java @@ -0,0 +1,16 @@ +package io.appium.java_client.pagefactory_tests.widgets.ios.annotated; + + +import io.appium.java_client.ios.IOSElement; +import io.appium.java_client.pagefactory.iOSFindBy; +import io.appium.java_client.pagefactory.iOSFindBys; +import io.appium.java_client.pagefactory_tests.widgets.Review; +import io.appium.java_client.pagefactory_tests.widgets.ios.simple.IOSReview; +import org.openqa.selenium.WebElement; + +@iOSFindBy(className = "UIAWindow") +public class AnnotatedIOSReview extends IOSReview { + protected AnnotatedIOSReview(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/extended/ExtendedIOSMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/extended/ExtendedIOSMovies.java new file mode 100644 index 000000000..55b23248c --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/extended/ExtendedIOSMovies.java @@ -0,0 +1,10 @@ +package io.appium.java_client.pagefactory_tests.widgets.ios.extended; + +import org.openqa.selenium.WebElement; +import io.appium.java_client.pagefactory_tests.widgets.ios.annotated.AnnotatedIOSMovies; + +public class ExtendedIOSMovies extends AnnotatedIOSMovies{ + protected ExtendedIOSMovies(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/extended/ExtendedIOSReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/extended/ExtendedIOSReview.java new file mode 100644 index 000000000..c18a233d1 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/extended/ExtendedIOSReview.java @@ -0,0 +1,11 @@ +package io.appium.java_client.pagefactory_tests.widgets.ios.extended; + + +import io.appium.java_client.pagefactory_tests.widgets.ios.annotated.AnnotatedIOSReview; +import org.openqa.selenium.WebElement; + +public class ExtendedIOSReview extends AnnotatedIOSReview { + protected ExtendedIOSReview(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSMovie.java new file mode 100644 index 000000000..3f72e0143 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSMovie.java @@ -0,0 +1,37 @@ +package io.appium.java_client.pagefactory_tests.widgets.ios.simple; + +import io.appium.java_client.ios.IOSElement; +import io.appium.java_client.pagefactory.iOSFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebElement; + +public class IOSMovie extends Movie{ + + @iOSFindBy(className = "UIAStaticText") + private IOSElement staticText; + + protected IOSMovie(WebElement element) { + super(element); + } + + @Override + public String title() { + return staticText.getText().split(",")[0]; + } + + @Override + public String score() { + return staticText.getText().split(",")[3]; + } + + @Override + public Object getPoster() { + return ((RemoteWebElement) getWrappedElement()).getSize(); + } + + @Override + public void goToReview() { + ((IOSElement) getWrappedElement()).tap(1, 1500); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSMovies.java new file mode 100644 index 000000000..22a729b33 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSMovies.java @@ -0,0 +1,34 @@ +package io.appium.java_client.pagefactory_tests.widgets.ios.simple; + +import io.appium.java_client.pagefactory.iOSFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.Movies; +import org.openqa.selenium.WebElement; + +import java.util.List; + +//classNme = UIATableView +public class IOSMovies extends Movies{ + + @iOSFindBy(className = "UIATableCell") + List movieList; + + /* + There could be additional behavior. + But for test it is enough. + */ + + protected IOSMovies(WebElement element) { + super(element); + } + + @Override + public int getMovieCount() { + return movieList.size(); + } + + @Override + public Movie getMovie(int index) { + return movieList.get(index); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSReview.java new file mode 100644 index 000000000..554773907 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSReview.java @@ -0,0 +1,50 @@ +package io.appium.java_client.pagefactory_tests.widgets.ios.simple; + + +import io.appium.java_client.android.AndroidElement; +import io.appium.java_client.ios.IOSElement; +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory.iOSFindBy; +import io.appium.java_client.pagefactory.iOSFindBys; +import io.appium.java_client.pagefactory_tests.widgets.Review; +import org.openqa.selenium.WebElement; + +//className = UIAWindow +public class IOSReview extends Review{ + + @iOSFindBys({@iOSFindBy(className = "UIANavigationBar"), + @iOSFindBy(className = "UIAStaticText")}) + private IOSElement title; + + @iOSFindBys({@iOSFindBy(className = "UIAScrollView"), + @iOSFindBy(className = "UIAStaticText")}) + private IOSElement synopsis; + + @iOSFindBy(className = "UIAImage") + private IOSElement poster; + + + protected IOSReview(WebElement element) { + super(element); + } + + @Override + public String title() { + return title.getText(); + } + + @Override + public String score() { + return "100"; + } + + @Override + public String info() { + return synopsis.getText(); + } + + @Override + public Object getPoster() { + return poster.getSize(); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/RottenTomatoesSelendroidApp.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/RottenTomatoesSelendroidApp.java new file mode 100644 index 000000000..62dd9a30c --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/RottenTomatoesSelendroidApp.java @@ -0,0 +1,102 @@ +package io.appium.java_client.pagefactory_tests.widgets.selendroid; + +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory.SelendroidFindBys; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.annotated.AnnotatedSelendroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.annotated.AnnotatedSelendroidReview; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.extended.ExtendedSelendroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.extended.ExtendedSelendroidReview; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.simple.SelendroidMovies; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.simple.SelendroidReview; +import org.apache.commons.lang3.StringUtils; + +import static junit.framework.Assert.assertTrue; + +/** + * This is the example of page object with declared Widgets + * instead of WebElement + */ +public class RottenTomatoesSelendroidApp { + + @SelendroidFindBy(id = "lvMovies") + private SelendroidMovies simpleMovies; + + @SelendroidFindBy(className = "android.widget.RelativeLayout") + private SelendroidReview simpleReview; + + private AnnotatedSelendroidMovies annotatedSelendroidMovies; + + private AnnotatedSelendroidReview annotatedSelendroidReview; + + private ExtendedSelendroidMovies extendedSelendroidMovies; + + private ExtendedSelendroidReview extendedSelendroidReview; + + @SelendroidFindBy(id = "fakeId") + private ExtendedSelendroidMovies fakeMovies; + + @SelendroidFindBy(id = "fakeId") + private ExtendedSelendroidReview fakeReview; + + + public int getSimpleMovieCount(){ + return simpleMovies.getMovieCount(); + } + + public Movie getASimpleMovie(int index){ + return simpleMovies.getMovie(index); + } + + public void checkSimpleReview(){ + assertTrue(!StringUtils.isBlank(simpleReview.title())); + assertTrue(!StringUtils.isBlank(simpleReview.score())); + assertTrue(!StringUtils.isBlank(simpleReview.info())); + assertTrue(simpleReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + public int getAnnotatedMovieCount(){ + return annotatedSelendroidMovies.getMovieCount(); + } + + public Movie getAnAnnotatedMovie(int index){ + return annotatedSelendroidMovies.getMovie(index); + } + + public void checkAnnotatedReview(){ + assertTrue(!StringUtils.isBlank(annotatedSelendroidReview.title())); + assertTrue(!StringUtils.isBlank(annotatedSelendroidReview.score())); + assertTrue(!StringUtils.isBlank(annotatedSelendroidReview.info())); + assertTrue(annotatedSelendroidReview.getPoster() != null); + } + ///////////////////////////////////////////////////////// + + public int getExtendeddMovieCount(){ + return extendedSelendroidMovies.getMovieCount(); + } + + public Movie getAnExtendedMovie(int index){ + return extendedSelendroidMovies.getMovie(index); + } + + public void checkExtendedReview(){ + assertTrue(!StringUtils.isBlank(extendedSelendroidReview.title())); + assertTrue(!StringUtils.isBlank(extendedSelendroidReview.score())); + assertTrue(!StringUtils.isBlank(extendedSelendroidReview.info())); + assertTrue(extendedSelendroidReview.getPoster() != null); + } + + ///////////////////////////////////////////////////////// + + public int getFakedMovieCount(){ + return fakeMovies.getMovieCount(); + } + + public void checkFakeReview(){ + assertTrue(!StringUtils.isBlank(fakeReview.title())); + assertTrue(!StringUtils.isBlank(fakeReview.score())); + assertTrue(!StringUtils.isBlank(fakeReview.info())); + assertTrue(fakeReview.getPoster() != null); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/SelendroidWidgetTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/SelendroidWidgetTest.java new file mode 100644 index 000000000..75493ea39 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/SelendroidWidgetTest.java @@ -0,0 +1,131 @@ +package io.appium.java_client.pagefactory_tests.widgets.selendroid; + +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.pagefactory.TimeOutDuration; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.WidgetTest; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import io.appium.java_client.service.local.flags.GeneralServerFlag; +import org.apache.commons.lang3.StringUtils; +import org.junit.*; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.support.PageFactory; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class SelendroidWidgetTest implements WidgetTest{ + private static int SELENDROID_PORT = 9999; + + private AndroidDriver driver; + private static AppiumDriverLocalService service; + private RottenTomatoesSelendroidApp rottenTomatoesApp; + TimeOutDuration duration; + + @BeforeClass + public static void beforeClass() throws Exception { + AppiumServiceBuilder builder = new AppiumServiceBuilder().withArgument(GeneralServerFlag.AUTOMATION_NAME, AutomationName.SELENDROID); + service = builder.build(); + service.start(); + } + + @Before + public void setUp() throws Exception { + File appDir = new File("src/test/java/io/appium/java_client"); + File app = new File(appDir, "android-rottentomatoes-demo-debug.apk"); + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + capabilities.setCapability(MobileCapabilityType.SELENDROID_PORT, SELENDROID_PORT); + driver = new AndroidDriver(service.getUrl(), capabilities); + + duration = new TimeOutDuration(20, TimeUnit.SECONDS); + rottenTomatoesApp = new RottenTomatoesSelendroidApp(); + PageFactory.initElements(new AppiumFieldDecorator(driver, duration), rottenTomatoesApp); + } + + @After + public void tearDown() throws Exception { + driver.quit(); + } + + @AfterClass + public static void afterClass() throws Exception { + if (service != null) + service.stop(); + } + + @Test + @Override + public void checkACommonWidget() { + assertTrue(rottenTomatoesApp.getSimpleMovieCount() == 10); + Movie movie = rottenTomatoesApp.getASimpleMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + rottenTomatoesApp.checkSimpleReview(); + } + + @Override + @Test + public void checkAnAnnotatedWidget() { + assertTrue(rottenTomatoesApp.getAnnotatedMovieCount() == 10); + Movie movie = rottenTomatoesApp.getAnAnnotatedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + rottenTomatoesApp.checkAnnotatedReview(); + } + + + @Override + @Test + public void checkAnExtendedWidget() { + assertTrue(rottenTomatoesApp.getExtendeddMovieCount() == 10); + Movie movie = rottenTomatoesApp.getAnExtendedMovie(0); + assertTrue(!StringUtils.isBlank(movie.title())); + assertTrue(!StringUtils.isBlank(movie.score())); + assertNotNull(movie.getPoster()); + movie.goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + rottenTomatoesApp.checkExtendedReview(); + } + + @Override + @Test + public void checkTheLocatorOverridingOnAWidget() { + duration.setTime(5); + try { + assertTrue(rottenTomatoesApp.getFakedMovieCount() == 0); + } + catch (Exception e){ + if (!NoSuchElementException.class.isAssignableFrom(e.getClass())) + throw e; + } + + rottenTomatoesApp.getASimpleMovie(0).goToReview(); + driver.getPageSource(); //forcing the refreshing hierarchy + try { + rottenTomatoesApp.checkFakeReview(); + } + catch (Exception e){ + if (NoSuchElementException.class.isAssignableFrom(e.getClass())) + return; + else + throw e; + } + throw new RuntimeException("Any exception was expected"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/annotated/AnnotatedSelendroidMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/annotated/AnnotatedSelendroidMovie.java new file mode 100644 index 000000000..9177c2453 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/annotated/AnnotatedSelendroidMovie.java @@ -0,0 +1,13 @@ +package io.appium.java_client.pagefactory_tests.widgets.selendroid.annotated; + +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.simple.SelendroidMovie; +import org.openqa.selenium.WebElement; + +@SelendroidFindBy(className = "android.widget.RelativeLayout") +public class AnnotatedSelendroidMovie extends SelendroidMovie{ + + protected AnnotatedSelendroidMovie(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/annotated/AnnotatedSelendroidMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/annotated/AnnotatedSelendroidMovies.java new file mode 100644 index 000000000..068a190d0 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/annotated/AnnotatedSelendroidMovies.java @@ -0,0 +1,34 @@ +package io.appium.java_client.pagefactory_tests.widgets.selendroid.annotated; + + +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.Movies; +import org.openqa.selenium.WebElement; + +import java.util.List; + +@SelendroidFindBy(id = "lvMovies") +public class AnnotatedSelendroidMovies extends Movies{ + + List movieList; + + /* + There could be additional behavior. + But for test it is enough. + */ + + protected AnnotatedSelendroidMovies(WebElement element) { + super(element); + } + + @Override + public int getMovieCount() { + return movieList.size(); + } + + @Override + public Movie getMovie(int index) { + return movieList.get(index); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/annotated/AnnotatedSelendroidReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/annotated/AnnotatedSelendroidReview.java new file mode 100644 index 000000000..ccc76aca1 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/annotated/AnnotatedSelendroidReview.java @@ -0,0 +1,16 @@ +package io.appium.java_client.pagefactory_tests.widgets.selendroid.annotated; + + +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory.SelendroidFindBys; +import io.appium.java_client.pagefactory_tests.widgets.selendroid.simple.SelendroidReview; +import org.openqa.selenium.WebElement; + + +@SelendroidFindBy(className = "android.widget.RelativeLayout") +public class AnnotatedSelendroidReview extends SelendroidReview { + + protected AnnotatedSelendroidReview(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/extended/ExtendedSelendroidMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/extended/ExtendedSelendroidMovies.java new file mode 100644 index 000000000..e3073250a --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/extended/ExtendedSelendroidMovies.java @@ -0,0 +1,11 @@ +package io.appium.java_client.pagefactory_tests.widgets.selendroid.extended; + +import io.appium.java_client.pagefactory_tests.widgets.selendroid.annotated.AnnotatedSelendroidMovies; +import org.openqa.selenium.WebElement; + +public class ExtendedSelendroidMovies extends AnnotatedSelendroidMovies { + + protected ExtendedSelendroidMovies(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/extended/ExtendedSelendroidReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/extended/ExtendedSelendroidReview.java new file mode 100644 index 000000000..fae3b7423 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/extended/ExtendedSelendroidReview.java @@ -0,0 +1,12 @@ +package io.appium.java_client.pagefactory_tests.widgets.selendroid.extended; + + +import io.appium.java_client.pagefactory_tests.widgets.selendroid.annotated.AnnotatedSelendroidReview; +import org.openqa.selenium.WebElement; + +public class ExtendedSelendroidReview extends AnnotatedSelendroidReview { + + protected ExtendedSelendroidReview(WebElement element) { + super(element); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/simple/SelendroidMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/simple/SelendroidMovie.java new file mode 100644 index 000000000..bb0dec1ce --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/simple/SelendroidMovie.java @@ -0,0 +1,42 @@ +package io.appium.java_client.pagefactory_tests.widgets.selendroid.simple; + +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebElement; + +public class SelendroidMovie extends Movie{ + + @SelendroidFindBy(id = "tvTitle") + private RemoteWebElement title; + + @SelendroidFindBy(id = "tvCriticsScore") + private RemoteWebElement score; + + @SelendroidFindBy(id = "ivPosterImage") + private RemoteWebElement poster; + + protected SelendroidMovie(WebElement element) { + super(element); + } + + @Override + public String title() { + return title.getText(); + } + + @Override + public String score() { + return score.getText(); + } + + @Override + public Object getPoster() { + return poster.getSize(); + } + + @Override + public void goToReview() { + getWrappedElement().click(); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/simple/SelendroidMovies.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/simple/SelendroidMovies.java new file mode 100644 index 000000000..04b8875ba --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/simple/SelendroidMovies.java @@ -0,0 +1,34 @@ +package io.appium.java_client.pagefactory_tests.widgets.selendroid.simple; + + +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Movie; +import io.appium.java_client.pagefactory_tests.widgets.Movies; +import org.openqa.selenium.WebElement; + +import java.util.List; + +public class SelendroidMovies extends Movies{ + + @SelendroidFindBy(className = "android.widget.RelativeLayout") + List movieList; + + /* + There could be additional behavior. + But for test it is enough. + */ + + protected SelendroidMovies(WebElement element) { + super(element); + } + + @Override + public int getMovieCount() { + return movieList.size(); + } + + @Override + public Movie getMovie(int index) { + return movieList.get(index); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/simple/SelendroidReview.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/simple/SelendroidReview.java new file mode 100644 index 000000000..0ac389d45 --- /dev/null +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/selendroid/simple/SelendroidReview.java @@ -0,0 +1,47 @@ +package io.appium.java_client.pagefactory_tests.widgets.selendroid.simple; + + +import io.appium.java_client.pagefactory.SelendroidFindBy; +import io.appium.java_client.pagefactory_tests.widgets.Review; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebElement; + +public class SelendroidReview extends Review{ + + @SelendroidFindBy(id = "tvTitle") + private RemoteWebElement title; + + @SelendroidFindBy(id = "tvCriticsScore") + private RemoteWebElement score; + + @SelendroidFindBy(id = "tvSynopsis") + private RemoteWebElement synopsis; + + @SelendroidFindBy(id = "ivPosterImage") + private RemoteWebElement poster; + + + protected SelendroidReview(WebElement element) { + super(element); + } + + @Override + public String title() { + return title.getText(); + } + + @Override + public String score() { + return score.getText(); + } + + @Override + public String info() { + return synopsis.getText(); + } + + @Override + public Object getPoster() { + return poster.getSize(); + } +}