diff --git a/java/src/org/openqa/selenium/Proxy.java b/java/src/org/openqa/selenium/Proxy.java index 880f9e2a1af43..e906201621850 100644 --- a/java/src/org/openqa/selenium/Proxy.java +++ b/java/src/org/openqa/selenium/Proxy.java @@ -39,16 +39,27 @@ public enum ProxyType { // Keep these in sync with the Firefox preferences numbers: // http://kb.mozillazine.org/Network.proxy.type - DIRECT, // Direct connection, no proxy (default on Windows) - MANUAL, // Manual proxy settings (e.g. for httpProxy) - PAC, // Proxy auto-configuration from URL + DIRECT("direct"), // Direct connection, no proxy (default on Windows) + MANUAL("manual"), // Manual proxy settings (e.g. for httpProxy) + PAC("pac"), // Proxy auto-configuration from URL - RESERVED_1, // Never used (but reserved in Firefox) + RESERVED_1("reserved_1"), // Never used (but reserved in Firefox) - AUTODETECT, // Proxy auto-detection (presumably with WPAD) - SYSTEM, // Use system settings (default on Linux) + AUTODETECT("autodetect"), // Proxy auto-detection (presumably with WPAD) + SYSTEM("system"), // Use system settings (default on Linux) - UNSPECIFIED + UNSPECIFIED("unspecified"); + + private final String type; + + ProxyType(String type) { + this.type = type; + } + + @Override + public String toString() { + return String.valueOf(type); + } } private static final String PROXY_TYPE = "proxyType"; diff --git a/java/src/org/openqa/selenium/grid/node/CapabilityResponseEncoder.java b/java/src/org/openqa/selenium/grid/node/CapabilityResponseEncoder.java index 6f2bfe5626185..e26981cb69b8f 100644 --- a/java/src/org/openqa/selenium/grid/node/CapabilityResponseEncoder.java +++ b/java/src/org/openqa/selenium/grid/node/CapabilityResponseEncoder.java @@ -26,7 +26,6 @@ import org.openqa.selenium.internal.Require; import org.openqa.selenium.json.Json; import org.openqa.selenium.remote.Dialect; -import org.openqa.selenium.remote.ErrorCodes; import org.openqa.selenium.remote.SessionId; import java.util.Map; @@ -36,8 +35,6 @@ public class CapabilityResponseEncoder { private static final Json JSON = new Json(); - private static final ResponseEncoder, byte[]> JWP_ENCODER = - new Encoder(Dialect.OSS); private static final ResponseEncoder, byte[]> W3C_ENCODER = new Encoder(Dialect.W3C); @@ -47,9 +44,6 @@ private CapabilityResponseEncoder() { public static ResponseEncoder, byte[]> getEncoder(Dialect dialect) { switch (dialect) { - case OSS: - return JWP_ENCODER; - case W3C: return W3C_ENCODER; @@ -108,10 +102,6 @@ private static byte[] encodeAsResponse( Map toEncode; switch (dialect) { - case OSS: - toEncode = encodeJsonWireProtocol(id, capabilities, metadata); - break; - case W3C: toEncode = encodeW3C(id, capabilities, metadata); break; @@ -134,19 +124,5 @@ private static Map encodeW3C( "capabilities", capabilities)) .build(); } - - private static Map encodeJsonWireProtocol( - SessionId id, - Capabilities capabilities, - Map metadata) { - return ImmutableMap.builder() - .putAll(metadata) - .put("status", ErrorCodes.SUCCESS) - .put("sessionId", id) - .put("value", capabilities) - .build(); - - } - } } diff --git a/java/src/org/openqa/selenium/grid/node/ProtocolConvertingSession.java b/java/src/org/openqa/selenium/grid/node/DefaultActiveSession.java similarity index 56% rename from java/src/org/openqa/selenium/grid/node/ProtocolConvertingSession.java rename to java/src/org/openqa/selenium/grid/node/DefaultActiveSession.java index 0bea7db0e79b4..021a98810c1e4 100644 --- a/java/src/org/openqa/selenium/grid/node/ProtocolConvertingSession.java +++ b/java/src/org/openqa/selenium/grid/node/DefaultActiveSession.java @@ -1,24 +1,6 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - package org.openqa.selenium.grid.node; import org.openqa.selenium.Capabilities; -import org.openqa.selenium.grid.web.ProtocolConverter; import org.openqa.selenium.grid.web.ReverseProxyHandler; import org.openqa.selenium.internal.Require; import org.openqa.selenium.remote.Dialect; @@ -29,6 +11,7 @@ import org.openqa.selenium.remote.http.HttpResponse; import org.openqa.selenium.remote.tracing.Tracer; +import java.io.UncheckedIOException; import java.net.URL; import java.time.Instant; import java.util.stream.Collectors; @@ -36,12 +19,12 @@ import static org.openqa.selenium.remote.http.HttpMethod.DELETE; -public abstract class ProtocolConvertingSession extends BaseActiveSession { +public class DefaultActiveSession extends BaseActiveSession { private final HttpHandler handler; private final String killUrl; - protected ProtocolConvertingSession( + protected DefaultActiveSession( Tracer tracer, HttpClient client, SessionId id, @@ -55,17 +38,12 @@ protected ProtocolConvertingSession( Require.nonNull("HTTP client", client); - if (downstream.equals(upstream)) { - this.handler = new ReverseProxyHandler(tracer, client); - } else { - this.handler = new ProtocolConverter(tracer, client, downstream, upstream); - } - + this.handler = new ReverseProxyHandler(tracer, client); this.killUrl = "/session/" + id; } @Override - public HttpResponse execute(HttpRequest req) { + public HttpResponse execute(HttpRequest req) throws UncheckedIOException { String host = "host"; StreamSupport.stream(req.getHeaderNames().spliterator(), true) .filter(host::equalsIgnoreCase) @@ -78,4 +56,9 @@ public HttpResponse execute(HttpRequest req) { } return res; } + + @Override + public void stop() { + // no-op + } } diff --git a/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java b/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java index 934f40661d546..522d23ccff964 100644 --- a/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java +++ b/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java @@ -19,6 +19,7 @@ import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; +import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.PersistentCapabilities; import org.openqa.selenium.Platform; import org.openqa.selenium.SessionNotCreatedException; @@ -26,7 +27,7 @@ import org.openqa.selenium.devtools.CdpEndpointFinder; import org.openqa.selenium.grid.data.CreateSessionRequest; import org.openqa.selenium.grid.node.ActiveSession; -import org.openqa.selenium.grid.node.ProtocolConvertingSession; +import org.openqa.selenium.grid.node.DefaultActiveSession; import org.openqa.selenium.grid.node.SessionFactory; import org.openqa.selenium.internal.Either; import org.openqa.selenium.internal.Require; @@ -119,7 +120,7 @@ public Either apply(CreateSessionRequest sess Optional platformName = Optional.ofNullable(capabilities.getPlatformName()); if (platformName.isPresent()) { - capabilities = generalizePlatform(capabilities); + capabilities = removePlatform(capabilities); } CAPABILITIES.accept(span, capabilities); @@ -171,7 +172,7 @@ public Either apply(CreateSessionRequest sess span.addEvent("Driver service created session", attributeMap); return Either.right( - new ProtocolConvertingSession( + new DefaultActiveSession( tracer, client, new SessionId(response.getSessionId()), @@ -293,10 +294,12 @@ private Capabilities readVncEndpoint(Capabilities requestedCaps, Capabilities re return returnedCaps; } - // We set the platform to ANY before sending the caps to the driver because some drivers will + // We remove the platform before sending the caps to the driver because some drivers will // reject session requests when they cannot parse the platform. - private Capabilities generalizePlatform(Capabilities caps) { - return new PersistentCapabilities(caps).setCapability("platformName", Platform.ANY); + private Capabilities removePlatform(Capabilities caps) { + MutableCapabilities noPlatformName = new MutableCapabilities(new HashMap<>(caps.asMap())); + noPlatformName.setCapability("platformName", (String) null); + return new PersistentCapabilities(noPlatformName); } private Capabilities setInitialPlatform(Capabilities caps, Platform platform) { diff --git a/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java b/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java index 758c57deb87d9..8ad9ef3e20668 100644 --- a/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java +++ b/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java @@ -19,7 +19,7 @@ import org.openqa.selenium.Capabilities; import org.openqa.selenium.docker.Container; -import org.openqa.selenium.grid.node.ProtocolConvertingSession; +import org.openqa.selenium.grid.node.DefaultActiveSession; import org.openqa.selenium.internal.Require; import org.openqa.selenium.remote.Dialect; import org.openqa.selenium.remote.SessionId; @@ -35,7 +35,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -public class DockerSession extends ProtocolConvertingSession { +public class DockerSession extends DefaultActiveSession { private static final Logger LOG = Logger.getLogger(DockerSession.class.getName()); private final Container container; diff --git a/java/src/org/openqa/selenium/grid/node/relay/RelaySessionFactory.java b/java/src/org/openqa/selenium/grid/node/relay/RelaySessionFactory.java index 7e766c9fa0cd6..448cf0b73abcd 100644 --- a/java/src/org/openqa/selenium/grid/node/relay/RelaySessionFactory.java +++ b/java/src/org/openqa/selenium/grid/node/relay/RelaySessionFactory.java @@ -23,7 +23,7 @@ import org.openqa.selenium.WebDriverException; import org.openqa.selenium.grid.data.CreateSessionRequest; import org.openqa.selenium.grid.node.ActiveSession; -import org.openqa.selenium.grid.node.ProtocolConvertingSession; +import org.openqa.selenium.grid.node.DefaultActiveSession; import org.openqa.selenium.grid.node.SessionFactory; import org.openqa.selenium.internal.Debug; import org.openqa.selenium.internal.Either; @@ -172,7 +172,7 @@ public Either apply(CreateSessionRequest sess span.addEvent("Relay service created session", attributeMap); LOG.fine(String.format("Created session: %s - %s", response.getSessionId(), capabilities)); - return Either.right(new ProtocolConvertingSession( + return Either.right(new DefaultActiveSession( tracer, client, new SessionId(response.getSessionId()), diff --git a/java/src/org/openqa/selenium/grid/session/remote/RemoteSession.java b/java/src/org/openqa/selenium/grid/session/remote/RemoteSession.java index 64866228ef647..5efa60c479439 100644 --- a/java/src/org/openqa/selenium/grid/session/remote/RemoteSession.java +++ b/java/src/org/openqa/selenium/grid/session/remote/RemoteSession.java @@ -23,7 +23,6 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.grid.session.ActiveSession; import org.openqa.selenium.grid.session.SessionFactory; -import org.openqa.selenium.grid.web.ProtocolConverter; import org.openqa.selenium.grid.web.ReverseProxyHandler; import org.openqa.selenium.internal.Require; import org.openqa.selenium.io.TemporaryFilesystem; @@ -52,8 +51,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import static org.openqa.selenium.remote.Dialect.OSS; - /** * Abstract class designed to do things like protocol conversion. */ @@ -150,8 +147,11 @@ protected Optional performHandshake( codec = new ReverseProxyHandler(tracer, client); downstream = upstream; } else { - downstream = downstreamDialects.isEmpty() ? OSS : downstreamDialects.iterator().next(); - codec = new ProtocolConverter(tracer, client, downstream, upstream); + LOG.warning(String.format( + "Unable to match protocol versions. Found %s upstream but can handle %s", + upstream, + downstreamDialects)); + return Optional.empty(); } Response response = result.createResponse(); diff --git a/java/src/org/openqa/selenium/grid/web/ProtocolConverter.java b/java/src/org/openqa/selenium/grid/web/ProtocolConverter.java deleted file mode 100644 index 3e93c03433add..0000000000000 --- a/java/src/org/openqa/selenium/grid/web/ProtocolConverter.java +++ /dev/null @@ -1,240 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.grid.web; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.net.MediaType; - -import org.openqa.selenium.internal.Require; -import org.openqa.selenium.json.Json; -import org.openqa.selenium.remote.Command; -import org.openqa.selenium.remote.CommandCodec; -import org.openqa.selenium.remote.Dialect; -import org.openqa.selenium.remote.DriverCommand; -import org.openqa.selenium.remote.JsonToWebElementConverter; -import org.openqa.selenium.remote.Response; -import org.openqa.selenium.remote.ResponseCodec; -import org.openqa.selenium.remote.SessionId; -import org.openqa.selenium.remote.codec.jwp.JsonHttpCommandCodec; -import org.openqa.selenium.remote.codec.jwp.JsonHttpResponseCodec; -import org.openqa.selenium.remote.codec.w3c.W3CHttpCommandCodec; -import org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec; -import org.openqa.selenium.remote.http.HttpClient; -import org.openqa.selenium.remote.http.HttpHandler; -import org.openqa.selenium.remote.http.HttpRequest; -import org.openqa.selenium.remote.http.HttpResponse; -import org.openqa.selenium.remote.tracing.AttributeKey; -import org.openqa.selenium.remote.tracing.EventAttribute; -import org.openqa.selenium.remote.tracing.EventAttributeValue; -import org.openqa.selenium.remote.tracing.HttpTracing; -import org.openqa.selenium.remote.tracing.Span; -import org.openqa.selenium.remote.tracing.Status; -import org.openqa.selenium.remote.tracing.Tracer; - -import java.io.UncheckedIOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.function.Function; - -import static java.net.HttpURLConnection.HTTP_OK; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.openqa.selenium.json.Json.MAP_TYPE; -import static org.openqa.selenium.remote.Dialect.W3C; -import static org.openqa.selenium.remote.RemoteTags.SESSION_ID; -import static org.openqa.selenium.remote.RemoteTags.SESSION_ID_EVENT; -import static org.openqa.selenium.remote.http.Contents.bytes; -import static org.openqa.selenium.remote.http.Contents.string; -import static org.openqa.selenium.remote.tracing.HttpTracing.newSpanAsChildOf; -import static org.openqa.selenium.remote.tracing.Tags.HTTP_REQUEST; -import static org.openqa.selenium.remote.tracing.Tags.HTTP_REQUEST_EVENT; -import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE; -import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE_EVENT; -import static org.openqa.selenium.remote.tracing.Tags.KIND; - -public class ProtocolConverter implements HttpHandler { - - private static final Json JSON = new Json(); - private static final ImmutableSet IGNORED_REQ_HEADERS = ImmutableSet.builder() - .add("connection") - .add("content-length") - .add("content-type") - .add("keep-alive") - .add("proxy-authorization") - .add("proxy-authenticate") - .add("proxy-connection") - .add("te") - .add("trailer") - .add("transfer-encoding") - .add("upgrade") - .build(); - - private final Tracer tracer; - private final HttpClient client; - private final CommandCodec downstream; - private final CommandCodec upstream; - private final ResponseCodec downstreamResponse; - private final ResponseCodec upstreamResponse; - private final JsonToWebElementConverter converter; - private final Function newSessionConverter; - - public ProtocolConverter( - Tracer tracer, - HttpClient client, - Dialect downstream, - Dialect upstream) { - this.tracer = Require.nonNull("Tracer", tracer); - this.client = Require.nonNull("HTTP client", client); - - this.downstream = getCommandCodec(Require.nonNull("Downstream dialect", downstream)); - this.downstreamResponse = getResponseCodec(downstream); - - this.upstream = getCommandCodec(Require.nonNull("Upstream dialect", upstream)); - this.upstreamResponse = getResponseCodec(upstream); - - converter = new JsonToWebElementConverter(null); - - newSessionConverter = downstream == W3C ? this::createW3CNewSessionResponse : this::createJwpNewSessionResponse; - } - - @Override - public HttpResponse execute(HttpRequest req) throws UncheckedIOException { - try (Span span = newSpanAsChildOf(tracer, req, "protocol_converter")) { - Map attributeMap = new HashMap<>(); - attributeMap.put(AttributeKey.HTTP_HANDLER_CLASS.getKey(), - EventAttribute.setValue(getClass().getName())); - Command command = downstream.decode(req); - KIND.accept(span, Span.Kind.SERVER); - HTTP_REQUEST.accept(span, req); - HTTP_REQUEST_EVENT.accept(attributeMap, req); - SessionId sessionId = command.getSessionId(); - SESSION_ID.accept(span, sessionId); - SESSION_ID_EVENT.accept(attributeMap, sessionId); - String commandName = command.getName(); - span.setAttribute("command.name", commandName); - attributeMap.put("command.name", EventAttribute.setValue(commandName)); - attributeMap.put("downstream.command.parameters", EventAttribute.setValue(command.getParameters().toString())); - - // Massage the webelements - @SuppressWarnings("unchecked") - Map parameters = (Map) converter.apply(command.getParameters()); - command = new Command( - command.getSessionId(), - command.getName(), - parameters); - - attributeMap.put("upstream.command.parameters", EventAttribute.setValue(command.getParameters().toString())); - HttpRequest request = upstream.encode(command); - - HttpTracing.inject(tracer, span, request); - HttpResponse res = makeRequest(request); - if(!res.isSuccessful()) { - span.setAttribute("error", true); - span.setStatus(Status.UNKNOWN); - } - HTTP_RESPONSE.accept(span, res); - HTTP_RESPONSE_EVENT.accept(attributeMap, res); - - HttpResponse toReturn; - if (DriverCommand.NEW_SESSION.equals(command.getName()) && res.getStatus() == HTTP_OK) { - toReturn = newSessionConverter.apply(res); - } else { - Response decoded = upstreamResponse.decode(res); - toReturn = downstreamResponse.encode(HttpResponse::new, decoded); - } - - res.getHeaderNames().forEach(name -> { - if (!IGNORED_REQ_HEADERS.contains(name)) { - res.getHeaders(name).forEach(value -> toReturn.addHeader(name, value)); - } - }); - - span.addEvent("Protocol conversion completed", attributeMap); - return toReturn; - } - } - - @VisibleForTesting - HttpResponse makeRequest(HttpRequest request) { - return client.execute(request); - } - - private CommandCodec getCommandCodec(Dialect dialect) { - switch (dialect) { - case OSS: - return new JsonHttpCommandCodec(); - - case W3C: - return new W3CHttpCommandCodec(); - - default: - throw new IllegalStateException("Unknown dialect: " + dialect); - } - } - - private ResponseCodec getResponseCodec(Dialect dialect) { - switch (dialect) { - case OSS: - return new JsonHttpResponseCodec(); - - case W3C: - return new W3CHttpResponseCodec(); - - default: - throw new IllegalStateException("Unknown dialect: " + dialect); - } - } - - private HttpResponse createW3CNewSessionResponse(HttpResponse response) { - Map value = JSON.toType(string(response), MAP_TYPE); - - Require.state("Session id", value.get("sessionId")).nonNull(); - Require.state("Response payload", value.get("value")).instanceOf(Map.class); - - return createResponse(ImmutableMap.of( - "value", ImmutableMap.of( - "sessionId", value.get("sessionId"), - "capabilities", value.get("value")))); - } - - private HttpResponse createJwpNewSessionResponse(HttpResponse response) { - Map value = Objects.requireNonNull(Values.get(response, MAP_TYPE)); - - // Check to see if the values we need are set - Require.state("Session id", value.get("sessionId")).nonNull(); - Require.state("Response payload", value.get("capabilities")).instanceOf(Map.class); - - return createResponse(ImmutableMap.of( - "status", 0, - "sessionId", value.get("sessionId"), - "value", value.get("capabilities"))); - } - - - private HttpResponse createResponse(ImmutableMap toSend) { - byte[] bytes = JSON.toJson(toSend).getBytes(UTF_8); - - return new HttpResponse() - .setHeader("Content-Type", MediaType.JSON_UTF_8.toString()) - .setHeader("Content-Length", String.valueOf(bytes.length)) - .setContent(bytes(bytes)); - } - -} diff --git a/java/src/org/openqa/selenium/remote/Dialect.java b/java/src/org/openqa/selenium/remote/Dialect.java index bb58f494c46c1..ab49072108be5 100644 --- a/java/src/org/openqa/selenium/remote/Dialect.java +++ b/java/src/org/openqa/selenium/remote/Dialect.java @@ -19,35 +19,12 @@ import org.openqa.selenium.remote.http.HttpRequest; import org.openqa.selenium.remote.http.HttpResponse; -import org.openqa.selenium.remote.codec.jwp.JsonHttpCommandCodec; -import org.openqa.selenium.remote.codec.jwp.JsonHttpResponseCodec; import org.openqa.selenium.remote.codec.w3c.W3CHttpCommandCodec; import org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec; import java.util.ServiceLoader; public enum Dialect { - OSS { - @Override - public CommandCodec getCommandCodec() { - return bindAdditionalCommands(new JsonHttpCommandCodec()); - } - - @Override - public ResponseCodec getResponseCodec() { - return new JsonHttpResponseCodec(); - } - - @Override - public String getEncodedElementKey() { - return "ELEMENT"; - } - - @Override - public String getShadowRootElementKey() { - return "shadow-6066-11e4-a52e-4f735466cecf"; - } - }, W3C { @Override public CommandCodec getCommandCodec() { diff --git a/java/src/org/openqa/selenium/remote/DriverCommand.java b/java/src/org/openqa/selenium/remote/DriverCommand.java index 140d1f3681c4c..1bfc303c71546 100644 --- a/java/src/org/openqa/selenium/remote/DriverCommand.java +++ b/java/src/org/openqa/selenium/remote/DriverCommand.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; /** @@ -168,9 +169,7 @@ static CommandPayload NEW_SESSION(Capabilities capabilities) { Require.nonNull("Capabilities", capabilities); return new CommandPayload( NEW_SESSION, - ImmutableMap.of( - "capabilities", capabilities, - "desiredCapabilities", capabilities)); + ImmutableMap.of("capabilities", singleton(capabilities))); } static CommandPayload NEW_SESSION(Collection capabilities) { @@ -181,10 +180,7 @@ static CommandPayload NEW_SESSION(Collection capabilities) { return new CommandPayload( NEW_SESSION, - ImmutableMap.of( - "capabilities", - capabilities, - "desiredCapabilities", capabilities.iterator().next())); + ImmutableMap.of("capabilities", capabilities)); } static CommandPayload GET(String url) { diff --git a/java/src/org/openqa/selenium/remote/JsonWireProtocolResponse.java b/java/src/org/openqa/selenium/remote/JsonWireProtocolResponse.java deleted file mode 100644 index 2990fdc643127..0000000000000 --- a/java/src/org/openqa/selenium/remote/JsonWireProtocolResponse.java +++ /dev/null @@ -1,94 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.remote; - - -import java.util.Map; -import java.util.function.Function; - -class JsonWireProtocolResponse implements HandshakeResponse { - - @Override - public Function errorHandler() { - return tuple -> { - if (!(tuple.getData().containsKey("status"))) { - return null; - } - - Object rawStatus = tuple.getData().get("status"); - if (!(rawStatus instanceof Number)) { - return null; - } - - if (((Number) rawStatus).intValue() == 0) { - return null; - } - - if (!(tuple.getData().containsKey("value"))) { - return null; - } - Object value = tuple.getData().get("value"); - if (!(value instanceof Map)) { - return null; - } - - Response response = new Response(null); - response.setStatus(((Number) rawStatus).intValue()); - response.setValue(value); - - new ErrorHandler().throwIfResponseFailed(response, tuple.getRequestDuration().toMillis()); - // We never get this far. - return null; - }; - } - - @Override - public Function successHandler() { - return tuple -> { - Object rawStatus = tuple.getData().get("status"); - if (!(rawStatus instanceof Number)) { - return null; - } - - if (0 != ((Number) rawStatus).intValue()) { - return null; - } - - Object rawSessionId = tuple.getData().get("sessionId"); - Object rawCapabilities = tuple.getData().get("value"); - - if (!(rawSessionId instanceof String) || !(rawCapabilities instanceof Map)) { - return null; - } - - // Ensure Map keys are all strings. - for (Object key : ((Map) rawCapabilities).keySet()) { - if (!(key instanceof String)) { - return null; - } - } - - @SuppressWarnings("unchecked") Map - caps = - (Map) rawCapabilities; - - String sessionId = (String) rawSessionId; - return new ProtocolHandshake.Result(Dialect.OSS, sessionId, caps); - }; - } -} diff --git a/java/src/org/openqa/selenium/remote/NewSessionPayload.java b/java/src/org/openqa/selenium/remote/NewSessionPayload.java index f73a92f5ddbb1..3a45b5638d336 100644 --- a/java/src/org/openqa/selenium/remote/NewSessionPayload.java +++ b/java/src/org/openqa/selenium/remote/NewSessionPayload.java @@ -26,7 +26,6 @@ import com.google.common.io.CharSource; import com.google.common.io.CharStreams; import com.google.common.io.FileBackedOutputStream; - import org.openqa.selenium.AcceptedW3CCapabilityKeys; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; @@ -43,11 +42,13 @@ import java.io.UncheckedIOException; import java.io.Writer; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; import static java.nio.charset.StandardCharsets.UTF_8; @@ -56,7 +57,7 @@ public class NewSessionPayload implements Closeable { - private static final Dialect DEFAULT_DIALECT = Dialect.OSS; + private static final Dialect DEFAULT_DIALECT = Dialect.W3C; private static final Predicate ACCEPTED_W3C_PATTERNS = new AcceptedW3CCapabilityKeys(); private final Json json = new Json(); @@ -81,10 +82,7 @@ private NewSessionPayload(Reader source) { ImmutableSet.Builder dialects = ImmutableSet.builder(); try { - if (getOss() != null) { - dialects.add(Dialect.OSS); - } - if (getAlwaysMatch() != null || getFirstMatches() != null) { + if (isW3C()) { dialects.add(Dialect.W3C); } @@ -104,12 +102,24 @@ private NewSessionPayload(Reader source) { } public static NewSessionPayload create(Capabilities caps) { + Require.nonNull("Capabilities", caps); + return create(Collections.singleton(caps)); + } + + public static NewSessionPayload create(Collection caps) { // 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. - return create(ImmutableMap.of("desiredCapabilities", caps.asMap())); + return create(ImmutableMap.of( + "capabilities", ImmutableMap.of( + "firstMatch", caps.stream().map(Capabilities::asMap).collect(Collectors.toList())))); } public static NewSessionPayload create(Map source) { + // It is expected that the input to this method contains a properly formed + // "new session" request, and not just a random blob of data. Make sure + // this precondition is met before continuing. + Require.precondition(source.containsKey("capabilities"), "New session payload must contain capabilities"); + String json = new Json().toJson(Require.nonNull("Payload", source)); return new NewSessionPayload(new StringReader(json)); } @@ -173,18 +183,6 @@ public void writeTo(Appendable appendable) throws IOException { try (JsonOutput json = new Json().newOutput(appendable)) { json.beginObject(); - Map first = getOss(); - if (first == null) { - first = stream().findFirst() - .orElse(new ImmutableCapabilities()) - .asMap(); - } - Map ossFirst = new HashMap<>(first); - - // Write the first capability we get as the desired capability. - json.name("desiredCapabilities"); - json.write(ossFirst); - // Now for the w3c capabilities json.name("capabilities"); json.beginObject(); @@ -214,6 +212,8 @@ private void writeMetaData(JsonOutput out) throws IOException { String name = input.nextName(); switch (name) { case "capabilities": + // These fields were used by the (now defunct) JSON Wire Protocol, but we + // keep them here since we might see them from ancient clients. case "desiredCapabilities": case "requiredCapabilities": input.skipValue(); @@ -230,23 +230,12 @@ private void writeMetaData(JsonOutput out) throws IOException { /** * Stream the {@link Capabilities} encoded in the payload used to create this instance. The - * {@link Stream} will start with a {@link Capabilities} object matching the OSS capabilities, and - * will then expand each of the "{@code firstMatch}" and "{@code alwaysMatch}" contents as defined - * in the W3C WebDriver spec. - *

- * The OSS {@link Capabilities} are listed first because converting the OSS capabilities to the - * equivalent W3C capabilities isn't particularly easy, so it's hoped that this approach gives us - * the most compatible implementation. + * {@link Stream} will expand each of the "{@code firstMatch}" and "{@code alwaysMatch}" + * contents as defined in the W3C WebDriver spec. */ public Stream stream() { try { - // OSS first - Stream> oss = Stream.of(getOss()); - - // And now W3C - Stream> w3c = getW3C(); - - return Stream.concat(oss, w3c) + return getW3C() .filter(Objects::nonNull) .distinct() .map(ImmutableCapabilities::new); @@ -260,7 +249,7 @@ public Set getDownstreamDialects() { } public Map getMetadata() { - Set ignoredMetadataKeys = ImmutableSet.of("capabilities", "desiredCapabilities"); + Set ignoredMetadataKeys = ImmutableSet.of("capabilities", "desiredCapabilities", "requiredCapabilities"); CharSource charSource = backingStore.asByteSource().asCharSource(UTF_8); try (Reader reader = charSource.openBufferedStream(); @@ -299,36 +288,10 @@ public void close() { } } - private Map getOss() throws IOException { - CharSource charSource = backingStore.asByteSource().asCharSource(UTF_8); - try (Reader reader = charSource.openBufferedStream(); - JsonInput input = json.newInput(reader)) { - input.beginObject(); - while (input.hasNext()) { - String name = input.nextName(); - if ("desiredCapabilities".equals(name)) { - return input.read(MAP_TYPE); - } else { - input.skipValue(); - } - } - } - return null; - } - private Stream> getW3C() throws IOException { - // If there's an OSS value, generate a stream of capabilities from that using the transforms, - // then add magic to generate each of the w3c capabilities. For the sake of simplicity, we're - // going to make the (probably wrong) assumption we can hold all of the firstMatch values and - // alwaysMatch value in memory at the same time. - - Stream> fromOss; - Map ossCapabilities = getOss(); - if (ossCapabilities != null) { - fromOss = CapabilitiesUtils.makeW3CSafe(ossCapabilities); - } else { - fromOss = Stream.of(); - } + // For the sake of simplicity, we're going to make the (probably wrong) + // assumption we can hold all of the firstMatch values and alwaysMatch + // value in memory at the same time. Stream> fromW3c; Map alwaysMatch = getAlwaysMatch(); @@ -349,7 +312,24 @@ private Stream> getW3C() throws IOException { .map(first -> ImmutableMap.builder().putAll(always).putAll(first).build()); } - return Stream.concat(fromOss, fromW3c).distinct(); + return fromW3c.distinct(); + } + + private boolean isW3C() throws IOException { + CharSource charSource = backingStore.asByteSource().asCharSource(UTF_8); + try (Reader reader = charSource.openBufferedStream(); + JsonInput input = json.newInput(reader)) { + input.beginObject(); + while (input.hasNext()) { + String name = input.nextName(); + if ("capabilities".equals(name)) { + return true; + } else { + input.skipValue(); + } + } + } + return false; } private Map getAlwaysMatch() throws IOException { @@ -375,7 +355,7 @@ private Map getAlwaysMatch() throws IOException { } } } - return null; + return ImmutableMap.of(); } private Collection> getFirstMatches() throws IOException { @@ -401,7 +381,7 @@ private Collection> getFirstMatches() throws IOException { } } } - return null; + return ImmutableList.of(ImmutableMap.of()); } @Override diff --git a/java/src/org/openqa/selenium/remote/ProtocolHandshake.java b/java/src/org/openqa/selenium/remote/ProtocolHandshake.java index 39997238294fa..f9e8029ff5841 100644 --- a/java/src/org/openqa/selenium/remote/ProtocolHandshake.java +++ b/java/src/org/openqa/selenium/remote/ProtocolHandshake.java @@ -20,7 +20,6 @@ import com.google.common.io.CountingOutputStream; import com.google.common.io.FileBackedOutputStream; -import org.openqa.selenium.AcceptedW3CCapabilityKeys; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.Proxy; @@ -38,19 +37,19 @@ import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.Writer; -import java.util.List; +import java.util.Collection; import java.util.Map; import java.util.Objects; import java.util.function.Function; import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; import java.util.stream.Stream; import static com.google.common.net.HttpHeaders.CONTENT_LENGTH; import static com.google.common.net.HttpHeaders.CONTENT_TYPE; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.singleton; import static org.openqa.selenium.json.Json.JSON_UTF_8; import static org.openqa.selenium.remote.CapabilityType.PROXY; import static org.openqa.selenium.remote.http.Contents.string; @@ -60,8 +59,10 @@ public class ProtocolHandshake { private static final Logger LOG = Logger.getLogger(ProtocolHandshake.class.getName()); public Result createSession(HttpHandler client, Command command) throws IOException { - Capabilities desired = (Capabilities) command.getParameters().get("desiredCapabilities"); - desired = desired == null ? new ImmutableCapabilities() : desired; + @SuppressWarnings("unchecked") + Collection desired = (Collection) command.getParameters().get("capabilities"); + + desired = desired == null || desired.isEmpty() ? singleton(new ImmutableCapabilities()) : desired; try (NewSessionPayload payload = NewSessionPayload.create(desired)) { Either result = createSession(client, payload); @@ -69,19 +70,6 @@ public Result createSession(HttpHandler client, Command command) throws IOExcept if (result.isRight()) { Result toReturn = result.right(); LOG.log(Level.FINE, "Detected upstream dialect: {0}", toReturn.dialect); - - List invalid = desired.asMap().keySet() - .stream() - .filter(key -> !(new AcceptedW3CCapabilityKeys().test(key))) - .collect(Collectors.toList()); - - if (!invalid.isEmpty()) { - LOG.log(Level.WARNING, - () -> String.format("Support for Legacy Capabilities is deprecated; " + - "You are sending the following invalid capabilities: %s; " + - "Please update to W3C Syntax: https://www.selenium.dev/blog/2022/legacy-protocol-support/", - invalid)); - } return toReturn; } else { throw result.left(); @@ -149,9 +137,7 @@ private Either createSession(HttpHandler cli initialResponse.getStatusCode(), responseMessage))); } - return Stream.of( - new W3CHandshakeResponse().getResponseFunction(), - new JsonWireProtocolResponse().getResponseFunction()) + return Stream.of(new W3CHandshakeResponse().getResponseFunction()) .map(func -> func.apply(initialResponse)) .filter(Objects::nonNull) .findFirst() diff --git a/java/src/org/openqa/selenium/remote/RemoteWebElement.java b/java/src/org/openqa/selenium/remote/RemoteWebElement.java index ede155a932feb..43defb02a4113 100644 --- a/java/src/org/openqa/selenium/remote/RemoteWebElement.java +++ b/java/src/org/openqa/selenium/remote/RemoteWebElement.java @@ -412,7 +412,6 @@ public String toString() { public Map toJson() { return ImmutableMap.of( - Dialect.OSS.getEncodedElementKey(), getId(), Dialect.W3C.getEncodedElementKey(), getId()); } } diff --git a/java/src/org/openqa/selenium/remote/W3CHandshakeResponse.java b/java/src/org/openqa/selenium/remote/W3CHandshakeResponse.java index a269e58e3f7e3..7046c9b8f5fb5 100644 --- a/java/src/org/openqa/selenium/remote/W3CHandshakeResponse.java +++ b/java/src/org/openqa/selenium/remote/W3CHandshakeResponse.java @@ -72,10 +72,6 @@ public Function errorHandler @Override public Function successHandler() { return tuple -> { - if (tuple.getData().containsKey("status")) { - return null; - } - Object rawValue = tuple.getData().get("value"); if (!(rawValue instanceof Map)) { return null; diff --git a/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java b/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java index e2eb7d5eca04d..c291a367c38da 100644 --- a/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java +++ b/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java @@ -255,7 +255,6 @@ public HttpRequest encode(Command command) { HttpRequest request = new HttpRequest(spec.method, uri); if (HttpMethod.POST == spec.method) { - String content = json.toJson(parameters); byte[] data = content.getBytes(UTF_8); diff --git a/java/src/org/openqa/selenium/remote/codec/jwp/JsonHttpCommandCodec.java b/java/src/org/openqa/selenium/remote/codec/jwp/JsonHttpCommandCodec.java deleted file mode 100644 index 193108d56293b..0000000000000 --- a/java/src/org/openqa/selenium/remote/codec/jwp/JsonHttpCommandCodec.java +++ /dev/null @@ -1,152 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.remote.codec.jwp; - -import com.google.common.collect.ImmutableMap; - -import org.openqa.selenium.InvalidArgumentException; -import org.openqa.selenium.remote.DriverCommand; -import org.openqa.selenium.remote.codec.AbstractHttpCommandCodec; - -import java.util.Map; - -import static org.openqa.selenium.remote.DriverCommand.ACCEPT_ALERT; -import static org.openqa.selenium.remote.DriverCommand.CLEAR_LOCAL_STORAGE; -import static org.openqa.selenium.remote.DriverCommand.CLEAR_SESSION_STORAGE; -import static org.openqa.selenium.remote.DriverCommand.DISMISS_ALERT; -import static org.openqa.selenium.remote.DriverCommand.EXECUTE_ASYNC_SCRIPT; -import static org.openqa.selenium.remote.DriverCommand.EXECUTE_SCRIPT; -import static org.openqa.selenium.remote.DriverCommand.GET_ACTIVE_ELEMENT; -import static org.openqa.selenium.remote.DriverCommand.GET_ALERT_TEXT; -import static org.openqa.selenium.remote.DriverCommand.GET_CURRENT_WINDOW_HANDLE; -import static org.openqa.selenium.remote.DriverCommand.GET_CURRENT_WINDOW_POSITION; -import static org.openqa.selenium.remote.DriverCommand.GET_CURRENT_WINDOW_SIZE; -import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_ATTRIBUTE; -import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW; -import static org.openqa.selenium.remote.DriverCommand.GET_LOCAL_STORAGE_ITEM; -import static org.openqa.selenium.remote.DriverCommand.GET_LOCAL_STORAGE_KEYS; -import static org.openqa.selenium.remote.DriverCommand.GET_LOCAL_STORAGE_SIZE; -import static org.openqa.selenium.remote.DriverCommand.GET_PAGE_SOURCE; -import static org.openqa.selenium.remote.DriverCommand.GET_SESSION_STORAGE_ITEM; -import static org.openqa.selenium.remote.DriverCommand.GET_SESSION_STORAGE_KEYS; -import static org.openqa.selenium.remote.DriverCommand.GET_SESSION_STORAGE_SIZE; -import static org.openqa.selenium.remote.DriverCommand.GET_WINDOW_HANDLES; -import static org.openqa.selenium.remote.DriverCommand.IS_ELEMENT_DISPLAYED; -import static org.openqa.selenium.remote.DriverCommand.MAXIMIZE_CURRENT_WINDOW; -import static org.openqa.selenium.remote.DriverCommand.REMOVE_LOCAL_STORAGE_ITEM; -import static org.openqa.selenium.remote.DriverCommand.REMOVE_SESSION_STORAGE_ITEM; -import static org.openqa.selenium.remote.DriverCommand.SET_ALERT_VALUE; -import static org.openqa.selenium.remote.DriverCommand.SET_CURRENT_WINDOW_POSITION; -import static org.openqa.selenium.remote.DriverCommand.SET_CURRENT_WINDOW_SIZE; -import static org.openqa.selenium.remote.DriverCommand.SET_LOCAL_STORAGE_ITEM; -import static org.openqa.selenium.remote.DriverCommand.SET_SESSION_STORAGE_ITEM; -import static org.openqa.selenium.remote.DriverCommand.SUBMIT_ELEMENT; -import static org.openqa.selenium.remote.DriverCommand.UPLOAD_FILE; - -/** - * A command codec that adheres to the Selenium project's JSON/HTTP wire protocol. - * - * @see - * JSON wire protocol - */ -public class JsonHttpCommandCodec extends AbstractHttpCommandCodec { - - public JsonHttpCommandCodec() { - String sessionId = "/session/:sessionId"; - - String elementId = sessionId + "/element/:id"; - defineCommand(GET_ELEMENT_ATTRIBUTE, get(elementId + "/attribute/:name")); - defineCommand(GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW, get(elementId + "/location_in_view")); - defineCommand(IS_ELEMENT_DISPLAYED, get(elementId + "/displayed")); - defineCommand(SUBMIT_ELEMENT, post(elementId + "/submit")); - - defineCommand(EXECUTE_SCRIPT, post(sessionId + "/execute")); - defineCommand(EXECUTE_ASYNC_SCRIPT, post(sessionId + "/execute_async")); - - defineCommand(GET_PAGE_SOURCE, get(sessionId + "/source")); - - String windowHandle = sessionId + "/window/:windowHandle"; - defineCommand(MAXIMIZE_CURRENT_WINDOW, post(windowHandle + "/maximize")); - defineCommand(GET_CURRENT_WINDOW_POSITION, get(windowHandle + "/position")); - defineCommand(SET_CURRENT_WINDOW_POSITION, post(windowHandle + "/position")); - defineCommand(GET_CURRENT_WINDOW_SIZE, get(windowHandle + "/size")); - defineCommand(SET_CURRENT_WINDOW_SIZE, post(windowHandle + "/size")); - defineCommand(GET_CURRENT_WINDOW_HANDLE, get(sessionId + "/window_handle")); - defineCommand(GET_WINDOW_HANDLES, get(sessionId + "/window_handles")); - - defineCommand(ACCEPT_ALERT, post(sessionId + "/accept_alert")); - defineCommand(DISMISS_ALERT, post(sessionId + "/dismiss_alert")); - defineCommand(GET_ALERT_TEXT, get(sessionId + "/alert_text")); - defineCommand(SET_ALERT_VALUE, post(sessionId + "/alert_text")); - - defineCommand(UPLOAD_FILE, post(sessionId + "/file")); - - defineCommand(GET_ACTIVE_ELEMENT, post(sessionId + "/element/active")); - - String localStorage = sessionId + "/local_storage"; - defineCommand(CLEAR_LOCAL_STORAGE, delete(localStorage)); - defineCommand(GET_LOCAL_STORAGE_KEYS, get(localStorage)); - defineCommand(SET_LOCAL_STORAGE_ITEM, post(localStorage)); - defineCommand(REMOVE_LOCAL_STORAGE_ITEM, delete(localStorage + "/key/:key")); - defineCommand(GET_LOCAL_STORAGE_ITEM, get(localStorage + "/key/:key")); - defineCommand(GET_LOCAL_STORAGE_SIZE, get(localStorage + "/size")); - - String sessionStorage = sessionId + "/session_storage"; - defineCommand(CLEAR_SESSION_STORAGE, delete(sessionStorage)); - defineCommand(GET_SESSION_STORAGE_KEYS, get(sessionStorage)); - defineCommand(SET_SESSION_STORAGE_ITEM, post(sessionStorage)); - defineCommand(REMOVE_SESSION_STORAGE_ITEM, delete(sessionStorage + "/key/:key")); - defineCommand(GET_SESSION_STORAGE_ITEM, get(sessionStorage + "/key/:key")); - defineCommand(GET_SESSION_STORAGE_SIZE, get(sessionStorage + "/size")); - } - - @Override - protected Map amendParameters(String name, Map parameters) { - switch (name) { - case DriverCommand.GET_CURRENT_WINDOW_SIZE: - case DriverCommand.MAXIMIZE_CURRENT_WINDOW: - case DriverCommand.SET_CURRENT_WINDOW_SIZE: - case DriverCommand.SET_CURRENT_WINDOW_POSITION: - return ImmutableMap.builder() - .putAll(parameters) - .put("windowHandle", "current") - .put("handle", "current") - .build(); - - case DriverCommand.SET_TIMEOUT: - if (parameters.size() != 1) { - throw new InvalidArgumentException( - "The JSON wire protocol only supports setting one time out at a time"); - } - Map.Entry entry = parameters.entrySet().iterator().next(); - String type = entry.getKey(); - if ("pageLoad".equals(type)) { - type = "page load"; - } - return ImmutableMap.of("type", type, "ms", entry.getValue()); - - case DriverCommand.SWITCH_TO_WINDOW: - return ImmutableMap.builder() - .put("name", parameters.get("handle")) - .build(); - - default: - return parameters; - } - } -} diff --git a/java/src/org/openqa/selenium/remote/codec/jwp/JsonHttpResponseCodec.java b/java/src/org/openqa/selenium/remote/codec/jwp/JsonHttpResponseCodec.java deleted file mode 100644 index 205b2f7f503ef..0000000000000 --- a/java/src/org/openqa/selenium/remote/codec/jwp/JsonHttpResponseCodec.java +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.remote.codec.jwp; - -import org.openqa.selenium.remote.ErrorHandler; -import org.openqa.selenium.remote.Response; -import org.openqa.selenium.remote.codec.AbstractHttpResponseCodec; -import org.openqa.selenium.remote.JsonToWebElementConverter; - -import java.util.function.Function; - -/** - * A response codec that adheres to the Selenium project's JSON/HTTP wire protocol. - * - * @see JSON wire - * protocol - */ -public class JsonHttpResponseCodec extends AbstractHttpResponseCodec { - - private final ErrorHandler errorHandler = new ErrorHandler(true); - private final Function elementConverter = new JsonToWebElementConverter(null); - - @Override - protected Response reconstructValue(Response response) { - try { - errorHandler.throwIfResponseFailed(response, 0); - } catch (Exception e) { - response.setValue(e); - } - - response.setValue(elementConverter.apply(response.getValue())); - - return response; - } - - @Override - protected Object getValueToEncode(Response response) { - return response; - } -} diff --git a/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java b/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java index 48777449b0315..eee8f855617bc 100644 --- a/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java +++ b/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java @@ -100,8 +100,6 @@ */ public class W3CHttpCommandCodec extends AbstractHttpCommandCodec { - private final PointerInput mouse = new PointerInput(PointerInput.Kind.MOUSE, "mouse"); - public W3CHttpCommandCodec() { String sessionId = "/session/:sessionId"; diff --git a/java/src/org/openqa/selenium/remote/internal/WebElementToJsonConverter.java b/java/src/org/openqa/selenium/remote/internal/WebElementToJsonConverter.java index 680171fe33b0e..b70a1be5c4bd5 100644 --- a/java/src/org/openqa/selenium/remote/internal/WebElementToJsonConverter.java +++ b/java/src/org/openqa/selenium/remote/internal/WebElementToJsonConverter.java @@ -23,7 +23,6 @@ import org.openqa.selenium.remote.RemoteWebElement; import java.util.ArrayList; import java.util.List; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -54,7 +53,6 @@ public Object apply(Object arg) { if (arg instanceof RemoteWebElement) { return ImmutableMap.of( - Dialect.OSS.getEncodedElementKey(), ((RemoteWebElement) arg).getId(), Dialect.W3C.getEncodedElementKey(), ((RemoteWebElement) arg).getId()); } @@ -84,7 +82,7 @@ public Object apply(Object arg) { throw new IllegalArgumentException( "Argument is of an illegal type: " + arg.getClass().getName()); } - + private static List arrayToList(Object array) { List list = new ArrayList<>(); for (int i = 0; i < Array.getLength(array); i++) { diff --git a/java/src/org/openqa/selenium/remote/server/InMemorySession.java b/java/src/org/openqa/selenium/remote/server/InMemorySession.java index df9bc43de7ac5..0919d760b046f 100644 --- a/java/src/org/openqa/selenium/remote/server/InMemorySession.java +++ b/java/src/org/openqa/selenium/remote/server/InMemorySession.java @@ -93,7 +93,7 @@ public SessionId getId() { @Override public Dialect getUpstreamDialect() { - return Dialect.OSS; + return Dialect.W3C; } @Override @@ -149,8 +149,8 @@ public Optional apply(CreateSessionRequest sessionRequest) { // Prefer the OSS dialect. Set downstreamDialects = sessionRequest.getDownstreamDialects(); - Dialect downstream = downstreamDialects.contains(Dialect.OSS) || downstreamDialects.isEmpty() ? - Dialect.OSS : + Dialect downstream = downstreamDialects.contains(Dialect.W3C) || downstreamDialects.isEmpty() ? + Dialect.W3C : downstreamDialects.iterator().next(); return Optional.of( new InMemorySession(driver, sessionRequest.getDesiredCapabilities(), downstream)); diff --git a/java/src/org/openqa/selenium/remote/server/JsonHttpCommandHandler.java b/java/src/org/openqa/selenium/remote/server/JsonHttpCommandHandler.java index 0f259573bde60..5baf013cd8fe1 100644 --- a/java/src/org/openqa/selenium/remote/server/JsonHttpCommandHandler.java +++ b/java/src/org/openqa/selenium/remote/server/JsonHttpCommandHandler.java @@ -24,9 +24,8 @@ import org.openqa.selenium.remote.Response; import org.openqa.selenium.remote.ResponseCodec; import org.openqa.selenium.remote.SessionId; -import org.openqa.selenium.remote.codec.jwp.JsonHttpCommandCodec; -import org.openqa.selenium.remote.codec.jwp.JsonHttpResponseCodec; import org.openqa.selenium.remote.codec.w3c.W3CHttpCommandCodec; +import org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec; import org.openqa.selenium.remote.http.HttpRequest; import org.openqa.selenium.remote.http.HttpResponse; import org.openqa.selenium.remote.server.handler.AcceptAlert; @@ -238,9 +237,8 @@ public JsonHttpCommandHandler(DriverSessions sessions, Logger log) { this.sessions = sessions; this.log = log; this.commandCodecs = new LinkedHashSet<>(); - this.commandCodecs.add(new JsonHttpCommandCodec()); this.commandCodecs.add(new W3CHttpCommandCodec()); - this.responseCodec = new JsonHttpResponseCodec(); + this.responseCodec = new W3CHttpResponseCodec(); setUpMappings(); } diff --git a/java/test/org/openqa/selenium/ProxyTest.java b/java/test/org/openqa/selenium/ProxyTest.java index 5dc3acc697d83..96b62a6c08caa 100644 --- a/java/test/org/openqa/selenium/ProxyTest.java +++ b/java/test/org/openqa/selenium/ProxyTest.java @@ -155,7 +155,7 @@ void testAutodetectProxy() { Proxy proxy = new Proxy(); proxy.setAutodetect(true); - assertThat(proxy.getProxyType()).isEqualTo(AUTODETECT); + assertThat(proxy.getProxyType().name()).isEqualTo(AUTODETECT.name()); assertThat(proxy.isAutodetect()).isTrue(); assertThat(proxy.getFtpProxy()).isNull(); @@ -233,7 +233,7 @@ void manualProxyToJson() { Map json = proxy.toJson(); - assertThat(json.get("proxyType")).isEqualTo("MANUAL"); + assertThat(json.get("proxyType")).isEqualTo("manual"); assertThat(json.get("ftpProxy")).isEqualTo("ftp.proxy"); assertThat(json.get("httpProxy")).isEqualTo("http.proxy:1234"); assertThat(json.get("sslProxy")).isEqualTo("ssl.proxy"); @@ -275,7 +275,7 @@ void pacProxyToJson() { Map json = proxy.toJson(); - assertThat(json.get("proxyType")).isEqualTo("PAC"); + assertThat(json.get("proxyType")).isEqualTo("pac"); assertThat(json.get("proxyAutoconfigUrl")).isEqualTo("http://aaa/bbb.pac"); assertThat(json.entrySet()).hasSize(2); } @@ -310,7 +310,7 @@ void autodetectProxyToJson() { Map json = proxy.toJson(); - assertThat(json.get("proxyType")).isEqualTo("AUTODETECT"); + assertThat(json.get("proxyType")).isEqualTo("autodetect"); assertThat((Boolean) json.get("autodetect")).isTrue(); assertThat(json.entrySet()).hasSize(2); } @@ -343,7 +343,7 @@ void systemProxyToJson() { Map json = proxy.toJson(); - assertThat(json.get("proxyType")).isEqualTo("SYSTEM"); + assertThat(json.get("proxyType")).isEqualTo("system"); assertThat(json.entrySet()).hasSize(1); } @@ -375,7 +375,7 @@ void directProxyToJson() { Map json = proxy.toJson(); - assertThat(json.get("proxyType")).isEqualTo("DIRECT"); + assertThat(json.get("proxyType")).isEqualTo("direct"); assertThat(json.entrySet()).hasSize(1); } diff --git a/java/test/org/openqa/selenium/UnexpectedAlertBehaviorTest.java b/java/test/org/openqa/selenium/UnexpectedAlertBehaviorTest.java index ade25be4a205b..05e7975500ced 100644 --- a/java/test/org/openqa/selenium/UnexpectedAlertBehaviorTest.java +++ b/java/test/org/openqa/selenium/UnexpectedAlertBehaviorTest.java @@ -20,7 +20,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.openqa.selenium.UnexpectedAlertBehaviour.IGNORE; import static org.openqa.selenium.WaitingConditions.elementTextToEqual; -import static org.openqa.selenium.remote.CapabilityType.UNEXPECTED_ALERT_BEHAVIOUR; +import static org.openqa.selenium.remote.CapabilityType.UNHANDLED_PROMPT_BEHAVIOUR; import static org.openqa.selenium.testing.drivers.Browser.CHROME; import static org.openqa.selenium.testing.drivers.Browser.EDGE; import static org.openqa.selenium.testing.drivers.Browser.HTMLUNIT; @@ -39,7 +39,7 @@ class UnexpectedAlertBehaviorTest extends JupiterTestBase { @Test - @Ignore(value = CHROME, reason = "Legacy behaviour, not W3C conformant") + @Ignore(value = CHROME, reason = "Legacy behaviour, not W3C conformance") @Ignore(value = EDGE, reason = "Legacy behaviour, not W3C conformant") @Ignore(value = HTMLUNIT, reason = "Legacy behaviour, not W3C conformant") @NoDriverBeforeTest @@ -98,7 +98,7 @@ private void runScenarioWithUnhandledAlert( boolean silently) { Capabilities caps = behaviour == null ? new ImmutableCapabilities() - : new ImmutableCapabilities(UNEXPECTED_ALERT_BEHAVIOUR, behaviour); + : new ImmutableCapabilities(UNHANDLED_PROMPT_BEHAVIOUR, behaviour); createNewDriver(caps); driver.get(pages.alertsPage); diff --git a/java/test/org/openqa/selenium/grid/graphql/GraphqlHandlerTest.java b/java/test/org/openqa/selenium/grid/graphql/GraphqlHandlerTest.java index 1badf7a12110c..05ed05d53bffa 100644 --- a/java/test/org/openqa/selenium/grid/graphql/GraphqlHandlerTest.java +++ b/java/test/org/openqa/selenium/grid/graphql/GraphqlHandlerTest.java @@ -76,7 +76,6 @@ import static org.assertj.core.api.InstanceOfAssertFactories.LIST; import static org.assertj.core.api.InstanceOfAssertFactories.MAP; import static org.openqa.selenium.json.Json.MAP_TYPE; -import static org.openqa.selenium.remote.Dialect.OSS; import static org.openqa.selenium.remote.Dialect.W3C; import static org.openqa.selenium.remote.http.HttpMethod.GET; @@ -112,7 +111,7 @@ public void setupGrid() { sessionRequest = new SessionRequest( new RequestId(UUID.randomUUID()), Instant.now(), - Set.of(OSS, W3C), + Set.of(W3C), Set.of(caps), Map.of(), Map.of()); diff --git a/java/test/org/openqa/selenium/grid/node/local/CreateSessionTest.java b/java/test/org/openqa/selenium/grid/node/local/CreateSessionTest.java index a7d31090a9426..191713ada68dc 100644 --- a/java/test/org/openqa/selenium/grid/node/local/CreateSessionTest.java +++ b/java/test/org/openqa/selenium/grid/node/local/CreateSessionTest.java @@ -32,7 +32,6 @@ import org.openqa.selenium.grid.testing.TestSessionFactory; import org.openqa.selenium.internal.Either; import org.openqa.selenium.json.Json; -import org.openqa.selenium.remote.ErrorCodes; import org.openqa.selenium.remote.http.HttpRequest; import org.openqa.selenium.remote.tracing.DefaultTestTracer; @@ -44,7 +43,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.openqa.selenium.json.Json.MAP_TYPE; -import static org.openqa.selenium.remote.Dialect.OSS; import static org.openqa.selenium.remote.Dialect.W3C; import static org.openqa.selenium.remote.http.Contents.utf8String; import static org.openqa.selenium.remote.http.HttpMethod.POST; @@ -110,51 +108,6 @@ void ifOnlyW3CPayloadSentAndRemoteEndIsJWPOnlyFailSessionCreationIfJWPNotConfigu // TODO: implement ifOnlyW3CPayloadSentAndRemoteEndIsJWPOnlyFailSessionCreationIfJWPNotConfigured test } - @Test - void ifOnlyJWPPayloadSentResponseShouldBeJWPOnlyIfJWPConfigured() - throws URISyntaxException { - String payload = json.toJson(ImmutableMap.of( - "desiredCapabilities", ImmutableMap.of("cheese", "brie"))); - - HttpRequest request = new HttpRequest(POST, "/session"); - request.setContent(utf8String(payload)); - - URI uri = new URI("http://example.com"); - - Node node = LocalNode.builder( - DefaultTestTracer.createTracer(), - new GuavaEventBus(), - uri, - uri, - registrationSecret) - .add(stereotype, new TestSessionFactory((id, caps) -> new Session(id, uri, new ImmutableCapabilities(), caps, Instant.now()))) - .build(); - - Either response = node.newSession( - new CreateSessionRequest( - ImmutableSet.of(OSS), - stereotype, - ImmutableMap.of())); - - if (response.isRight()) { - CreateSessionResponse sessionResponse = response.right(); - Map all = json.toType( - new String(sessionResponse.getDownstreamEncodedResponse(), UTF_8), - MAP_TYPE); - - // The status field is used by local ends to determine whether or not the session is a JWP one. - assertThat(all.get("status")).matches(obj -> ((Number) obj).intValue() == ErrorCodes.SUCCESS); - - // The session id is a top level field - assertThat(all.get("sessionId")).isInstanceOf(String.class); - - // And the value should contain the capabilities. - assertThat(all.get("value")).isInstanceOf(Map.class); - } else { - throw new AssertionError("Unable to create session" + response.left().getMessage()); - } - } - @Test void shouldPreferUsingTheW3CProtocol() throws URISyntaxException { String payload = json.toJson(ImmutableMap.of( diff --git a/java/test/org/openqa/selenium/grid/router/EndToEndTest.java b/java/test/org/openqa/selenium/grid/router/EndToEndTest.java index b3fc423a06b6d..696b324038e43 100644 --- a/java/test/org/openqa/selenium/grid/router/EndToEndTest.java +++ b/java/test/org/openqa/selenium/grid/router/EndToEndTest.java @@ -171,7 +171,7 @@ void success(Supplier values) { setFields(values); // The node added only has a single node. Make sure we can start and stop sessions. - Capabilities caps = new ImmutableCapabilities("browserName", "cheese", "type", "cheddar"); + Capabilities caps = new ImmutableCapabilities("browserName", "cheese", "se:type", "cheddar"); WebDriver driver = new RemoteWebDriver(server.getUrl(), caps); driver.get("http://www.google.com"); @@ -187,7 +187,7 @@ void exerciseDriver(Supplier values) { setFields(values); // The node added only has a single node. Make sure we can start and stop sessions. - Capabilities caps = new ImmutableCapabilities("browserName", "cheese", "type", "cheddar"); + Capabilities caps = new ImmutableCapabilities("browserName", "cheese", "se:type", "cheddar"); WebDriver driver = new RemoteWebDriver(server.getUrl(), caps); driver.get("http://www.google.com"); @@ -255,33 +255,6 @@ void shouldRejectSessionRequestIfCapsNotSupported(Supplier values) { } } - @ParameterizedTest - @MethodSource("data") - void shouldAllowPassthroughForJWPMode(Supplier values) { - setFields(values); - - HttpRequest request = new HttpRequest(POST, "/session"); - request.setContent(asJson( - ImmutableMap.of( - "desiredCapabilities", ImmutableMap.of( - "browserName", "cheese")))); - - HttpResponse response = client.execute(request); - - assertEquals(200, response.getStatus()); - - Map topLevel = json.toType(string(response), MAP_TYPE); - - // There should be a numeric status field - assertEquals(0L, topLevel.get("status"), topLevel.toString()); - // The session id - assertTrue(topLevel.containsKey("sessionId"), string(request)); - - // And the value should be the capabilities. - Map value = (Map) topLevel.get("value"); - assertEquals("cheese", value.get("browserName"), string(request)); - } - @ParameterizedTest @MethodSource("data") void shouldDoProtocolTranslationFromW3CLocalEndToJWPRemoteEnd(Supplier values) { diff --git a/java/test/org/openqa/selenium/grid/router/ReverseProxyEndToEndTest.java b/java/test/org/openqa/selenium/grid/router/ReverseProxyEndToEndTest.java index 0d0449773d506..59f012625d999 100644 --- a/java/test/org/openqa/selenium/grid/router/ReverseProxyEndToEndTest.java +++ b/java/test/org/openqa/selenium/grid/router/ReverseProxyEndToEndTest.java @@ -178,7 +178,7 @@ void success(Supplier values) { setFields(values); // The node added only has a single node. Make sure we can start and stop sessions. - Capabilities caps = new ImmutableCapabilities("browserName", "cheese", "type", "cheddar"); + Capabilities caps = new ImmutableCapabilities("browserName", "cheese", "se:type", "cheddar"); URL url = url(server); WebDriver driver = new RemoteWebDriver(url, caps); driver.get(gridUi(server)); diff --git a/java/test/org/openqa/selenium/grid/router/RouterTest.java b/java/test/org/openqa/selenium/grid/router/RouterTest.java index 4116b86a2d188..6f0de2133e98b 100644 --- a/java/test/org/openqa/selenium/grid/router/RouterTest.java +++ b/java/test/org/openqa/selenium/grid/router/RouterTest.java @@ -73,7 +73,6 @@ import static org.openqa.selenium.grid.data.Availability.DOWN; import static org.openqa.selenium.grid.data.Availability.UP; import static org.openqa.selenium.json.Json.MAP_TYPE; -import static org.openqa.selenium.remote.Dialect.OSS; import static org.openqa.selenium.remote.Dialect.W3C; import static org.openqa.selenium.remote.http.HttpMethod.GET; @@ -255,7 +254,7 @@ void ifNodesHaveSpareSlotsButAlreadyHaveMaxSessionsGridIsNotReady() SessionRequest sessionRequest = new SessionRequest( new RequestId(UUID.randomUUID()), Instant.now(), - ImmutableSet.of(OSS, W3C), + ImmutableSet.of(W3C), ImmutableSet.of(chromeCapabilities), ImmutableMap.of(), ImmutableMap.of()); diff --git a/java/test/org/openqa/selenium/grid/web/ProtocolConverterTest.java b/java/test/org/openqa/selenium/grid/web/ProtocolConverterTest.java deleted file mode 100644 index 7934ca013a9fa..0000000000000 --- a/java/test/org/openqa/selenium/grid/web/ProtocolConverterTest.java +++ /dev/null @@ -1,315 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.grid.web; - - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.net.MediaType; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.openqa.selenium.WebDriverException; -import org.openqa.selenium.json.Json; -import org.openqa.selenium.remote.Command; -import org.openqa.selenium.remote.DriverCommand; -import org.openqa.selenium.remote.SessionId; -import org.openqa.selenium.remote.codec.jwp.JsonHttpCommandCodec; -import org.openqa.selenium.remote.codec.w3c.W3CHttpCommandCodec; -import org.openqa.selenium.remote.http.HttpClient; -import org.openqa.selenium.remote.http.HttpHandler; -import org.openqa.selenium.remote.http.HttpRequest; -import org.openqa.selenium.remote.http.HttpResponse; -import org.openqa.selenium.remote.tracing.DefaultTestTracer; -import org.openqa.selenium.remote.tracing.Tracer; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; - -import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.openqa.selenium.json.Json.MAP_TYPE; -import static org.openqa.selenium.remote.Dialect.OSS; -import static org.openqa.selenium.remote.Dialect.W3C; -import static org.openqa.selenium.remote.ErrorCodes.UNHANDLED_ERROR; -import static org.openqa.selenium.remote.http.Contents.asJson; -import static org.openqa.selenium.remote.http.Contents.bytes; -import static org.openqa.selenium.remote.http.Contents.string; -import static org.openqa.selenium.remote.http.Contents.utf8String; -import static org.openqa.selenium.remote.http.HttpMethod.POST; - - -class ProtocolConverterTest { - - private final Json json = new Json(); - private final Tracer tracer = DefaultTestTracer.createTracer(); - - @Test - void shouldRoundTripASimpleCommand() throws IOException { - SessionId sessionId = new SessionId("1234567"); - - HttpHandler handler = new ProtocolConverter( - tracer, - HttpClient.Factory.createDefault().createClient(new URL("http://example.com/wd/hub")), - W3C, - OSS) { - @Override - protected HttpResponse makeRequest(HttpRequest request) { - HttpResponse response = new HttpResponse(); - - response.setHeader("Content-Type", MediaType.JSON_UTF_8.toString()); - response.setHeader("Cache-Control", "none"); - - Map obj = new HashMap<>(); - obj.put("sessionId", sessionId.toString()); - obj.put("status", 0); - obj.put("value", null); - String payload = json.toJson(obj); - response.setContent(utf8String(payload)); - - return response; - } - }; - - Command command = new Command( - sessionId, - DriverCommand.GET, - ImmutableMap.of("url", "http://example.com/cheese")); - - HttpRequest w3cRequest = new W3CHttpCommandCodec().encode(command); - - HttpResponse resp = handler.execute(w3cRequest); - - assertEquals(MediaType.JSON_UTF_8, MediaType.parse(resp.getHeader("Content-type"))); - assertEquals(HttpURLConnection.HTTP_OK, resp.getStatus()); - - Map parsed = json.toType(string(resp), MAP_TYPE); - assertNull(parsed.get("sessionId"), parsed.toString()); - assertTrue(parsed.containsKey("value"), parsed.toString()); - assertNull(parsed.get("value"), parsed.toString()); - } - - @Test - void shouldAliasAComplexCommand() throws IOException { - SessionId sessionId = new SessionId("1234567"); - - // Downstream is JSON, upstream is W3C. This way we can force "isDisplayed" to become JS - // execution. - HttpHandler handler = new ProtocolConverter( - tracer, - HttpClient.Factory.createDefault().createClient(new URL("http://example.com/wd/hub")), - OSS, - W3C) { - @Override - protected HttpResponse makeRequest(HttpRequest request) { - assertEquals(String.format("/session/%s/execute/sync", sessionId), request.getUri()); - Map params = json.toType(string(request), MAP_TYPE); - - assertEquals( - ImmutableList.of( - ImmutableMap.of(W3C.getEncodedElementKey(), "4567890")), - params.get("args")); - - HttpResponse response = new HttpResponse(); - - response.setHeader("Content-Type", MediaType.JSON_UTF_8.toString()); - response.setHeader("Cache-Control", "none"); - - Map obj = ImmutableMap.of( - "sessionId", sessionId.toString(), - "status", 0, - "value", true); - String payload = json.toJson(obj); - response.setContent(utf8String(payload)); - - return response; - } - }; - - Command command = new Command( - sessionId, - DriverCommand.IS_ELEMENT_DISPLAYED, - ImmutableMap.of("id", "4567890")); - - HttpRequest w3cRequest = new JsonHttpCommandCodec().encode(command); - - HttpResponse resp = handler.execute(w3cRequest); - - assertEquals(MediaType.JSON_UTF_8, MediaType.parse(resp.getHeader("Content-type"))); - assertEquals(HttpURLConnection.HTTP_OK, resp.getStatus()); - - Map parsed = json.toType(string(resp), MAP_TYPE); - assertNull(parsed.get("sessionId")); - assertTrue(parsed.containsKey("value")); - assertEquals(true, parsed.get("value")); - } - - @Test - void shouldConvertAnException() throws IOException { - // Json upstream, w3c downstream - SessionId sessionId = new SessionId("1234567"); - - HttpHandler handler = new ProtocolConverter( - tracer, - HttpClient.Factory.createDefault().createClient(new URL("http://example.com/wd/hub")), - W3C, - OSS) { - @Override - protected HttpResponse makeRequest(HttpRequest request) { - HttpResponse response = new HttpResponse(); - - response.setHeader("Content-Type", MediaType.JSON_UTF_8.toString()); - response.setHeader("Cache-Control", "none"); - - String payload = new Json().toJson( - ImmutableMap.of( - "sessionId", sessionId.toString(), - "status", UNHANDLED_ERROR, - "value", new WebDriverException("I love cheese and peas"))); - response.setContent(utf8String(payload)); - response.setStatus(HTTP_INTERNAL_ERROR); - - return response; - } - }; - - Command command = new Command( - sessionId, - DriverCommand.GET, - ImmutableMap.of("url", "http://example.com/cheese")); - - HttpRequest w3cRequest = new W3CHttpCommandCodec().encode(command); - - HttpResponse resp = handler.execute(w3cRequest); - - assertEquals(MediaType.JSON_UTF_8, MediaType.parse(resp.getHeader("Content-type"))); - assertEquals(HTTP_INTERNAL_ERROR, resp.getStatus()); - - Map parsed = json.toType(string(resp), MAP_TYPE); - assertNull(parsed.get("sessionId")); - assertTrue(parsed.containsKey("value")); - @SuppressWarnings("unchecked") Map value = - (Map) parsed.get("value"); - System.out.println("value = " + value.keySet()); - assertEquals("unknown error", value.get("error")); - assertTrue(((String) value.get("message")).startsWith("I love cheese and peas")); - } - - @Test - void newJwpSessionResponseShouldBeCorrectlyConvertedToW3C() { - Map jwpNewSession = ImmutableMap.of("desiredCapabilities", ImmutableMap.of("cheese", "brie")); - - Map w3cResponse = ImmutableMap.of( - "value", ImmutableMap.of( - "sessionId", "i like cheese very much", - "capabilities", ImmutableMap.of("cheese", "brie"))); - - HttpClient client = mock(HttpClient.class); - Mockito.when(client.execute(any())).thenReturn(new HttpResponse().setContent(asJson(w3cResponse))); - - ProtocolConverter converter = new ProtocolConverter(tracer, client, OSS, W3C); - - HttpResponse response = converter.execute(new HttpRequest(POST, "/session").setContent(asJson(jwpNewSession))); - - Map convertedResponse = json.toType(string(response), MAP_TYPE); - - assertThat(convertedResponse.get("sessionId")).isEqualTo("i like cheese very much"); - assertThat(convertedResponse.get("status")).isEqualTo(0L); - assertThat(convertedResponse.get("value")).isEqualTo(ImmutableMap.of("cheese", "brie")); - } - - @Test - void newW3CSessionResponseShouldBeCorrectlyConvertedToJwp() { - Map w3cNewSession = ImmutableMap.of( - "capabilities", ImmutableMap.of()); - - Map jwpResponse = ImmutableMap.of( - "status", 0, - "sessionId", "i like cheese very much", - "value", ImmutableMap.of("cheese", "brie")); - - HttpClient client = mock(HttpClient.class); - Mockito.when(client.execute(any())).thenReturn(new HttpResponse().setContent(asJson(jwpResponse))); - - ProtocolConverter converter = new ProtocolConverter(tracer, client, W3C, OSS); - - HttpResponse response = converter.execute(new HttpRequest(POST, "/session").setContent(asJson(w3cNewSession))); - - Map convertedResponse = json.toType(string(response), MAP_TYPE); - Map value = (Map) convertedResponse.get("value"); - assertThat(value.get("capabilities")) - .as("capabilities: " + convertedResponse) - .isEqualTo(jwpResponse.get("value")); - assertThat(value.get("sessionId")) - .as("session id: " + convertedResponse) - .isEqualTo(jwpResponse.get("sessionId")); - } - - @Test - void newJwpSessionResponseShouldBeConvertedToW3CCorrectly() { - Map w3cResponse = ImmutableMap.of( - "value", ImmutableMap.of( - "capabilities", ImmutableMap.of("cheese", "brie"), - "sessionId", "i like cheese very much")); - - Map jwpNewSession = ImmutableMap.of( - "desiredCapabilities", ImmutableMap.of()); - - HttpClient client = mock(HttpClient.class); - Mockito.when(client.execute(any())).thenReturn(new HttpResponse().setContent(asJson(w3cResponse))); - - ProtocolConverter converter = new ProtocolConverter(tracer, client, OSS, W3C); - - HttpResponse response = converter.execute(new HttpRequest(POST, "/session").setContent(asJson(jwpNewSession))); - - Map convertedResponse = json.toType(string(response), MAP_TYPE); - assertThat(convertedResponse.get("status")).isEqualTo(0L); - assertThat(convertedResponse.get("sessionId")).isEqualTo("i like cheese very much"); - assertThat(convertedResponse.get("value")).isEqualTo(ImmutableMap.of("cheese", "brie")); - } - - @Test - void contentLengthShouldBeSetCorrectlyOnSuccessfulNewSessionRequest() { - Map w3cResponse = ImmutableMap.of( - "value", ImmutableMap.of( - "capabilities", ImmutableMap.of("cheese", "brie"), - "sessionId", "i like cheese very much")); - byte[] bytes = json.toJson(w3cResponse).getBytes(UTF_8); - - HttpClient client = mock(HttpClient.class); - Mockito.when(client.execute(any())) - .thenReturn( - new HttpResponse().setHeader("Content-Length", String.valueOf(bytes.length)).setContent(bytes(bytes))); - - ProtocolConverter converter = new ProtocolConverter(tracer, client, OSS, W3C); - - HttpResponse response = converter.execute( - new HttpRequest(POST, "/session") - .setContent(asJson(ImmutableMap.of("desiredCapabilities", ImmutableMap.of())))); - - assertThat(response.getHeader("Content-Length")).isNotEqualTo(String.valueOf(bytes.length)); - } -} diff --git a/java/test/org/openqa/selenium/remote/JsonWireProtocolResponseTest.java b/java/test/org/openqa/selenium/remote/JsonWireProtocolResponseTest.java deleted file mode 100644 index a0807f98ed94a..0000000000000 --- a/java/test/org/openqa/selenium/remote/JsonWireProtocolResponseTest.java +++ /dev/null @@ -1,119 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.remote; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import com.google.common.collect.ImmutableMap; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Tag; -import org.openqa.selenium.Capabilities; -import org.openqa.selenium.ImmutableCapabilities; -import org.openqa.selenium.SessionNotCreatedException; -import org.openqa.selenium.WebDriverException; -import org.openqa.selenium.json.Json; - -import java.util.Map; - -@Tag("UnitTests") -class JsonWireProtocolResponseTest { - - @Test - void successfulResponseGetsParsedProperly() { - Capabilities caps = new ImmutableCapabilities("cheese", "peas"); - Map payload = - ImmutableMap.of( - "status", 0, - "value", caps.asMap(), - "sessionId", "cheese is opaque"); - InitialHandshakeResponse initialResponse = new InitialHandshakeResponse( - 0, - 200, - payload); - - ProtocolHandshake.Result result = - new JsonWireProtocolResponse().getResponseFunction().apply(initialResponse); - - assertThat(result).isNotNull(); - assertThat(result.getDialect()).isEqualTo(Dialect.OSS); - Response response = result.createResponse(); - - assertThat(response.getState()).isEqualTo("success"); - assertThat((int) response.getStatus()).isZero(); - - assertThat(response.getValue()).isEqualTo(caps.asMap()); - } - - @Test - void shouldIgnoreAw3CProtocolReply() { - Capabilities caps = new ImmutableCapabilities("cheese", "peas"); - Map> payload = - ImmutableMap.of( - "value", ImmutableMap.of( - "capabilities", caps.asMap(), - "sessionId", "cheese is opaque")); - InitialHandshakeResponse initialResponse = new InitialHandshakeResponse( - 0, - 200, - payload); - - ProtocolHandshake.Result result = - new JsonWireProtocolResponse().getResponseFunction().apply(initialResponse); - - assertThat(result).isNull(); - } - - @Test - void shouldIgnoreAGeckodriver013Reply() { - Capabilities caps = new ImmutableCapabilities("cheese", "peas"); - Map payload = - ImmutableMap.of( - "value", caps.asMap(), - "sessionId", "cheese is opaque"); - InitialHandshakeResponse initialResponse = new InitialHandshakeResponse( - 0, - 200, - payload); - - ProtocolHandshake.Result result = - new JsonWireProtocolResponse().getResponseFunction().apply(initialResponse); - - assertThat(result).isNull(); - } - - @Test - void shouldProperlyPopulateAnError() { - WebDriverException exception = new SessionNotCreatedException("me no likey"); - Json json = new Json(); - - Map payload = ImmutableMap.of( - "value", json.toType(json.toJson(exception), Json.MAP_TYPE), - "status", ErrorCodes.SESSION_NOT_CREATED); - - InitialHandshakeResponse initialResponse = new InitialHandshakeResponse( - 0, - 500, - payload); - - assertThatExceptionOfType(SessionNotCreatedException.class) - .isThrownBy(() -> new JsonWireProtocolResponse().getResponseFunction().apply(initialResponse)) - .withMessageContaining("me no likey"); - } -} diff --git a/java/test/org/openqa/selenium/remote/NewSessionPayloadTest.java b/java/test/org/openqa/selenium/remote/NewSessionPayloadTest.java index 71ff07a29db58..673fe87dba0c3 100644 --- a/java/test/org/openqa/selenium/remote/NewSessionPayloadTest.java +++ b/java/test/org/openqa/selenium/remote/NewSessionPayloadTest.java @@ -48,22 +48,6 @@ @Tag("UnitTests") class NewSessionPayloadTest { - @Test - void shouldIndicateDownstreamOssDialect() { - Map> caps = singletonMap( - "desiredCapabilities", singletonMap( - "browserName", "cheese")); - - try (NewSessionPayload payload = NewSessionPayload.create(caps)) { - assertEquals(singleton(Dialect.OSS), payload.getDownstreamDialects()); - } - - String json = new Json().toJson(caps); - try (NewSessionPayload payload = NewSessionPayload.create(new StringReader(json))) { - assertEquals(singleton(Dialect.OSS), payload.getDownstreamDialects()); - } - } - @Test void shouldIndicateDownstreamW3cDialect() { Map>> caps = singletonMap( @@ -81,29 +65,6 @@ void shouldIndicateDownstreamW3cDialect() { } } - @Test - void shouldDefaultToAssumingADownstreamOssDialect() { - Map caps = emptyMap(); - try (NewSessionPayload payload = NewSessionPayload.create(caps)) { - assertEquals(singleton(Dialect.OSS), payload.getDownstreamDialects()); - } - - String json = new Json().toJson(caps); - try (NewSessionPayload payload = NewSessionPayload.create(new StringReader(json))) { - assertEquals(singleton(Dialect.OSS), payload.getDownstreamDialects()); - } - } - - @Test - void shouldOfferStreamOfSingleOssCapabilitiesIfThatIsOnlyOption() { - List capabilities = create(singletonMap( - "desiredCapabilities", singletonMap( - "browserName", "cheese"))); - - assertEquals(1, capabilities.size()); - assertEquals("cheese", capabilities.get(0).getBrowserName()); - } - @Test void shouldReturnAlwaysMatchIfNoFirstMatchIsPresent() { List capabilities = create(singletonMap( @@ -131,15 +92,13 @@ void shouldReturnEachFirstMatchIfNoAlwaysMatchIsPresent() { @Test void shouldOfferStreamOfW3cCapabilitiesIfPresent() { List capabilities = create(ImmutableMap.of( - "desiredCapabilities", singletonMap( - "browserName", "cheese"), "capabilities", singletonMap( "alwaysMatch", singletonMap( "browserName", "peas")))); // We expect a synthetic w3c capability for the mismatching OSS capabilities - assertEquals(2, capabilities.size(), capabilities.toString()); - assertEquals("peas", capabilities.get(1).getBrowserName()); + assertEquals(1, capabilities.size(), capabilities.toString()); + assertEquals("peas", capabilities.get(0).getBrowserName()); } @Test @@ -160,17 +119,6 @@ void shouldMergeAlwaysAndFirstMatches() { } - // The name for the platform capability changed from "platform" to "platformName" in the spec. - @Test - void shouldCorrectlyExtractPlatformNameFromOssCapabilities() { - List capabilities = create(singletonMap( - "desiredCapabilities", singletonMap( - "platformName", "linux"))); - - assertEquals(Platform.LINUX, capabilities.get(0).getPlatformName()); - assertEquals(Platform.LINUX, capabilities.get(0).getCapability("platformName")); - } - @Test void shouldCorrectlyExtractPlatformFromW3cCapabilities() { List capabilities = create(singletonMap( @@ -204,9 +152,6 @@ void shouldValidateW3cCapabilitiesByComplainingAboutDuplicateFirstAndAlwaysMatch @Test void convertEverythingToFirstMatchOnlyIfPayloadContainsAlwaysMatchSectionAndOssCapabilities() { List capabilities = create(ImmutableMap.of( - "desiredCapabilities", ImmutableMap.of( - "browserName", "firefox", - "platform", "WINDOWS"), "capabilities", ImmutableMap.of( "alwaysMatch", singletonMap( "platformName", "macos"), @@ -216,10 +161,6 @@ void convertEverythingToFirstMatchOnlyIfPayloadContainsAlwaysMatchSectionAndOssC assertEquals( asList( - // From OSS - new ImmutableCapabilities("browserName", "firefox", "platform", "WINDOWS"), - // Generated from OSS - new ImmutableCapabilities("browserName", "firefox"), // From the actual W3C capabilities new ImmutableCapabilities("browserName", "foo", "platformName", "macos"), new ImmutableCapabilities("browserName", "firefox", "platformName", "macos")), @@ -229,7 +170,7 @@ void convertEverythingToFirstMatchOnlyIfPayloadContainsAlwaysMatchSectionAndOssC @Test void forwardsMetaDataAssociatedWithARequest() throws IOException { try (NewSessionPayload payload = NewSessionPayload.create(ImmutableMap.of( - "desiredCapabilities", EMPTY_MAP, + "capabilities", ImmutableMap.of("alwaysMatch", EMPTY_MAP), "cloud:user", "bob", "cloud:key", "there is no cake"))) { StringBuilder toParse = new StringBuilder(); diff --git a/java/test/org/openqa/selenium/remote/ProtocolHandshakeTest.java b/java/test/org/openqa/selenium/remote/ProtocolHandshakeTest.java index 05056341fd42d..4e240f33826af 100644 --- a/java/test/org/openqa/selenium/remote/ProtocolHandshakeTest.java +++ b/java/test/org/openqa/selenium/remote/ProtocolHandshakeTest.java @@ -19,11 +19,11 @@ import com.google.common.collect.ImmutableMap; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; -import org.openqa.selenium.Proxy; import org.openqa.selenium.json.Json; import org.openqa.selenium.remote.http.HttpClient; import org.openqa.selenium.remote.http.HttpRequest; @@ -40,11 +40,12 @@ import static java.net.HttpURLConnection.HTTP_OK; import static java.util.Collections.EMPTY_MAP; +import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; -import static org.openqa.selenium.Proxy.ProxyType.AUTODETECT; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.openqa.selenium.remote.http.Contents.string; import static org.openqa.selenium.remote.http.Contents.utf8String; @@ -52,27 +53,9 @@ @Tag("UnitTests") class ProtocolHandshakeTest { - @Test - void requestShouldIncludeJsonWireProtocolCapabilities() throws IOException { - Map params = singletonMap("desiredCapabilities", new ImmutableCapabilities()); - Command command = new Command(null, DriverCommand.NEW_SESSION, params); - - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent(utf8String( - "{\"value\": {\"sessionId\": \"23456789\", \"capabilities\": {}}}")); - RecordingHttpClient client = new RecordingHttpClient(response); - - new ProtocolHandshake().createSession(client, command); - - Map json = getRequestPayloadAsMap(client); - - assertThat(json.get("desiredCapabilities")).isEqualTo(EMPTY_MAP); - } - @Test void requestShouldIncludeSpecCompliantW3CCapabilities() throws IOException { - Map params = singletonMap("desiredCapabilities", new ImmutableCapabilities()); + Map params = singletonMap("capabilities", singleton(new ImmutableCapabilities())); Command command = new Command(null, DriverCommand.NEW_SESSION, params); HttpResponse response = new HttpResponse(); @@ -92,7 +75,7 @@ void requestShouldIncludeSpecCompliantW3CCapabilities() throws IOException { @Test void shouldParseW3CNewSessionResponse() throws IOException { - Map params = singletonMap("desiredCapabilities", new ImmutableCapabilities()); + Map params = singletonMap("capabilities", singleton(new ImmutableCapabilities())); Command command = new Command(null, DriverCommand.NEW_SESSION, params); HttpResponse response = new HttpResponse(); @@ -105,35 +88,19 @@ void shouldParseW3CNewSessionResponse() throws IOException { assertThat(result.getDialect()).isEqualTo(Dialect.W3C); } - @Test - void shouldParseWireProtocolNewSessionResponse() throws IOException { - Map params = singletonMap("desiredCapabilities", new ImmutableCapabilities()); - Command command = new Command(null, DriverCommand.NEW_SESSION, params); - - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent(utf8String( - "{\"sessionId\": \"23456789\", \"status\": 0, \"value\": {}}")); - RecordingHttpClient client = new RecordingHttpClient(response); - - ProtocolHandshake.Result result = new ProtocolHandshake().createSession(client, command); - assertThat(result.getDialect()).isEqualTo(Dialect.OSS); - } - @Test void shouldNotIncludeNonProtocolExtensionKeys() throws IOException { Capabilities caps = new ImmutableCapabilities( "se:option", "cheese", - "option", "I like sausages", "browserName", "amazing cake browser"); - Map params = singletonMap("desiredCapabilities", caps); + Map params = singletonMap("capabilities", singleton(caps)); Command command = new Command(null, DriverCommand.NEW_SESSION, params); HttpResponse response = new HttpResponse(); response.setStatus(HTTP_OK); response.setContent(utf8String( - "{\"sessionId\": \"23456789\", \"status\": 0, \"value\": {}}")); + "{\"value\": {\"sessionId\": \"23456789\", \"capabilities\": {}}}")); RecordingHttpClient client = new RecordingHttpClient(response); new ProtocolHandshake().createSession(client, command); @@ -165,85 +132,12 @@ void doesNotCreateFirstMatchForNonW3CCaps() throws IOException { "moz:firefoxOptions", EMPTY_MAP, "browserName", "firefox"); - Map params = singletonMap("desiredCapabilities", caps); - Command command = new Command(null, DriverCommand.NEW_SESSION, params); - - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent(utf8String( - "{\"sessionId\": \"23456789\", \"status\": 0, \"value\": {}}")); - RecordingHttpClient client = new RecordingHttpClient(response); - - new ProtocolHandshake().createSession(client, command); - - Map handshakeRequest = getRequestPayloadAsMap(client); - - List> w3c = mergeW3C(handshakeRequest); - - assertThat(w3c).hasSize(1); - // firstMatch should not contain an object for Chrome-specific capabilities. Because - // "chromeOptions" is not a W3C capability name, it is stripped from any firstMatch objects. - // The resulting empty object should be omitted from firstMatch; if it is present, then the - // Firefox-specific capabilities might be ignored. - assertThat(w3c.get(0)) - .containsKey("moz:firefoxOptions") - .containsEntry("browserName", "firefox"); - } - - @Test - void shouldLowerCaseProxyTypeForW3CRequest() throws IOException { - Proxy proxy = new Proxy(); - proxy.setProxyType(AUTODETECT); - Capabilities caps = new ImmutableCapabilities(CapabilityType.PROXY, proxy); - Map params = singletonMap("desiredCapabilities", caps); - Command command = new Command(null, DriverCommand.NEW_SESSION, params); - - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent(utf8String( - "{\"sessionId\": \"23456789\", \"status\": 0, \"value\": {}}")); - RecordingHttpClient client = new RecordingHttpClient(response); - - new ProtocolHandshake().createSession(client, command); - - Map handshakeRequest = getRequestPayloadAsMap(client); - - mergeW3C(handshakeRequest).forEach(always -> { - Map seenProxy = (Map) always.get("proxy"); - assertThat(seenProxy.get("proxyType")).isEqualTo("autodetect"); - }); - - Map jsonCaps = (Map) handshakeRequest.get("desiredCapabilities"); - Map seenProxy = (Map) jsonCaps.get("proxy"); - assertThat(seenProxy.get("proxyType")).isEqualTo("AUTODETECT"); - } - - @Test - void shouldNotIncludeMappingOfANYPlatform() throws IOException { - Capabilities caps = new ImmutableCapabilities( - "platform", "ANY", - "platformName", "ANY", - "browserName", "cake"); - - Map params = singletonMap("desiredCapabilities", caps); + Map params = singletonMap("capabilities", singleton(caps)); Command command = new Command(null, DriverCommand.NEW_SESSION, params); - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent(utf8String( - "{\"sessionId\": \"23456789\", \"status\": 0, \"value\": {}}")); - RecordingHttpClient client = new RecordingHttpClient(response); - - new ProtocolHandshake().createSession(client, command); - - Map handshakeRequest = getRequestPayloadAsMap(client); - - mergeW3C(handshakeRequest) - .forEach(capabilities -> { - assertThat(capabilities.get("browserName")).isEqualTo("cake"); - assertThat(capabilities.get("platformName")).isNull(); - assertThat(capabilities.get("platform")).isNull(); - }); + ProtocolHandshake handshake = new ProtocolHandshake(); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> handshake.createSession(null, command)); } private List> mergeW3C(Map caps) { diff --git a/java/test/org/openqa/selenium/remote/RemoteWebDriverInitializationTest.java b/java/test/org/openqa/selenium/remote/RemoteWebDriverInitializationTest.java index 73ae67e3fb409..0f5009a0c3d83 100644 --- a/java/test/org/openqa/selenium/remote/RemoteWebDriverInitializationTest.java +++ b/java/test/org/openqa/selenium/remote/RemoteWebDriverInitializationTest.java @@ -39,6 +39,7 @@ import java.util.UUID; import static java.util.Collections.emptyMap; +import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -82,7 +83,7 @@ void constructorShouldThrowIfExecutorThrowsOnAnAttemptToStartASession() { .isThrownBy(() -> new RemoteWebDriver(executor, new ImmutableCapabilities())) .withMessageContaining("Build info: ") .withMessageContaining("Driver info: org.openqa.selenium.remote.RemoteWebDriver") - .withMessageContaining("Command: [null, newSession {capabilities=[Capabilities {}], desiredCapabilities=Capabilities {}}]"); + .withMessageContaining("Command: [null, newSession {capabilities=[Capabilities {}]}]"); verifyNoCommands(executor); } @@ -124,7 +125,7 @@ void constructorStartsSessionAndPassesCapabilities() throws IOException { verify(executor).execute(argThat( command -> command.getName().equals(DriverCommand.NEW_SESSION) && command.getSessionId() == null - && command.getParameters().get("desiredCapabilities") == capabilities + && singleton(capabilities).equals(command.getParameters().get("capabilities")) )); verifyNoMoreInteractions(executor); assertThat(driver.getSessionId()).isNotNull(); @@ -141,17 +142,6 @@ void canHandlePlatformNameCapability() { .satisfies(p -> p.is(Platform.MOJAVE)); } - @Test - void canHandlePlatformOSSCapability() { - WebDriverFixture fixture = new WebDriverFixture( - new ImmutableCapabilities( - "browserName", "cheese browser", "platform", Platform.MOJAVE), - echoCapabilities, nullValueResponder); - - assertThat(fixture.driver.getCapabilities().getPlatformName()) - .satisfies(p -> p.is(Platform.MOJAVE)); - } - @Test void canHandleUnknownPlatformNameAndFallsBackToUnix() { WebDriverFixture fixture = new WebDriverFixture( diff --git a/java/test/org/openqa/selenium/remote/RemoteWebDriverUnitTest.java b/java/test/org/openqa/selenium/remote/RemoteWebDriverUnitTest.java index bfb324234cb07..7ee9d8ce286b2 100644 --- a/java/test/org/openqa/selenium/remote/RemoteWebDriverUnitTest.java +++ b/java/test/org/openqa/selenium/remote/RemoteWebDriverUnitTest.java @@ -140,19 +140,6 @@ void canHandleExecuteAsyncScriptCommand() { "script", "return 1", "args", Arrays.asList(1, "2")))); } - @Test - void canHandleFindElementOSSCommand() { - WebDriverFixture fixture = new WebDriverFixture( - echoCapabilities, - valueResponder(ImmutableMap.of("ELEMENT", UUID.randomUUID().toString()))); - - assertThat(fixture.driver.findElement(By.id("cheese"))).isNotNull(); - - fixture.verifyCommands( - new CommandPayload(DriverCommand.FIND_ELEMENT, ImmutableMap.of( - "using", "id", "value", "cheese"))); - } - @Test void canHandleFindElementW3CCommand() { WebDriverFixture fixture = new WebDriverFixture( @@ -183,21 +170,6 @@ public List findElements(SearchContext context) { fixture.verifyNoCommands(); } - @Test - void canHandleFindElementsOSSCommand() { - WebDriverFixture fixture = new WebDriverFixture( - echoCapabilities, - valueResponder(Arrays.asList( - ImmutableMap.of("ELEMENT", UUID.randomUUID().toString()), - ImmutableMap.of("ELEMENT", UUID.randomUUID().toString())))); - - assertThat(fixture.driver.findElements(By.id("cheese"))).hasSize(2); - - fixture.verifyCommands( - new CommandPayload(DriverCommand.FIND_ELEMENTS, ImmutableMap.of( - "using", "id", "value", "cheese"))); - } - @Test void canHandleFindElementsW3CCommand() { WebDriverFixture fixture = new WebDriverFixture( @@ -356,7 +328,7 @@ void canHandleSwitchToFrameByNameCommand() { new CommandPayload(DriverCommand.FIND_ELEMENTS, ImmutableMap.of( "using", "css selector", "value", "frame[name='frameName'],iframe[name='frameName']")), new CommandPayload(DriverCommand.SWITCH_TO_FRAME, ImmutableMap.of( - "id", ImmutableMap.of("ELEMENT", elementId, ELEMENT_KEY, elementId)))); + "id", ImmutableMap.of(ELEMENT_KEY, elementId)))); } @Test diff --git a/java/test/org/openqa/selenium/remote/SyntheticNewSessionPayloadTest.java b/java/test/org/openqa/selenium/remote/SyntheticNewSessionPayloadTest.java deleted file mode 100644 index bed1208832dd1..0000000000000 --- a/java/test/org/openqa/selenium/remote/SyntheticNewSessionPayloadTest.java +++ /dev/null @@ -1,175 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.remote; - -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static java.util.stream.Collectors.toList; -import static org.assertj.core.api.Assertions.assertThat; - -import com.google.common.collect.ImmutableMap; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Tag; -import org.openqa.selenium.Capabilities; -import org.openqa.selenium.ImmutableCapabilities; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -/** - * There's an interesting problem that appears relatively often with New Session and incorrectly - * prepared payloads. The key problem manifests where something is added to the OSS payload, and - * this is incorrectly reflected in the w3c payload. To work around this, we'll inspect the two sets - * of data. If we can expand the W3C payloads (using the guidelines from the spec) into something - * that matches the OSS payload, we'll just forward the blob unchanged. If, however, we can't then - * we need to do some fancy foot work, in which we'll: - * - *
    - *
  1. Create a new W3C "firstMatch" blob that matches the OSS payload
  2. - *
  3. Expand all W3C payloads, so that they are complete
  4. - *
  5. Forward a new New Session payload composed of the OSS payload and the combined list of - * "firstMatches", with the OSS equivalent first.
  6. - *
- *

- * This test has been broken out to make this behaviour clearer, and to allow for this comment. - */ -@Tag("UnitTests") -class SyntheticNewSessionPayloadTest { - - @Test - void shouldDoNothingIfOssAndW3CPayloadsAreBothEmpty() { - Map empty = ImmutableMap.of( - "desiredCapabilities", emptyMap(), - "capabilities", ImmutableMap.of( - "alwaysMatch", emptyMap(), - "firstMatch", singletonList(emptyMap()))); - - List allCaps = getCapabilities(empty); - - assertThat(allCaps).containsExactly(new ImmutableCapabilities()); - } - - @Test - void shouldDoNothingIfCapabilitiesArePresentButLeftEmpty() { - Map empty = ImmutableMap.of( - "desiredCapabilities", emptyMap(), - "capabilities", emptyMap()); - - List allCaps = getCapabilities(empty); - - assertThat(allCaps).containsExactly(new ImmutableCapabilities()); - } - - @Test - void shouldDoNothingIfOssPayloadMatchesAlwaysMatchAndThereAreNoFirstMatches() { - Map identicalCaps = ImmutableMap.of("browserName", "cheese"); - - Map payload= ImmutableMap.of( - "desiredCapabilities", identicalCaps, - "capabilities", ImmutableMap.of( - "alwaysMatch", identicalCaps)); - - List allCaps = getCapabilities(payload); - - assertThat(allCaps).containsExactly(new ImmutableCapabilities(identicalCaps)); - } - - @Test - void shouldDoNothingIfOssPayloadMatchesAFirstMatchAndThereIsNoAlwaysMatch() { - Map identicalCaps = ImmutableMap.of("browserName", "cheese"); - - Map payload= ImmutableMap.of( - "desiredCapabilities", identicalCaps, - "capabilities", ImmutableMap.of( - "firstMatch", singletonList(identicalCaps))); - - List allCaps = getCapabilities(payload); - - assertThat(allCaps).containsExactly(new ImmutableCapabilities(identicalCaps)); - } - - @Test - void shouldDoNothingIfOssPayloadMatchesAValidMergedW3CPayload() { - Map caps = ImmutableMap.of( - "browserName", "cheese", - "se:cake", "more cheese"); - - Map payload= ImmutableMap.of( - "desiredCapabilities", caps, - "capabilities", ImmutableMap.of( - "alwaysMatch", ImmutableMap.of("browserName", "cheese"), - "firstMatch", singletonList(ImmutableMap.of("se:cake", "more cheese")))); - - List allCaps = getCapabilities(payload); - - assertThat(allCaps).containsExactly(new ImmutableCapabilities(caps)); - } - - @Test - void shouldExpandAllW3CMatchesToFirstMatchesAndRemoveAlwaysMatchIfSynthesizingAPayload() { - Map payload = ImmutableMap.of( - // OSS capabilities request a chrome webdriver - "desiredCapabilities", ImmutableMap.of("browserName", "chrome"), - // Yet the w3c ones ask for IE and edge - "capabilities", ImmutableMap.of( - "alwaysMatch", ImmutableMap.of("se:cake", "cheese"), - "firstMatch", Arrays.asList( - ImmutableMap.of("browserName", "edge"), - ImmutableMap.of("browserName", "cheese")))); - - try (NewSessionPayload newSession = NewSessionPayload.create(payload)) { - List allCaps = newSession.stream().collect(toList()); - - assertThat(allCaps).containsExactlyInAnyOrder( - new ImmutableCapabilities("browserName", "cheese", "se:cake", "cheese"), - new ImmutableCapabilities("browserName", "chrome"), - new ImmutableCapabilities("browserName", "edge", "se:cake", "cheese")); - } - } - - @Test - void ossPayloadWillBeFirstW3CPayload() { - // This is one of the common cases --- asking for marionette to be false. There's no way to - // encode this legally into the w3c payload (as it doesn't start with "se:"), yet it's a use- - // case that needs to be properly supported. - Map rawCapabilities = ImmutableMap.of( - "desiredCapabilities", ImmutableMap.of("marionette", false), - "capabilities", ImmutableMap.of( - "alwaysMatch", ImmutableMap.of("browserName", "chrome"))); - - List allCaps = getCapabilities(rawCapabilities); - - assertThat(allCaps).hasSize(3); - assertThat(allCaps.get(0).getCapability("marionette")).isEqualTo(false); - } - - private List getCapabilities(Map payload) { - try (NewSessionPayload newSessionPayload = NewSessionPayload.create(payload)) { - StringBuilder b = new StringBuilder(); - newSessionPayload.writeTo(b); - return newSessionPayload.stream().collect(toList()); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - -} diff --git a/java/test/org/openqa/selenium/remote/WebDriverFixture.java b/java/test/org/openqa/selenium/remote/WebDriverFixture.java index 7954e673fdfa8..885695b920a24 100644 --- a/java/test/org/openqa/selenium/remote/WebDriverFixture.java +++ b/java/test/org/openqa/selenium/remote/WebDriverFixture.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.Array; +import java.util.Collection; import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -169,10 +170,14 @@ public static Function errorResponder(String state, Object va public static final Function echoCapabilities = cmd -> { Response response = new Response(); + + @SuppressWarnings("unchecked") + Collection capabilities = (Collection) cmd.getParameters().get("capabilities"); + response.setValue( - ((Capabilities) cmd.getParameters().get("desiredCapabilities")).asMap() + capabilities.iterator().next().asMap() .entrySet().stream() - .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().toString()))); + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toString()))); response.setSessionId(UUID.randomUUID().toString()); return response; }; diff --git a/java/test/org/openqa/selenium/remote/codec/jwp/JsonHttpCommandCodecTest.java b/java/test/org/openqa/selenium/remote/codec/jwp/JsonHttpCommandCodecTest.java deleted file mode 100644 index 5ad86829d6c19..0000000000000 --- a/java/test/org/openqa/selenium/remote/codec/jwp/JsonHttpCommandCodecTest.java +++ /dev/null @@ -1,351 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.remote.codec.jwp; - -import static com.google.common.net.HttpHeaders.CACHE_CONTROL; -import static com.google.common.net.HttpHeaders.CONTENT_LENGTH; -import static com.google.common.net.HttpHeaders.CONTENT_TYPE; -import static com.google.common.net.MediaType.JSON_UTF_8; -import static java.nio.charset.StandardCharsets.UTF_16; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.openqa.selenium.remote.Dialect.OSS; -import static org.openqa.selenium.remote.http.Contents.bytes; -import static org.openqa.selenium.remote.http.Contents.string; -import static org.openqa.selenium.remote.http.Contents.utf8String; -import static org.openqa.selenium.remote.http.HttpMethod.DELETE; -import static org.openqa.selenium.remote.http.HttpMethod.GET; -import static org.openqa.selenium.remote.http.HttpMethod.POST; - -import com.google.common.collect.ImmutableMap; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Tag; -import org.openqa.selenium.UnsupportedCommandException; -import org.openqa.selenium.json.Json; -import org.openqa.selenium.remote.Command; -import org.openqa.selenium.remote.DriverCommand; -import org.openqa.selenium.remote.SessionId; -import org.openqa.selenium.remote.http.HttpRequest; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Tag("UnitTests") -class JsonHttpCommandCodecTest { - - private final JsonHttpCommandCodec codec = new JsonHttpCommandCodec(); - - @Test - void throwsIfCommandNameIsNotRecognized() { - Command command = new Command(null, "garbage-command-name"); - assertThatExceptionOfType(UnsupportedCommandException.class) - .isThrownBy(() -> codec.encode(command)) - .withMessageStartingWith(command.getName() + "\n"); - } - - @Test - void throwsIfCommandHasNullSessionId() { - codec.defineCommand("foo", DELETE, "/foo/:sessionId"); - Command command = new Command(null, "foo"); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> codec.encode(command)) - .withMessageContaining("Session ID"); - } - - @Test - void throwsIfCommandIsMissingUriParameter() { - codec.defineCommand("foo", DELETE, "/foo/:bar"); - Command command = new Command(new SessionId("id"), "foo"); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> codec.encode(command)) - .withMessageContaining("bar"); - } - - @Test - void encodingAPostWithNoParameters() { - codec.defineCommand("foo", POST, "/foo/bar"); - Command command = new Command(null, "foo"); - - HttpRequest request = codec.encode(command); - assertThat(request.getMethod()).isEqualTo(POST); - assertThat(request.getHeader(CONTENT_TYPE)).isEqualTo(JSON_UTF_8.toString()); - assertThat(request.getHeader(CONTENT_LENGTH)).isEqualTo("3"); - assertThat(request.getUri()).isEqualTo("/foo/bar"); - assertThat(string(request)).isEqualTo("{\n}"); - } - - @Test - void encodingAPostWithUrlParameters() { - codec.defineCommand("foo", POST, "/foo/:bar/baz"); - Command command = new Command(null, "foo", ImmutableMap.of("bar", "apples123")); - - String encoding = "{\n \"bar\": \"apples123\"\n}"; - - HttpRequest request = codec.encode(command); - assertThat(request.getMethod()).isEqualTo(POST); - assertThat(request.getHeader(CONTENT_TYPE)).isEqualTo(JSON_UTF_8.toString()); - assertThat(request.getHeader(CONTENT_LENGTH)).isEqualTo(String.valueOf(encoding.length())); - assertThat(request.getUri()).isEqualTo("/foo/apples123/baz"); - assertThat(string(request)).isEqualTo(encoding); - } - - @Test - void encodingANonPostWithNoParameters() { - codec.defineCommand("foo", DELETE, "/foo/bar/baz"); - HttpRequest request = codec.encode(new Command(null, "foo")); - assertThat(request.getMethod()).isEqualTo(DELETE); - assertThat(request.getHeader(CONTENT_TYPE)).isNull(); - assertThat(request.getHeader(CONTENT_LENGTH)).isNull(); - assertThat(bytes(request.getContent()).length).isZero(); - assertThat(request.getUri()).isEqualTo("/foo/bar/baz"); - } - - @Test - void encodingANonPostWithParameters() { - codec.defineCommand("eat", GET, "/fruit/:fruit/:size"); - HttpRequest request = codec.encode(new Command(null, "eat", ImmutableMap.of( - "fruit", "apple", "size", "large"))); - assertThat(request.getHeader(CONTENT_TYPE)).isNull(); - assertThat(request.getHeader(CONTENT_LENGTH)).isNull(); - assertThat(bytes(request.getContent()).length).isZero(); - assertThat(request.getUri()).isEqualTo("/fruit/apple/large"); - } - - @Test - void preventsCachingGetRequests() { - codec.defineCommand("foo", GET, "/foo"); - HttpRequest request = codec.encode(new Command(null, "foo")); - assertThat(request.getMethod()).isEqualTo(GET); - assertThat(request.getHeader(CACHE_CONTROL)).isEqualTo("no-cache"); - } - - @Test - void throwsIfEncodedCommandHasNoMapping() { - HttpRequest request = new HttpRequest(GET, "/foo/bar/baz"); - assertThatExceptionOfType(UnsupportedCommandException.class) - .isThrownBy(() -> codec.decode(request)) - .withMessageStartingWith("GET /foo/bar/baz\n"); - } - - @Test - void canDecodeCommandWithNoParameters() { - HttpRequest request = new HttpRequest(GET, "/foo/bar/baz"); - codec.defineCommand("foo", GET, "/foo/bar/baz"); - - Command decoded = codec.decode(request); - assertThat(decoded.getName()).isEqualTo("foo"); - assertThat(decoded.getSessionId()).isNull(); - assertThat(decoded.getParameters()).isEmpty(); - } - - @Test - void canExtractSessionIdFromPathParameters() { - HttpRequest request = new HttpRequest(GET, "/foo/bar/baz"); - codec.defineCommand("foo", GET, "/foo/:sessionId/baz"); - - Command decoded = codec.decode(request); - assertThat(decoded.getSessionId()).isEqualTo(new SessionId("bar")); - } - - @Test - void removesSessionIdFromParameterMap() { - HttpRequest request = new HttpRequest(GET, "/foo/bar/baz"); - codec.defineCommand("foo", GET, "/foo/:sessionId/baz"); - - Command decoded = codec.decode(request); - assertThat(decoded.getSessionId()).isEqualTo(new SessionId("bar")); - assertThat(decoded.getParameters()).isEmpty(); - } - - @Test - void canExtractSessionIdFromRequestBody() { - String data = new Json().toJson(ImmutableMap.of("sessionId", "sessionX")); - HttpRequest request = new HttpRequest(POST, "/foo/bar/baz"); - request.setContent(utf8String(data)); - codec.defineCommand("foo", POST, "/foo/bar/baz"); - - Command decoded = codec.decode(request); - assertThat(decoded.getSessionId()).isEqualTo(new SessionId("sessionX")); - } - - @Test - void extractsAllParametersFromUrl() { - HttpRequest request = new HttpRequest(GET, "/fruit/apple/size/large"); - codec.defineCommand("pick", GET, "/fruit/:fruit/size/:size"); - - Command decoded = codec.decode(request); - assertThat(decoded.getParameters()).isEqualTo((Map) ImmutableMap.of( - "fruit", "apple", - "size", "large")); - } - - @Test - void extractsAllParameters() { - String data = new Json().toJson(ImmutableMap.of("sessionId", "sessionX", - "fruit", "apple", - "color", "red", - "size", "large")); - HttpRequest request = new HttpRequest(POST, "/fruit/apple/size/large"); - request.setContent(utf8String(data)); - codec.defineCommand("pick", POST, "/fruit/:fruit/size/:size"); - - Command decoded = codec.decode(request); - assertThat(decoded.getSessionId()).isEqualTo(new SessionId("sessionX")); - assertThat(decoded.getParameters()).isEqualTo((Map) ImmutableMap.of( - "fruit", "apple", "size", "large", "color", "red")); - } - - @Test - void ignoresNullSessionIdInSessionBody() { - Map map = new HashMap<>(); - map.put("sessionId", null); - map.put("fruit", "apple"); - map.put("color", "red"); - map.put("size", "large"); - String data = new Json().toJson(map); - HttpRequest request = new HttpRequest(POST, "/fruit/apple/size/large"); - request.setContent(utf8String(data)); - codec.defineCommand("pick", POST, "/fruit/:fruit/size/:size"); - - Command decoded = codec.decode(request); - assertThat(decoded.getSessionId()).isNull(); - assertThat(decoded.getParameters()).isEqualTo((Map) ImmutableMap.of( - "fruit", "apple", "size", "large", "color", "red")); - } - - @Test - void decodeRequestWithUtf16Encoding() { - codec.defineCommand("num", POST, "/one"); - - byte[] data = "{\"char\":\"水\"}".getBytes(UTF_16); - HttpRequest request = new HttpRequest(POST, "/one"); - request.setHeader(CONTENT_TYPE, JSON_UTF_8.withCharset(UTF_16).toString()); - request.setHeader(CONTENT_LENGTH, String.valueOf(data.length)); - request.setContent(bytes(data)); - - Command command = codec.decode(request); - assertThat((String) command.getParameters().get("char")).isEqualTo("水"); - } - - @Test - void decodingUsesUtf8IfNoEncodingSpecified() { - codec.defineCommand("num", POST, "/one"); - - byte[] data = "{\"char\":\"水\"}".getBytes(UTF_8); - HttpRequest request = new HttpRequest(POST, "/one"); - request.setHeader(CONTENT_TYPE, JSON_UTF_8.withoutParameters().toString()); - request.setHeader(CONTENT_LENGTH, String.valueOf(data.length)); - request.setContent(bytes(data)); - - Command command = codec.decode(request); - assertThat((String) command.getParameters().get("char")).isEqualTo("水"); - } - - @Test - void codecRoundTrip() { - codec.defineCommand("buy", POST, "/:sessionId/fruit/:fruit/size/:size"); - - Command original = new Command(new SessionId("session123"), "buy", ImmutableMap.of( - "fruit", "apple", "size", "large", "color", "red", "rotten", "false")); - HttpRequest request = codec.encode(original); - Command decoded = codec.decode(request); - - assertThat(decoded.getName()).isEqualTo(original.getName()); - assertThat(decoded.getSessionId()).isEqualTo(original.getSessionId()); - assertThat(decoded.getParameters()).isEqualTo((Map) original.getParameters()); - } - - @Test - void treatsEmptyPathAsRoot_recognizedCommand() { - codec.defineCommand("num", POST, "/"); - - byte[] data = "{\"char\":\"水\"}".getBytes(UTF_8); - HttpRequest request = new HttpRequest(POST, ""); - request.setHeader(CONTENT_TYPE, JSON_UTF_8.withoutParameters().toString()); - request.setHeader(CONTENT_LENGTH, String.valueOf(data.length)); - request.setContent(bytes(data)); - - Command command = codec.decode(request); - assertThat(command.getName()).isEqualTo("num"); - } - - @Test - void treatsNullPathAsRoot_recognizedCommand() { - codec.defineCommand("num", POST, "/"); - - byte[] data = "{\"char\":\"水\"}".getBytes(UTF_8); - HttpRequest request = new HttpRequest(POST, null); - request.setHeader(CONTENT_TYPE, JSON_UTF_8.withoutParameters().toString()); - request.setHeader(CONTENT_LENGTH, String.valueOf(data.length)); - request.setContent(bytes(data)); - - Command command = codec.decode(request); - assertThat(command.getName()).isEqualTo("num"); - } - - @Test - void treatsEmptyPathAsRoot_unrecognizedCommand() { - codec.defineCommand("num", GET, "/"); - - byte[] data = "{\"char\":\"水\"}".getBytes(UTF_8); - HttpRequest request = new HttpRequest(POST, ""); - request.setHeader(CONTENT_TYPE, JSON_UTF_8.withoutParameters().toString()); - request.setHeader(CONTENT_LENGTH, String.valueOf(data.length)); - request.setContent(bytes(data)); - - assertThatExceptionOfType(UnsupportedCommandException.class) - .isThrownBy(() -> codec.decode(request)); - } - - @Test - void treatsNullPathAsRoot_unrecognizedCommand() { - codec.defineCommand("num", GET, "/"); - - byte[] data = "{\"char\":\"水\"}".getBytes(UTF_8); - HttpRequest request = new HttpRequest(POST, null); - request.setHeader(CONTENT_TYPE, JSON_UTF_8.withoutParameters().toString()); - request.setHeader(CONTENT_LENGTH, String.valueOf(data.length)); - request.setContent(bytes(data)); - - assertThatExceptionOfType(UnsupportedCommandException.class) - .isThrownBy(() -> codec.decode(request)); - } - - @Test - void whenDecodingAnHttpRequestDoesNotRecreateWebElements() { - Command command = new Command( - new SessionId("1234567"), - DriverCommand.EXECUTE_SCRIPT, - ImmutableMap.of( - "script", "", - "args", Arrays.asList(ImmutableMap.of(OSS.getEncodedElementKey(), "67890")))); - - HttpRequest request = codec.encode(command); - - Command decoded = codec.decode(request); - - List args = (List) decoded.getParameters().get("args"); - - Map element = (Map) args.get(0); - assertThat(element.get(OSS.getEncodedElementKey())).isEqualTo("67890"); - } -} diff --git a/java/test/org/openqa/selenium/remote/codec/jwp/JsonHttpResponseCodecTest.java b/java/test/org/openqa/selenium/remote/codec/jwp/JsonHttpResponseCodecTest.java deleted file mode 100644 index b1f2e2af48a94..0000000000000 --- a/java/test/org/openqa/selenium/remote/codec/jwp/JsonHttpResponseCodecTest.java +++ /dev/null @@ -1,212 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.remote.codec.jwp; - -import static com.google.common.net.HttpHeaders.CONTENT_TYPE; -import static com.google.common.net.MediaType.JSON_UTF_8; -import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; -import static java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT; -import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; -import static java.net.HttpURLConnection.HTTP_NO_CONTENT; -import static java.net.HttpURLConnection.HTTP_OK; -import static java.nio.charset.StandardCharsets.UTF_16; -import static org.assertj.core.api.Assertions.assertThat; -import static org.openqa.selenium.remote.http.Contents.asJson; -import static org.openqa.selenium.remote.http.Contents.string; -import static org.openqa.selenium.remote.http.Contents.utf8String; - -import com.google.common.collect.ImmutableMap; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Tag; -import org.openqa.selenium.ScriptTimeoutException; -import org.openqa.selenium.WebDriverException; -import org.openqa.selenium.json.Json; -import org.openqa.selenium.remote.Dialect; -import org.openqa.selenium.remote.ErrorCodes; -import org.openqa.selenium.remote.RemoteWebElement; -import org.openqa.selenium.remote.Response; -import org.openqa.selenium.remote.http.HttpResponse; - -@Tag("UnitTests") -class JsonHttpResponseCodecTest { - - private final JsonHttpResponseCodec codec = new JsonHttpResponseCodec(); - - @Test - void convertsResponses_success() { - Response response = new Response(); - response.setStatus(ErrorCodes.SUCCESS); - response.setValue(ImmutableMap.of("color", "red")); - - HttpResponse converted = codec.encode(HttpResponse::new, response); - assertThat(converted.getStatus()).isEqualTo(HTTP_OK); - assertThat(converted.getHeader(CONTENT_TYPE)).isEqualTo(JSON_UTF_8.toString()); - - Response rebuilt = new Json().toType(string(converted), Response.class); - - assertThat(rebuilt.getStatus()).isEqualTo(response.getStatus()); - assertThat(rebuilt.getState()).isEqualTo(new ErrorCodes().toState(response.getStatus())); - assertThat(rebuilt.getSessionId()).isEqualTo(response.getSessionId()); - assertThat(rebuilt.getValue()).isEqualTo(response.getValue()); - } - - @Test - void convertsResponses_failure() { - Response response = new Response(); - response.setStatus(ErrorCodes.NO_SUCH_ELEMENT); - response.setValue(ImmutableMap.of("color", "red")); - - HttpResponse converted = codec.encode(HttpResponse::new, response); - assertThat(converted.getStatus()).isEqualTo(HTTP_INTERNAL_ERROR); - assertThat(converted.getHeader(CONTENT_TYPE)).isEqualTo(JSON_UTF_8.toString()); - - Response rebuilt = new Json().toType(string(converted), Response.class); - - assertThat(rebuilt.getStatus()).isEqualTo(response.getStatus()); - assertThat(rebuilt.getState()).isEqualTo(new ErrorCodes().toState(response.getStatus())); - assertThat(rebuilt.getSessionId()).isEqualTo(response.getSessionId()); - assertThat(rebuilt.getValue()).isEqualTo(response.getValue()); - } - - @Test - void roundTrip() { - Response response = new Response(); - response.setStatus(ErrorCodes.SUCCESS); - response.setValue(ImmutableMap.of("color", "red")); - - HttpResponse httpResponse = codec.encode(HttpResponse::new, response); - Response decoded = codec.decode(httpResponse); - - assertThat(decoded.getStatus()).isEqualTo(response.getStatus()); - assertThat(decoded.getSessionId()).isEqualTo(response.getSessionId()); - assertThat(decoded.getValue()).isEqualTo(response.getValue()); - } - - @Test - void decodeNonJsonResponse_200() { - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent(utf8String("{\"foobar\"}")); - - Response decoded = codec.decode(response); - assertThat(decoded.getStatus().longValue()).isZero(); - assertThat(decoded.getValue()).isEqualTo("{\"foobar\"}"); - } - - @Test - void decodeNonJsonResponse_204() { - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_NO_CONTENT); - - Response decoded = codec.decode(response); - assertThat(decoded.getStatus()).isNull(); - assertThat(decoded.getValue()).isNull(); - } - - @Test - void decodeNonJsonResponse_4xx() { - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_BAD_REQUEST); - response.setContent(utf8String("{\"foobar\"}")); - - Response decoded = codec.decode(response); - assertThat(decoded.getStatus().intValue()).isEqualTo(ErrorCodes.UNKNOWN_COMMAND); - assertThat(decoded.getValue()).isEqualTo("{\"foobar\"}"); - } - - @Test - void decodeNonJsonResponse_5xx() { - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_INTERNAL_ERROR); - response.setContent(utf8String("{\"foobar\"}")); - - Response decoded = codec.decode(response); - assertThat(decoded.getStatus().intValue()).isEqualTo(ErrorCodes.UNHANDLED_ERROR); - assertThat(decoded.getValue()).isEqualTo("{\"foobar\"}"); - } - - @Test - void decodeJsonResponseMissingContentType() { - Response response = new Response(); - response.setStatus(ErrorCodes.SUCCESS); - response.setValue(ImmutableMap.of("color", "red")); - - HttpResponse httpResponse = new HttpResponse(); - httpResponse.setStatus(HTTP_OK); - httpResponse.setContent(asJson(response)); - - Response decoded = codec.decode(httpResponse); - assertThat(decoded.getStatus()).isEqualTo(response.getStatus()); - assertThat(decoded.getSessionId()).isEqualTo(response.getSessionId()); - assertThat(decoded.getValue()).isEqualTo(response.getValue()); - } - - @Test - void decodeUtf16EncodedResponse() { - HttpResponse httpResponse = new HttpResponse(); - httpResponse.setStatus(200); - httpResponse.setHeader(CONTENT_TYPE, JSON_UTF_8.withCharset(UTF_16).toString()); - httpResponse.setContent(string("{\"status\":0,\"value\":\"水\"}", UTF_16)); - - Response response = codec.decode(httpResponse); - assertThat(response.getValue()).isEqualTo("水"); - } - - @Test - void decodeJsonResponseWithTrailingNullBytes() { - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent(utf8String("{\"status\":0,\"value\":\"foo\"}\0\0")); - - Response decoded = codec.decode(response); - assertThat(decoded.getStatus().intValue()).isEqualTo(ErrorCodes.SUCCESS); - assertThat(decoded.getValue()).isEqualTo("foo"); - } - - @Test - void shouldConvertElementReferenceToRemoteWebElement() { - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent(asJson(ImmutableMap.of( - "status", 0, - "value", ImmutableMap.of(Dialect.OSS.getEncodedElementKey(), "345678")))); - - Response decoded = codec.decode(response); - assertThat(((RemoteWebElement) decoded.getValue()).getId()).isEqualTo("345678"); - } - - @Test - void shouldAttemptToConvertAnExceptionIntoAnActualExceptionInstance() { - Response response = new Response(); - response.setStatus(ErrorCodes.ASYNC_SCRIPT_TIMEOUT); - WebDriverException exception = new ScriptTimeoutException("I timed out"); - response.setValue(exception); - - HttpResponse httpResponse = new HttpResponse(); - httpResponse.setStatus(HTTP_CLIENT_TIMEOUT); - httpResponse.setContent(asJson(response)); - - Response decoded = codec.decode(httpResponse); - assertThat(decoded.getStatus().intValue()).isEqualTo(ErrorCodes.ASYNC_SCRIPT_TIMEOUT); - - WebDriverException seenException = (WebDriverException) decoded.getValue(); - assertThat(seenException.getClass()).isEqualTo(exception.getClass()); - assertThat(seenException.getMessage().startsWith(exception.getMessage())).isTrue(); - } -} diff --git a/java/test/org/openqa/selenium/remote/internal/WebElementToJsonConverterTest.java b/java/test/org/openqa/selenium/remote/internal/WebElementToJsonConverterTest.java index 68d62e6d38851..5fa9fc59cff8f 100644 --- a/java/test/org/openqa/selenium/remote/internal/WebElementToJsonConverterTest.java +++ b/java/test/org/openqa/selenium/remote/internal/WebElementToJsonConverterTest.java @@ -198,9 +198,9 @@ void convertsAnArray() { assertThat(value1).isInstanceOf(Collection.class); assertContentsInOrder(new ArrayList<>((Collection) value1), "abc123", true, 123, Math.PI); - + Object value2 = CONVERTER.apply(new int[] { 123, 456, 789 }); - + assertThat(value2).isInstanceOf(Collection.class); assertContentsInOrder(new ArrayList<>((Collection) value2), 123, 456, 789); } @@ -213,7 +213,6 @@ void convertsAnArrayWithAWebElement() { Object value = CONVERTER.apply(new Object[] { element }); assertContentsInOrder(new ArrayList<>((Collection) value), ImmutableMap.of( - Dialect.OSS.getEncodedElementKey(), "abc123", Dialect.W3C.getEncodedElementKey(), "abc123")); } @@ -225,9 +224,7 @@ private static void assertIsWebElementObject(Object value, String expectedKey) { assertThat(value).isInstanceOf(Map.class); Map map = (Map) value; - assertThat(map).hasSize(2); - assertThat(map.containsKey(Dialect.OSS.getEncodedElementKey())).isTrue(); - assertThat(map.get(Dialect.OSS.getEncodedElementKey())).isEqualTo(expectedKey); + assertThat(map).hasSize(1); assertThat(map.containsKey(Dialect.W3C.getEncodedElementKey())).isTrue(); assertThat(map.get(Dialect.W3C.getEncodedElementKey())).isEqualTo(expectedKey); }