Skip to content

Commit

Permalink
Adding a w3c compliant remote webdriver builder
Browse files Browse the repository at this point in the history
The builder only supports drivers that are w3c compliant,
and so has a pretty simple structure to it. For now, it's
hidden with package level visibility until we want people
to play with it.
  • Loading branch information
shs96c committed May 9, 2018
1 parent 6a6b658 commit fd92d1c
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import static org.openqa.selenium.ie.InternetExplorerDriver.REQUIRE_WINDOW_FOCUS;
import static org.openqa.selenium.remote.CapabilityType.BROWSER_NAME;
import static org.openqa.selenium.remote.CapabilityType.PAGE_LOAD_STRATEGY;
import static org.openqa.selenium.remote.CapabilityType.PLATFORM;
import static org.openqa.selenium.remote.CapabilityType.UNHANDLED_PROMPT_BEHAVIOUR;

import com.google.common.base.Preconditions;
Expand All @@ -43,13 +42,11 @@
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.MutableCapabilities;
import org.openqa.selenium.PageLoadStrategy;
import org.openqa.selenium.Platform;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.UnexpectedAlertBehaviour;
import org.openqa.selenium.internal.ElementScrollBehavior;
import org.openqa.selenium.remote.BrowserType;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.time.Duration;
import java.util.Arrays;
Expand Down Expand Up @@ -100,17 +97,13 @@ public class InternetExplorerOptions extends MutableCapabilities {
private Map<String, Object> ieOptions = new HashMap<>();

public InternetExplorerOptions() {
this(DesiredCapabilities.internetExplorer());
setCapability(BROWSER_NAME, BrowserType.IE);
setCapability(IE_OPTIONS, ieOptions);
}

public InternetExplorerOptions(Capabilities source) {
super();

setCapability(IE_OPTIONS, ieOptions);
setCapability(BROWSER_NAME, BrowserType.IE);
setCapability(PLATFORM, Platform.WINDOWS);
setCapability(CapabilityType.ForSeleniumServer.ENSURING_CLEAN_SESSION, true);

merge(source);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;

public class AcceptedW3CCapabilityKeys implements Predicate<String> {

private final static Predicate<String> ACCEPTED_W3C_PATTERNS = Stream.of(
"^[\\w-]+:.*$",
"^acceptInsecureCerts$",
"^browserName$",
"^browserVersion$",
"^platformName$",
"^pageLoadStrategy$",
"^proxy$",
"^setWindowRect$",
"^timeouts$",
"^unhandledPromptBehavior$")
.map(Pattern::compile)
.map(Pattern::asPredicate)
.reduce(identity -> false, Predicate::or);


@Override
public boolean test(String capabilityName) {
return ACCEPTED_W3C_PATTERNS.test(capabilityName);
}
}
2 changes: 2 additions & 0 deletions java/client/src/org/openqa/selenium/remote/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ java_library(name = 'remote',

java_library(name = 'remote-lib',
srcs = [
'AcceptedW3CCapabilityKeys.java',
'CommandCodec.java',
'CommandInfo.java',
'Dialect.java',
Expand All @@ -96,6 +97,7 @@ java_library(name = 'remote-lib',
'RemoteStatus.java',
'RemoteTouchScreen.java',
'RemoteWebDriver.java',
'RemoteWebDriverBuilder.java',
'RemoteWebElement.java',
'ResponseCodec.java',
'UnreachableBrowserException.java',
Expand Down
19 changes: 2 additions & 17 deletions java/client/src/org/openqa/selenium/remote/NewSessionPayload.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand All @@ -79,20 +78,7 @@ public class NewSessionPayload implements Closeable {
private final Set<CapabilityTransform> transforms;

private static final Dialect DEFAULT_DIALECT = Dialect.OSS;
private final static Predicate<String> ACCEPTED_W3C_PATTERNS = Stream.of(
"^[\\w-]+:.*$",
"^acceptInsecureCerts$",
"^browserName$",
"^browserVersion$",
"^platformName$",
"^pageLoadStrategy$",
"^proxy$",
"^setWindowRect$",
"^timeouts$",
"^unhandledPromptBehavior$")
.map(Pattern::compile)
.map(Pattern::asPredicate)
.reduce(identity -> false, Predicate::or);
private final static Predicate<String> ACCEPTED_W3C_PATTERNS = new AcceptedW3CCapabilityKeys();

private final Json json = new Json();
private final FileBackedOutputStream backingStore;
Expand Down Expand Up @@ -398,8 +384,7 @@ private Map<String, Object> convertOssToW3C(Map<String, Object> capabilities) {
return null;
}

Map<String, Object> toReturn = new TreeMap<>();
toReturn.putAll(capabilities);
Map<String, Object> toReturn = new TreeMap<>(capabilities);

// Platform name
if (capabilities.containsKey(PLATFORM) && !capabilities.containsKey(PLATFORM_NAME)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ public RemoteWebDriver(URL remoteAddress, Capabilities capabilities) {
this(new HttpCommandExecutor(remoteAddress), capabilities);
}

@Beta
static RemoteWebDriverBuilder builder() {
return new RemoteWebDriverBuilder();
}

private void init(Capabilities capabilities) {
capabilities = capabilities == null ? new ImmutableCapabilities() : capabilities;

Expand Down
131 changes: 131 additions & 0 deletions java/client/src/org/openqa/selenium/remote/RemoteWebDriverBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// 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 com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

import org.openqa.selenium.Beta;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.SessionNotCreatedException;
import org.openqa.selenium.json.JsonOutput;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;

@Beta
class RemoteWebDriverBuilder {

private final static Set<String> ILLEGAL_METADATA_KEYS = ImmutableSet.of("alwaysMatch", "firstMatch");
private final static AcceptedW3CCapabilityKeys OK_KEYS = new AcceptedW3CCapabilityKeys();
private final List<Map<String, Object>> options = new ArrayList<>();
private final Map<String, Object> metadata = new TreeMap<>();

public RemoteWebDriverBuilder addOptions(Capabilities options) {
Map<String, Object> serialized = validate(Objects.requireNonNull(options));
this.options.add(serialized);
return this;
}

public RemoteWebDriverBuilder addMetadata(String key, Object value) {
if (ILLEGAL_METADATA_KEYS.contains(key)) {
throw new IllegalArgumentException(key + " is a reserved key");
}
metadata.put(Objects.requireNonNull(key), Objects.requireNonNull(value));
return this;
}

public RemoteWebDriver build() {
if (options.isEmpty()) {
throw new SessionNotCreatedException("Refusing to create session without any capabilities");
}

return null;
}

private Map<String, Object> validate(Capabilities options) {
return options.asMap().entrySet().stream()
// Ensure that the keys are ok
.peek(
entry -> {
if (!OK_KEYS.test(entry.getKey())) {
throw new IllegalArgumentException(
"Capability key is not a valid w3c key: " + entry.getKey());
}
})
// And remove null values, as these are ignored.
.filter(entry -> entry.getValue() != null)
.collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
}

@VisibleForTesting
void writePayload(JsonOutput out) {
out.beginObject();

// Try and minimise payload by finding keys that have the same value in every option. This isn't
// terribly efficient, but we expect the number of entries to be very low in almost every case,
// so this should be fine.
Map<String, Object> always = new HashMap<>(options.get(0));
for (Map<String, Object> option : options) {
for (Map.Entry<String, Object> entry : option.entrySet()) {
if (!always.containsKey(entry.getKey())) {
continue;
}

if (!always.get(entry.getKey()).equals(entry.getValue())) {
always.remove(entry.getKey());
}
}
}

out.name("alwaysMatch");
out.beginObject();
always.forEach((key, value) -> {
out.name(key);
out.write(value);
});
out.endObject();

out.name("firstMatch");
out.beginArray();
options.forEach(option -> {
out.beginObject();
option.entrySet().stream()
.filter(entry -> !always.containsKey(entry.getKey()))
.forEach(entry -> {
out.name(entry.getKey());
out.write(entry.getValue());
});
out.endObject();
});
out.endArray();

metadata.forEach((key, value) -> {
out.name(key);
out.write(value);
});

out.endObject();
}
}
4 changes: 4 additions & 0 deletions java/client/test/org/openqa/selenium/remote/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ java_test(name = 'client-tests',
'RemoteLogsTest.java',
'RemoteWebDriverInitializationTest.java',
'W3CHandshakeResponseTest.java',
'W3CRemoteDriverTest.java',
'internal/ApacheHttpClientTest.java',
'internal/CircularOutputStreamTest.java',
'internal/ApacheHttpClientTest.java',
Expand All @@ -43,6 +44,9 @@ java_test(name = 'client-tests',
],
deps = [
'//java/client/src/org/openqa/selenium:selenium',
'//java/client/src/org/openqa/selenium/chrome:chrome',
'//java/client/src/org/openqa/selenium/firefox:firefox',
'//java/client/src/org/openqa/selenium/ie:ie',
'//java/client/src/org/openqa/selenium/remote:remote',
'//java/client/test/org/openqa/selenium:helpers',
'//java/client/test/org/openqa/selenium/testing:test-base',
Expand Down
Loading

0 comments on commit fd92d1c

Please sign in to comment.