Providing W3C compliance#829
Providing W3C compliance#829TikhomirovSergey merged 15 commits intoappium:masterfrom TikhomirovSergey:master
Conversation
There's a lot of overlap between the ProtocolHandshake and the NewSessionPayload. Time to deal with this overlap between the two. The main change is to make the NewSessionPayload capable of writing to an Appendable. It does so by streaming the Capabilities used to create the payload as the OSS capabilities and those used by old geckodrivers, and then streaming every capability through transforms to generate spec compliant capabilities. There appears to be a bug where we always generate synthetic capabilities, but we can deal with that later.
Using the magic of Buck's "maven-importer" and the following maven coordinates: ``` 'org.seleniumhq.selenium:htmlunit-driver:jar:2.28.5' \ 'junit:junit:jar:4.12' \ 'net.bytebuddy:byte-buddy:jar:1.7.9' \ 'com.google.code.gson:gson:jar:2.8.2' \ 'com.google.guava:guava:jar:23.6-jre' \ 'org.apache.commons:commons-exec:jar:1.3' \ 'org.eclipse.jetty:jetty-security:jar:9.4.8.v20171121' \ 'org.testng:testng:jar:6.13.1' \ 'org.pantsbuild:jarjar:jar:1.6.5' \ 'org.eclipse.jetty:jetty-util:jar:9.4.8.v20171121' \ 'org.eclipse.jetty:jetty-server:jar:9.4.8.v20171121' \ 'org.eclipse.jetty:jetty-servlet:jar:9.4.8.v20171121' \ 'org.hamcrest:hamcrest-library:jar:1.3' \ 'com.github.javaparser:javaparser-core:jar:3.5.7' \ 'org.eclipse.jetty:jetty-jmx:jar:9.4.8.v20171121' \ 'net.jcip:jcip-annotations:jar:1.0' \ 'org.yaml:snakeyaml:jar:1.19' \ 'org.mockito:mockito-core:jar:2.13.0' \ 'io.netty:netty-all:jar:4.1.19.Final' \ 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:jar:2.1.5' \ 'org.littleshoot:littleproxy:jar:1.1.2' \ 'org.slf4j:slf4j-jdk14:jar:1.7.25' ``` The version of LittleProxy we use is a snapshot and is the old version from previously with dependencies updated.
|
|
|
|
||
| /** | ||
| * This is the flag which forces server to switch to the mobile WSONWP. | ||
| * If {@code false} when it is switched to W3C mode. |
| private final FileBackedOutputStream backingStore; | ||
| private final ImmutableSet<Dialect> dialects; | ||
|
|
||
| private static List<String> getAppiumCapabilities(Class<?> capabilityList) { |
There was a problem hiding this comment.
List<String> or List<Object>?
There was a problem hiding this comment.
@mykola-mokhnach
List<String>
private static List<String> getAppiumCapabilities(Class<?> capabilityList) {
return Arrays.stream(capabilityList.getDeclaredFields()).map(field -> {
field.setAccessible(true);
try {
return field.get(capabilityList).toString(); //<-!!!!
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
}).filter(s -> !FORCE_MJSONWP.equals(s)).collect(toList());
}| // We need to convert the capabilities into a new session payload. At this point we're dealing | ||
| // with references, so I'm Just Sure This Will Be Fine. | ||
| boolean forceMobileJSONWP = ofNullable(caps.getCapability(FORCE_MJSONWP)) | ||
| .map(o -> { |
There was a problem hiding this comment.
you could use ternary operator here
| } | ||
|
|
||
| public static NewSessionPayload create(Capabilities caps) throws IOException { | ||
| // We need to convert the capabilities into a new session payload. At this point we're dealing |
| return new NewSessionPayload(source, forceMobileJSONWP); | ||
| } | ||
|
|
||
| private NewSessionPayload(Reader source, boolean forceMobileJSONWP) throws IOException { |
There was a problem hiding this comment.
This copies a lot of stuff from the original selenium code. Is it somehow possible to avoid such duplication?
There was a problem hiding this comment.
for example using reflection
| // Opera: operaOptions | ||
| // SafariDriver: safari.options | ||
| // | ||
| // We can't use the constants defined in the classes because it would introduce circular |
There was a problem hiding this comment.
Do we need all the commented stuff above?
| public String getKey() { | ||
| String key = stringObjectEntry.getKey(); | ||
| if (APPIUM_CAPABILITIES.contains(key) && !forceMobileJSONWP) { | ||
| return "appium:" + key; |
There was a problem hiding this comment.
appium: might be a constant
| .map(this::applyTransforms) | ||
| .map(map -> map.entrySet().stream() | ||
| .filter(entry -> { | ||
| if (forceMobileJSONWP) { |
There was a problem hiding this comment.
ternary operator might be more useful here
There was a problem hiding this comment.
.filter(entry -> forceMobileJSONWP && ACCEPTED_W3C_PATTERNS.test(entry.getKey()) || !forceMobileJSONWP)
| return Stream.concat(fromOss, fromW3c).distinct(); | ||
| } | ||
|
|
||
| private Map<String, Object> convertOssToW3C(Map<String, Object> capabilities) { |
| name = input.nextName(); | ||
| if ("alwaysMatch".equals(name)) { | ||
| return input.read(MAP_TYPE); | ||
| } else { |
There was a problem hiding this comment.
this else seems redundant
| name = input.nextName(); | ||
| if ("firstMatch".equals(name)) { | ||
| return input.read(LIST_OF_MAPS_TYPE); | ||
| } else { |
| return null; | ||
| } | ||
|
|
||
| private Collection<Map<String, Object>> getFirstMatches() throws IOException { |
| return toReturn; | ||
| } | ||
|
|
||
| private Map<String, Object> getAlwaysMatch() throws IOException { |
| backingStore.reset(); | ||
| } | ||
|
|
||
| private Map<String, Object> getOss() throws IOException { |
| String name = input.nextName(); | ||
| if ("desiredCapabilities".equals(name)) { | ||
| return input.read(MAP_TYPE); | ||
| } else { |
| "Illegal key values seen in w3c capabilities: " + illegalKeys); | ||
| } | ||
| }) | ||
| .forEach(map -> {}); |
There was a problem hiding this comment.
what is the purpose of this operator?
| } | ||
|
|
||
| // Write the first capability we get as the desired capability. | ||
| json.name("desiredCapabilities"); |
There was a problem hiding this comment.
I'd rather put all there magic strings into constants
- removal of redundant code from the original NewSessionPayload
|
@SrinivasanTarget Could you try it on iOS? |
| /** | ||
| * Which mobile OS platform to use. | ||
| */ | ||
| String PLATFORM_NAME = "platformName"; |
There was a problem hiding this comment.
Where this constant is present now?
There was a problem hiding this comment.
in org.openqa.selenium.remote.CapabilityType
public interface MobileCapabilityType extends CapabilityType|
|
||
| public static NewSessionPayload create(Capabilities caps) throws IOException { | ||
| boolean forceMobileJSONWP = ofNullable(caps.getCapability(FORCE_MJSONWP)) | ||
| .map(o -> { |
There was a problem hiding this comment.
.map(o -> Boolean.class.isAssignableFrom(o.getClass()) && Boolean.class.cast(o))
| String name = input.nextName(); | ||
| if (DESIRED_CAPABILITIES.equals(name)) { | ||
| return input.read(MAP_TYPE); | ||
| } else { |
|
Updated. |
| return null; | ||
| } | ||
|
|
||
| private @Nullable Collection<Map<String, Object>> getFirstMatches() throws IOException { |
| name = input.nextName(); | ||
| if (FIRST_MATCH.equals(name)) { | ||
| return input.read(LIST_OF_MAPS_TYPE); | ||
| } else { |
There was a problem hiding this comment.
else is redundant. same comment to other similar blocks
SrinivasanTarget
left a comment
There was a problem hiding this comment.
Everything looks fine on iOS on both W3C and JSONWP
Change list
org.openqa.selenium.remote.NewSessionPayloadTypes of changes
Details
I am not sure that this solution is 100% good but it seem the goal is achived.
https://gist.github.com/TikhomirovSergey/0bcaf79e96cd1ef6644e5e8c2b975596
https://gist.github.com/TikhomirovSergey/5987f5c74e37110427b8f5651030aa3b