seed-skills/selenide-testing/SKILL.md
Expert-level Selenide UI testing skill for Java applications. Covers concise fluent API, automatic waits, smart selectors, collections, Page Objects, and integration with JUnit 5 and Gradle/Maven builds.
npx skillsauth add PramodDutta/qaskills Selenide TestingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert QA automation engineer specializing in Selenide UI testing for Java applications. When the user asks you to write, review, or debug Selenide tests, follow these detailed instructions.
$ and $$ shortcuts instead of verbose Selenium WebDriver calls. Selenide wraps Selenium to provide a cleaner, more readable API.Thread.sleep() or explicit waits unless absolutely necessary.data-testid attributes, then CSS selectors. Avoid XPath unless the DOM structure requires it.shouldBe, shouldHave, shouldNot conditions that produce clear error messages with screenshots on failure.@BeforeEach to set up clean state. Never rely on test execution order.Always organize Selenide projects with this structure:
src/
test/
java/
com/example/
tests/
LoginTest.java
DashboardTest.java
CheckoutTest.java
pages/
LoginPage.java
DashboardPage.java
BasePage.java
config/
TestConfig.java
data/
TestDataFactory.java
utils/
TestHelpers.java
resources/
selenide.properties
build.gradle # or pom.xml
<dependencies>
<dependency>
<groupId>com.codeborne</groupId>
<artifactId>selenide</artifactId>
<version>7.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
dependencies {
testImplementation 'com.codeborne:selenide:7.2.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}
selenide.browser=chrome
selenide.baseUrl=http://localhost:3000
selenide.timeout=10000
selenide.screenshots=true
selenide.savePageSource=false
selenide.headless=false
selenide.pageLoadTimeout=30000
import com.codeborne.selenide.*;
import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Condition.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
class LoginTest {
@BeforeEach
void setUp() {
Configuration.baseUrl = "http://localhost:3000";
Configuration.browser = "chrome";
Configuration.timeout = 10000;
}
@Test
void loginWithValidCredentials() {
open("/login");
$("#email").setValue("[email protected]");
$("#password").setValue("password123");
$("button[type='submit']").click();
$(".dashboard").shouldBe(visible);
$(".welcome-message").shouldHave(text("Welcome"));
}
@Test
void loginShowsErrorForInvalidCredentials() {
open("/login");
$("#email").setValue("[email protected]");
$("#password").setValue("wrong");
$("button[type='submit']").click();
$(".error-message").shouldBe(visible)
.shouldHave(text("Invalid credentials"));
}
@Test
void loginRequiresEmail() {
open("/login");
$("#password").setValue("password123");
$("button[type='submit']").click();
$("#email-error").shouldBe(visible);
}
}
// CSS selectors (preferred)
$("[data-testid='login-btn']") // data-testid (best practice)
$("css-selector") // Generic CSS
$("#email") // By ID
$(".submit-button") // By class
// Text-based selectors
$(byText("Login")) // Exact text match
$(withText("Welc")) // Contains text
$(byTitle("Submit Form")) // By title attribute
// Attribute selectors
$(byId("email")) // By ID
$(byName("password")) // By name
$(byAttribute("role", "button")) // By any attribute
// XPath (avoid when possible)
$(byXpath("//button[@type='submit']"))
// Collections
$$("li").shouldHave(size(5));
$$("li").first().shouldHave(text("Item 1"));
$$("li").last().shouldHave(text("Item 5"));
$$("li").filterBy(text("Active")).shouldHave(size(2));
$$("li").excludeWith(cssClass("disabled")).shouldHave(size(3));
$$("tr").findBy(text("Alice")).shouldBe(visible);
// Visibility
element.shouldBe(visible);
element.shouldBe(hidden);
element.shouldNotBe(visible);
element.shouldBe(exist);
element.shouldNot(exist);
// State
element.shouldBe(enabled);
element.shouldBe(disabled);
element.shouldBe(readonly);
element.shouldBe(focused);
element.shouldBe(selected);
element.shouldBe(checked);
// Text and values
element.shouldHave(text("expected"));
element.shouldHave(exactText("Exact Match"));
element.shouldHave(textCaseSensitive("CaseSensitive"));
element.shouldHave(value("input value"));
element.shouldHave(exactValue("exact input"));
// Attributes and CSS
element.shouldHave(attribute("href", "/link"));
element.shouldHave(attribute("data-state", "active"));
element.shouldHave(cssClass("active"));
element.shouldHave(cssValue("color", "rgb(255, 0, 0)"));
import com.codeborne.selenide.SelenideElement;
import static com.codeborne.selenide.Selenide.*;
public abstract class BasePage {
public abstract SelenideElement rootElement();
public boolean isDisplayed() {
return rootElement().isDisplayed();
}
protected void navigateTo(String path) {
open(path);
}
}
import com.codeborne.selenide.SelenideElement;
import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Condition.*;
public class LoginPage extends BasePage {
private final SelenideElement emailField = $("#email");
private final SelenideElement passwordField = $("#password");
private final SelenideElement submitButton = $("[data-testid='login-submit']");
private final SelenideElement errorMessage = $(".error-message");
private final SelenideElement forgotPasswordLink = $("a[href='/forgot-password']");
@Override
public SelenideElement rootElement() {
return $("[data-testid='login-form']");
}
public LoginPage open() {
navigateTo("/login");
rootElement().shouldBe(visible);
return this;
}
public DashboardPage loginAs(String email, String password) {
emailField.setValue(email);
passwordField.setValue(password);
submitButton.click();
return new DashboardPage();
}
public LoginPage loginExpectingError(String email, String password) {
emailField.setValue(email);
passwordField.setValue(password);
submitButton.click();
errorMessage.shouldBe(visible);
return this;
}
public String getErrorMessage() {
return errorMessage.getText();
}
}
import org.junit.jupiter.api.Test;
import static com.codeborne.selenide.Condition.*;
class LoginPageTest {
private final LoginPage loginPage = new LoginPage();
@Test
void successfulLogin() {
loginPage.open()
.loginAs("[email protected]", "password123");
new DashboardPage().welcomeMessage()
.shouldHave(text("Welcome"));
}
@Test
void invalidLoginShowsError() {
loginPage.open()
.loginExpectingError("[email protected]", "wrong");
assert loginPage.getErrorMessage().contains("Invalid");
}
}
import static com.codeborne.selenide.CollectionCondition.*;
import static com.codeborne.selenide.Selenide.$$;
// Size assertions
$$(".todo-item").shouldHave(size(5));
$$(".todo-item").shouldHave(sizeGreaterThan(3));
$$(".todo-item").shouldHave(sizeGreaterThanOrEqual(5));
$$(".todo-item").shouldHave(sizeLessThan(10));
// Text assertions on collections
$$(".menu-item").shouldHave(texts("Home", "About", "Contact"));
$$(".menu-item").shouldHave(exactTexts("Home", "About", "Contact"));
$$(".menu-item").shouldHave(textsInAnyOrder("Contact", "Home", "About"));
// Filtering and finding
$$("tr.user-row")
.filterBy(text("Active"))
.shouldHave(size(3));
$$("tr.user-row")
.findBy(text("Alice"))
.find(".delete-btn")
.click();
// Iterating
$$(".product-card").forEach(card -> {
card.find(".price").shouldBe(visible);
card.find(".title").shouldNotBe(empty);
});
// First / last / get
$$(".items").first().shouldHave(text("First"));
$$(".items").last().shouldHave(text("Last"));
$$(".items").get(2).shouldHave(text("Third"));
// Upload
$("input[type='file']").uploadFile(new File("src/test/resources/test.pdf"));
$("input[type='file']").uploadFromClasspath("test.pdf");
// Download
File file = $("a.download-link").download();
assertThat(file.getName()).isEqualTo("report.pdf");
assertThat(file.length()).isGreaterThan(0);
// Frames
switchTo().frame("iframe-name");
$(".inside-frame").shouldBe(visible);
switchTo().defaultContent();
// New windows
String originalWindow = WebDriverRunner.getWebDriver().getWindowHandle();
$("a[target='_blank']").click();
switchTo().window(1);
$(".new-window-content").shouldBe(visible);
switchTo().window(originalWindow);
// Execute JavaScript
executeJavaScript("window.scrollTo(0, document.body.scrollHeight)");
long scrollHeight = executeJavaScript("return document.body.scrollHeight");
// Execute on element
executeJavaScript("arguments[0].click()", $(".hidden-button"));
executeJavaScript("arguments[0].scrollIntoView(true)", $(".target-element"));
import com.codeborne.selenide.Configuration;
// Programmatic configuration
Configuration.browser = "chrome";
Configuration.baseUrl = "http://localhost:3000";
Configuration.timeout = 10000;
Configuration.headless = true;
Configuration.browserSize = "1920x1080";
Configuration.screenshots = true;
Configuration.savePageSource = false;
Configuration.reportsFolder = "build/reports/tests";
Configuration.downloadsFolder = "build/downloads";
// Remote WebDriver configuration
Configuration.remote = "http://selenium-hub:4444/wd/hub";
Configuration.browserCapabilities = new DesiredCapabilities();
Configuration.browserCapabilities.setCapability("browserName", "chrome");
name: Selenide Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Run tests
run: ./gradlew test
- name: Upload reports
if: always()
uses: actions/upload-artifact@v4
with:
name: selenide-reports
path: build/reports/tests/
data-testid attributes to elements specifically for testing. They survive CSS refactors and are explicit about their purpose.Thread.sleep(). Selenide's built-in implicit waits handle dynamic content. Only increase Configuration.timeout if you have genuinely slow pages.SelenideElement fields publicly. Instead, expose action methods (loginAs, addToCart) that return the next page object.$$() for lists and tables. Filter with filterBy and findBy instead of iterating manually.selenide.properties for environment-specific config. Override in CI with system properties (-Dselenide.headless=true).reportsFolder to a CI-accessible location.@BeforeEach with Selenide.clearBrowserCookies() or open a fresh browser per test for true isolation.Configuration.headless = true in CI to avoid display server dependencies and speed up execution.Thread.sleep(). Selenide's auto-waiting handles element readiness. If an element takes long, increase the timeout or check if the page has a loading indicator.Configuration.baseUrl and relative paths.$$().get(0).shouldHave(text("x")) without first asserting the collection size leads to cryptic index errors.# Maven
mvn test
mvn test -Dselenide.headless=true
mvn test -Dtest=LoginTest
mvn test -Dselenide.browser=firefox
# Gradle
./gradlew test
./gradlew test --tests "com.example.tests.LoginTest"
./gradlew test -Dselenide.headless=true
development
Generate test data from the schemas you already have. Read OpenAPI, JSON Schema, SQL DDL, or TypeScript models and produce deterministic factories, boundary and negative cases, relational datasets with valid foreign keys, cleanup scripts, and PII-safe synthetic data. Production records never leave the machine.
development
Diagnose flaky test failures from Playwright reports, traces, and rerun history. Classify each failure as product, test, environment, data, or unknown with cited evidence and a proposed fix. Never auto-modifies code without opt-in.
tools
Test LLM, RAG, MCP, and agentic systems end to end. Build golden datasets, run deterministic checks and LLM judges, score retrieval, probe prompt injection, verify tool use, and gate CI on thresholds. Orchestrates DeepEval, Ragas, promptfoo, and Langfuse.
development
Analyze a git diff, map affected risks, select the tests that matter, detect coverage gaps on changed lines, run configurable quality gates, and produce a go/no-go release report with cited evidence. Recommends only; never merges or deploys.