All notable changes to Selenium Boot are documented in this file.
The format is loosely based on Keep a Changelog, and this project adheres to Semantic Versioning.
-
Headed local browsers start maximized by default — Chrome gets
--start-maximizedand Firefox callsmaximize()after launch, so a watched test is no longer left in a small window. Headless runs and containers (fixed 1920x1080) are unchanged, and your own--start-maximized,--window-size,--kioskor Firefox--width/--heightarguments still win. (#245) -
HTML report: sortable columns in Test Cases table — clicking headers for Test, Status, Logic (ms), and Total (ms) sorts rows within each test class group ascending or descending with visual sort indicators, while preserving detail row attachments and cooperating with active status, tag, and search filters. (#217, #242)
-
HTML report: Expand all / Collapse all — one control on the Test Cases tab opens or closes every class group. (#218, #240)
-
HTML report: screenshot lightbox navigation — previous/next buttons move between a test's screenshots without closing the lightbox. (#219, #241)
-
HTML report: flaky trend chart hidden when empty — the chart no longer renders as an empty frame when no run had flaky tests. (#243)
-
Docs: quickstart fixed — example.com no longer has an "Example Domain" heading, so the quickstart test now asserts the "Learn more" link. (#244)
-
HTML report: print stylesheet for "Save as PDF" — printing the report now produces a clean document: UI chrome is hidden, all sections print instead of only the active tab, collapsed groups and failed-test details (error and full stack trace) are expanded, the light palette is used even in dark mode, and your on-screen filters are restored afterwards. (#221, #236)
-
HTML report: execution timeline — the dashboard now shows a Gantt-style timeline of when each test started and ended, built from the new per-test
startedAtMsexport. (#201) -
HTML report: results by tag — a tag breakdown from TestNG groups, Cucumber tags and JUnit 5 tags, with click-to-filter in the Test Cases tab. (#167, #189)
-
HTML report: refreshed theme and restructured markup — the report's CSS, JavaScript and HTML partials are now separate files and the theme is aligned across tabs. (#192, #193, #194)
-
Tracing: failure traces recorded by default in CI — when
tracing.enabledis not set, failed-test traces are recorded in CI and stay off locally. An explicittrue/falsestill wins. (#198) -
Selenium Java 4.51.0 → 4.52.0 — internal upgrade. 4.52.0 no longer ships the CDP
devtools.v153module, soNetworkMock,DeviceEmulatorand the network trackers now usedevtools.v154. No API change. (#238) -
HTML report: status and tag filter persistence — the active status filter (All, Passed, Failed, Skipped, Flaky) and tag filter in the Test Cases tab are now persisted across page reloads and reopens using
localStorage(sb-status-filterandsb-tag-filter). Storage reads and writes are safely guarded against restricted environments (private browsing, disabled storage, quota limits); if a previously stored tag is not present in the current report, it is gracefully ignored without restricting rows, and restoring filters preserves the default Dashboard tab view. (#215) -
HTML report: "Copy failure details" button — failed tests with an error now have a button in the expanded detail row (and on the Failures tab) that copies the test id, error message and stack trace to the clipboard, with brief "Copied" feedback. Falls back to
execCommand('copy')whennavigator.clipboardis unavailable (e.g. somefile://setups). (#216) -
Fix:
@PreConditionsession restore on a fresh browser — the cached cookies and localStorage were silently dropped when the next test started onabout:blank, so only the first test per thread was logged in. Restore now navigates to the origin the session was captured on first. -
LambdaTest cloud provider —
execution.mode: lambdatestnow creates W3CLT:Optionssessions, supports environment-based credentials and capability overrides, and links each cloud session from the HTML report. (#197) -
Cloud execution mode validation —
execution.modenow accepts the existingbrowserstackandsaucelabsproviders, alongsidelocal,remote, and the newlambdatestprovider. -
Trace shows browser console errors — when
browser.captureConsoleErrors: true, the JS errors collected for a test now appear in its trace file under "Console Errors", next to the steps and failure details, so a shared trace includes what the browser logged. No change when the option is off. (#130) -
Network mocking on Firefox via WebDriver BiDi —
networkMock().stub(...)now uses WebDriver BiDi network interception under the hood when supported by the driver (including Firefox and BiDi-enabled drivers), falling back to Chrome DevTools Protocol (CDP) for Chromium-based drivers. The existing stubbing API (returnJson,returnBody,returnStatus,delay,clear) works identically across Chrome and Firefox. (#196) -
Trace shows the locators each step used — every terminal
Locatoraction (click, type, getText, …) is now recorded with its time offset and listed in the trace file under "Locators Used", grouped beneath the step it ran in. A lookup that timed out is marked "not found". Collected automatically, no config. (#130) -
Trace: console errors and failed requests under the step they happened in — console errors (Chrome) now carry their timestamp and are listed beneath the step they occurred in. New opt-in
tracing.captureNetwork: truerecords requests that returned 4xx/5xx or failed outright (CDP on Chrome/Edge, WebDriver BiDi on Firefox) and lists them the same way, so a trace shows the500behind a failed step. Off by default; successful requests are never recorded and at most 200 are kept per test. (#130)
-
GitHub Actions job summary: link to the run's artifacts — the failed-tests summary now adds a line linking to the workflow run page (built from
GITHUB_SERVER_URL,GITHUB_REPOSITORYandGITHUB_RUN_ID, no config) where uploadedtarget/traces/artifacts live, so a reviewer can get from a red PR to the trace in one click. Omitted when those variables are missing. (#130) -
Locatorkeyboard and pointer actions:press(Keys...),focus(),doubleClick(),rightClick()— send keys without clearing the value, focus without clicking, or perform native double/context clicks directly on a locator. Each action re-resolves on every poll and retries stale or not-yet-present elements untiltimeouts.explicitelapses. (#134, #188) -
HTML report: flaky-test trend line — the dashboard gains a third "Flaky Test Trend" line chart (flaky tests per run) next to the pass-rate and duration charts. (#185)
-
Fix: Cucumber quarantine check no longer silently skipped —
checkCucumberQuarantinethrew when the framework context was not yet initialised and the error was swallowed, so quarantined scenarios ran instead of being skipped. It now guards on initialisation. (#186) -
Fix: Gemini default model —
ai.provider: gemininow defaults to thegemini-flash-latestalias; the previously pinnedgemini-2.5-flashreturned 404 for new API keys. (#171) -
Dependency updates — selenium-java 4.51.0, cucumber-testng 8.0.4, cucumber-junit-platform-engine, jackson-databind, mockito-core 5.24.0, jakarta.mail 2.0.2.
-
Results by Tag (TestNG groups, Cucumber tags, JUnit 5 @Tag) — capture test tags across TestNG groups, Cucumber scenario tags, and JUnit 5
@Tag, export them inselenium-boot-metrics.json, and render a "Results by Tag" card (passed/failed/skipped/pass rate per tag), tag chips on test table rows, and interactive click-to-filter in the HTML report. Absent when no tests have tags. (#167) -
HTML report: tab and failure fixes — the report always opens on the Dashboard (it no longer restores the last-open tab), failure details start collapsed, and the Failures tab no longer goes empty after you expand or collapse a class in Test Cases (both tabs shared one group id).
-
Failure Intelligence: high-level triage and suggested actions — each failure is classified as
Product bug,Test bug,Environment,FlakyorUnknown(flaky keeps its cause, e.g.Flaky (Locator not found)), with a rule-based suggested action. Shown in the HTML report, asfailureType/suggestedActionin the metrics JSON, and in the JUnit XML; opt-in AI analysis uses it as a baseline. No API key needed. (#138) -
AI provider is configurable —
ai.providerselects the backend for failure analysis and the run summary:anthropic(default, unchanged),gemini, oropenai(any OpenAI-compatible/chat/completionsendpoint — OpenAI, Azure, Groq, local Ollama viaai.baseUrl).ai.modelnow defaults per provider. Existing configs keep working with no change. -
AI run summary (opt-in) —
ai.runSummary: trueadds one short paragraph to the HTML dashboard summarising the run's failures: which same-cause groups account for most of them, which look like product bugs versus test or environment problems, and what to check first. One API call per run, independent ofai.failureAnalysis; only grouped error messages and test names are sent (see the data note in the HTML report docs). Absent when disabled, when nothing failed, or on any error; the suite result is never affected. The text is exported asaiRunSummaryin the metrics JSON. -
HTML report: pass-rate and duration trend charts — the dashboard now shows two line charts under the donut and percentile row, built from the last 20 runs in the metrics history (the same
flakiness.historyDir/-Dseleniumboot.history.dirlocation the Flakiness Radar reads). They appear once there are at least two runs of history and stay hidden otherwise. (#160) -
Cucumber on the JUnit Platform: quarantine and suite-lifecycle parity — quarantined scenarios run under
cucumber-junit-platform-engineare now reported as aborted instead of failed (TestAbortedException; TestNG still getsSkipException).SeleniumBootLauncherListenernow also clears the API health cache, loads pre-conditions and starts test-management reporting at test-plan start, matching the TestNG path, and the suite-start work is idempotent.docs-site/docs/cucumber.mddocuments the JUnit Platform runner as the recommended option. (#155, #158) -
BaseCucumberTestdeprecated —@Deprecated(since = "3.9.0"), becauseAbstractTestNGCucumberTestsis deprecated for removal upstream in Cucumber 8.x. Nothing changes at runtime and existing TestNG Cucumber suites keep working; subclasses now get a deprecation warning. Plan to remove it in 4.0.0, once Cucumber drops its TestNG runner, in favour ofcucumber-junit-platform-engine(added as an optional dependency).CucumberStepLoggeralso moves off the deprecatedgetStepText(). (#155, #157) -
JUnit Platform: suite-start hooks now fire —
ExecutionHook.onSuiteStart()was never called on the JUnit 5 path (only TestNG), so hooks that open a run or start a clock silently did nothing.SeleniumBootLauncherListenernow bootstraps the framework and firesonSuiteStart()once per test plan. -
Failed tests with trace links in GitHub Actions job summary — when running inside GitHub Actions and
tracing.enabled: trueis on, failed tests are automatically summarized in a Markdown table (test name, error message, and path to the HTML trace file) appended to$GITHUB_STEP_SUMMARY. Triage failures and locate trace paths directly from the Actions job summary without downloading artifact bundles; no-op outside Actions or when all tests pass. (#137) -
Bounded failure detail record — every failed test now exports one optional-field
failureDetailobject with the failing locator, top application frame, last successful step, page URL/title, heal events, console errors, retry/history status, and representative same-cause context. The HTML failure panel and opt-in AI prompt reuse this record; raw errors and stack traces remain available. Text/list/URL limits and the exact AI data disclosure are documented. (#142) -
Richer auto-retrying locator assertions —
LocatorAssertaddsisFocused()andisEmpty(), plusPatternoverloads forhasText,containsText, andhasValue. Regex failures report the locator and last observed value, and stale elements are re-resolved untiltimeouts.explicit. (#136) -
Locator:waitFor(State),allTextContents(), and elementscreenshot()—waitFor(State)(andwaitFor()) waits for an element to reach a target lifecycle state (VISIBLE,HIDDEN,ATTACHED,DETACHED), retrying up totimeouts.explicit.allTextContents()returns the text content of all matched elements in a single pass without waiting if empty, re-resolving if an element goes stale during read.screenshot()returns the element's PNG bytes, andscreenshot(Path)writes them to a file path; both terminal actions re-resolve on every poll to survive stale elements. (#135) -
flakiness.historyDir/-Dseleniumboot.history.dir— metrics history can now live outsidetarget/. Builds that cleantarget/or discard the CI workspace between runs had no history, so the Flakiness Radar and the newly/consistently-failing and intermittent statuses stayed empty. Point it at a directory your CI restores and saves (a cache or artifact store); the system property wins over the config. Default unchanged:<reports dir>/metrics-history. (#141) -
Retry- and history-aware test status — each test now gets a status from this run's retries and the earlier runs in
target/metrics-history/: Passed on retry, Newly failing (passed in every earlier run in the window), Consistently failing (failed in the two or more earlier runs) or Intermittent (mixed earlier results). A "Test status" card sits next to the Flakiness Radar, the status shows in the failure detail, and newly failing tests are listed first in the Failures tab. The metrics JSON gainstestStatus. Radar thresholds andfailOnHighFlakinessare unchanged. (#141) -
Failure grouping — the report's Failures tab now opens with a "N tests, same cause" panel: failed tests are grouped by a signature of category, exception type, normalised message (ids, numbers and quoted values stripped) and top application stack frame. The flat per-class list is unchanged below it. With
ai.failureAnalysison, one Claude call is made per group; the other tests in the group reuse its analysis. The metrics JSON gainsfailureSignature. (#140) -
Failure classification — every failed test is tagged with a category (
LOCATOR_NOT_FOUND,ELEMENT_NOT_INTERACTABLE,STALE_ELEMENT,TIMEOUT,ASSERTION_MISMATCH,NAVIGATION_ERROR,BROWSER_CRASH, orUNKNOWN) and the evidence for it, from the exception class chain and message alone. No AI, no API key, no network. Shown as a badge in the HTML report's failure panel, asfailureCategory/failureEvidencein the metrics JSON, and as thetypeof<failure>in the JUnit XML. The raw error and stack trace are unchanged. (#139)
-
Locator.first()andLocator.last()— narrow matches to the first or last matching element (e.g.getByRole(Role.LISTITEM).first()). Both resolve at action time, re-evaluating candidate elements on every poll and retrying stale/not-yet-present elements up totimeouts.explicit. (#132) -
Locatorform actions:clear(),check(),uncheck(),isChecked(),selectOption(text),selectOptionByValue(value),inputValue()— form controls can now be manipulated and inspected directly onLocatorwithout falling back toBasePagehelpers or rawWebElements.check()anduncheck()are idempotent (no click if already in the target state),isChecked()queries checked state without waiting, and terminal actions (clear,check,uncheck,selectOption,selectOptionByValue,inputValue) auto-wait, re-resolving from scratch on every poll to survive stale or late-appearing elements. (#133) -
HTML report: the "Avg Time (ms)" stat tile is now "Suite Time" — the wall-clock suite duration in
hh:mm:ss, matching the sidebar. -
HTML report: suite duration in
hh:mm:ss, wall-clock. The report previously showed the sum of every test's time in milliseconds, which overstates a parallel run. It now shows how long the suite actually ran (01:42:07; hours aren't capped). The metrics JSON gainssuiteDurationMs;totalTimeMs(summed test time) is unchanged. -
HTML report: results first. The dashboard now opens with a one-line run summary and the stat cards; the full run configuration moved into a collapsed "Run configuration" block at the bottom, with empty rows (Grid URL, unset CI fields) dropped. A "View failures" shortcut appears when any test failed.
Locator.dragTo(Locator)— drags one element onto another, including HTML5 drag-and-drop pages that ignore the native gesture. See Drag and drop. (#101)debug.slowMoMs/debug.highlight— dev-only live-triage aids: pause after eachLocatoraction and outline the element about to be acted on. Off by default, no cost when off; not for CI. See Debug. (#52)withNewWindow(opener, body)/withNewTab(...)onBasePage— run the click that opens a new window, switch to it, run your checks, close it and return to the original window, even if the checks throw. No window opening withintimeouts.explicitthrowsTimeoutException. See Handle new windows and tabs. (#49)browser.version— optional exact browser version for local Chrome/Firefox runs, resolved by Selenium Manager. Omit it to use the installed browser. (#51)
WaitEngine.waitForNetworkIdle()/waitForResponse(urlPattern)— waits until no network request has been in flight for a quiet period (default 500ms, or a customDuration), or until a response whose URL matches a glob pattern (same syntax asNetworkMock.stub) is observed, returning its HTTP status code. Both are bounded by the configured explicit timeout. Works on Chrome/Edge (via CDP) and any BiDi-capable browser such as Firefox; throwsUnsupportedOperationExceptionon others. Firefox sessions now request a BiDi connection by default (webSocketUrlcapability) so this works with no extra configuration — an explicit value in the user's ownbrowser.capabilitiesstill wins.open()namesexecution.baseUrlwhen the site can't be reached — a DNS or connection failure now readsCould not reach <url> (ERR_NAME_NOT_RESOLVED) — check execution.baseUrl in selenium-boot.yml …(the driver's error stays as the cause) instead of a barenet::ERR_NAME_NOT_RESOLVED. Applies toBaseTest,BaseJUnit5Test,BaseCucumberStepsandBaseConditions. Also fixesBaseJUnit5Test.open()with noexecution.baseUrlfailing with a bare driver error instead of naming the missing key. (#84)- Gradle projects now get their reports under
build/(#81) — the base directory is decided frombuild.gradle/build.gradle.kts(apom.xmlwins if both exist) instead of "doestarget/exist", which other components had already created. Flakiness report, healed locators, traces, recordings, Allure results, screenshots and JUnit XML all follow it;-Dseleniumboot.reports.dirstill overrides. - Docs: the Gradle
systemProperties System.propertiessnippet is replaced (#82) — copying every JVM property broke test runs when the test JVM differs from Gradle's. It now forwards onlybrowserandenv.
- Zero-config first run:
selenium-boot.ymlis now optional — with no defaultselenium-boot.ymlin the working directory or on the classpath, the framework runs on built-in defaults (browser.name: chrome,execution.mode: local,timeouts.explicit: 10,timeouts.pageLoad: 30) and prints one[Selenium Boot] No selenium-boot.yml found — running with built-in defaults ...notice line. Fields a file omits (and an empty file) fall back to the same defaults;browser.nameis only defaulted whenbrowser.matrixis not set, andSeleniumBootDefaultsoverrides still take precedence over the built-ins. Nothing that was an error before stops being one: a requested profile (-Dselenium.boot.profile) or explicit path (-Dselenium.boot.config) whose file is missing still throws, so a typo never silently runs on defaults, and present-but-invalid values (an unknownexecution.mode, a negative timeout) still fail validation. AddsConfigurationLoader.load(File, ClassLoader)(working directory and classpath supplied, for tests). (#66) - Terminal
Locatoractions wait for the element and re-resolve it on every poll —click(),type(),append(),getText(),getAttribute(),hover(),scrollIntoView(),jsClick()andelement()used to look the element up once and act on that cached reference, so a page that re-rendered it in between surfaced as aStaleElementReferenceException, and a locator that matched nothing yet threw immediately. They now resolve the locator from scratch on each poll, retrying on a stale or not-yet-present element untiltimeouts.explicitelapses. Behavior change: a locator that matches nothing is now retried for up totimeouts.explicitinstead of failing at once. If it never matches, the call still ends in aLocatorException(cause:TimeoutException), so existing catches keep working; an element that exists but never becomes visible/clickable still times out with aTimeoutException, as before.isVisible(),isHidden(),isEnabled()andcount()still never wait, androws()inherits the wait for its table throughelement(). (#67, #68) - JUnit Jupiter API bumped 5.10.2 → 6.1.3 (
junit-jupiter-api, optional dependency of the JUnit 5 bridge).junit-platform-launcheris now bumped to match (6.1.3, was 1.10.2), closing the pairing gap left by #64. Verified both directions against the consumer integration suite (11/11 real-browser JUnit 5 tests): a consumer still on JUnit 5.10.2 + launcher 1.10.2 keeps working unchanged, and a consumer on JUnit 6 (jupiter 6.1.3 + launcher 6.1.3) now works too. (#64) - Clearer error when
execution.baseUrlis unset —open()andopen(String)now throwexecution.baseUrl is not set — add it to selenium-boot.yml, or navigate with getDriver().get(url)instead ofbaseURL is null or empty. Message only; still anIllegalStateException. The getting-started guide now says Step 3 needs the Step 2 file, since zero-config has no defaultbaseUrl. (#77)
Locator.rows()— resolves a locator to a<table>and returns its data rows as header-keyed maps (List<Map<String, String>>), one map per row, keyed by column header. Headers are taken from the first<thead>row with<th>cells, falling back to the first<tr>anywhere with<th>cells, then the first<tr>'s<td>cells; whichever row supplies headers is never returned as data. Short rows are padded with"", extra cells are dropped, and<th>-only rows (group headings) are skipped. Contributed by @timothytkim. (#54)- Bootstrap failures now expose their root cause — when framework initialization throws,
SuiteExecutionListenernow logs the full cause chain (Caused by: ..., walked to the root) toSystem.errbefore rethrowing, so the actual failure reason is visible in normal Maven/TestNG/Surefire output instead of being buried one level down in the wrappingIllegalStateException. (Fixes #45) - Selenium Java bumped 4.40.0 → 4.49.0 (9 releases). This is a behavior-preserving internal upgrade:
NetworkMockandDeviceEmulatorare repointed from the now-removed CDPdevtools.v144module todevtools.v153(the newer of the two bundled with 4.49.0), and two new trailingOptional.empty()arguments were added toEmulation.setDeviceMetricsOverride()calls to match v153's changed signature, preserving the existing defaults. Verified with the full 508-test suite. - Docs — the README and getting-started quickstarts now show the locator API (
$(...)) instead ofgetDriver()for the first example, and the getting-started example is runnable as written. (#40, #44)
execution.parallelnow validates against TestNG's own parallel modes, not a hand-written allowlist that had drifted from them —testsandinstancesare legitimate TestNG modes that flow straight through toXmlSuite.setParallel()and work exactly likemethods/classesdownstream, but Selenium Boot's bootstrap validator rejected them anyway, and did so with a "Parallel execution configuration missing" message — telling users their config was absent when it was present but simply not on the list. Validation now delegates to TestNG'sXmlSuite.ParallelModeenum directly, so anything TestNG accepts, Selenium Boot accepts, and an unrecognised value's error message names both the rejected value and the full valid set instead of going stale again. (Fixes #35)
- Three new
WaitEngineconditions, consistent with the existingwaitFor*naming:waitForAttribute(By, attribute, value)— waits for an exact attribute match (use the existingwaitForAttributeContainsfor a substring match)waitForUrlMatches(String regex)— waits for the current URL to match a regular expression (use the existingwaitForUrlContainsfor a substring match)waitForTextMatches(By, String regex)— waits for an element's visible text to match a regular expression- Purely additive to the
@SeleniumBootApisurface — no breaking changes. (Closes #12)
- Per-engine report output directory — the metrics JSON, HTML report, and metrics history now honor the
seleniumboot.reports.dirsystem property (the same switch already used for the JUnit XML report), defaulting totargetas before. This fixes the HTML report being overwritten when two test engines run in one build — e.g. a TestNG suite (Surefire) and JUnit 5 tests (Failsafe): point each engine's run at its own directory (-Dseleniumboot.reports.dir=target/junit5) and each produces a self-contained report instead of the last one clobbering the first. - New
com.seleniumboot.reporting.ReportPathshelper centralizes report path resolution.
- Accessibility-first locators — new Playwright-style semantic locators on
BaseTestandBasePage:getByRole,getByText,getByLabel,getByPlaceholder,getByTestId,getByAltText,getByTitle. They target the accessibility tree the user perceives instead of brittle CSS/DOM structure, so tests survive redesigns getByRole(Role)— 38 WAI-ARIA roles, each matching both implicit HTML elements (<button>,<a href>,<h1>…) and explicitrole="…"attributes; refine with.withName("Submit")(accessible-name match) and.withLevel(1)(heading level)- Case-insensitive substring by default, exact opt-in via
.exact()— mirrors Playwright ergonomics; all locators flow through the existing auto-waitLocatorchain (noThread.sleep, no explicit waits) toBy()escape hatch — every semantic locator can return its synthesized SeleniumByfor drop-down to raw Selenium orSmartLocator- Configurable test-id attribute —
locators.testIdAttributeinselenium-boot.yml(defaultdata-testid) - Accessible-name computation follows ARIA precedence (
aria-label→aria-labelledby→ associated<label>→ text →value/alt/title)
- TestRail + Xray Integration — annotate any test method with
@TestRailCase("C1234")or@XrayTest("PROJ-123")(both support arrays for multiple IDs); results are pushed automatically after each test completes — no extra code in@Testmethods; TestRail results are pushed per-test immediately; Xray results are batch-imported at suite end for efficiency - TestRail — creates a named run automatically (
testmanagement.testrail.autoCreateRun: true); configurableprojectId,suiteId,runName; maps PASSED→1, FAILED→5, SKIPPED→Retest(4); failure message is sent as the TestRail result comment - Xray Cloud — authenticates via OAuth2 client credentials (
clientId+clientSecret); useshttps://xray.cloud.getxpecto.com/api/v2endpoints; supportstestPlanKeylinking - Xray Server/DC — HTTP Basic auth (
jiraUrl+username+password); posts to/rest/raven/1.0/import/execution; both modes use the same@XrayTestannotation - Zero extra dependencies — clients use
java.net.http.HttpClient(Java 17 built-in); no OkHttp, Apache HttpClient, or Jackson required for this feature - Works with TestNG and JUnit 5 —
SuiteExecutionListener+TestExecutionListenerhandle TestNG;SeleniumBootExtension+SeleniumBootLauncherListenerhandle JUnit 5
- Gradle Build Support —
testImplementation 'io.github.seleniumboot:selenium-boot:2.6.0'inbuild.gradle;test { useTestNG() }is all that's needed; full docs with Groovy + Kotlin DSL samples, parallel config, JUnit 5 setup, and optional-dep table;JUnitXmlReporterauto-detects Maven vs Gradle by directory layout and writes tobuild/test-results/test/(Gradle) ortarget/surefire-reports/(Maven); override with-Dseleniumboot.reports.dir=;FrameworkVersionnow readsImplementation-Versionfrom MANIFEST.MF (set bymaven-jar-pluginand Gradlejartask) soFrameworkVersion.get()returns the correct version in both build tools
- Accessibility Assertions (axe-core) —
accessibility().withTags("wcag2a","wcag21aa").withLevel(Impact.SERIOUS).excluding("#cookie-banner").run()runs an axe-core WCAG scan on the active page; axe-core 4.10.2 bundled in the JAR — no CDN, no extra Maven dependency;withContext("#main-form")scopes the scan to a subtree;collect()returns rawAccessibilityResultfor custom inspection; detailedAssertionErrormessage shows rule ID, severity, fix guidance, element selector, and docs URL for every failing node; available inBaseTestandBaseJUnit5Test
- 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