From 955581f308e3a1d5533f3f495a495df2173e7c8f Mon Sep 17 00:00:00 2001 From: Puja Jagani Date: Mon, 28 Nov 2022 19:03:35 +0530 Subject: [PATCH] [java] Merge mutable capabilities with options correctly --- .../openqa/selenium/MutableCapabilities.java | 1 + .../openqa/selenium/chrome/ChromeOptions.java | 2 + .../selenium/chromium/ChromiumOptions.java | 45 +++++++++++++ .../org/openqa/selenium/edge/EdgeOptions.java | 5 ++ .../selenium/firefox/FirefoxOptions.java | 48 ++++++++++++++ .../selenium/chrome/ChromeOptionsTest.java | 57 +++++++++++++++++ .../openqa/selenium/edge/EdgeOptionsTest.java | 59 +++++++++++++++++ .../selenium/firefox/FirefoxOptionsTest.java | 64 +++++++++++++++++++ 8 files changed, 281 insertions(+) diff --git a/java/src/org/openqa/selenium/MutableCapabilities.java b/java/src/org/openqa/selenium/MutableCapabilities.java index 6ed8072df3546..7b831d1776b0e 100644 --- a/java/src/org/openqa/selenium/MutableCapabilities.java +++ b/java/src/org/openqa/selenium/MutableCapabilities.java @@ -32,6 +32,7 @@ public class MutableCapabilities implements Capabilities { static { HashSet keys = new HashSet<>(); keys.add("goog:chromeOptions"); + keys.add("ms:edgeOptions"); keys.add("moz:firefoxOptions"); keys.add("se:ieOptions"); OPTION_KEYS = Collections.unmodifiableSet(keys); diff --git a/java/src/org/openqa/selenium/chrome/ChromeOptions.java b/java/src/org/openqa/selenium/chrome/ChromeOptions.java index 730d28ae856b7..586f1e565351f 100644 --- a/java/src/org/openqa/selenium/chrome/ChromeOptions.java +++ b/java/src/org/openqa/selenium/chrome/ChromeOptions.java @@ -70,6 +70,8 @@ public ChromeOptions merge(Capabilities extraCapabilities) { ChromeOptions newInstance = new ChromeOptions(); newInstance.mergeInPlace(this); newInstance.mergeInPlace(extraCapabilities); + newInstance.mergeInOptionsFromCaps(CAPABILITY, extraCapabilities); + return newInstance; } diff --git a/java/src/org/openqa/selenium/chromium/ChromiumOptions.java b/java/src/org/openqa/selenium/chromium/ChromiumOptions.java index dfc8df1f24fc8..31f5a654e4e39 100644 --- a/java/src/org/openqa/selenium/chromium/ChromiumOptions.java +++ b/java/src/org/openqa/selenium/chromium/ChromiumOptions.java @@ -290,4 +290,49 @@ protected void mergeInPlace(Capabilities capabilities) { options.experimentalOptions.forEach(this::setExperimentalOption); } } + + protected void mergeInOptionsFromCaps(String capabilityName, Capabilities capabilities) { + if (!(capabilities instanceof ChromiumOptions)) { + Object object = capabilities.getCapability(capabilityName); + + if (object instanceof Map) { + @SuppressWarnings("unchecked") Map options = (Map) object; + + @SuppressWarnings("unchecked") List + arguments = + (List) (options.getOrDefault("args", new HashMap<>())); + @SuppressWarnings("unchecked") List extensionList = + (List) (options.getOrDefault("extensions", new ArrayList<>())); + + arguments.forEach(arg -> { + if (!args.contains(arg)) { + addArguments(arg); + } + }); + + extensionList.forEach(extension -> { + if (!extensions.contains(extension)) { + if (extension instanceof File) { + addExtensions((File) extension); + } else if (extension instanceof String) { + addEncodedExtensions((String) extension); + } + } + }); + + Object binary = options.get("binary"); + if (binary instanceof String) { + setBinary((String) binary); + } else if (binary instanceof File) { + setBinary((File) binary); + } + + options.forEach((k, v) -> { + if (!k.equals("binary") && !k.equals("extensions") && !k.equals("args")) { + setExperimentalOption(k, v); + } + }); + } + } + } } diff --git a/java/src/org/openqa/selenium/edge/EdgeOptions.java b/java/src/org/openqa/selenium/edge/EdgeOptions.java index ac1d287fdc73d..88ede4e23ec1a 100644 --- a/java/src/org/openqa/selenium/edge/EdgeOptions.java +++ b/java/src/org/openqa/selenium/edge/EdgeOptions.java @@ -18,6 +18,7 @@ import org.openqa.selenium.Capabilities; import org.openqa.selenium.chromium.ChromiumOptions; +import org.openqa.selenium.internal.Require; import org.openqa.selenium.remote.CapabilityType; import static org.openqa.selenium.remote.Browser.EDGE; @@ -56,9 +57,13 @@ public EdgeOptions() { @Override public EdgeOptions merge(Capabilities extraCapabilities) { + Require.nonNull("Capabilities to merge", extraCapabilities); + EdgeOptions newInstance = new EdgeOptions(); newInstance.mergeInPlace(this); newInstance.mergeInPlace(extraCapabilities); + newInstance.mergeInOptionsFromCaps(CAPABILITY, extraCapabilities); + return newInstance; } } diff --git a/java/src/org/openqa/selenium/firefox/FirefoxOptions.java b/java/src/org/openqa/selenium/firefox/FirefoxOptions.java index 111e1bb362b75..2ef1d21bbecdb 100644 --- a/java/src/org/openqa/selenium/firefox/FirefoxOptions.java +++ b/java/src/org/openqa/selenium/firefox/FirefoxOptions.java @@ -376,7 +376,55 @@ public FirefoxOptions merge(Capabilities capabilities) { newInstance.mirror(this); if (capabilities instanceof FirefoxOptions) { newInstance.mirror((FirefoxOptions) capabilities); + } else { + Object optionsValue = capabilities.getCapability(FIREFOX_OPTIONS); + + if (optionsValue instanceof Map) { + @SuppressWarnings("unchecked") Map + options = + (Map) optionsValue; + + @SuppressWarnings("unchecked") List arguments = + (List) (options.getOrDefault("args", new ArrayList<>())); + @SuppressWarnings("unchecked") Map prefs = + (Map) options.getOrDefault("prefs", new HashMap<>()); + String rawProfile = (String) options.get("profile"); + @SuppressWarnings("unchecked") Map logLevelMap = + (Map) options.getOrDefault("log", new HashMap<>()); + FirefoxDriverLogLevel logLevel = + FirefoxDriverLogLevel.fromString((String) logLevelMap.get("level")); + + arguments.forEach(arg -> { + if (!((List) newInstance.firefoxOptions.get(Keys.ARGS.key())).contains(arg)) { + newInstance.addArguments(arg); + } + }); + + Object binary = options.get("binary"); + if (binary instanceof String) { + newInstance.setBinary((String) binary); + } else if (binary instanceof Path) { + newInstance.setBinary((Path) binary); + } else if (binary instanceof FirefoxBinary) { + newInstance.setBinary((FirefoxBinary) binary); + } + + prefs.forEach(newInstance::addPreference); + + if (rawProfile != null) { + try { + newInstance.setProfile(FirefoxProfile.fromJson(rawProfile)); + } catch (IOException e) { + throw new WebDriverException(e); + } + } + + if (logLevel != null) { + newInstance.setLogLevel(logLevel); + } + } } + return newInstance; } diff --git a/java/test/org/openqa/selenium/chrome/ChromeOptionsTest.java b/java/test/org/openqa/selenium/chrome/ChromeOptionsTest.java index ca3dd5236c5b1..311212b4da49c 100644 --- a/java/test/org/openqa/selenium/chrome/ChromeOptionsTest.java +++ b/java/test/org/openqa/selenium/chrome/ChromeOptionsTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.openqa.selenium.AcceptedW3CCapabilityKeys; +import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.PageLoadStrategy; import org.openqa.selenium.UnexpectedAlertBehaviour; import org.openqa.selenium.testing.TestUtilities; @@ -37,9 +38,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.InstanceOfAssertFactories.LIST; +import static org.assertj.core.api.InstanceOfAssertFactories.STRING; import static org.assertj.core.api.InstanceOfAssertFactories.MAP; import static org.openqa.selenium.chrome.ChromeDriverLogLevel.OFF; import static org.openqa.selenium.chrome.ChromeDriverLogLevel.SEVERE; +import static org.openqa.selenium.remote.CapabilityType.ACCEPT_INSECURE_CERTS; import static org.openqa.selenium.remote.CapabilityType.TIMEOUTS; @Tag("UnitTests") @@ -207,6 +210,60 @@ void mergingOptionsMergesExperimentalOptions() { .containsEntry("opt3", "val3"); } + @Test + void mergingOptionsWithMutableCapabilities() { + File ext1 = TestUtilities.createTmpFile("ext1"); + String ext1Encoded = Base64.getEncoder().encodeToString("ext1".getBytes()); + String ext2 = Base64.getEncoder().encodeToString("ext2".getBytes()); + + MutableCapabilities one = new MutableCapabilities(); + + ChromeOptions options = new ChromeOptions(); + options.addArguments("verbose"); + options.addArguments("silent"); + options.setExperimentalOption("opt1", "val1"); + options.setExperimentalOption("opt2", "val4"); + options.addExtensions(ext1); + options.addEncodedExtensions(ext2); + options.setAcceptInsecureCerts(true); + File binary = TestUtilities.createTmpFile("binary"); + options.setBinary(binary); + + one.setCapability(ChromeOptions.CAPABILITY, options); + + ChromeOptions two = new ChromeOptions(); + two.addArguments("verbose"); + two.setExperimentalOption("opt2", "val2"); + two.setExperimentalOption("opt3", "val3"); + two = two.merge(one); + + Map map = two.asMap(); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(ChromeOptions.CAPABILITY).asInstanceOf(MAP) + .extractingByKey("args").asInstanceOf(LIST) + .containsExactly("verbose", "silent"); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(ChromeOptions.CAPABILITY).asInstanceOf(MAP) + .containsEntry("opt1", "val1") + .containsEntry("opt2", "val4") + .containsEntry("opt3", "val3"); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(ACCEPT_INSECURE_CERTS).isExactlyInstanceOf(Boolean.class); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(ChromeOptions.CAPABILITY).asInstanceOf(MAP) + .extractingByKey("extensions").asInstanceOf(LIST) + .containsExactly(ext1Encoded, ext2); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(ChromeOptions.CAPABILITY).asInstanceOf(MAP) + .extractingByKey("binary").asInstanceOf(STRING) + .isEqualTo(binary.getPath()); + } + @Test void isW3CSafe() { Map converted = new ChromeOptions() diff --git a/java/test/org/openqa/selenium/edge/EdgeOptionsTest.java b/java/test/org/openqa/selenium/edge/EdgeOptionsTest.java index 3b5a3df25513a..4fe76ba07e738 100644 --- a/java/test/org/openqa/selenium/edge/EdgeOptionsTest.java +++ b/java/test/org/openqa/selenium/edge/EdgeOptionsTest.java @@ -20,8 +20,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.openqa.selenium.ImmutableCapabilities; +import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.PageLoadStrategy; import org.openqa.selenium.remote.CapabilityType; +import org.openqa.selenium.testing.TestUtilities; import java.io.File; import java.io.IOException; @@ -39,7 +41,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.InstanceOfAssertFactories.LIST; import static org.assertj.core.api.InstanceOfAssertFactories.MAP; +import static org.assertj.core.api.InstanceOfAssertFactories.STRING; import static org.openqa.selenium.remote.Browser.EDGE; +import static org.openqa.selenium.remote.CapabilityType.ACCEPT_INSECURE_CERTS; @Tag("UnitTests") class EdgeOptionsTest { @@ -88,6 +92,61 @@ void canMergeWithoutChangingOriginalObject() { assertThat(merged.getCapability(CapabilityType.PAGE_LOAD_STRATEGY)).isEqualTo(PageLoadStrategy.NONE); } + @Test + void mergingOptionsWithMutableCapabilities() { + File ext1 = TestUtilities.createTmpFile("ext1"); + String ext1Encoded = Base64.getEncoder().encodeToString("ext1".getBytes()); + String ext2 = Base64.getEncoder().encodeToString("ext2".getBytes()); + + MutableCapabilities one = new MutableCapabilities(); + + EdgeOptions options = new EdgeOptions(); + options.addArguments("verbose"); + options.addArguments("silent"); + options.setExperimentalOption("opt1", "val1"); + options.setExperimentalOption("opt2", "val4"); + options.addExtensions(ext1); + options.addEncodedExtensions(ext2); + options.setAcceptInsecureCerts(true); + File binary = TestUtilities.createTmpFile("binary"); + options.setBinary(binary); + + one.setCapability(EdgeOptions.CAPABILITY, options); + + EdgeOptions two = new EdgeOptions(); + two.addArguments("verbose"); + two.setExperimentalOption("opt2", "val2"); + two.setExperimentalOption("opt3", "val3"); + + two = two.merge(one); + + Map map = two.asMap(); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(EdgeOptions.CAPABILITY).asInstanceOf(MAP) + .extractingByKey("args").asInstanceOf(LIST) + .containsExactly("verbose", "silent"); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(EdgeOptions.CAPABILITY).asInstanceOf(MAP) + .containsEntry("opt1", "val1") + .containsEntry("opt2", "val4") + .containsEntry("opt3", "val3"); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(ACCEPT_INSECURE_CERTS).isExactlyInstanceOf(Boolean.class); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(EdgeOptions.CAPABILITY).asInstanceOf(MAP) + .extractingByKey("extensions").asInstanceOf(LIST) + .containsExactly(ext1Encoded, ext2); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(EdgeOptions.CAPABILITY).asInstanceOf(MAP) + .extractingByKey("binary").asInstanceOf(STRING) + .isEqualTo(binary.getPath()); + } + private void checkCommonStructure(EdgeOptions options) { assertThat(options.asMap()) .containsEntry(CapabilityType.BROWSER_NAME, EDGE.browserName()) diff --git a/java/test/org/openqa/selenium/firefox/FirefoxOptionsTest.java b/java/test/org/openqa/selenium/firefox/FirefoxOptionsTest.java index 53704b493cd33..4f59b330c55ec 100644 --- a/java/test/org/openqa/selenium/firefox/FirefoxOptionsTest.java +++ b/java/test/org/openqa/selenium/firefox/FirefoxOptionsTest.java @@ -26,6 +26,7 @@ import static org.assertj.core.api.Assumptions.assumeThat; import static org.assertj.core.api.InstanceOfAssertFactories.LIST; import static org.assertj.core.api.InstanceOfAssertFactories.MAP; +import static org.assertj.core.api.InstanceOfAssertFactories.STRING; import static org.openqa.selenium.PageLoadStrategy.EAGER; import static org.openqa.selenium.firefox.FirefoxDriver.SystemProperty.BROWSER_BINARY; import static org.openqa.selenium.firefox.FirefoxDriver.SystemProperty.BROWSER_PROFILE; @@ -375,6 +376,69 @@ void mergingOptionsMergesPreferences() { .containsEntry("opt3", "val3"); } + @Test + void mergingOptionsWithMutableCapabilities() { + MutableCapabilities one = new MutableCapabilities(); + + FirefoxOptions options = new FirefoxOptions(); + options.addArguments("verbose"); + options.addArguments("silent"); + options.addPreference("opt1", "val1"); + options.addPreference("opt2", "val4"); + options.setAcceptInsecureCerts(true); + + String key = "browser.startup.homepage"; + String value = "about:robots"; + + FirefoxProfile profile = new FirefoxProfile(); + profile.setPreference(key, value); + + options.setProfile(profile); + + options.setLogLevel(DEBUG); + + File binary = TestUtilities.createTmpFile("binary"); + options.setBinary(binary.toPath()); + + one.setCapability(FIREFOX_OPTIONS, options); + + FirefoxOptions two = new FirefoxOptions(); + two.addArguments("verbose"); + two.addPreference("opt2", "val2"); + two.addPreference("opt3", "val3"); + two = two.merge(one); + + Map map = two.asMap(); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(FIREFOX_OPTIONS).asInstanceOf(MAP) + .extractingByKey("args").asInstanceOf(LIST) + .containsExactly("verbose", "silent"); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(FIREFOX_OPTIONS).asInstanceOf(MAP) + .extractingByKey("prefs").asInstanceOf(MAP) + .containsEntry("opt1", "val1") + .containsEntry("opt2", "val4") + .containsEntry("opt3", "val3"); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(ACCEPT_INSECURE_CERTS).isExactlyInstanceOf(Boolean.class); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(FIREFOX_OPTIONS).asInstanceOf(MAP) + .extractingByKey("binary").asInstanceOf(STRING) + .isEqualTo(binary.getPath()); + + assertThat(map).asInstanceOf(MAP) + .extractingByKey(FIREFOX_OPTIONS).asInstanceOf(MAP) + .extractingByKey("log").asInstanceOf(MAP) + .containsEntry("level", "debug"); + + FirefoxProfile extractedProfile = two.getProfile(); + assertThat(extractedProfile.getStringPreference(key, "-")).isEqualTo(value); + } + @Test void firefoxOptionsShouldEqualEquivalentImmutableCapabilities() { FirefoxOptions