Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions docs-site/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ browser:
name: chrome # chrome | firefox | edge | safari
headless: false # true in CI (auto-forced when CI detected)
lifecycle: per-test # per-test (default) | per-suite
# version: "120.0.6099.109" # optional: pin local Chrome/Firefox to an exact build

# Optional: extra browser arguments
arguments:
Expand Down Expand Up @@ -184,6 +185,13 @@ The browser to use. Selenium Manager downloads the matching driver automatically
### `headless`
Runs the browser without a visible window. Automatically forced to `true` when a CI environment is detected (GitHub Actions, Jenkins, etc.).

### `version`
Pins a local Chrome or Firefox run to an exact browser version, for when "passes on my
laptop, fails in CI" is a browser-version mismatch. Passed to Selenium Manager, which resolves
and downloads the matching build. Optional — omit it to use whatever browser is installed.
Ignored by `remote`, `browserstack` and `saucelabs` modes (those use their own
`browserVersion`).

### `lifecycle`
Controls when the WebDriver session is closed.

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/seleniumboot/config/SeleniumBootConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ public static final class Browser {
private boolean failOnConsoleErrors = false;
private List<String> matrix = Collections.emptyList();
private String device; // optional device profile name, e.g. "iPhone 14"
private String version; // optional exact browser version for local Chrome/Firefox runs

public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }

public String getDevice() { return device; }
public void setDevice(String device) { this.device = device; }
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/seleniumboot/driver/BrowserVersionPin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.seleniumboot.driver;

import org.openqa.selenium.remote.AbstractDriverOptions;

/**
* Applies {@code browser.version} to local driver options. Selenium Manager resolves
* (and downloads if needed) the matching browser build. Omitted or blank leaves the options
* untouched, so the browser installed on the machine is used.
*/
final class BrowserVersionPin {

private BrowserVersionPin() {}

static void apply(AbstractDriverOptions<?> options, String version) {
if (version != null && !version.isBlank()) {
options.setBrowserVersion(version.trim());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public WebDriver createDriver() {
capabilities.forEach(options::setCapability);
}

BrowserVersionPin.apply(options, config.getBrowser().getVersion());

if (config.getBrowser().isHeadless()) {
options.addArguments("--headless=new");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public WebDriver createDriver() {
capabilities.forEach(options::setCapability);
}

BrowserVersionPin.apply(options, config.getBrowser().getVersion());

if (config.getBrowser().isHeadless()) {
options.addArguments("-headless");
}
Expand Down
52 changes: 52 additions & 0 deletions src/test/java/com/seleniumboot/unit/BrowserVersionPinTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.seleniumboot.unit;

import com.seleniumboot.config.SeleniumBootConfig;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;

/** {@code browser.version} is passed to local driver options only when set. */
public class BrowserVersionPinTest {

private static void apply(Object options, String version) throws Exception {
Class<?> pin = Class.forName("com.seleniumboot.driver.BrowserVersionPin");
var m = pin.getDeclaredMethod("apply", org.openqa.selenium.remote.AbstractDriverOptions.class, String.class);
m.setAccessible(true);
m.invoke(null, options, version);
}

@Test
public void chrome_versionSet_isPassedThrough() throws Exception {
ChromeOptions o = new ChromeOptions();
apply(o, "120.0.6099.109");
assertEquals(o.getBrowserVersion(), "120.0.6099.109");
}

@Test
public void firefox_versionSet_isPassedThrough() throws Exception {
FirefoxOptions o = new FirefoxOptions();
apply(o, " 121.0 ");
assertEquals(o.getBrowserVersion(), "121.0");
}

@Test
public void nullOrBlank_leavesVersionUnset() throws Exception {
ChromeOptions a = new ChromeOptions();
apply(a, null);
ChromeOptions b = new ChromeOptions();
apply(b, " ");
assertEquals(a.getBrowserVersion(), new ChromeOptions().getBrowserVersion());
assertEquals(b.getBrowserVersion(), new ChromeOptions().getBrowserVersion());
}

@Test
public void config_defaultsToNull_andRoundTrips() {
SeleniumBootConfig.Browser b = new SeleniumBootConfig.Browser();
assertNull(b.getVersion());
b.setVersion("120");
assertEquals(b.getVersion(), "120");
}
}
Loading