Skip to main content

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 Site configuration
  • 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 WebDriver from WebDriverContextHolder automatically

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 on function in the DSL navigates to baseUrl + 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.

note

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 driver explicitly 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.

note

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
}
@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 Site type via the generic parameter S : Site
  • The path property is used by the DSL's on function to navigate to the page
  • All locator delegates work directly on Page instances because Page implements SearchContext
  • Pages automatically inherit site-level configuration (wait strategies, decorators, readiness conditions)
  • The driver property is protected and should be used sparingly; prefer locator delegates
  • Pages must be used within Kolibrium DSL contexts (seleniumTest, on, etc.)