A Zero-Boilerplate, Production-Ready Framework for Java QA Automation
Documentation · Sample Project · Changelog
AI-powered test authoring for Selenium Boot users Use seleniumboot-mcp to let Claude / GitHub Copilot control a real browser, record your session, and generate ready-to-run Selenium Boot test code — TestNG, JUnit 5, Page Object, Gherkin, or C# NUnit.
pip install seleniumboot-mcpPyPI · GitHub · 76 tools · self-healing locators · codegen for Java / Python / C# / Playwright
Selenium Boot is a zero-boilerplate, production-ready automation framework for Java Selenium, inspired by the philosophy of Spring Boot.
It eliminates repetitive boilerplate by providing sensible defaults, a standardized project structure, and a convention-over-configuration approach — while keeping Selenium fully visible and accessible.
- Automatic WebDriver lifecycle management (no setup/teardown boilerplate)
- YAML-based configuration with environment profile switching
- Parallel execution with thread-safe driver isolation
- Smart explicit waits via
WaitEngine— no moreThread.sleep() - Automatic retry for flaky tests via
@Retryable - Screenshot capture on failure, embedded in report
- Advanced HTML report — pass rate gauge, donut chart, slowest tests, step timeline, dark mode
BasePage— wait-backedclick,type,getText,isDisplayed, iFrame helpers, file uploadSmartLocator— tries multipleBystrategies in order, returns first visible element@PreCondition— session-aware pre-conditions with automatic cookie + localStorage cachingConsoleErrorCollector— capture JS console errors (Chrome via logs, Firefox via shim)DownloadManager— poll download directory, handle partial filesStepLogger— named test steps with timestamps and per-step screenshots- API testing —
BaseApiTestfor pure API tests; fluentApiClientwith auth, schema validation, JSONPath; hybrid UI + API tests in the same suite - Plugin system — custom browser providers, report adapters, lifecycle hooks via Java SPI
- CI-ready — auto-detects GitHub Actions, Jenkins, CircleCI; forces headless, emits JUnit XML
- Java 17+
- Maven 3.8+
- Chrome or Firefox installed
No WebDriver binaries required — Selenium Manager handles it automatically.
Add to your pom.xml:
<dependency>
<groupId>io.github.seleniumboot</groupId>
<artifactId>selenium-boot</artifactId>
<version>2.4.0</version>
</dependency>Also add the Surefire plugin so mvn test discovers TestNG tests:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
</plugins>
</build>Create selenium-boot.yml at your project root (same level as pom.xml):
execution:
mode: local # local | remote
baseUrl: https://example.com
parallel: methods # none | methods | classes
threadCount: 4
maxActiveSessions: 4
browser:
name: chrome # chrome | firefox
headless: false
lifecycle: per-test # per-test (default) | per-suite
captureConsoleErrors: true
arguments:
- --start-maximized
- --disable-notifications
retry:
enabled: true
maxAttempts: 2
timeouts:
explicit: 10 # seconds — used by WaitEngine
pageLoad: 30 # secondsThat is the only configuration file needed. All fields have defaults — start with the minimum:
execution:
mode: local
baseUrl: https://example.com
browser:
name: chromeyour-project/
├── pom.xml
├── selenium-boot.yml
└── src/
└── test/
└── java/
└── com/yourcompany/
├── conditions/
│ └── AppConditions.java
├── pages/
│ └── LoginPage.java
└── tests/
└── LoginTest.java
Extend the framework's built-in BasePage — it provides wait-backed interaction helpers out of the box:
package com.yourcompany.pages;
import com.seleniumboot.test.BasePage;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage extends BasePage {
private final By usernameField = By.id("username");
private final By passwordField = By.id("password");
private final By loginButton = By.id("login-btn");
public LoginPage(WebDriver driver) {
super(driver);
}
public void login(String username, String password) {
type(usernameField, username);
type(passwordField, password);
click(loginButton);
}
}BasePage provides: click, type, getText, getAttribute, isDisplayed, withinFrame, withinFrameIndex, upload. All backed by WaitEngine — no manual waits needed.
Extend BaseTest — that's all the setup needed:
package com.yourcompany.tests;
import com.seleniumboot.steps.StepLogger;
import com.seleniumboot.test.BaseTest;
import com.yourcompany.pages.LoginPage;
import org.testng.annotations.Test;
import static org.testng.Assert.assertTrue;
public class LoginTest extends BaseTest {
@Test
public void loginWithValidCredentials() {
StepLogger.step("Open login page");
open();
StepLogger.step("Enter credentials and submit", true);
new LoginPage(getDriver()).login("admin", "password123");
assertTrue(getDriver().getCurrentUrl().contains("/dashboard"));
}
}Rules:
- Always extend
BaseTest - Never instantiate or quit
WebDrivermanually — the framework manages it - Use
getDriver()to access the current thread's driver instance - Use
open()to navigate tobaseUrl, oropen("/path")for a sub-path
mvn testThat's it. Selenium Boot handles driver creation, parallel execution, retries, screenshots, and report generation automatically.
After execution, open the HTML report:
target/selenium-boot-report.html
The report includes:
- Pass rate gauge with colour coding
- Donut chart — pass/fail/skip distribution
- Per-test execution time and retry badges
- Step timeline per test
- Failure screenshots (base64 embedded, click to expand)
- Dark mode toggle
Eliminate repeated login boilerplate. Declare a condition once, cache the session, reuse it across tests:
// 1. Define conditions
public class AppConditions extends BaseConditions {
@ConditionProvider("loginAsAdmin")
public void loginAsAdmin() {
open("/");
new LoginPage(getDriver()).login("admin", "secret");
}
}// 2. Register via SPI
src/test/resources/META-INF/services/com.seleniumboot.precondition.BaseConditions
→ com.yourcompany.conditions.AppConditions
// 3. Use in tests
@Test
@PreCondition("loginAsAdmin")
public void viewDashboard() {
open("/dashboard"); // session already established — no re-login
}
@Test
@PreCondition("loginAsAdmin")
public void editProfile() {
open("/profile"); // session restored from cache
}Cache is per-thread — safe for parallel execution. On retry, cache is invalidated and the condition re-runs fresh.
Selenium Boot supports pure API tests and hybrid UI + API tests — same framework, same config, same HTML report.
Extend BaseApiTest instead of BaseTest. No browser is launched.
public class UserApiTest extends BaseApiTest {
@Test
public void getUserById() {
ApiClient.get("https://api.example.com/users/1")
.send()
.assertStatus(200)
.assertJson("$.name", "John Doe");
}
}// GET
ApiClient.get("/api/users").send();
// POST with body
ApiClient.post("/api/users")
.body(Map.of("name", "Alice", "email", "alice@example.com"))
.send()
.assertStatus(201);
// Custom header
ApiClient.get("/api/orders")
.header("X-Request-ID", "abc123")
.send();
// Different base URL for one request
ApiClient.to("https://other-service.com").get("/health").send();Configure the default base URL in selenium-boot.yml:
api:
baseUrl: https://api.example.com
timeoutSeconds: 30
logBody: false # set true to include body in step timelineApiResponse res = ApiClient.get("/api/users/1").send();
res.assertStatus(200);
res.assertBodyContains("Alice");
res.assertJson("$.name", "Alice");
// Extract values
String name = res.json("$.name");
int id = res.json("$.id", Integer.class);
User user = res.asObject(User.class);
// Fluent chaining
res.assertStatus(200)
.assertJson("$.name", "Alice")
.assertSchema("schemas/user.json");Bearer token:
ApiClient.get("/api/me")
.auth(ApiAuth.bearerToken("my-token"))
.send();Basic auth:
ApiClient.get("/api/admin")
.auth(ApiAuth.basicAuth("user", "pass"))
.send();Set auth once for the entire suite — all requests use it automatically:
@BeforeSuite
public void authenticate() {
ApiResponse login = ApiClient.post("/api/auth/login")
.body(Map.of("username", "admin", "password", "pass"))
.send();
ApiClient.setGlobalAuth(ApiAuth.bearerToken(login.json("$.token")));
}OAuth2 client credentials — token fetched and cached automatically:
ApiClient.setGlobalAuth(ApiAuth.oauth2(
"https://auth.example.com/token",
System.getenv("CLIENT_ID"),
System.getenv("CLIENT_SECRET")
));Config-based auth with @UseAuth — define strategies in YAML, apply per test:
api:
auth:
adminToken:
type: bearer
token: ${ADMIN_TOKEN} # resolved from env var
serviceAccount:
type: oauth2
tokenUrl: https://auth.example.com/token
clientId: ${CLIENT_ID}
clientSecret: ${CLIENT_SECRET}@Test
@UseAuth("adminToken")
public void createUser() {
ApiClient.post("/api/users").body(...).send().assertStatus(201);
}Validate response structure against a JSON Schema file:
ApiClient.get("/api/users/1")
.send()
.assertStatus(200)
.assertSchema("schemas/user.json");Place schema files under src/test/resources/schemas/. Requires one additional dependency:
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
<version>1.4.3</version>
</dependency>Mix API calls and browser interactions in the same test via apiClient() in BaseTest:
public class CheckoutTest extends BaseTest {
@Test
public void placeOrder() {
// Set up via API (fast, no UI navigation)
String orderId = apiClient().post("/api/orders")
.body(Map.of("productId", 42, "qty", 1))
.send()
.assertStatus(201)
.json("$.orderId");
// Verify in the UI
open("/orders/" + orderId);
Assert.assertEquals(getText(By.id("status")), "Pending");
}
}Share state within a test or across tests without static fields:
// ScenarioContext — lives for one test, auto-cleared after
ctx().set("token", loginRes.json("$.token"));
String token = ctx().get("token");
// SuiteContext — survives between tests, thread-safe
suiteCtx().set("createdUserId", res.json("$.id")); // in test 1
String userId = suiteCtx().get("createdUserId"); // in test 2WaitEngine uses the timeouts.explicit value from your config. Never use Thread.sleep().
import com.seleniumboot.wait.WaitEngine;
import org.openqa.selenium.By;
WebElement el = WaitEngine.waitForVisible(By.id("submit-btn"));
WebElement btn = WaitEngine.waitForClickable(By.cssSelector(".next-btn"));
WaitEngine.waitForTitle("Dashboard");
WaitEngine.waitForUrlContains("/dashboard");
WaitEngine.waitForText(By.id("status"), "Complete");
WaitEngine.waitForPageLoad();Add @Retryable to any @Test method to enable retry on failure. The number of retries is controlled by retry.maxAttempts in your config. Retries can be globally disabled with retry.enabled: false.
@Retryable
@Test
public void flakyTest() {
// retried up to maxAttempts times if it fails
}Update selenium-boot.yml:
execution:
mode: remote
baseUrl: https://example.com
gridUrl: http://localhost:4444/wd/hub
parallel: methods
threadCount: 4
browser:
name: chrome
headless: trueNo code changes required — just config.
Name your config files by environment and activate with a system property:
selenium-boot.yml # default
selenium-boot-staging.yml # staging profile
selenium-boot-prod.yml # prod profile
mvn test -Denv=stagingSelenium Boot exposes four extension points. All support both Java SPI (automatic discovery) and programmatic registration.
public class EdgeDriverProvider implements NamedDriverProvider {
@Override public String browserName() { return "edge"; }
@Override public WebDriver createDriver() { return new EdgeDriver(); }
}Register via SPI (META-INF/services/com.seleniumboot.driver.NamedDriverProvider) or:
DriverProviderRegistry.register(new EdgeDriverProvider());public class SlackReportAdapter implements ReportAdapter {
@Override public String getName() { return "slack"; }
@Override public void generate(File metricsJson) { /* post to Slack */ }
}Register via SPI (META-INF/services/com.seleniumboot.reporting.ReportAdapter) or:
ReportAdapterRegistry.register(new SlackReportAdapter());public class TimingHook implements ExecutionHook {
@Override
public void onTestFailure(String testId, Throwable cause) {
alerting.notify(testId, cause.getMessage());
}
}Available events: onSuiteStart, onSuiteEnd, onTestStart, onTestEnd, onTestFailure.
Combine driver providers, report adapters, and hooks into a single deployable unit:
public class MyPlugin implements SeleniumBootPlugin {
@Override public String getName() { return "my-plugin"; }
@Override public void onLoad(SeleniumBootConfig config) {
ReportAdapterRegistry.register(new SlackReportAdapter());
}
}Declare minimum required framework version to prevent incompatibility:
@Override public String minFrameworkVersion() { return "0.8.0"; }Selenium Boot auto-detects CI environments and applies sensible defaults — no YAML changes required.
browser.headlessis forced totruethreadCountis auto-derived from available CPU cores- Docker/container flags (
--no-sandbox,--disable-dev-shm-usage) are auto-applied to Chrome - JUnit XML written to
target/surefire-reports/TEST-SeleniumBoot.xmlon every run
ci:
failOnPassRateBelow: 80 # fail build if pass rate drops below 80%
maxFlakyTests: 3 # fail build if more than 3 tests were retried- Performance Assertions —
assertPerformance().lcp().isBelow(2500).fcp().isBelow(1800).ttfb().isBelow(600).cls().isBelow(0.1)— Core Web Vitals via browser-nativewindow.performanceAPI, no extra tool needed; LCP/CLS Chrome/Edge only, others all browsers; unavailable metrics silently skipped;performance.captureOnEveryTest: trueshows ⚡ metrics strip in HTML report
- Test Quarantine —
selenium-quarantine.ymlcommitted to the repo lists tests to skip; survives fresh CI clones; plain string or structured-with-reason format; class-only entry skips all methods in the class; Cucumber support via@quarantinetag;quarantine.enabled: falsedisables without editing the file
- External
@TestDatasources —@TestData("csv:testdata/logins.csv")(built-in parser),@TestData(value="excel:testdata/users.xlsx", sheet="Login")(Apache POI optional dep),@TestData("db:SELECT username, password FROM test_users LIMIT 1")(JDBC viadatabaseconfig);rowattribute selects zero-based data row (header excluded); type coercion: integers, doubles, booleans TestClock—clock().set("2030-01-01T00:00:00Z")injects a JSDateoverride into the browser;clock().advance(Duration.ofDays(30))fast-forwards from current mock;clock().reset()restores real time; auto-reset after every test (no cleanup needed); available viaclock()inBaseTestandBaseJUnit5Test
- BrowserStack integration —
execution.mode: browserstack+browserstack.username/accessKey/os/osVersion/browser/browserVersion; W3Cbstack:optionscapabilities; mobile device support viadevice+realMobile; session dashboard URL auto-injected into HTML report as "☁ View Session" link - Sauce Labs integration —
execution.mode: saucelabs+saucelabs.username/accessKey/region/platformName/browser/browserVersion; three regions:us-west-1,eu-central,apac-southeast; W3Csauce:optionscapabilities; session dashboard URL in HTML report - Existing
mode: remote(self-hosted Selenium Grid) unchanged — all three remote modes coexist
- Email Verification —
mailbox().waitForEmail(to("user@example.com"))polls until an email arrives;email.assertSubject(),email.assertBodyContains(),email.extractLink(linkText)for anchor extraction;mailbox().clear()purges the inbox;email.autoClear: trueclears automatically before each test - Four backends: Mailhog (local/Docker), Mailtrap (hosted sandbox), Outlook/Office 365 (Microsoft Graph API — app-only OAuth2, no user login), IMAP (Gmail, Yahoo, any standards-compliant server via optional
jakarta.maildep) - Outlook config:
tenantId,clientId,clientSecret,mailbox— OAuth2 token auto-refreshed and cached
@NoBrowser— annotate a test class or method to skip WebDriver creation entirely; no Chrome/Firefox window opened, no screenshot/recording/trace; all other framework services (report, steps, metrics, retry, hooks) still active; ideal for database assertions, API-only tests that extendBaseTest, and any test that does not touch the browser
- Multi-Session Testing —
withSession("alice", () -> { ... })runs a lambda with a named browser session active;session("name")returns the rawWebDriver; all named sessions are automatically closed at test end; nestedwithSession()calls supported via stack-based driver restoration; available inBaseTestandBaseJUnit5Test - Database Assertions —
db().assertRowExists(table, conditions),db().assertNoRow(),db().assertRowCount(),db().query(sql, params).assertValue(column, expected),db().scalar(sql, params); plain JDBC, no ORM dependency; named datasources viadb("reporting"); connections pooled per thread and closed automatically at test end - New
sessions.maxPerTestanddatabase.*config blocks inselenium-boot.yml
@Retryablefor JUnit 5 —InvocationInterceptorinSeleniumBootExtensionretries failed test methods with full driver recreation between attempts;@Retryable(maxAttempts = N)on method or class overrides global config@Retryablefor Cucumber — entire scenario reruns from step 1 with a fresh driver viaRetryAnnotationTransformer; retry detected inCucumberHooksand shown as retry badge in HTML report@Retryable— newmaxAttemptsattribute + class-level target; fully backward-compatible
- JUnit 5 Support —
SeleniumBootExtension(@ExtendWith) handles driver lifecycle, screenshot on failure, AI analysis, trace, and recording;WebDriverinjectable as test method parameter;BaseJUnit5Testbase class withgetDriver(),getWait(),open(),$(),assertThat(),step();@EnableSeleniumBootcomposed annotation;SeleniumBootLauncherListenergenerates HTML report when the JUnit Platform test plan finishes (registered via ServiceLoader — no config needed); parallel execution viajunit-platform.properties
- BDD / Cucumber Integration —
BaseCucumberTestrunner base +BaseCucumberStepsstep definition base (getDriver(),open(),$(),assertThat());CucumberHooksmanages driver lifecycle, metrics, and screenshots per scenario automatically;CucumberStepLoggerplugin streams Gherkin step names into the HTML report step timeline;cucumber-javaandcucumber-testngdeclared as optional — consumers add their own version;cucumber.propertiessupport for IDE single-scenario runs; Scenario Outlines produce individual report entries per example row
- Self-Healing Locators —
locators.selfHealing: true; whenwaitForVisible/waitForClickabletimes out, framework automatically tries fallback strategies: extractidfrom CSS#fooor XPath@id,namefrom CSS[name]or XPath@name, text from XPathtext(), class from CSS.btn,data-testid; healed tests get⚠ healedbadge in HTML report;target/healed-locators.jsonlists every healed locator after suite - AI-Assisted Failure Analysis —
ai.failureAnalysis: true+ai.apiKey: ${CLAUDE_API_KEY}; on test failure callsclaude-haiku-4-5-20251001with error message, stack trace, step log, URL, and page title; plain-English root-cause + fix suggestion embedded in HTML report below the stack trace; bounded byai.timeoutSeconds(default 20s); fully non-blocking — never affects suite result - Flakiness Prediction — reads last
flakiness.historyRuns(default 20) JSON files fromtarget/metrics-history/; computes per-test failure rate; classifies asSTABLE(<10%),WATCH(10–33%),HIGH(≥33%); Flakiness Radar card in HTML report;target/flakiness-report.json;flakiness.failOnHighFlakiness: trueto gate builds
- Trace Viewer —
tracing.enabled: truegenerates a self-contained dark-themedtarget/traces/{Class}/{method}-trace.htmlper failed test; embeds step timeline with screenshots, final-state screenshot, error message + stack trace; zero CDN dependencies;captureOnPass: trueoption to trace passing tests too; "View Trace" link appears in the HTML report's failure detail panel
- Visual regression testing —
VisualAssert.assertScreenshot()pixel-by-pixel comparison; auto-creates baseline on first run; diff image saved totarget/visual-diffs/; configurable tolerance viaVisualTolerance.of(n);-DupdateBaselines=trueto regenerate - Mobile device emulation —
DeviceEmulator.emulate("iPhone 14")/emulateDevice()inBasePage/BaseTest; CDP-based on Chrome/Edge (viewport, scale factor, UA); window-resize fallback on Firefox; 6 built-in profiles (iPhone 14, iPhone SE, Pixel 7, Galaxy S23, iPad, iPad Pro 12) viaDeviceProfilesregistry - Clipboard helpers —
ClipboardHelper.write/read/clear()backed by a reliable JS global store - GeoLocation mock — CDP-based on Chrome/Edge, JS
navigator.geolocationoverride on Firefox - Network interception —
NetworkMock.stub(pattern)stubs API responses via CDPFetchdomain; glob patterns, custom status, delay, auto-cleanup after each test - Browser storage helpers —
StorageHelper.localStorage(),sessionStorage(),cookies()for reading/writing browser storage in tests - Fluent Locator API —
$(css)/$(By)chainable locator:filter(),withText(),within(),nth(), auto-wait terminals - Web-First Assertions —
assertThat(By/Locator)with auto-retry:isVisible,isHidden,hasText,containsText,hasAttribute,hasClass,count
- Shadow DOM helpers —
ShadowDomutility + 7BasePagemethods (shadowFind,shadowFindAll,shadowClick,shadowType,shadowGetText,shadowPierce,shadowExists) - Alert handling fix —
unhandledPromptBehavior: ignoreon all driver providers;BasePage.getAndAcceptAlert()convenience method - Component-aware waits —
WaitEngine.waitForAngular()(Angular 2+/AngularJS 1.x) andWaitEngine.waitForReactHydration()(React 18/17/16, Next.js) - Enhanced HTML report — pass rate gauge, donut chart, retry badges, expandable error/stack trace rows, filter bar, search, dark mode, slowest-5 section
- JUnit XML error details —
<failure message>now contains the actual assertion message and full stack trace - Allure adapter — opt-in Allure 2 JSON result files in
target/allure-results/(reporting.allureEnabled: true) - Slack / Teams notifications — webhook-based post-suite summary with pass/fail counts and failed test list
@DependsOnApi— skip test immediately (before browser open) if a dependent HTTP endpoint is unreachable; repeatable; cached per suite
- Schema validation —
ApiResponse.assertSchema("schemas/user.json")validates response body against a JSON Schema file; requirescom.networknt:json-schema-validator:1.4.3on classpath @UseAuthannotation — apply named auth strategy from config to any test method or classApiAuth.oauth2()— OAuth2 client credentials flow with automatic token caching and expiry refreshApiClient.setGlobalAuth()/clearGlobalAuth()— set auth once per suite, applied automatically to all requests
BaseApiTest— pure API test base class; no browser started; full framework lifecycle (reporting,@TestData, retry, CI gates)ApiClient— fluent HTTP client backed by Java's built-inHttpClient;GET,POST,PUT,PATCH,DELETE; auto step-loggingApiResponse— rich response wrapper; JSONPath extraction ($.user.id),asObject(Class), fluent assertions (assertStatus,assertJson,assertBodyContains)ApiAuth—bearerToken(token),basicAuth(user, pass)auth strategiesScenarioContext— thread-local in-test store;ctx().set/get; auto-cleared after each testSuiteContext— global thread-safe store for cross-test state;suiteCtx().set/getapiClient(),ctx(),suiteCtx()added toBaseTestfor hybrid UI+API tests
@TestData— annotation-driven test data injection; loads JSON/YAML fromsrc/test/resources/testdata/; env-specific override (admin.staging.jsonoverridesadmin.jsonwhen-Denv=staging);getTestData()inBaseTest- Browser matrix —
browser.matrix: [chrome, firefox]in YAML runs every test on every browser in onemvn test;Browsercolumn in HTML report; per-browser JUnit XML for Jenkins matrix view SessionCache—SessionCache.store("name")/SessionCache.restore("name"); global cross-thread session reuse; reduces repeated login overhead in large parallel suites- SoftAssert —
softAssert().that(condition, "message")collects failures without throwing; framework flushes at test end; single screenshot captured; all failures appear as individual step entries in the report
- DownloadManager browser auto-configuration — Chrome and Firefox automatically configure download directory from
browser.downloadDirconfig; no save dialog shown; partial downloads (.crdownload, .part) filtered out - Remote mode guard — download prefs skipped when
execution.mode: remote(Grid sessions download to node filesystem, not local) - File upload helper —
BasePage.upload(By, String)resolves paths via absolute → classpath → project-root; CI-safe
- ConsoleErrorCollector auto-integration — when
browser.captureConsoleErrors: true, the JS shim is auto-injected on everyopen()call and errors are auto-collected at test end asWARNstep entries in the report failOnConsoleErrorsenforcement — whenbrowser.failOnConsoleErrors: true, a passing test with JS errors is marked FAILED with a clear error listing the detected errorsStepStatus.WARN— new step status for JS error entries (does not affect test outcome)
- iFrame helpers expanded —
withinFrameName(String, Runnable)added;withinFrame,withinFrameIndex,withinFrameNamenow support nested frames (inner calls restore toparentFrame(), outermost restores todefaultContent())
- Bug fix — alert methods (
acceptAlert,dismissAlert,getAlertText,typeInAlert) inBasePagenow usethis.driverdirectly instead ofWaitEngine/DriverManager, matching the driver reference used by the page object - Bug fix —
@PreConditionfailure error message now shows the real exception instead of "null" (unwrapsInvocationTargetException)
- Bug fix — precondition failure now throws
SkipException(notRuntimeException); prevents retry from firing and a second browser from opening on@PreConditionsetup failures - Bug fix —
maxAttempts: 0in YAML now respected; changedintfield default to nullableIntegerso intentional0is never silently overridden by programmatic defaults
- Alert wait fix —
acceptAlert,dismissAlert,getAlertText,typeInAlertnow useWaitEngine.waitForAlert()— no moreNoAlertPresentExceptionon slow pages WaitEngine.waitForAlert()— new explicit wait for browser alert presenceBasePage.smartFind()— convenience wrapper; use inside page objects without passing the driver
BasePageexpanded — dropdowns (selectByText,selectByValue,selectByIndex,getSelectedOption), alerts (acceptAlert,dismissAlert,getAlertText,typeInAlert), mouse actions (hover,doubleClick,rightClick), scroll (scrollTo,scrollToTop,scrollToBottom), JS fallbacks (jsClick,jsType)
BasePage— page object base class:click,type,getText,isDisplayed,withinFrame,withinFrameIndex,uploadSmartLocator— tries multipleBystrategies in order, returns first visible elementDownloadManager—waitForFile,waitForAnyFile,clearDownloadswith partial-download detectionConsoleErrorCollector— JS console error capture (Chrome via WebDriver logs, Firefox via injected shim)@PreCondition— session-aware pre-conditions with cookie + localStorage caching per thread@ConditionProvider+BaseConditions— define named condition providers, registered via SPI@SeleniumBootApi— annotation marking stable public API withsinceversionFrameworkVersion+minFrameworkVersion()— runtime version checks, plugin compatibility enforcement- Config additions —
browser.downloadDir,browser.captureConsoleErrors,browser.failOnConsoleErrors
StepLogger— named test steps with timestamps and optional per-step screenshots- Step timeline — step-by-step execution timeline in the HTML report Failures tab
- Tabbed HTML report — Dashboard, Test Cases, and Failures tabs with collapsible rows
- Browser lifecycle —
browser.lifecycle: per-test | per-suiteconfiguration
- Advanced HTML report — pass rate gauge, donut chart, slowest tests card, retry badges
- Self-contained report — screenshots base64-encoded, single file, no external dependencies
- Retry support —
retry.enabled+retry.maxAttemptsinselenium-boot.yml @Retryable— per-method retry override- Retry metrics — retry counts tracked in
ExecutionMetricsand exported to JSON
- CI auto-detection — GitHub Actions, Jenkins, CircleCI, GitLab CI; forces headless, tunes thread count
- Container detection — Docker and Kubernetes; auto-applies Chrome container flags
- JUnit XML reporter —
target/surefire-reports/TEST-SeleniumBoot.xml - Build quality gates —
BuildThresholdEnforcerenforces pass-rate and flaky-test thresholds - CI templates — GitHub Actions workflow and Jenkinsfile included
- Plugin system —
SeleniumBootPlugin+PluginRegistrywith SPI discovery - Custom driver providers —
NamedDriverProvider+DriverProviderRegistry - Custom report adapters —
ReportAdapter+ReportAdapterRegistry - Lifecycle hooks —
ExecutionHook+HookRegistry SeleniumBootDefaults— programmatic config defaults for shared test-base JARs
WaitEngine— fluent explicit wait API- Thread-safe config —
SeleniumBootContextwithAtomicReference - Session semaphore —
maxActiveSessionscap with fair wait - Global retry —
retry.enabled: trueretries all tests without@Retryable
- Initial release — Chrome/Firefox, TestNG integration,
selenium-boot.yml, basic HTML report, screenshot on failure
A working demo project covering all framework features is available at: github.com/seleniumboot/selenium-boot-test
Full documentation at seleniumboot.github.io/selenium-boot
Licensed under the Apache License, Version 2.0.
See CONTRIBUTING.md.
Selenium Boot is an independent open-source project and is not affiliated with Selenium or the Spring Framework.