Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods to options classes for w3c compliant capabilities #9828

Merged
merged 3 commits into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 44 additions & 7 deletions java/src/org/openqa/selenium/remote/AbstractDriverOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,63 @@

package org.openqa.selenium.remote;

import static org.openqa.selenium.remote.CapabilityType.ACCEPT_INSECURE_CERTS;
import static org.openqa.selenium.remote.CapabilityType.PAGE_LOAD_STRATEGY;
import static org.openqa.selenium.remote.CapabilityType.PROXY;
import static org.openqa.selenium.remote.CapabilityType.STRICT_FILE_INTERACTABILITY;
import static org.openqa.selenium.remote.CapabilityType.UNEXPECTED_ALERT_BEHAVIOUR;
import static org.openqa.selenium.remote.CapabilityType.UNHANDLED_PROMPT_BEHAVIOUR;

import org.openqa.selenium.MutableCapabilities;
import org.openqa.selenium.PageLoadStrategy;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.UnexpectedAlertBehaviour;
import org.openqa.selenium.internal.Require;

import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import static org.openqa.selenium.remote.CapabilityType.ACCEPT_INSECURE_CERTS;
import static org.openqa.selenium.remote.CapabilityType.BROWSER_VERSION;
import static org.openqa.selenium.remote.CapabilityType.PAGE_LOAD_STRATEGY;
import static org.openqa.selenium.remote.CapabilityType.PLATFORM_NAME;
import static org.openqa.selenium.remote.CapabilityType.PROXY;
import static org.openqa.selenium.remote.CapabilityType.STRICT_FILE_INTERACTABILITY;
import static org.openqa.selenium.remote.CapabilityType.TIMEOUTS;
import static org.openqa.selenium.remote.CapabilityType.UNHANDLED_PROMPT_BEHAVIOUR;

public abstract class AbstractDriverOptions<DO extends AbstractDriverOptions> extends MutableCapabilities {
private Map<String, Long> timeouts = new HashMap<>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be tempted to avoid this variable. Consider a user doing:

options.setCapability(TIMEOUTS, Map.of("implicit", 1000));
options.setPageLoadTimeout(Duration.ofSeconds(10));

I think a user would expect both timeouts to have been set, but the current implementation would only set one. I also think the opposite case (where the timeouts capability is set after calling the specific method) would mean that the user would expect the timeout map they passed in to be used.

One drawback to doing this is that you can't expect the Map the user passes in to be mutable. You'll need a check like:

Map<String, Long> newTimeouts = new HashMap<>();
Object raw = getCapability(TIMEOUTS);
if (!(raw instanceof Map)) {
  ((Map<?, ?>) raw).foreach((key, value) -> {
    if (key instanceof String && value instanceof Long) {
      newTimeouts.put((String) key, (Long) value);
    }
  }
  newTimeouts.put("implicit", timeout.toMillis());
  setCapability(TIMEOUTS, Collections.immutableMap(newTimeouts));
))

Finally, it's good practice to make the collection "unmodifiable" when we set it, to prevent users from changing state in unexpected ways.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I put this in a private getCapabilities() method. I'm not sure the reason for instanceof Map conditional. What alternative object is valid that will start a session?


public DO setBrowserVersion(String browserVersion) {
setCapability(
BROWSER_VERSION,
Require.nonNull("Browser version", browserVersion));
return (DO) this;
}

public DO setPlatformName(String platformName) {
setCapability(
PLATFORM_NAME,
Require.nonNull("Platform Name", platformName));
return (DO) this;
}

public DO setImplicitWaitTimeout(Duration timeout) {
this.timeouts.put("implicit", timeout.toMillis());
setCapability(TIMEOUTS, this.timeouts);
return (DO) this;
}

public DO setPageLoadTimeout(Duration timeout) {
this.timeouts.put("pageLoad", timeout.toMillis());
setCapability(TIMEOUTS, this.timeouts);
return (DO) this;
}

public DO setScriptTimeout(Duration timeout) {
this.timeouts.put("script", timeout.toMillis());
setCapability(TIMEOUTS, this.timeouts);
return (DO) this;
}

public DO setPageLoadStrategy(PageLoadStrategy strategy) {
setCapability(
Expand Down
1 change: 1 addition & 0 deletions java/src/org/openqa/selenium/remote/CapabilityType.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public interface CapabilityType {
String HAS_TOUCHSCREEN = "hasTouchScreen";
String OVERLAPPING_CHECK_DISABLED = "overlappingCheckDisabled";
String STRICT_FILE_INTERACTABILITY = "strictFileInteractability";
String TIMEOUTS = "timeouts";

String LOGGING_PREFS = "loggingPrefs";

Expand Down
52 changes: 51 additions & 1 deletion java/test/org/openqa/selenium/chrome/ChromeOptionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@

import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.openqa.selenium.PageLoadStrategy;
import org.openqa.selenium.UnexpectedAlertBehaviour;
import org.openqa.selenium.remote.AcceptedW3CCapabilityKeys;
import org.openqa.selenium.testing.TestUtilities;
import org.openqa.selenium.testing.UnitTests;

import java.io.File;
import java.time.Duration;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toSet;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -67,6 +70,53 @@ public void canBuildLogLevelFromStringRepresentation() {
assertThat(ChromeDriverLogLevel.fromString("SEVERE")).isEqualTo(SEVERE);
}

@Test
public void canAddW3CCompliantOptions() {
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setBrowserVersion("99")
.setPlatformName("9 3/4")
.setUnhandledPromptBehaviour(UnexpectedAlertBehaviour.IGNORE)
.setAcceptInsecureCerts(true)
.setPageLoadStrategy(PageLoadStrategy.EAGER)
.setStrictFileInteractability(true)
.setImplicitWaitTimeout(Duration.ofSeconds(1))
.setPageLoadTimeout(Duration.ofSeconds(2))
.setScriptTimeout(Duration.ofSeconds(3));

Map<String, Object> mappedOptions = chromeOptions.asMap();
assertThat(mappedOptions.get("browserName")).isEqualTo("chrome");
assertThat(mappedOptions.get("browserVersion")).isEqualTo("99");
assertThat(mappedOptions.get("platformName")).isEqualTo("9 3/4");
assertThat(mappedOptions.get("unhandledPromptBehavior").toString()).isEqualTo("ignore");
assertThat(mappedOptions.get("acceptInsecureCerts")).isEqualTo(true);
assertThat(mappedOptions.get("pageLoadStrategy").toString()).isEqualTo("eager");
assertThat(mappedOptions.get("strictFileInteractability")).isEqualTo(true);

Map<String, Long> expectedTimeouts = new HashMap<>();
expectedTimeouts.put("implicit", 1000L);
expectedTimeouts.put("pageLoad", 2000L);
expectedTimeouts.put("script", 3000L);

assertThat(expectedTimeouts).isEqualTo(mappedOptions.get("timeouts"));
}

@Test
public void canAddSequentialTimeouts() {
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setImplicitWaitTimeout(Duration.ofSeconds(1));

Map<String, Object> mappedOptions = chromeOptions.asMap();
Map<String, Long> expectedTimeouts = new HashMap<>();

expectedTimeouts.put("implicit", 1000L);
assertThat(expectedTimeouts).isEqualTo(mappedOptions.get("timeouts"));

chromeOptions.setPageLoadTimeout(Duration.ofSeconds(2));
expectedTimeouts.put("pageLoad", 2000L);
Map<String, Object> mappedOptions2 = chromeOptions.asMap();
assertThat(expectedTimeouts).isEqualTo(mappedOptions2.get("timeouts"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps:

ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setImplicitWaitTimeout(Duration.ofSeconds(1));

assertThat(chromeOptions.getCapability(TIMEOUTS)).isEqualTo(Map.of("implicit", 1000L);

chromeOptions.setPageLoadTimeout(Duration.ofSeconds(2));
assertThat(chromeOptions.getCapability(TIMEOUTS)).isEqualTo(Map.of("implicit", 1000L, "page", 2000L);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to convert the options to an intermediate map.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, doesn't make sense if needing to look at existing capabilities

}

@Test
public void mergingOptionsMergesArguments() {
ChromeOptions one = new ChromeOptions().addArguments("verbose");
Expand Down