diff --git a/CLAUDE.md b/CLAUDE.md
index 214a8d6..f1492fc 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -77,7 +77,7 @@ All source lives under `src/main/java/com/seleniumboot/`. Key packages:
- **Registry + SPI** — `DriverProviderRegistry`, `PluginRegistry`, `ReportAdapterRegistry` use `ServiceLoader` for extensibility.
- **ThreadLocal driver isolation** — `DriverManager` stores `WebDriver` per thread; safe for parallel TestNG execution.
- **Template Method** — `BaseTest` / `BasePage` define framework-managed lifecycle; users override hook methods.
-- **Convention over configuration** — `selenium-boot.yml` is optional. `ConfigurationLoader` falls back to built-in defaults (`browser.name: chrome`, `execution.mode: local`, `timeouts.explicit: 10`, `timeouts.pageLoad: 30`) for an absent file (only when no profile / explicit path was requested — those still throw if missing) and for any omitted field; `SeleniumBootDefaults` programmatic overrides take precedence over the built-ins. Present-but-invalid values (unknown `execution.mode`, negative timeout) still fail validation.
+- **Convention over configuration** — `selenium-boot.yml` is optional. `ConfigurationLoader` falls back to built-in defaults (`browser.name: chrome`, `execution.mode: local`, `timeouts.explicit: 10`, `timeouts.pageLoad: 30`) for an absent file (only when no profile / explicit path was requested — those still throw if missing) and for any omitted field listed there (`execution.baseUrl` has no default; `open()` requires it); `SeleniumBootDefaults` programmatic overrides take precedence over the built-ins. Present-but-invalid values (unknown `execution.mode`, negative timeout) still fail validation.
### Public API Contract
diff --git a/README.md b/README.md
index 48c74a7..b696656 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,7 @@ Three files. Copy them as-is and `mvn test` goes green against a real Chrome.
```
-**2. `selenium-boot.yml`** — optional; project root, next to `pom.xml`. Omit it entirely and the framework runs on built-in defaults (Chrome, local execution, 10s/30s timeouts). Shown below with every key set explicitly, but each one defaults if left out.
+**2. `selenium-boot.yml`** — project root, next to `pom.xml`. The test below calls `open()`, which needs `baseUrl`. The browser, mode and timeouts shown are the built-in defaults, listed for reference.
```yaml
execution:
@@ -134,49 +134,19 @@ The second version has no driver lifecycle, no explicit waits, and nothing coupl
markup — rename a CSS class or reorder the DOM and it still passes. The raw `WebDriver` is
still there via `getDriver()` whenever you need it.
-Next: [the full Getting Started walkthrough](#getting-started) adds page objects, parallel execution, and reporting.
-
----
-
-### Already have a Selenium Java project?
-
-[`selenium-boot-migrator`](https://github.com/seleniumboot/selenium-boot-migrator) analyzes it first and
-changes nothing: what maps cleanly, what needs manual work. Maven and Gradle (including version catalogs).
-
-```bash
-java -jar selenium-boot-migrator.jar analyze path/to/your-project
-```
-
-Grab the jar from the [latest release](https://github.com/seleniumboot/selenium-boot-migrator/releases/latest). Requires Java 17+.
-
-> **Let an AI assistant drive a real browser and write the tests**
-> **seleniumboot-mcp** is a standalone MCP server for Claude / GitHub Copilot: it controls Chrome, records your session, and generates ready-to-run test code (Java TestNG / JUnit 5 / Gherkin, Python, C#, Playwright) — Selenium Boot-native when the dependency is present. Its `migrate` tool runs the analysis above for you.
-> ```
-> pip install seleniumboot-mcp
-> ```
-> [PyPI](https://pypi.org/project/seleniumboot-mcp/) · [GitHub](https://github.com/seleniumboot/selenium-mcp) · 43 tools by default (77 total) · self-healing locators
+Next: [page objects and tests](#page-objects-and-tests), [configuration](#configuration), and the [documentation](https://docs.seleniumboot.com).
---
## Overview
-Selenium Boot is a zero-boilerplate, production-ready automation framework for Java Selenium, inspired by the philosophy of Spring Boot.
+Selenium Boot is an opinionated framework for Java Selenium, inspired by Spring Boot. Add one dependency, extend `BaseTest` / `BasePage`, and the driver lifecycle, waits, retries, reporting and CI wiring are already decided.
-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.
+- **Convention over configuration.** Browser, mode and timeouts have built-in defaults; `selenium-boot.yml` only says what you want to change.
+- **Never hides Selenium.** The raw `WebDriver` / `By` / `WebElement` is always one call away via `getDriver()`.
+- **Extensible, not required.** Custom drivers, report adapters and hooks plug in through SPI. Most users never touch it.
-### Design Philosophy
-
-Selenium Boot is **the Spring Boot of Java test automation** — and that positioning is deliberately layered:
-
-1. **Opinionated core (primary).** Convention over configuration, zero boilerplate by default. Add one dependency, extend `BaseTest` / `BasePage`, and the framework has already made the sensible decisions — driver lifecycle, waits, retries, reporting, CI wiring. `selenium-boot.yml` is optional and every key in it defaults — `SeleniumBootDefaults` and the framework's own built-ins cover the rest.
-2. **Never hides Selenium (the constraint).** Unlike heavier abstractions, Selenium Boot never takes the raw `WebDriver` away from you. When the conventions don't fit, drop straight down to `WebDriver` / `By` / `WebElement`. Opinionated without being a cage.
-3. **Extensible toolkit (the escape hatch).** An SPI/registry plugin system (`DriverProviderRegistry`, `PluginRegistry`, `ReportAdapterRegistry`) makes it modular for the power users who need it — serving the opinionated core, not replacing it. Most users never touch it.
-
-**Already invested in Selenium?** Selenium Boot gives you the productivity features people love in Playwright — **accessibility-first locators** (`getByRole` / `getByLabel` / `getByText`), **auto-waiting** so `Thread.sleep()` disappears, and **web-first assertions** — while keeping your existing Selenium / Java / TestNG stack, team skills, and Selenium Grid. You get the modern ergonomics without leaving the ecosystem you've already built on, and without ever hiding raw Selenium.
-
-**Why not just build your own framework?** Most teams already have a home-grown `BaseTest` + `DriverFactory` + wait-utils they've rewritten a dozen times. Selenium Boot *is* that framework — maintained, tested, parallel-safe, and documented — so the driver lifecycle, retries, reporting, and CI wiring stop being unpaid infrastructure you own forever. You keep your test code; you delete the plumbing.
-
-> Selenium Boot is the Spring Boot of Selenium — zero setup, smarter defaults, Playwright-inspired APIs, and enterprise features, without hiding Selenium.
+Already on Selenium? You keep your stack, TestNG, team skills and Grid, and gain accessibility-first locators, auto-waiting and web-first assertions. Why it exists and how it compares: [Why Selenium Boot](https://docs.seleniumboot.com/why/why-selenium-boot).
---
@@ -184,192 +154,43 @@ Selenium Boot is **the Spring Boot of Java test automation** — and that positi
Outcomes first — the API that delivers each one is named so you can find it in the docs.
-- **Never write driver setup or teardown again** — automatic WebDriver lifecycle, thread-safe per test
-- **Never write `Thread.sleep()` again** — auto-waiting `WaitEngine` with 10+ built-in conditions
-- **Tests survive CSS and DOM refactors** — accessibility-first locators (`getByRole`, `getByText`, `getByLabel`, `getByPlaceholder`, `getByTestId`, `getByAltText`, `getByTitle`) plus a `SmartLocator` fallback that tries multiple strategies
-- **Flaky tests stop failing your build** — automatic retry via `@Retryable`
-- **Run your whole suite in parallel, safely** — thread-isolated drivers, `parallel` in one YAML line
-- **Switch environments without touching code** — YAML config with environment profile switching
-- **See exactly why a test failed** — screenshot auto-captured on failure and embedded in the report
-- **Hand stakeholders a report they'll actually read** — HTML dashboard with pass-rate gauge, donut chart, slowest tests, step timeline, dark mode
-- **Write pages, not plumbing** — `BasePage` with wait-backed `click`, `type`, `getText`, `isDisplayed`, iFrame helpers, file upload
-- **Log in once, reuse the session** — `@PreCondition` with automatic cookie + localStorage caching
-- **Catch JavaScript errors your users would hit** — `ConsoleErrorCollector` (Chrome via logs, Firefox via shim)
-- **File download testing that just works** — `DownloadManager` polls the download dir and handles partial files
-- **Read the test like a spec** — `StepLogger` named steps with timestamps and per-step screenshots
-- **Test UI and API in the same suite** — `BaseApiTest` + fluent `ApiClient` with auth, schema validation, JSONPath; hybrid UI + API tests
-- **Accessibility testing in one line** — `accessibility().withTags("wcag2a","wcag21aa").run()`; axe-core bundled in the JAR, no extra dependency
-- **Extend it without forking it** — Java SPI plugins for custom browser providers, report adapters, lifecycle hooks
-- **CI that configures itself** — auto-detects GitHub Actions, Jenkins, CircleCI; forces headless, emits JUnit XML
-
----
-
-## Getting Started
-
-### Prerequisites
-
-- Java 17+
-- Maven 3.8+
-- Chrome or Firefox installed
+- **No driver setup, teardown or `Thread.sleep()`** — thread-safe WebDriver lifecycle per test and an auto-waiting `WaitEngine`
+- **Tests less coupled to markup** — accessibility-first locators (`getByRole`, `getByText`, `getByLabel`, `getByPlaceholder`, `getByTestId`, `getByAltText`, `getByTitle`) plus a `SmartLocator` fallback
+- **Transient failures don't fail the build** — automatic retry via `@Retryable`; flaky tests are still reported as retried
+- **Parallel runs that are safe** — thread-isolated drivers, `parallel` in one YAML line
+- **See exactly why a test failed** — screenshot on failure, `StepLogger` named steps, and an HTML report with pass-rate gauge, slowest tests and step timeline
+- **Write pages, not plumbing** — `BasePage` with wait-backed `click`, `type`, `getText`, iFrame helpers and file upload; `@PreCondition` logs in once and reuses the session
+- **UI and API in the same suite** — `BaseApiTest` + fluent `ApiClient` with auth, schema validation and JSONPath
+- **CI-aware defaults** — detects GitHub Actions, Jenkins, CircleCI and others; forces headless, emits JUnit XML
-No WebDriver binaries required — Selenium Manager handles it automatically.
+Also built in: accessibility checks (axe-core bundled), download testing, JavaScript console-error capture, environment profiles, and SPI plugins for custom drivers, report adapters and hooks.
---
-### Step 1: Add the Dependency
+## Page Objects and Tests
-Add to your `pom.xml`:
-
-```xml
-
- io.github.seleniumboot
- selenium-boot
- 3.7.0
-
-```
-
-Also pin the compiler plugin and add Surefire so `mvn test` discovers TestNG tests:
-
-```xml
-
- 17
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.11.0
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 3.2.5
-
-
-
-```
-
-> Pinning `maven-compiler-plugin` matters: on Maven 3.8.x and earlier the default compiler level falls back to source/target 5, and the build fails with *"Source option 5 is no longer supported"* on any modern JDK.
-
----
-
-### Step 2: Configuration File (optional)
-
-`selenium-boot.yml` is optional — omit it to run on built-in defaults. Create it at your
-**project root** (same level as `pom.xml`) when you want to change anything:
-
-```yaml
-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 # seconds
-```
-
-That is the only configuration file needed, and it is **required** — Selenium Boot fails fast at startup if it is missing. Most fields are optional, but `execution.mode`, `browser.name` (or `browser.matrix`), and both `timeouts` values are validated and must be present:
-
-```yaml
-execution:
- mode: local
- baseUrl: https://example.com
-
-browser:
- name: chrome
-
-timeouts:
- explicit: 10
- pageLoad: 30
-```
-
----
-
-### Step 3: Project Structure
-
-```
-your-project/
-├── pom.xml
-├── selenium-boot.yml
-└── src/
- └── test/
- └── java/
- └── com/yourcompany/
- ├── conditions/
- │ └── AppConditions.java
- ├── pages/
- │ └── LoginPage.java
- └── tests/
- └── LoginTest.java
-```
-
----
-
-### Step 4: Create a Page Object
-
-Extend the framework's built-in `BasePage` — it provides wait-backed interaction helpers out of the box:
+Extend `BasePage` for the same semantic locators plus wait-backed helpers (`click`, `type`, `getText`, `isDisplayed`, `withinFrame`, `upload`):
```java
-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);
+ getByLabel("Username").type(username);
+ getByLabel("Password").type(password);
+ getByRole(Role.BUTTON, "Log in").click();
}
}
```
-`BasePage` provides: `click`, `type`, `getText`, `getAttribute`, `isDisplayed`, `withinFrame`, `withinFrameIndex`, `upload`. All backed by `WaitEngine` — no manual waits needed.
-
----
+Prefer semantic locators. When an element has no accessible name, fall back to `By` — `type(By.id("username"), username)` — or to the raw `WebDriver`.
-### Step 5: Write Your Tests
-
-Extend `BaseTest` — that's all the setup needed:
+Extend `BaseTest` — that's all the setup a test needs:
```java
-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
@@ -385,422 +206,81 @@ public class LoginTest extends BaseTest {
}
```
-**Rules:**
-- Always extend `BaseTest`
-- Never instantiate or quit `WebDriver` manually — the framework manages it
-- Use `getDriver()` to access the current thread's driver instance
-- Use `open()` to navigate to `baseUrl`, or `open("/path")` for a sub-path
-
----
-
-### Step 6: Run Tests
-
-```bash
-mvn test
-```
-
-That's it. Selenium Boot handles driver creation, parallel execution, retries, screenshots, and report generation automatically.
-
----
-
-### Step 7: View the Report
-
-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
-
----
-
-## @PreCondition — Session Caching
-
-Eliminate repeated login boilerplate. Declare a condition once, cache the session, reuse it across tests:
-
-```java
-// 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
-```
-
-```java
-// 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.
+- Never instantiate or quit `WebDriver` yourself — the framework manages it.
+- `getDriver()` returns the current thread's driver.
+- `open()` navigates to `baseUrl`; `open("/path")` to a sub-path.
---
-## API Testing
-
-Selenium Boot supports **pure API tests** and **hybrid UI + API tests** — same framework, same config, same HTML report.
-
-### Pure API Tests
-
-Extend `BaseApiTest` instead of `BaseTest`. No browser is launched.
-
-```java
-public class UserApiTest extends BaseApiTest {
-
- @Test
- public void getUserById() {
- ApiClient.get("https://api.example.com/users/1")
- .send()
- .assertStatus(200)
- .assertJson("$.name", "John Doe");
- }
-}
-```
-
-### `ApiClient` — Fluent HTTP Client
-
-```java
-// 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`:
-
-```yaml
-api:
- baseUrl: https://api.example.com
- timeoutSeconds: 30
- logBody: false # set true to include body in step timeline
-```
-
-### `ApiResponse` — Assertions and Extraction
-
-```java
-ApiResponse 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");
-```
-
-### Authentication
+## Configuration
-**Bearer token:**
-```java
-ApiClient.get("/api/me")
- .auth(ApiAuth.bearerToken("my-token"))
- .send();
-```
-
-**Basic auth:**
-```java
-ApiClient.get("/api/admin")
- .auth(ApiAuth.basicAuth("user", "pass"))
- .send();
-```
-
-**Set auth once for the entire suite** — all requests use it automatically:
-```java
-@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:
-```java
-ApiClient.setGlobalAuth(ApiAuth.oauth2(
- "https://auth.example.com/token",
- System.getenv("CLIENT_ID"),
- System.getenv("CLIENT_SECRET")
-));
-```
+`selenium-boot.yml` lives at the project root, next to `pom.xml`. It is optional: omit the file, or any key, and the built-in default applies. The one exception is `execution.baseUrl`, which has no default and is needed by `open()`. Values that are present but invalid (an unknown `execution.mode`, a negative timeout) fail at startup.
-**Config-based auth with `@UseAuth`** — define strategies in YAML, apply per test:
```yaml
-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}
-```
-
-```java
-@Test
-@UseAuth("adminToken")
-public void createUser() {
- ApiClient.post("/api/users").body(...).send().assertStatus(201);
-}
-```
-
-### Schema Validation
-
-Validate response structure against a JSON Schema file:
-
-```java
-ApiClient.get("/api/users/1")
- .send()
- .assertStatus(200)
- .assertSchema("schemas/user.json");
-```
-
-Place schema files under `src/test/resources/schemas/`. Requires one additional dependency:
-
-```xml
-
- com.networknt
- json-schema-validator
- 1.4.3
-
-```
-
-### Hybrid UI + API Tests
-
-Mix API calls and browser interactions in the same test via `apiClient()` in `BaseTest`:
-
-```java
-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");
- }
-}
-```
-
-### Scenario & Suite Context
-
-Share state within a test or across tests without static fields:
-
-```java
-// 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 2
-```
-
----
-
-## WaitEngine Reference
-
-`WaitEngine` uses the `timeouts.explicit` value from your config. Never use `Thread.sleep()`.
-
-```java
-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();
-```
-
----
+execution:
+ mode: local # local | remote
+ baseUrl: https://example.com
+ parallel: methods # none | methods | classes
+ threadCount: 4
-## Retry Reference
+browser:
+ name: chrome # chrome | firefox
+ headless: false
-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`.
+retry:
+ enabled: true
+ maxAttempts: 2
-```java
-@Retryable
-@Test
-public void flakyTest() {
- // retried up to maxAttempts times if it fails
-}
+timeouts:
+ explicit: 10 # seconds — used by WaitEngine
+ pageLoad: 30 # seconds
```
----
-
-## Remote Execution (Selenium Grid)
-
-Update `selenium-boot.yml`:
+Run on a Selenium Grid by switching mode — no code changes:
```yaml
execution:
mode: remote
- baseUrl: https://example.com
gridUrl: http://localhost:4444/wd/hub
- parallel: methods
- threadCount: 4
-
-browser:
- name: chrome
- headless: true
```
-No code changes required — just config.
+Environment profiles (`-Dselenium.boot.profile=staging`), `ci:` quality gates and every other key are in the [configuration reference](https://docs.seleniumboot.com/configuration); the `api:` keys are in [API testing](https://docs.seleniumboot.com/guides/api-testing).
---
-## Environment Profiles
+## Learn More
-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
-```
-
-```bash
-mvn test -Denv=staging
-```
+| | |
+|---|---|
+| **Core** | [BaseTest](https://docs.seleniumboot.com/guides/base-test) · [BasePage](https://docs.seleniumboot.com/guides/base-page) · [Locators](https://docs.seleniumboot.com/guides/semantic-locators) · [Waits](https://docs.seleniumboot.com/guides/wait-engine) · [Retry](https://docs.seleniumboot.com/guides/retry) · [Parallel](https://docs.seleniumboot.com/guides/parallel) · [@PreCondition](https://docs.seleniumboot.com/guides/precondition) |
+| **API testing** | [API testing](https://docs.seleniumboot.com/guides/api-testing) · [Authentication](https://docs.seleniumboot.com/guides/api-auth) · [Scenario & suite context](https://docs.seleniumboot.com/guides/scenario-context) |
+| **Reporting** | [HTML report](https://docs.seleniumboot.com/reporting/html-report) · [JUnit XML](https://docs.seleniumboot.com/reporting/junit-xml) |
+| **CI/CD** | [GitHub Actions](https://docs.seleniumboot.com/ci/github-actions) · [Jenkins](https://docs.seleniumboot.com/ci/jenkins) · [Quality gates](https://docs.seleniumboot.com/ci/quality-gates) |
+| **Extending** | [Custom drivers](https://docs.seleniumboot.com/extensibility/custom-drivers) · [Report adapters](https://docs.seleniumboot.com/extensibility/report-adapters) · [Hooks](https://docs.seleniumboot.com/extensibility/hooks) · [Plugins](https://docs.seleniumboot.com/extensibility/plugins) |
+| **Migrating** | [From Selenium + TestNG](https://docs.seleniumboot.com/migration/from-selenium-testng) · [Coming from Playwright](https://docs.seleniumboot.com/migration/coming-from-playwright) |
+| **Examples** | [Sample project](https://github.com/seleniumboot/selenium-boot-test) · [Starter template](https://github.com/seleniumboot/selenium-boot-starter) |
---
-## Extending the Framework
-
-Selenium Boot exposes four extension points. All support both **Java SPI** (automatic discovery) and **programmatic registration**.
-
-### Custom Driver Provider
-
-```java
-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:
-```java
-DriverProviderRegistry.register(new EdgeDriverProvider());
-```
-
-### Custom Report Adapter
-
-```java
-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:
-```java
-ReportAdapterRegistry.register(new SlackReportAdapter());
-```
-
-### Lifecycle Hooks
+## Related Tools
-```java
-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`.
-
-### Plugin System
-
-Combine driver providers, report adapters, and hooks into a single deployable unit:
+### Already have a Selenium Java project?
-```java
-public class MyPlugin implements SeleniumBootPlugin {
- @Override public String getName() { return "my-plugin"; }
- @Override public void onLoad(SeleniumBootConfig config) {
- ReportAdapterRegistry.register(new SlackReportAdapter());
- }
-}
-```
+[`selenium-boot-migrator`](https://github.com/seleniumboot/selenium-boot-migrator) analyzes it first and
+changes nothing: what maps cleanly, what needs manual work. Maven and Gradle (including version catalogs).
-Declare minimum required framework version to prevent incompatibility:
-```java
-@Override public String minFrameworkVersion() { return "0.8.0"; }
+```bash
+java -jar selenium-boot-migrator.jar analyze path/to/your-project
```
----
-
-## CI/CD Integration
-
-Selenium Boot auto-detects CI environments and applies sensible defaults — no YAML changes required.
-
-- `browser.headless` is forced to `true`
-- `threadCount` is 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.xml` on every run
-
-### Build Quality Gates
+Grab the jar from the [latest release](https://github.com/seleniumboot/selenium-boot-migrator/releases/latest). Requires Java 17+.
-```yaml
-ci:
- failOnPassRateBelow: 80 # fail build if pass rate drops below 80%
- maxFlakyTests: 3 # fail build if more than 3 tests were retried
-```
+> **Let an AI assistant drive a real browser and write the tests**
+> **seleniumboot-mcp** is a standalone MCP server for Claude / GitHub Copilot: it controls Chrome, records your session, and generates ready-to-run test code (Java TestNG / JUnit 5 / Gherkin, Python, C#, Playwright) — Selenium Boot-native when the dependency is present. Its `migrate` tool runs the analysis above for you.
+> ```
+> pip install seleniumboot-mcp
+> ```
+> [PyPI](https://pypi.org/project/seleniumboot-mcp/) · [GitHub](https://github.com/seleniumboot/selenium-mcp) · 43 tools by default (77 total) · self-healing locators
---
@@ -812,19 +292,6 @@ See the full version history in **[CHANGELOG.md](CHANGELOG.md)**.
---
-## Sample Project
-
-A working demo project covering all framework features is available at:
-**[github.com/seleniumboot/selenium-boot-test](https://github.com/seleniumboot/selenium-boot-test)**
-
----
-
-## Documentation
-
-Full documentation at **[seleniumboot.github.io/selenium-boot](https://seleniumboot.github.io/selenium-boot)**
-
----
-
## License
Licensed under the [Apache License, Version 2.0](LICENSE).
diff --git a/docs-site/docs/ci/github-actions.md b/docs-site/docs/ci/github-actions.md
index d378629..d1fe786 100644
--- a/docs-site/docs/ci/github-actions.md
+++ b/docs-site/docs/ci/github-actions.md
@@ -51,24 +51,16 @@ jobs:
---
-## Headless Chrome
+## What Selenium Boot configures on CI
-Chrome on CI runners must run headless. Configure this in `selenium-boot.yml`:
+When it detects a CI environment (GitHub Actions, Jenkins, CircleCI and others), Selenium Boot adjusts itself — no YAML changes needed:
-```yaml title="selenium-boot.yml"
-browser:
- type: chrome
- headless: true
-```
-
-Or set it only in CI using an environment variable override (if supported by your config loading):
+- `browser.headless` is forced to `true`.
+- `execution.threadCount` is derived from the available CPU cores, capped at `execution.maxActiveSessions` — only when you left it at the default of `1` and `parallel` is not `none`.
+- Inside Docker or Kubernetes, Chrome and Firefox get the container flags they need (for Chrome: `--no-sandbox`, `--disable-dev-shm-usage`, `--disable-gpu`, `--window-size=1920,1080`).
+- JUnit XML is written to `target/surefire-reports/TEST-SeleniumBoot.xml` on every run.
-```yaml
- - name: Run tests
- run: mvn test -B
- env:
- SELENIUM_HEADLESS: true
-```
+Each override is printed to the console as `[Selenium Boot] CI override: …`.
---