Skip to content

Commit 9fb06b1

Browse files
JamesSassanomykola-mokhnach
authored andcommitted
fix: Page factory list element not initialized when parameterized by generic type (#1237)
1 parent 822f004 commit 9fb06b1

File tree

2 files changed

+224
-21
lines changed

2 files changed

+224
-21
lines changed

src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@
4545
import java.lang.reflect.Field;
4646
import java.lang.reflect.ParameterizedType;
4747
import java.lang.reflect.Type;
48+
import java.lang.reflect.TypeVariable;
4849
import java.time.Duration;
4950
import java.util.ArrayList;
51+
import java.util.Arrays;
5052
import java.util.List;
5153
import java.util.Map;
5254

@@ -129,6 +131,12 @@ protected boolean isDecoratableList(Field field) {
129131
return true;
130132
}
131133
}
134+
135+
if ((listType instanceof TypeVariable)
136+
&& Arrays.asList(((TypeVariable<?>) listType).getBounds())
137+
.stream().anyMatch(item -> availableElementClasses.contains(item))) {
138+
return true;
139+
}
132140
return false;
133141
}
134142
};

src/test/java/io/appium/java_client/pagefactory_tests/GenericTest.java

Lines changed: 216 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,156 @@
11
package io.appium.java_client.pagefactory_tests;
22

3-
import static org.junit.Assert.assertTrue;
3+
import static org.junit.Assert.assertNotNull;
4+
import static org.junit.Assert.assertNull;
45

56
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
7+
import io.appium.java_client.remote.AutomationName;
8+
import io.appium.java_client.remote.MobileCapabilityType;
9+
10+
import org.apache.commons.lang3.NotImplementedException;
611
import org.junit.Test;
712
import org.openqa.selenium.By;
13+
import org.openqa.selenium.Capabilities;
14+
import org.openqa.selenium.HasCapabilities;
15+
import org.openqa.selenium.Platform;
816
import org.openqa.selenium.WebDriver;
917
import org.openqa.selenium.WebElement;
18+
import org.openqa.selenium.remote.CapabilityType;
1019
import org.openqa.selenium.support.PageFactory;
20+
import io.appium.java_client.MobileElement;
1121

22+
import java.util.HashMap;
1223
import java.util.List;
24+
import java.util.Map;
1325
import java.util.Set;
14-
import java.util.function.Supplier;
1526

1627
public class GenericTest {
1728

18-
static class TempGenericPage<T> {
29+
/**
30+
* The Generic types are null on an unbound page.
31+
*/
32+
private static <T> void assertUnboundGenericsNull(T genericItem,
33+
List<? extends T> genericItems) {
34+
assertNull(genericItem);
35+
assertNull(genericItems);
36+
}
37+
38+
/**
39+
* The Generic types are not null on a page bound by a WebElement (or WebElement
40+
* sub type).
41+
*/
42+
private static <T extends WebElement> void assertBoundGenericsNotNull(T genericItem,
43+
List<? extends T> genericItems) {
44+
assertNotNull(genericItem);
45+
assertNotNull(genericItems);
46+
}
47+
48+
/**
49+
* The Element types are never null.
50+
*/
51+
private static void assertElementsNotNull(WebElement elementItem,
52+
List<? extends WebElement> elementItems) {
53+
assertNotNull(elementItem);
54+
assertNotNull(elementItems);
55+
}
56+
57+
/**
58+
* The Object types are always null.
59+
*/
60+
private static void assertObjectsNull(Object objectItem, List<? extends Object> objectItems) {
61+
assertNull(objectItem);
62+
assertNull(objectItems);
63+
}
64+
65+
/**
66+
* A page with no generic types. The Object types are never initialized.
67+
*/
68+
static class TempNoGenericsPage {
69+
public WebElement webElementItem;
70+
public MobileElement mobileElementItem;
71+
public Object objectItem;
72+
73+
public List<WebElement> webElementItems;
74+
public List<MobileElement> mobileElementItems;
75+
public List<Object> objectItems;
76+
77+
public void assertInit() {
78+
assertElementsNotNull(webElementItem, webElementItems);
79+
assertElementsNotNull(mobileElementItem, mobileElementItems);
80+
assertObjectsNull(objectItem, objectItems);
81+
}
82+
}
83+
84+
/**
85+
* A page with an unbound generic type. The generic and Object types are never
86+
* initialized.
87+
*/
88+
static class TempUnboundPage<T> {
89+
public T genericItem;
90+
public WebElement webElementItem;
91+
public MobileElement mobileElementItem;
92+
public Object objectItem;
1993

20-
public List<T> items;
94+
public List<T> genericItems;
95+
public List<WebElement> webElementItems;
96+
public List<MobileElement> mobileElementItems;
97+
public List<Object> objectItems;
2198

22-
public List<T> getItems() {
23-
return items;
99+
public void assertInit() {
100+
assertUnboundGenericsNull(genericItem, genericItems);
101+
assertElementsNotNull(webElementItem, webElementItems);
102+
assertElementsNotNull(mobileElementItem, mobileElementItems);
103+
assertObjectsNull(objectItem, objectItems);
24104
}
25105
}
26106

27-
static class MockWebDriver implements WebDriver {
107+
/**
108+
* A page with a WebElement bound generic type. The Object types are never
109+
* initialized.
110+
*/
111+
static class TempWebBoundPage<T extends WebElement> {
112+
public T genericItem;
113+
public WebElement webElementItem;
114+
public MobileElement mobileElementItem;
115+
public Object objectItem;
116+
117+
public List<T> genericItems;
118+
public List<WebElement> webElementItems;
119+
public List<MobileElement> mobileElementItems;
120+
public List<Object> objectItems;
121+
122+
public void assertInit() {
123+
assertBoundGenericsNotNull(genericItem, genericItems);
124+
assertElementsNotNull(webElementItem, webElementItems);
125+
assertElementsNotNull(mobileElementItem, mobileElementItems);
126+
assertObjectsNull(objectItem, objectItems);
127+
}
128+
}
129+
130+
/**
131+
* A page with a MobileElement bound generic type. The Object types are never
132+
* initialized.
133+
*/
134+
static class TempMobileBoundPage<T extends MobileElement> {
135+
public T genericItem;
136+
public WebElement webElementItem;
137+
public MobileElement mobileElementItem;
138+
public Object objectItem;
139+
140+
public List<T> genericItems;
141+
public List<WebElement> webElementItems;
142+
public List<MobileElement> mobileElementItems;
143+
public List<Object> objectItems;
144+
145+
public void assertInit() {
146+
assertBoundGenericsNotNull(genericItem, genericItems);
147+
assertElementsNotNull(webElementItem, webElementItems);
148+
assertElementsNotNull(mobileElementItem, mobileElementItems);
149+
assertObjectsNull(objectItem, objectItems);
150+
}
151+
}
152+
153+
static class MockWebDriver implements WebDriver, HasCapabilities {
28154

29155
@Override
30156
public void get(String url) {
@@ -42,13 +168,13 @@ public String getTitle() {
42168
}
43169

44170
@Override
45-
public List<WebElement> findElements(By by) {
46-
return null;
171+
public <T extends WebElement> List<T> findElements(By by) {
172+
throw new NotImplementedException("MockWebDriver did not expect to findElements");
47173
}
48174

49175
@Override
50-
public WebElement findElement(By by) {
51-
return null;
176+
public <T extends WebElement> T findElement(By by) {
177+
throw new NotImplementedException("MockWebDriver did not expect to findElement");
52178
}
53179

54180
@Override
@@ -90,16 +216,85 @@ public Navigation navigate() {
90216
public Options manage() {
91217
return null;
92218
}
219+
220+
@Override
221+
public Capabilities getCapabilities() {
222+
223+
final Map<String, Object> capabilities = new HashMap<>();
224+
225+
// These are needed to map the proxy element to a MobileElement.
226+
capabilities.put(CapabilityType.PLATFORM_NAME, Platform.ANY.toString());
227+
capabilities.put(MobileCapabilityType.AUTOMATION_NAME,
228+
AutomationName.IOS_XCUI_TEST.toString());
229+
230+
return new Capabilities() {
231+
232+
@Override
233+
public Object getCapability(String capabilityName) {
234+
return capabilities.get(capabilityName);
235+
}
236+
237+
@Override
238+
public Map<String, Object> asMap() {
239+
return capabilities;
240+
}
241+
};
242+
}
243+
}
244+
245+
@Test
246+
public void noGenericsTestCase() {
247+
TempNoGenericsPage page = new TempNoGenericsPage();
248+
PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page);
249+
page.assertInit();
250+
}
251+
252+
@Test
253+
public void unBoundTestCase() {
254+
TempUnboundPage<?> page = new TempUnboundPage<>();
255+
PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page);
256+
page.assertInit();
257+
}
258+
259+
@Test
260+
public void unboundWebElementTestCase() {
261+
TempUnboundPage<WebElement> page = new TempUnboundPage<>();
262+
PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page);
263+
page.assertInit();
264+
}
265+
266+
@Test
267+
public void webBoundUnknownElementTestCase() {
268+
TempWebBoundPage<?> page = new TempWebBoundPage<>();
269+
PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page);
270+
page.assertInit();
271+
}
272+
273+
@Test
274+
public void webBoundWebElementTestCase() {
275+
TempWebBoundPage<WebElement> page = new TempWebBoundPage<>();
276+
PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page);
277+
page.assertInit();
93278
}
94279

95280
@Test
96-
public void genericTestCse() {
97-
Supplier<Boolean> result = () -> {
98-
PageFactory
99-
.initElements(new AppiumFieldDecorator(new MockWebDriver()),
100-
new TempGenericPage<>());
101-
return true;
102-
};
103-
assertTrue(result.get());
104-
}
105-
}
281+
public void webBoundMobileElementTestCase() {
282+
TempWebBoundPage<MobileElement> page = new TempWebBoundPage<>();
283+
PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page);
284+
page.assertInit();
285+
}
286+
287+
@Test
288+
public void mobileBoundUnknownElementTestCase() {
289+
TempMobileBoundPage<?> page = new TempMobileBoundPage<>();
290+
PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page);
291+
page.assertInit();
292+
}
293+
294+
@Test
295+
public void mobileBoundMobileElementTestCase() {
296+
TempMobileBoundPage<MobileElement> page = new TempMobileBoundPage<>();
297+
PageFactory.initElements(new AppiumFieldDecorator(new MockWebDriver()), page);
298+
page.assertInit();
299+
}
300+
}

0 commit comments

Comments
 (0)