Skip to content

Commit 955581f

Browse files
authored
[java] Merge mutable capabilities with options correctly
1 parent ef63f5d commit 955581f

File tree

8 files changed

+281
-0
lines changed

8 files changed

+281
-0
lines changed

java/src/org/openqa/selenium/MutableCapabilities.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class MutableCapabilities implements Capabilities {
3232
static {
3333
HashSet<String> keys = new HashSet<>();
3434
keys.add("goog:chromeOptions");
35+
keys.add("ms:edgeOptions");
3536
keys.add("moz:firefoxOptions");
3637
keys.add("se:ieOptions");
3738
OPTION_KEYS = Collections.unmodifiableSet(keys);

java/src/org/openqa/selenium/chrome/ChromeOptions.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ public ChromeOptions merge(Capabilities extraCapabilities) {
7070
ChromeOptions newInstance = new ChromeOptions();
7171
newInstance.mergeInPlace(this);
7272
newInstance.mergeInPlace(extraCapabilities);
73+
newInstance.mergeInOptionsFromCaps(CAPABILITY, extraCapabilities);
74+
7375
return newInstance;
7476
}
7577

java/src/org/openqa/selenium/chromium/ChromiumOptions.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,4 +290,49 @@ protected void mergeInPlace(Capabilities capabilities) {
290290
options.experimentalOptions.forEach(this::setExperimentalOption);
291291
}
292292
}
293+
294+
protected void mergeInOptionsFromCaps(String capabilityName, Capabilities capabilities) {
295+
if (!(capabilities instanceof ChromiumOptions)) {
296+
Object object = capabilities.getCapability(capabilityName);
297+
298+
if (object instanceof Map) {
299+
@SuppressWarnings("unchecked") Map<String, Object> options = (Map<String, Object>) object;
300+
301+
@SuppressWarnings("unchecked") List<String>
302+
arguments =
303+
(List<String>) (options.getOrDefault("args", new HashMap<>()));
304+
@SuppressWarnings("unchecked") List<Object> extensionList =
305+
(List<Object>) (options.getOrDefault("extensions", new ArrayList<>()));
306+
307+
arguments.forEach(arg -> {
308+
if (!args.contains(arg)) {
309+
addArguments(arg);
310+
}
311+
});
312+
313+
extensionList.forEach(extension -> {
314+
if (!extensions.contains(extension)) {
315+
if (extension instanceof File) {
316+
addExtensions((File) extension);
317+
} else if (extension instanceof String) {
318+
addEncodedExtensions((String) extension);
319+
}
320+
}
321+
});
322+
323+
Object binary = options.get("binary");
324+
if (binary instanceof String) {
325+
setBinary((String) binary);
326+
} else if (binary instanceof File) {
327+
setBinary((File) binary);
328+
}
329+
330+
options.forEach((k, v) -> {
331+
if (!k.equals("binary") && !k.equals("extensions") && !k.equals("args")) {
332+
setExperimentalOption(k, v);
333+
}
334+
});
335+
}
336+
}
337+
}
293338
}

java/src/org/openqa/selenium/edge/EdgeOptions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.openqa.selenium.Capabilities;
2020
import org.openqa.selenium.chromium.ChromiumOptions;
21+
import org.openqa.selenium.internal.Require;
2122
import org.openqa.selenium.remote.CapabilityType;
2223

2324
import static org.openqa.selenium.remote.Browser.EDGE;
@@ -56,9 +57,13 @@ public EdgeOptions() {
5657

5758
@Override
5859
public EdgeOptions merge(Capabilities extraCapabilities) {
60+
Require.nonNull("Capabilities to merge", extraCapabilities);
61+
5962
EdgeOptions newInstance = new EdgeOptions();
6063
newInstance.mergeInPlace(this);
6164
newInstance.mergeInPlace(extraCapabilities);
65+
newInstance.mergeInOptionsFromCaps(CAPABILITY, extraCapabilities);
66+
6267
return newInstance;
6368
}
6469
}

java/src/org/openqa/selenium/firefox/FirefoxOptions.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,55 @@ public FirefoxOptions merge(Capabilities capabilities) {
376376
newInstance.mirror(this);
377377
if (capabilities instanceof FirefoxOptions) {
378378
newInstance.mirror((FirefoxOptions) capabilities);
379+
} else {
380+
Object optionsValue = capabilities.getCapability(FIREFOX_OPTIONS);
381+
382+
if (optionsValue instanceof Map) {
383+
@SuppressWarnings("unchecked") Map<String, Object>
384+
options =
385+
(Map<String, Object>) optionsValue;
386+
387+
@SuppressWarnings("unchecked") List<String> arguments =
388+
(List<String>) (options.getOrDefault("args", new ArrayList<>()));
389+
@SuppressWarnings("unchecked") Map<String, Object> prefs =
390+
(Map<String, Object>) options.getOrDefault("prefs", new HashMap<>());
391+
String rawProfile = (String) options.get("profile");
392+
@SuppressWarnings("unchecked") Map<String, Object> logLevelMap =
393+
(Map<String, Object>) options.getOrDefault("log", new HashMap<>());
394+
FirefoxDriverLogLevel logLevel =
395+
FirefoxDriverLogLevel.fromString((String) logLevelMap.get("level"));
396+
397+
arguments.forEach(arg -> {
398+
if (!((List<String>) newInstance.firefoxOptions.get(Keys.ARGS.key())).contains(arg)) {
399+
newInstance.addArguments(arg);
400+
}
401+
});
402+
403+
Object binary = options.get("binary");
404+
if (binary instanceof String) {
405+
newInstance.setBinary((String) binary);
406+
} else if (binary instanceof Path) {
407+
newInstance.setBinary((Path) binary);
408+
} else if (binary instanceof FirefoxBinary) {
409+
newInstance.setBinary((FirefoxBinary) binary);
410+
}
411+
412+
prefs.forEach(newInstance::addPreference);
413+
414+
if (rawProfile != null) {
415+
try {
416+
newInstance.setProfile(FirefoxProfile.fromJson(rawProfile));
417+
} catch (IOException e) {
418+
throw new WebDriverException(e);
419+
}
420+
}
421+
422+
if (logLevel != null) {
423+
newInstance.setLogLevel(logLevel);
424+
}
425+
}
379426
}
427+
380428
return newInstance;
381429
}
382430

java/test/org/openqa/selenium/chrome/ChromeOptionsTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.junit.jupiter.api.Test;
2121
import org.junit.jupiter.api.Tag;
2222
import org.openqa.selenium.AcceptedW3CCapabilityKeys;
23+
import org.openqa.selenium.MutableCapabilities;
2324
import org.openqa.selenium.PageLoadStrategy;
2425
import org.openqa.selenium.UnexpectedAlertBehaviour;
2526
import org.openqa.selenium.testing.TestUtilities;
@@ -37,9 +38,11 @@
3738
import static org.assertj.core.api.Assertions.assertThat;
3839
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
3940
import static org.assertj.core.api.InstanceOfAssertFactories.LIST;
41+
import static org.assertj.core.api.InstanceOfAssertFactories.STRING;
4042
import static org.assertj.core.api.InstanceOfAssertFactories.MAP;
4143
import static org.openqa.selenium.chrome.ChromeDriverLogLevel.OFF;
4244
import static org.openqa.selenium.chrome.ChromeDriverLogLevel.SEVERE;
45+
import static org.openqa.selenium.remote.CapabilityType.ACCEPT_INSECURE_CERTS;
4346
import static org.openqa.selenium.remote.CapabilityType.TIMEOUTS;
4447

4548
@Tag("UnitTests")
@@ -207,6 +210,60 @@ void mergingOptionsMergesExperimentalOptions() {
207210
.containsEntry("opt3", "val3");
208211
}
209212

213+
@Test
214+
void mergingOptionsWithMutableCapabilities() {
215+
File ext1 = TestUtilities.createTmpFile("ext1");
216+
String ext1Encoded = Base64.getEncoder().encodeToString("ext1".getBytes());
217+
String ext2 = Base64.getEncoder().encodeToString("ext2".getBytes());
218+
219+
MutableCapabilities one = new MutableCapabilities();
220+
221+
ChromeOptions options = new ChromeOptions();
222+
options.addArguments("verbose");
223+
options.addArguments("silent");
224+
options.setExperimentalOption("opt1", "val1");
225+
options.setExperimentalOption("opt2", "val4");
226+
options.addExtensions(ext1);
227+
options.addEncodedExtensions(ext2);
228+
options.setAcceptInsecureCerts(true);
229+
File binary = TestUtilities.createTmpFile("binary");
230+
options.setBinary(binary);
231+
232+
one.setCapability(ChromeOptions.CAPABILITY, options);
233+
234+
ChromeOptions two = new ChromeOptions();
235+
two.addArguments("verbose");
236+
two.setExperimentalOption("opt2", "val2");
237+
two.setExperimentalOption("opt3", "val3");
238+
two = two.merge(one);
239+
240+
Map<String, Object> map = two.asMap();
241+
242+
assertThat(map).asInstanceOf(MAP)
243+
.extractingByKey(ChromeOptions.CAPABILITY).asInstanceOf(MAP)
244+
.extractingByKey("args").asInstanceOf(LIST)
245+
.containsExactly("verbose", "silent");
246+
247+
assertThat(map).asInstanceOf(MAP)
248+
.extractingByKey(ChromeOptions.CAPABILITY).asInstanceOf(MAP)
249+
.containsEntry("opt1", "val1")
250+
.containsEntry("opt2", "val4")
251+
.containsEntry("opt3", "val3");
252+
253+
assertThat(map).asInstanceOf(MAP)
254+
.extractingByKey(ACCEPT_INSECURE_CERTS).isExactlyInstanceOf(Boolean.class);
255+
256+
assertThat(map).asInstanceOf(MAP)
257+
.extractingByKey(ChromeOptions.CAPABILITY).asInstanceOf(MAP)
258+
.extractingByKey("extensions").asInstanceOf(LIST)
259+
.containsExactly(ext1Encoded, ext2);
260+
261+
assertThat(map).asInstanceOf(MAP)
262+
.extractingByKey(ChromeOptions.CAPABILITY).asInstanceOf(MAP)
263+
.extractingByKey("binary").asInstanceOf(STRING)
264+
.isEqualTo(binary.getPath());
265+
}
266+
210267
@Test
211268
void isW3CSafe() {
212269
Map<String, Object> converted = new ChromeOptions()

java/test/org/openqa/selenium/edge/EdgeOptionsTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
import org.junit.jupiter.api.Test;
2121
import org.junit.jupiter.api.Tag;
2222
import org.openqa.selenium.ImmutableCapabilities;
23+
import org.openqa.selenium.MutableCapabilities;
2324
import org.openqa.selenium.PageLoadStrategy;
2425
import org.openqa.selenium.remote.CapabilityType;
26+
import org.openqa.selenium.testing.TestUtilities;
2527

2628
import java.io.File;
2729
import java.io.IOException;
@@ -39,7 +41,9 @@
3941
import static org.assertj.core.api.Assertions.assertThat;
4042
import static org.assertj.core.api.InstanceOfAssertFactories.LIST;
4143
import static org.assertj.core.api.InstanceOfAssertFactories.MAP;
44+
import static org.assertj.core.api.InstanceOfAssertFactories.STRING;
4245
import static org.openqa.selenium.remote.Browser.EDGE;
46+
import static org.openqa.selenium.remote.CapabilityType.ACCEPT_INSECURE_CERTS;
4347

4448
@Tag("UnitTests")
4549
class EdgeOptionsTest {
@@ -88,6 +92,61 @@ void canMergeWithoutChangingOriginalObject() {
8892
assertThat(merged.getCapability(CapabilityType.PAGE_LOAD_STRATEGY)).isEqualTo(PageLoadStrategy.NONE);
8993
}
9094

95+
@Test
96+
void mergingOptionsWithMutableCapabilities() {
97+
File ext1 = TestUtilities.createTmpFile("ext1");
98+
String ext1Encoded = Base64.getEncoder().encodeToString("ext1".getBytes());
99+
String ext2 = Base64.getEncoder().encodeToString("ext2".getBytes());
100+
101+
MutableCapabilities one = new MutableCapabilities();
102+
103+
EdgeOptions options = new EdgeOptions();
104+
options.addArguments("verbose");
105+
options.addArguments("silent");
106+
options.setExperimentalOption("opt1", "val1");
107+
options.setExperimentalOption("opt2", "val4");
108+
options.addExtensions(ext1);
109+
options.addEncodedExtensions(ext2);
110+
options.setAcceptInsecureCerts(true);
111+
File binary = TestUtilities.createTmpFile("binary");
112+
options.setBinary(binary);
113+
114+
one.setCapability(EdgeOptions.CAPABILITY, options);
115+
116+
EdgeOptions two = new EdgeOptions();
117+
two.addArguments("verbose");
118+
two.setExperimentalOption("opt2", "val2");
119+
two.setExperimentalOption("opt3", "val3");
120+
121+
two = two.merge(one);
122+
123+
Map<String, Object> map = two.asMap();
124+
125+
assertThat(map).asInstanceOf(MAP)
126+
.extractingByKey(EdgeOptions.CAPABILITY).asInstanceOf(MAP)
127+
.extractingByKey("args").asInstanceOf(LIST)
128+
.containsExactly("verbose", "silent");
129+
130+
assertThat(map).asInstanceOf(MAP)
131+
.extractingByKey(EdgeOptions.CAPABILITY).asInstanceOf(MAP)
132+
.containsEntry("opt1", "val1")
133+
.containsEntry("opt2", "val4")
134+
.containsEntry("opt3", "val3");
135+
136+
assertThat(map).asInstanceOf(MAP)
137+
.extractingByKey(ACCEPT_INSECURE_CERTS).isExactlyInstanceOf(Boolean.class);
138+
139+
assertThat(map).asInstanceOf(MAP)
140+
.extractingByKey(EdgeOptions.CAPABILITY).asInstanceOf(MAP)
141+
.extractingByKey("extensions").asInstanceOf(LIST)
142+
.containsExactly(ext1Encoded, ext2);
143+
144+
assertThat(map).asInstanceOf(MAP)
145+
.extractingByKey(EdgeOptions.CAPABILITY).asInstanceOf(MAP)
146+
.extractingByKey("binary").asInstanceOf(STRING)
147+
.isEqualTo(binary.getPath());
148+
}
149+
91150
private void checkCommonStructure(EdgeOptions options) {
92151
assertThat(options.asMap())
93152
.containsEntry(CapabilityType.BROWSER_NAME, EDGE.browserName())

java/test/org/openqa/selenium/firefox/FirefoxOptionsTest.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import static org.assertj.core.api.Assumptions.assumeThat;
2727
import static org.assertj.core.api.InstanceOfAssertFactories.LIST;
2828
import static org.assertj.core.api.InstanceOfAssertFactories.MAP;
29+
import static org.assertj.core.api.InstanceOfAssertFactories.STRING;
2930
import static org.openqa.selenium.PageLoadStrategy.EAGER;
3031
import static org.openqa.selenium.firefox.FirefoxDriver.SystemProperty.BROWSER_BINARY;
3132
import static org.openqa.selenium.firefox.FirefoxDriver.SystemProperty.BROWSER_PROFILE;
@@ -375,6 +376,69 @@ void mergingOptionsMergesPreferences() {
375376
.containsEntry("opt3", "val3");
376377
}
377378

379+
@Test
380+
void mergingOptionsWithMutableCapabilities() {
381+
MutableCapabilities one = new MutableCapabilities();
382+
383+
FirefoxOptions options = new FirefoxOptions();
384+
options.addArguments("verbose");
385+
options.addArguments("silent");
386+
options.addPreference("opt1", "val1");
387+
options.addPreference("opt2", "val4");
388+
options.setAcceptInsecureCerts(true);
389+
390+
String key = "browser.startup.homepage";
391+
String value = "about:robots";
392+
393+
FirefoxProfile profile = new FirefoxProfile();
394+
profile.setPreference(key, value);
395+
396+
options.setProfile(profile);
397+
398+
options.setLogLevel(DEBUG);
399+
400+
File binary = TestUtilities.createTmpFile("binary");
401+
options.setBinary(binary.toPath());
402+
403+
one.setCapability(FIREFOX_OPTIONS, options);
404+
405+
FirefoxOptions two = new FirefoxOptions();
406+
two.addArguments("verbose");
407+
two.addPreference("opt2", "val2");
408+
two.addPreference("opt3", "val3");
409+
two = two.merge(one);
410+
411+
Map<String, Object> map = two.asMap();
412+
413+
assertThat(map).asInstanceOf(MAP)
414+
.extractingByKey(FIREFOX_OPTIONS).asInstanceOf(MAP)
415+
.extractingByKey("args").asInstanceOf(LIST)
416+
.containsExactly("verbose", "silent");
417+
418+
assertThat(map).asInstanceOf(MAP)
419+
.extractingByKey(FIREFOX_OPTIONS).asInstanceOf(MAP)
420+
.extractingByKey("prefs").asInstanceOf(MAP)
421+
.containsEntry("opt1", "val1")
422+
.containsEntry("opt2", "val4")
423+
.containsEntry("opt3", "val3");
424+
425+
assertThat(map).asInstanceOf(MAP)
426+
.extractingByKey(ACCEPT_INSECURE_CERTS).isExactlyInstanceOf(Boolean.class);
427+
428+
assertThat(map).asInstanceOf(MAP)
429+
.extractingByKey(FIREFOX_OPTIONS).asInstanceOf(MAP)
430+
.extractingByKey("binary").asInstanceOf(STRING)
431+
.isEqualTo(binary.getPath());
432+
433+
assertThat(map).asInstanceOf(MAP)
434+
.extractingByKey(FIREFOX_OPTIONS).asInstanceOf(MAP)
435+
.extractingByKey("log").asInstanceOf(MAP)
436+
.containsEntry("level", "debug");
437+
438+
FirefoxProfile extractedProfile = two.getProfile();
439+
assertThat(extractedProfile.getStringPreference(key, "-")).isEqualTo(value);
440+
}
441+
378442
@Test
379443
void firefoxOptionsShouldEqualEquivalentImmutableCapabilities() {
380444
FirefoxOptions

0 commit comments

Comments
 (0)