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

Conversation

titusfortner
Copy link
Member

@titusfortner titusfortner commented Sep 14, 2021

Description

Adds methods and tests for:

  • setBrowserVersion()
  • setPlatformName()
  • setTimeouts()
    edit:
  • setImplicitWaitTimeout()
  • setPageLoadTimeout()
  • setScriptTimeout()

Timeouts has user-facing Duration that gets converted to millis when adding it to capabilities.

Motivation and Context

It makes sense to have specific methods for known capabilities, and this is backwards compatible.

return (DO) this;
}

public DO setTimeouts(Map<String, Duration> timeouts) {
Copy link
Member

Choose a reason for hiding this comment

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

Prefer a setImplicitTimeout, setPageLoadTimeout and setScriptTimeout, and then we don't need to do any validation, and it's really hard to get wrong.

Copy link
Member Author

Choose a reason for hiding this comment

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

Much better implementation.

}
});

setCapability(TIMEOUTS, convertedTimeouts);
Copy link
Member

Choose a reason for hiding this comment

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

I think this has the potential to get confusing --- somewhere in a framework the implicit timeout is set, and then user code sets the page load timeout: what do we expect to happen to the implicit timeout? I think people will expect both to be set.

Copy link
Member Author

Choose a reason for hiding this comment

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

splitting up the timeouts as suggested above fixes this issue nicely.

@@ -47,6 +47,10 @@
String HAS_TOUCHSCREEN = "hasTouchScreen";
String OVERLAPPING_CHECK_DISABLED = "overlappingCheckDisabled";
String STRICT_FILE_INTERACTABILITY = "strictFileInteractability";
String TIMEOUTS = "timeouts";
String IMPLICIT_TIMEOUT = "implicit";
Copy link
Member

Choose a reason for hiding this comment

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

These are really meant to be the keys in the capabilities, not the keys of values within the capabilities

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

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?

@sonarcloud
Copy link

sonarcloud bot commented Sep 20, 2021

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 7 Code Smells

0.0% 0.0% Coverage
0.0% 0.0% Duplication


private Map<String, Number> getTimeouts() {
Map<String, Number> newTimeouts = new HashMap<>();
Object raw = getCapability(TIMEOUTS);
Copy link
Member

Choose a reason for hiding this comment

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

You need to check that raw is actually a Map before casting to it. If not, if the user has set some garbage value (eg: setCapability(TIMEOUTS, "cheese") chaos will follow.

@shs96c shs96c merged commit b86d847 into SeleniumHQ:trunk Sep 20, 2021
@titusfortner titusfortner deleted the java_w3c_options branch December 4, 2022 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants