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: …`. ---