From 9d1a1b56886619f935b1bdfd0fe5cd74e3eccf3d Mon Sep 17 00:00:00 2001 From: Sergey Tikhomirov Date: Sun, 5 Jul 2015 23:07:37 +0300 Subject: [PATCH 1/2] Fix #184 Fix #211 --- .../pagefactory/AppiumAnnotations.java | 7 +- .../pagefactory/AppiumElementLocator.java | 72 ++++++++++++++----- .../pagefactory/ContentMappedBy.java | 17 ++++- .../java_client/remote/AutomationName.java | 7 ++ .../AndroidPageObjectTest.java | 24 +++++++ .../pagefactory_tests/SelendroidModeTest.java | 3 +- .../pagefactory_tests/iOSPageObjectTest.java | 24 +++++++ 7 files changed, 133 insertions(+), 21 deletions(-) create mode 100644 src/main/java/io/appium/java_client/remote/AutomationName.java diff --git a/src/main/java/io/appium/java_client/pagefactory/AppiumAnnotations.java b/src/main/java/io/appium/java_client/pagefactory/AppiumAnnotations.java index 3d8bdbbe7..22ef6ae9a 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumAnnotations.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumAnnotations.java @@ -1,6 +1,7 @@ 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; @@ -300,7 +301,7 @@ public By buildBy() { SelendroidFindBy selendroidBy = mobileField .getAnnotation(SelendroidFindBy.class); if (selendroidBy != null && ANDROID.toUpperCase().equals(platform) - && "Selendroid".toUpperCase().equals(automation)) { + && SELENDROID.toUpperCase().equals(automation)) { return setByForTheNativeContentAndReturn( getMobileBy(selendroidBy, getFilledValue(selendroidBy)), contentMap); @@ -309,7 +310,7 @@ public By buildBy() { SelendroidFindBys selendroidBys = mobileField .getAnnotation(SelendroidFindBys.class); if (selendroidBys != null && ANDROID.toUpperCase().equals(platform) - && "Selendroid".toUpperCase().equals(automation)) { + && SELENDROID.toUpperCase().equals(automation)) { return setByForTheNativeContentAndReturn( getComplexMobileBy(selendroidBys.value(), ByChained.class), contentMap); @@ -318,7 +319,7 @@ public By buildBy() { SelendroidFindAll selendroidAll = mobileField .getAnnotation(SelendroidFindAll.class); if (selendroidAll != null && ANDROID.toUpperCase().equals(platform) - && "Selendroid".toUpperCase().equals(automation)) { + && SELENDROID.toUpperCase().equals(automation)) { return setByForTheNativeContentAndReturn( getComplexMobileBy(selendroidAll.value(), ByAll.class), 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 c272bd51a..7d859bd70 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java @@ -1,5 +1,7 @@ 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; @@ -7,14 +9,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import org.openqa.selenium.By; -import org.openqa.selenium.Capabilities; -import org.openqa.selenium.HasCapabilities; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.SearchContext; -import org.openqa.selenium.StaleElementReferenceException; -import org.openqa.selenium.TimeoutException; -import org.openqa.selenium.WebElement; +import io.appium.java_client.remote.MobilePlatform; +import org.openqa.selenium.*; import org.openqa.selenium.support.pagefactory.ElementLocator; import org.openqa.selenium.support.ui.FluentWait; @@ -26,6 +22,7 @@ class AppiumElementLocator implements ElementLocator { 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; @@ -37,12 +34,26 @@ public List apply(By by) { 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; } } + + private 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()); + } } private final SearchContext searchContext; @@ -66,15 +77,9 @@ public List apply(By by) { AppiumElementLocator(SearchContext searchContext, Field field, TimeOutDuration timeOutDuration) { this.searchContext = searchContext; - // All known webdrivers implement HasCapabilities - Capabilities capabilities = ((HasCapabilities) WebDriverUnpackUtility. - unpackWebDriverFromSearchContext(this.searchContext)) - .getCapabilities(); - String platform = String.valueOf(capabilities - .getCapability(MobileCapabilityType.PLATFORM_NAME)); - String automation = String.valueOf(capabilities - .getCapability(MobileCapabilityType.AUTOMATION_NAME)); + String platform = getPlatform(); + String automation = getAutomation(); AppiumAnnotations annotations = new AppiumAnnotations(field, platform, automation); @@ -88,6 +93,41 @@ public List apply(By by) { 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 here customers implementation of WebDriver + //At this case it has to implement HasCapabilities + 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; + + if (HasCapabilities.class.isAssignableFrom(d.getClass())) + return String.valueOf(((HasCapabilities) d).getCapabilities(). + getCapability(MobileCapabilityType.AUTOMATION_NAME)); + + return null; + } + private void changeImplicitlyWaitTimeOut(long newTimeOut, TimeUnit newTimeUnit) { WebDriverUnpackUtility.unpackWebDriverFromSearchContext(searchContext) diff --git a/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java b/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java index 07c80662c..501e63602 100644 --- a/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java +++ b/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java @@ -22,7 +22,9 @@ private By returnRelevantBy(SearchContext context){ if (!ContextAware.class.isAssignableFrom(driver.getClass())){ //it is desktop browser return map.get(ContentType.HTML); } - + + By result = null; + ContextAware contextAware = ContextAware.class.cast(driver); String currentContext = contextAware.getContext(); if (currentContext.contains(NATIVE_APP_PATTERN)) @@ -35,4 +37,17 @@ 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/remote/AutomationName.java b/src/main/java/io/appium/java_client/remote/AutomationName.java new file mode 100644 index 000000000..02b3ff016 --- /dev/null +++ b/src/main/java/io/appium/java_client/remote/AutomationName.java @@ -0,0 +1,7 @@ +package io.appium.java_client.remote; + + +public interface AutomationName { + String APPIUM = "Appium"; + String SELENDROID = "Selendroid"; +} 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 dd0cc7253..37561ed2c 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 @@ -160,6 +160,14 @@ public class AndroidPageObjectTest { @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"android:id/text1\")") private TouchableElement touchabletextVieW; + + @iOSFindBy(uiAutomator = ".elements()[0]") + @FindBy(css = "e.e1.e2") + private List elementsWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy; + + @iOSFindBy(uiAutomator = ".elements()[0]") + @FindBy(css = "e.e1.e2") + private WebElement elementWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy; @SuppressWarnings("rawtypes") @Before @@ -348,4 +356,20 @@ public void isTheFieldAndroidElement(){ androidElement = (AndroidElement) remotetextVieW; //declared as RemoteWedElement androidElement = (AndroidElement) touchabletextVieW; //declared as TouchABLEElement } + + @Test + public void checkThatTestWillNotBeFailedBecauseOfInvalidFindBy(){ + try { + Assert.assertNotEquals(null, elementWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy.getAttribute("text")); + } + catch (NoSuchElementException ignored){ + return; + } + throw new RuntimeException(NoSuchElementException.class.getName() + " has been expected."); + } + + @Test + public void checkThatTestWillNotBeFailedBecauseOfInvalidFindBy_List(){ + Assert.assertEquals(0, elementsWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy.size()); + } } 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 1b7857c8b..09c652bb7 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 @@ -6,6 +6,7 @@ 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.remote.AutomationName; import io.appium.java_client.remote.MobileCapabilityType; import org.openqa.selenium.WebElement; @@ -79,7 +80,7 @@ public void setUp() throws Exception { DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); - capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "Selendroid"); + capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.SELENDROID); capabilities.setCapability(MobileCapabilityType.SELENDROID_PORT, SELENDROID_PORT); driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities); 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 2417bbc6f..96dc142bc 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 @@ -117,6 +117,14 @@ public class iOSPageObjectTest { }) private List findAllElements; + @AndroidFindBy(className = "android.widget.TextView") + @FindBy(css = "e.e1.e2") + private List elementsWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy; + + @AndroidFindBy(className = "android.widget.TextView") + @FindBy(css = "e.e1.e2") + private WebElement elementWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy; + @SuppressWarnings("rawtypes") @Before public void setUp() throws Exception { @@ -280,4 +288,20 @@ public void isTheFieldIOSElement(){ iOSElement = (IOSElement) remotetextVieW; //declared as RemoteWebElement iOSElement = (IOSElement) touchableButton; //declared as TouchABLEElement } + + @Test + public void checkThatTestWillNotBeFailedBecauseOfInvalidFindBy(){ + try { + Assert.assertNotEquals(null, elementWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy.getAttribute("text")); + } + catch (NoSuchElementException ignored){ + return; + } + throw new RuntimeException(NoSuchElementException.class.getName() + " has been expected."); + } + + @Test + public void checkThatTestWillNotBeFailedBecauseOfInvalidFindBy_List(){ + Assert.assertEquals(0, elementsWhenAndroidLocatorIsNotDefinedAndThereIsInvalidFindBy.size()); + } } From 36286277adf8b0b90df5bb0cf1dc833ce2aadac6 Mon Sep 17 00:00:00 2001 From: Sergey Tikhomirov Date: Mon, 6 Jul 2015 17:51:09 +0300 Subject: [PATCH 2/2] ...some cleaning --- .../appium/java_client/pagefactory/AppiumElementLocator.java | 5 +++-- .../io/appium/java_client/pagefactory/ContentMappedBy.java | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) 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 7d859bd70..21a171ca6 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumElementLocator.java @@ -106,8 +106,9 @@ private String getPlatform(){ if (IOSDriver.class.isAssignableFrom(driverClass)) return MobilePlatform.IOS; - //it is possible that here customers implementation of WebDriver - //At this case it has to implement HasCapabilities + //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)); diff --git a/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java b/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java index 501e63602..8c8f7c1ed 100644 --- a/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java +++ b/src/main/java/io/appium/java_client/pagefactory/ContentMappedBy.java @@ -23,8 +23,6 @@ private By returnRelevantBy(SearchContext context){ return map.get(ContentType.HTML); } - By result = null; - ContextAware contextAware = ContextAware.class.cast(driver); String currentContext = contextAware.getContext(); if (currentContext.contains(NATIVE_APP_PATTERN))