From d6f69a46d87e1fbf9e2d0e5a1f469492597f731a Mon Sep 17 00:00:00 2001 From: Mohammad Saddam Hossain Date: Fri, 13 Nov 2026 09:22:08 +0530 Subject: [PATCH] feat(config): -Dbrowser switches the browser from the command line -Dbrowser=firefox (or -Dselenium.boot.browser=firefox, which wins if both are set) now replaces browser.name from the file, profiles and SeleniumBootDefaults, and clears browser.matrix so one browser means one browser. Blank is ignored; the value is trimmed and lower-cased. Docs: new "Switching browser from the command line" section; the Gradle snippets now forward browser and selenium.boot.profile (the latter was documented but never forwarded, so it did nothing under Gradle). Co-Authored-By: Claude Opus 5.5 --- docs-site/docs/configuration.md | 18 +++++ docs-site/docs/gradle.md | 23 ++++--- .../config/ConfigurationLoader.java | 23 +++++++ .../unit/ConfigurationLoaderTest.java | 68 +++++++++++++++++++ 4 files changed, 122 insertions(+), 10 deletions(-) diff --git a/docs-site/docs/configuration.md b/docs-site/docs/configuration.md index 3119b37..4cf7711 100644 --- a/docs-site/docs/configuration.md +++ b/docs-site/docs/configuration.md @@ -297,3 +297,21 @@ mvn test -Dselenium.boot.profile=staging ``` Only the fields present in the profile file are overridden — everything else falls back to the base config. + +--- + +## Switching browser from the command line + +Pick the browser for a single run without editing any file: + +```bash +mvn test -Dbrowser=firefox +``` + +`-Dselenium.boot.browser=firefox` does the same and wins if both are set. The value replaces +`browser.name` from `selenium-boot.yml`, any profile, and `SeleniumBootDefaults`. It also +replaces `browser.matrix`: asking for one browser runs only that browser. Values are +case-insensitive; a blank value is ignored. + +Gradle runs tests in a separate JVM, so forward the property in `build.gradle`. See +[Gradle setup](./gradle). diff --git a/docs-site/docs/gradle.md b/docs-site/docs/gradle.md index ed3fa6f..0e55882 100644 --- a/docs-site/docs/gradle.md +++ b/docs-site/docs/gradle.md @@ -69,9 +69,9 @@ test { // suites 'src/test/resources/testng.xml' } - // Forward -Denv=staging from the CLI. Forward only the keys you use: + // Forward -Denv / -Dbrowser / -Dselenium.boot.profile from the CLI. Forward only the keys you use: // copying every JVM property (java.home, ...) breaks the run if the test JVM differs from Gradle's - ['env'].each { k -> System.getProperty(k)?.with { systemProperty k, it } } + ['env', 'browser', 'selenium.boot.profile'].each { k -> System.getProperty(k)?.with { systemProperty k, it } } // Display test output in the console testLogging { @@ -91,9 +91,9 @@ tasks.test { // suites("src/test/resources/testng.xml") } - // Forward -Denv=staging from the CLI. Forward only the keys you use: + // Forward -Denv / -Dbrowser / -Dselenium.boot.profile from the CLI. Forward only the keys you use: // copying every JVM property (java.home, ...) breaks the run if the test JVM differs from Gradle's - listOf("env").forEach { k -> System.getProperty(k)?.let { systemProperty(k, it) } } + listOf("env", "browser", "selenium.boot.profile").forEach { k -> System.getProperty(k)?.let { systemProperty(k, it) } } testLogging { events("passed", "skipped", "failed") @@ -121,7 +121,7 @@ dependencies { test { useJUnitPlatform() - ['env'].each { k -> System.getProperty(k)?.with { systemProperty k, it } } + ['env', 'browser', 'selenium.boot.profile'].each { k -> System.getProperty(k)?.with { systemProperty k, it } } } ``` @@ -137,7 +137,7 @@ dependencies { tasks.test { useJUnitPlatform() - listOf("env").forEach { k -> System.getProperty(k)?.let { systemProperty(k, it) } } + listOf("env", "browser", "selenium.boot.profile").forEach { k -> System.getProperty(k)?.let { systemProperty(k, it) } } } ``` @@ -183,7 +183,10 @@ retry: # Run with an environment profile ./gradlew test -Denv=staging -# Pass multiple JVM args +# Switch browser for one run (overrides browser.name and browser.matrix) +./gradlew test -Dbrowser=firefox + +# Use a config profile ./gradlew test -Dselenium.boot.profile=firefox # reads selenium-boot-firefox.yml ``` @@ -215,7 +218,7 @@ For parallel runs with Gradle, configure the `test` task alongside `selenium-boo test { useTestNG() maxParallelForks = 4 // Gradle worker processes - ['env'].each { k -> System.getProperty(k)?.with { systemProperty k, it } } + ['env', 'browser', 'selenium.boot.profile'].each { k -> System.getProperty(k)?.with { systemProperty k, it } } } ``` @@ -226,7 +229,7 @@ test { tasks.test { useTestNG() maxParallelForks = 4 - listOf("env").forEach { k -> System.getProperty(k)?.let { systemProperty(k, it) } } + listOf("env", "browser", "selenium.boot.profile").forEach { k -> System.getProperty(k)?.let { systemProperty(k, it) } } } ``` @@ -283,7 +286,7 @@ dependencies { test { useTestNG() - ['env'].each { k -> System.getProperty(k)?.with { systemProperty k, it } } + ['env', 'browser', 'selenium.boot.profile'].each { k -> System.getProperty(k)?.with { systemProperty k, it } } testLogging { events 'passed', 'skipped', 'failed' } } ``` diff --git a/src/main/java/com/seleniumboot/config/ConfigurationLoader.java b/src/main/java/com/seleniumboot/config/ConfigurationLoader.java index 93146bd..8697e19 100644 --- a/src/main/java/com/seleniumboot/config/ConfigurationLoader.java +++ b/src/main/java/com/seleniumboot/config/ConfigurationLoader.java @@ -4,6 +4,8 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; +import java.util.Locale; import java.util.Objects; import org.yaml.snakeyaml.LoaderOptions; @@ -117,10 +119,31 @@ private static void completeAndValidate(SeleniumBootConfig config) { // Programmatic defaults (SeleniumBootDefaults) take precedence over the built-in ones. SeleniumBootDefaults.applyMissing(config); applyBuiltInDefaults(config); + applyBrowserOverride(config); validate(config); } + /** + * Command-line browser override: {@code -Dselenium.boot.browser=firefox}, or the shorter + * {@code -Dbrowser=firefox} (the namespaced key wins when both are set). It beats + * {@code browser.name} from the file, profiles and {@link SeleniumBootDefaults}, and it + * replaces {@code browser.matrix} — asking for one browser must not still fan out to several. + * A blank value is ignored. + */ + private static void applyBrowserOverride(SeleniumBootConfig config) { + String override = System.getProperty("selenium.boot.browser"); + if (override == null || override.isBlank()) { + override = System.getProperty("browser"); + } + if (override == null || override.isBlank()) { + return; + } + SeleniumBootConfig.Browser browser = config.getBrowser(); + browser.setName(override.trim().toLowerCase(Locale.ROOT)); + browser.setMatrix(Collections.emptyList()); + } + /** * Fills fields the file left out. Only absent values are defaulted: * {@code browser.name} (unless {@code browser.matrix} is set), {@code execution.mode}, diff --git a/src/test/java/com/seleniumboot/unit/ConfigurationLoaderTest.java b/src/test/java/com/seleniumboot/unit/ConfigurationLoaderTest.java index 722572d..f08d794 100644 --- a/src/test/java/com/seleniumboot/unit/ConfigurationLoaderTest.java +++ b/src/test/java/com/seleniumboot/unit/ConfigurationLoaderTest.java @@ -26,12 +26,16 @@ public class ConfigurationLoaderTest { public void clearSystemProperties() { System.clearProperty("selenium.boot.config"); System.clearProperty("selenium.boot.profile"); + System.clearProperty("browser"); + System.clearProperty("selenium.boot.browser"); } @AfterMethod public void restoreSystemProperties() { System.clearProperty("selenium.boot.config"); System.clearProperty("selenium.boot.profile"); + System.clearProperty("browser"); + System.clearProperty("selenium.boot.browser"); } // ---------------------------------------------------------- @@ -204,4 +208,68 @@ public void load_negativeTimeoutStillRejected() throws IOException { System.setProperty("selenium.boot.config", file.toString()); ConfigurationLoader.load(); } + + // ---------------------------------------------------------- + // -Dbrowser / -Dselenium.boot.browser override + // ---------------------------------------------------------- + + private static Path configFile(String yaml) throws IOException { + Path file = Files.createTempFile("sb-browser", ".yml"); + Files.writeString(file, yaml); + System.setProperty("selenium.boot.config", file.toString()); + return file; + } + + @Test + public void load_browserSystemProperty_overridesYamlName() throws IOException { + configFile("browser:\n name: chrome\n"); + System.setProperty("browser", "firefox"); + + assertEquals(ConfigurationLoader.load().getBrowser().getName(), "firefox"); + } + + @Test + public void load_browserSystemProperty_overridesBuiltInDefaultWithoutAFile() throws IOException { + System.setProperty("browser", "edge"); + + SeleniumBootConfig config = ConfigurationLoader.load(emptyWorkingDir(), emptyClasspath()); + + assertEquals(config.getBrowser().getName(), "edge"); + } + + @Test + public void load_browserSystemProperty_replacesTheMatrix() throws IOException { + configFile("browser:\n matrix: [chrome, firefox]\n"); + System.setProperty("browser", "firefox"); + + SeleniumBootConfig config = ConfigurationLoader.load(); + + assertEquals(config.getBrowser().getName(), "firefox"); + assertTrue(config.getBrowser().getMatrix().isEmpty(), "a single-browser override must not still fan out"); + } + + @Test + public void load_namespacedBrowserProperty_winsOverPlainBrowser() throws IOException { + configFile("browser:\n name: chrome\n"); + System.setProperty("browser", "edge"); + System.setProperty("selenium.boot.browser", "firefox"); + + assertEquals(ConfigurationLoader.load().getBrowser().getName(), "firefox"); + } + + @Test + public void load_blankBrowserProperty_isIgnored() throws IOException { + configFile("browser:\n name: firefox\n"); + System.setProperty("browser", " "); + + assertEquals(ConfigurationLoader.load().getBrowser().getName(), "firefox"); + } + + @Test + public void load_browserPropertyValue_isTrimmedAndLowerCased() throws IOException { + configFile("browser:\n name: chrome\n"); + System.setProperty("browser", " Firefox "); + + assertEquals(ConfigurationLoader.load().getBrowser().getName(), "firefox"); + } }