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 1e651f00b..933795729 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java @@ -45,8 +45,10 @@ import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -129,6 +131,12 @@ protected boolean isDecoratableList(Field field) { return true; } } + + if ((listType instanceof TypeVariable) + && Arrays.asList(((TypeVariable) listType).getBounds()) + .stream().anyMatch(item -> availableElementClasses.contains(item))) { + return true; + } return false; } }; diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/GenericTest.java b/src/test/java/io/appium/java_client/pagefactory_tests/GenericTest.java index 7c06ba2a5..16e83e2fe 100644 --- a/src/test/java/io/appium/java_client/pagefactory_tests/GenericTest.java +++ b/src/test/java/io/appium/java_client/pagefactory_tests/GenericTest.java @@ -1,30 +1,156 @@ package io.appium.java_client.pagefactory_tests; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import io.appium.java_client.pagefactory.AppiumFieldDecorator; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.remote.MobileCapabilityType; + +import org.apache.commons.lang3.NotImplementedException; import org.junit.Test; import org.openqa.selenium.By; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.HasCapabilities; +import org.openqa.selenium.Platform; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.CapabilityType; import org.openqa.selenium.support.PageFactory; +import io.appium.java_client.MobileElement; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; -import java.util.function.Supplier; public class GenericTest { - static class TempGenericPage { + /** + * The Generic types are null on an unbound page. + */ + private static void assertUnboundGenericsNull(T genericItem, + List genericItems) { + assertNull(genericItem); + assertNull(genericItems); + } + + /** + * The Generic types are not null on a page bound by a WebElement (or WebElement + * sub type). + */ + private static void assertBoundGenericsNotNull(T genericItem, + List genericItems) { + assertNotNull(genericItem); + assertNotNull(genericItems); + } + + /** + * The Element types are never null. + */ + private static void assertElementsNotNull(WebElement elementItem, + List elementItems) { + assertNotNull(elementItem); + assertNotNull(elementItems); + } + + /** + * The Object types are always null. + */ + private static void assertObjectsNull(Object objectItem, List objectItems) { + assertNull(objectItem); + assertNull(objectItems); + } + + /** + * A page with no generic types. The Object types are never initialized. + */ + static class TempNoGenericsPage { + public WebElement webElementItem; + public MobileElement mobileElementItem; + public Object objectItem; + + public List webElementItems; + public List mobileElementItems; + public List objectItems; + + public void assertInit() { + assertElementsNotNull(webElementItem, webElementItems); + assertElementsNotNull(mobileElementItem, mobileElementItems); + assertObjectsNull(objectItem, objectItems); + } + } + + /** + * A page with an unbound generic type. The generic and Object types are never + * initialized. + */ + static class TempUnboundPage { + public T genericItem; + public WebElement webElementItem; + public MobileElement mobileElementItem; + public Object objectItem; - public List items; + public List genericItems; + public List webElementItems; + public List mobileElementItems; + public List objectItems; - public List getItems() { - return items; + public void assertInit() { + assertUnboundGenericsNull(genericItem, genericItems); + assertElementsNotNull(webElementItem, webElementItems); + assertElementsNotNull(mobileElementItem, mobileElementItems); + assertObjectsNull(objectItem, objectItems); } } - static class MockWebDriver implements WebDriver { + /** + * A page with a WebElement bound generic type. The Object types are never + * initialized. + */ + static class TempWebBoundPage { + public T genericItem; + public WebElement webElementItem; + public MobileElement mobileElementItem; + public Object objectItem; + + public List genericItems; + public List webElementItems; + public List mobileElementItems; + public List objectItems; + + public void assertInit() { + assertBoundGenericsNotNull(genericItem, genericItems); + assertElementsNotNull(webElementItem, webElementItems); + assertElementsNotNull(mobileElementItem, mobileElementItems); + assertObjectsNull(objectItem, objectItems); + } + } + + /** + * A page with a MobileElement bound generic type. The Object types are never + * initialized. + */ + static class TempMobileBoundPage { + public T genericItem; + public WebElement webElementItem; + public MobileElement mobileElementItem; + public Object objectItem; + + public List genericItems; + public List webElementItems; + public List mobileElementItems; + public List objectItems; + + public void assertInit() { + assertBoundGenericsNotNull(genericItem, genericItems); + assertElementsNotNull(webElementItem, webElementItems); + assertElementsNotNull(mobileElementItem, mobileElementItems); + assertObjectsNull(objectItem, objectItems); + } + } + + static class MockWebDriver implements WebDriver, HasCapabilities { @Override public void get(String url) { @@ -42,13 +168,13 @@ public String getTitle() { } @Override - public List findElements(By by) { - return null; + public List findElements(By by) { + throw new NotImplementedException("MockWebDriver did not expect to findElements"); } @Override - public WebElement findElement(By by) { - return null; + public T findElement(By by) { + throw new NotImplementedException("MockWebDriver did not expect to findElement"); } @Override @@ -90,16 +216,85 @@ public Navigation navigate() { public Options manage() { return null; } + + @Override + public Capabilities getCapabilities() { + + final Map capabilities = new HashMap<>(); + + // These are needed to map the proxy element to a MobileElement. + capabilities.put(CapabilityType.PLATFORM_NAME, Platform.ANY.toString()); + capabilities.put(MobileCapabilityType.AUTOMATION_NAME, + AutomationName.IOS_XCUI_TEST.toString()); + + return new Capabilities() { + + @Override + public Object getCapability(String capabilityName) { + return capabilities.get(capabilityName); + } + + @Override + public Map asMap() { + return capabilities; + } + }; + } + } + + @Test + public void noGenericsTestCase() { + TempNoGenericsPage page = new TempNoGenericsPage(); + PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page); + page.assertInit(); + } + + @Test + public void unBoundTestCase() { + TempUnboundPage page = new TempUnboundPage<>(); + PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page); + page.assertInit(); + } + + @Test + public void unboundWebElementTestCase() { + TempUnboundPage page = new TempUnboundPage<>(); + PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page); + page.assertInit(); + } + + @Test + public void webBoundUnknownElementTestCase() { + TempWebBoundPage page = new TempWebBoundPage<>(); + PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page); + page.assertInit(); + } + + @Test + public void webBoundWebElementTestCase() { + TempWebBoundPage page = new TempWebBoundPage<>(); + PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page); + page.assertInit(); } @Test - public void genericTestCse() { - Supplier result = () -> { - PageFactory - .initElements(new AppiumFieldDecorator(new MockWebDriver()), - new TempGenericPage<>()); - return true; - }; - assertTrue(result.get()); - } -} \ No newline at end of file + public void webBoundMobileElementTestCase() { + TempWebBoundPage page = new TempWebBoundPage<>(); + PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page); + page.assertInit(); + } + + @Test + public void mobileBoundUnknownElementTestCase() { + TempMobileBoundPage page = new TempMobileBoundPage<>(); + PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page); + page.assertInit(); + } + + @Test + public void mobileBoundMobileElementTestCase() { + TempMobileBoundPage page = new TempMobileBoundPage<>(); + PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page); + page.assertInit(); + } +}