Page abstract class
The Page<S : Site> abstract class is the foundation for creating page objects in Kolibrium. It provides a type-safe way to bind page objects to a specific Site configuration and enables seamless integration with Kolibrium's locator delegates and DSL.
Purpose
Page serves as a base type for page objects that:
- Are bound to a specific
Siteconfiguration - Automatically inherit site-level defaults (wait strategies, decorators, readiness conditions)
- Implement
SearchContext, allowing locator delegates to work directly on page instances - Provide lifecycle hooks for page readiness verification
- Obtain the
WebDriverfromWebDriverContextHolderautomatically
Key members
path — Relative URL
public open val path: String = ""
The relative path of the page within the site. Defaults to an empty string (no navigation).
- When non-empty, the
onfunction in the DSL navigates tobaseUrl + path - Override this property in your page class to specify the page's URL path
Example:
class LoginPage : Page<MySite>() {
override val path: String = "/login"
}
currentUrl — Current page URL
protected val currentUrl: String
Returns the current URL as reported by the driver. This is a protected property available to page subclasses.
Example:
init {
check(currentUrl.contains("/products")) {
"Expected products page, but got: $currentUrl"
}
}
pageTitle — Current page title
protected val pageTitle: String
Returns the current page title as reported by the driver. This is a protected property available to page subclasses.
Example:
fun verifyTitle() {
check(pageTitle == "Product Catalog") {
"Unexpected page title: $pageTitle"
}
}
awaitReady() — Wait for page readiness
public open fun awaitReady()
Override this function to define custom waiting logic for when the page is considered ready for interaction. The default implementation is a no-op.
Example:
override fun awaitReady() {
// Wait for a specific element to be present
driver.findElement(By.id("page-loaded-indicator"))
}
assertReady() — Verify page invariants
public open fun assertReady()
Override this function to define post-ready assertions that verify page invariants. The default implementation is a no-op.
Example:
override fun assertReady() {
check(headerElement.isDisplayed) {
"Header should be visible on products page"
}
}
driver — WebDriver accessor
protected val driver: WebDriver
Provides direct access to the underlying WebDriver for advanced operations. This is a protected property available to page subclasses.
Prefer using locator delegates where possible. Direct driver access should be reserved for operations not covered by the delegate API.
Implements SearchContext
Page implements Selenium's SearchContext interface, which means:
- Locator delegates can be called directly on page instances
- No need to pass
driverexplicitly to locator functions - Pages act as the search root for element location
Example:
class ProductsPage : Page<MySite>() {
// Locator delegates work directly on the page
private val productList by className("product-list")
private val products by classNames("product-item")
}
Usage within Kolibrium DSL
Pages must be used within Kolibrium's DSL context. The WebDriver is obtained from WebDriverContextHolder, which is managed by the DSL.
Attempting to use pages outside the DSL will result in a runtime error.
Basic usage
object MySite : Site(baseUrl = "https://example.com")
class HomePage : Page<MySite>() {
override val path: String = "/"
private val searchInput by id("search")
private val searchButton by cssSelector("button[type='submit']")
fun search(query: String) {
searchInput.sendKeys(query)
searchButton.click()
}
}
@Test
fun `search for products`() = seleniumTest(site = MySite, driverFactory = ::ChromeDriver) {
on(::HomePage {
search("laptop")
}
}
With lifecycle hooks
class ProductsPage : Page<MySite>() {
override val path: String = "/products"
private val productGrid by id("product-grid")
private val products by classNames("product-card")
override fun awaitReady() {
// Wait for the product grid to be visible
productGrid.isDisplayed
}
override fun assertReady() {
check(products.isNotEmpty()) {
"Products page should display at least one product"
}
}
fun getProductCount(): Int = products.size
}
Navigating between pages
@Test
fun `navigate through pages`() = seleniumTest(site = MySite, driverFactory = ::ChromeDriver) {
on(::HomePage) {
search("laptop")
}
on(::SearchResultsPage) {
assertReady()
selectFirstProduct()
}
on(::ProductDetailPage) {
assertReady()
addToCart()
}
}
Constructor
The Page constructor is protected, meaning you cannot instantiate pages directly:
// ❌ This will not compile
val page = ProductsPage()
// ✅ Use the DSL instead
on(::ProductsPage) {
// page operations
}
This design ensures pages are always used within the proper Kolibrium context where the WebDriver is available.
Complete example
object MySite : Site(baseUrl = "https://shop.example.com") {
override val waitConfig: WaitConfig = WaitConfig.Default.copy(
timeout = 10.seconds
)
}
class LoginPage : Page<MySite>() {
override val path: String = "/login"
private val usernameInput by id("username")
private val passwordInput by id("password")
private val loginButton by cssSelector("button[type='submit']")
private val errorMessage by className("error-message")
override fun awaitReady() {
loginButton.isDisplayed
}
fun login(username: String, password: String) {
usernameInput.sendKeys(username)
passwordInput.sendKeys(password)
loginButton.click()
}
fun hasError(): Boolean = try {
errorMessage.isDisplayed
} catch (e: NoSuchElementException) {
false
}
}
class DashboardPage : Page<MySite>() {
override val path: String = "/dashboard"
private val welcomeMessage by id("welcome")
override fun assertReady() {
check(welcomeMessage.isDisplayed) {
"Welcome message should be visible on dashboard"
}
}
fun getWelcomeText(): String = welcomeMessage.text
}
@Test
fun `successful login flow`() = seleniumTest(site = MySite, driverFactory = ::ChromeDriver) {
on(::LoginPage) {
awaitReady()
login("testuser", "password123")
}
on(::DashboardPage) {
assertReady()
getWelcomeText() shouldContain "Welcome, testuser"
}
}
Notes
- Pages are bound to a specific
Sitetype via the generic parameterS : Site - The
pathproperty is used by the DSL'sonfunction to navigate to the page - All locator delegates work directly on
Pageinstances becausePageimplementsSearchContext - Pages automatically inherit site-level configuration (wait strategies, decorators, readiness conditions)
- The
driverproperty is protected and should be used sparingly; prefer locator delegates - Pages must be used within Kolibrium DSL contexts (
seleniumTest,on, etc.)