Skip to content

Commit c97fa47

Browse files
Merge pull request #825 from TikhomirovSergey/mykola-mokhnach-appium_w3c_codec
Addition to the #817. JsonToMobileElementConverter: fixed
2 parents eecd044 + 5da4998 commit c97fa47

File tree

3 files changed

+143
-33
lines changed

3 files changed

+143
-33
lines changed

src/main/java/io/appium/java_client/internal/JsonToMobileElementConverter.java

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,13 @@
1818

1919
import static io.appium.java_client.internal.ElementMap.getElementClass;
2020

21-
import com.google.common.collect.Iterables;
22-
import com.google.common.collect.Lists;
23-
2421
import io.appium.java_client.HasSessionDetails;
2522
import org.openqa.selenium.WebDriverException;
2623
import org.openqa.selenium.remote.RemoteWebDriver;
2724
import org.openqa.selenium.remote.RemoteWebElement;
2825
import org.openqa.selenium.remote.internal.JsonToWebElementConverter;
2926

3027
import java.lang.reflect.Constructor;
31-
import java.util.Collection;
3228

3329
/**
3430
* Reconstitutes {@link org.openqa.selenium.WebElement}s from their JSON representation. Will recursively convert Lists
@@ -54,45 +50,30 @@ public JsonToMobileElementConverter(RemoteWebDriver driver, HasSessionDetails ha
5450
this.automation = hasSessionDetails.getAutomationName();
5551
}
5652

57-
/**
58-
* This method converts a command result.
59-
*
60-
* @param result is the result of a command execution.
61-
* @return the result
62-
*/
53+
@Override
6354
public Object apply(Object result) {
64-
if (result instanceof Collection<?>) {
65-
Collection<?> results = (Collection<?>) result;
66-
return Lists.newArrayList(Iterables.transform(results, this));
67-
}
68-
69-
if (result instanceof RemoteWebElement) {
70-
RemoteWebElement resultElement = RemoteWebElement.class.cast(result);
71-
RemoteWebElement element = newMobileElement();
72-
element.setParent(driver);
73-
element.setId(resultElement.getId());
74-
element.setFileDetector(driver.getFileDetector());
75-
return element;
55+
Object toBeReturned = result;
56+
if (toBeReturned instanceof RemoteWebElement) {
57+
toBeReturned = newRemoteWebElement();
58+
((RemoteWebElement) toBeReturned).setId(((RemoteWebElement) result).getId());
7659
}
7760

78-
if (result instanceof Number) {
79-
if (result instanceof Float || result instanceof Double) {
80-
return ((Number) result).doubleValue();
81-
}
82-
return ((Number) result).longValue();
83-
}
84-
85-
return result;
61+
return super.apply(toBeReturned);
8662
}
8763

88-
private RemoteWebElement newMobileElement() {
64+
@Override
65+
protected RemoteWebElement newRemoteWebElement() {
8966
Class<? extends RemoteWebElement> target;
9067
target = getElementClass(platform, automation);
68+
9169
try {
9270
Constructor<? extends RemoteWebElement> constructor = target.getDeclaredConstructor();
9371
constructor.setAccessible(true);
9472
RemoteWebElement result = constructor.newInstance();
73+
9574
result.setParent(driver);
75+
result.setFileDetector(driver.getFileDetector());
76+
9677
return result;
9778
} catch (Exception e) {
9879
throw new WebDriverException(e);

src/main/java/io/appium/java_client/remote/AppiumCommandExecutor.java

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,19 @@
2525

2626
import org.openqa.selenium.WebDriverException;
2727
import org.openqa.selenium.remote.Command;
28+
import org.openqa.selenium.remote.CommandCodec;
2829
import org.openqa.selenium.remote.CommandInfo;
2930
import org.openqa.selenium.remote.DriverCommand;
3031
import org.openqa.selenium.remote.HttpCommandExecutor;
3132
import org.openqa.selenium.remote.Response;
3233
import org.openqa.selenium.remote.http.HttpClient;
34+
import org.openqa.selenium.remote.http.HttpRequest;
35+
import org.openqa.selenium.remote.http.W3CHttpCommandCodec;
3336
import org.openqa.selenium.remote.internal.ApacheHttpClient;
3437
import org.openqa.selenium.remote.service.DriverService;
3538

3639
import java.io.IOException;
40+
import java.lang.reflect.Field;
3741
import java.net.ConnectException;
3842
import java.net.URL;
3943
import java.util.Map;
@@ -74,7 +78,42 @@ public AppiumCommandExecutor(Map<String, CommandInfo> additionalCommands,
7478
this(additionalCommands, service, new ApacheHttpClient.Factory());
7579
}
7680

77-
@Override public Response execute(Command command) throws WebDriverException {
81+
private <B> B getPrivateFieldValue(String fieldName, Class<B> fieldType) {
82+
try {
83+
final Field f = getClass().getSuperclass().getDeclaredField(fieldName);
84+
f.setAccessible(true);
85+
return fieldType.cast(f.get(this));
86+
} catch (NoSuchFieldException | IllegalAccessException e) {
87+
throw new WebDriverException(e);
88+
}
89+
}
90+
91+
private void setPrivateFieldValue(String fieldName, Object newValue) {
92+
try {
93+
final Field f = getClass().getSuperclass().getDeclaredField(fieldName);
94+
f.setAccessible(true);
95+
f.set(this, newValue);
96+
} catch (NoSuchFieldException | IllegalAccessException e) {
97+
throw new WebDriverException(e);
98+
}
99+
}
100+
101+
private Map<String, CommandInfo> getAdditionalCommands() {
102+
//noinspection unchecked
103+
return getPrivateFieldValue("additionalCommands", Map.class);
104+
}
105+
106+
private CommandCodec<HttpRequest> getCommandCodec() {
107+
//noinspection unchecked
108+
return getPrivateFieldValue("commandCodec", CommandCodec.class);
109+
}
110+
111+
private void setCommandCodec(CommandCodec<HttpRequest> newCodec) {
112+
setPrivateFieldValue("commandCodec", newCodec);
113+
}
114+
115+
@Override
116+
public Response execute(Command command) throws WebDriverException {
78117
if (DriverCommand.NEW_SESSION.equals(command.getName())) {
79118
serviceOptional.ifPresent(driverService -> {
80119
try {
@@ -85,8 +124,9 @@ public AppiumCommandExecutor(Map<String, CommandInfo> additionalCommands,
85124
});
86125
}
87126

127+
Response response;
88128
try {
89-
return super.execute(command);
129+
response = super.execute(command);
90130
} catch (Throwable t) {
91131
Throwable rootCause = Throwables.getRootCause(t);
92132
if (rootCause instanceof ConnectException
@@ -107,5 +147,13 @@ public AppiumCommandExecutor(Map<String, CommandInfo> additionalCommands,
107147
serviceOptional.ifPresent(DriverService::stop);
108148
}
109149
}
150+
151+
if (DriverCommand.NEW_SESSION.equals(command.getName())
152+
&& getCommandCodec() instanceof W3CHttpCommandCodec) {
153+
setCommandCodec(new AppiumW3CHttpCommandCodec());
154+
getAdditionalCommands().forEach(this::defineCommand);
155+
}
156+
157+
return response;
110158
}
111159
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* See the NOTICE file distributed with this work for additional
5+
* information regarding copyright ownership.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appium.java_client.remote;
18+
19+
import org.openqa.selenium.remote.http.W3CHttpCommandCodec;
20+
21+
import java.util.Map;
22+
23+
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_ATTRIBUTE;
24+
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_LOCATION;
25+
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW;
26+
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_SIZE;
27+
import static org.openqa.selenium.remote.DriverCommand.GET_PAGE_SOURCE;
28+
import static org.openqa.selenium.remote.DriverCommand.IS_ELEMENT_DISPLAYED;
29+
import static org.openqa.selenium.remote.DriverCommand.SEND_KEYS_TO_ACTIVE_ELEMENT;
30+
import static org.openqa.selenium.remote.DriverCommand.SEND_KEYS_TO_ELEMENT;
31+
import static org.openqa.selenium.remote.DriverCommand.SET_ALERT_VALUE;
32+
import static org.openqa.selenium.remote.DriverCommand.SET_TIMEOUT;
33+
import static org.openqa.selenium.remote.DriverCommand.SUBMIT_ELEMENT;
34+
35+
36+
public class AppiumW3CHttpCommandCodec extends W3CHttpCommandCodec {
37+
38+
/**
39+
* This class overrides the built-in Selenium W3C commands codec,
40+
* since the latter hardcodes many commands in Javascript,
41+
* which does not work with Appium.
42+
* Check https://www.w3.org/TR/webdriver/ to see all available W3C
43+
* endpoints.
44+
*/
45+
public AppiumW3CHttpCommandCodec() {
46+
defineCommand(GET_ELEMENT_ATTRIBUTE, get("/session/:sessionId/element/:id/attribute/:name"));
47+
defineCommand(GET_PAGE_SOURCE, get("/session/:sessionId/source"));
48+
}
49+
50+
@Override
51+
public void alias(String commandName, String isAnAliasFor) {
52+
// This blocks parent constructor from undesirable aliases assigning
53+
switch (commandName) {
54+
case GET_ELEMENT_ATTRIBUTE:
55+
case GET_ELEMENT_LOCATION:
56+
case GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW:
57+
case GET_ELEMENT_SIZE:
58+
case IS_ELEMENT_DISPLAYED:
59+
case SUBMIT_ELEMENT:
60+
case GET_PAGE_SOURCE:
61+
return;
62+
default:
63+
super.alias(commandName, isAnAliasFor);
64+
break;
65+
}
66+
}
67+
68+
@Override
69+
protected Map<String, ?> amendParameters(String name, Map<String, ?> parameters) {
70+
// This blocks parent constructor from undesirable parameters amending
71+
switch (name) {
72+
case SEND_KEYS_TO_ACTIVE_ELEMENT:
73+
case SEND_KEYS_TO_ELEMENT:
74+
case SET_ALERT_VALUE:
75+
case SET_TIMEOUT:
76+
return super.amendParameters(name, parameters);
77+
default:
78+
return parameters;
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)