Skip to main content

Define project-level configuration

Kolibrium is flexible and has many ways to configure element readiness checks, waiting strategies and decorators. Sometimes you may want to set configuration for default behaviors at a global level and for that you need to use project-level configuration. Project-level configuration allows you to customize element readiness criteria, define a global waiting strategy, and apply decorators across your entire test suite.

Configuration example

Project-level configuration can be created by implementing an object that extends from AbstractSeleniumProjectConfiguration class and by overriding the desired properties.

object SeleniumConfiguration : AbstractSeleniumProjectConfiguration() {
override val elementReadyCondition: (WebElement.() -> Boolean) = { isClickable }

override val elementsReadyCondition: (WebElements.() -> Boolean) = { isClickable }

override val waitConfig = DEFAULT.copy(
pollingInterval = 250.milliseconds,
timeout = 5.seconds
)

override val decorators = listOf(
HighlighterDecorator(style = DASHED, color = GREEN, width = 5),
SlowMotionDecorator(wait = 1.seconds),
ElementStateCacheDecorator(cacheDisplayed = true, cacheEnabled = true)
)
}

With the above configuration:

  • By default isClickable is used to determine if an element or elements are ready for use
  • The waiting uses a 250 milliseconds polling interval with 5 seconds of timeout
  • All WebDriver and WebElement instances will be decorated with:
    • A green dashed highlight border (5px wide) around elements for visual feedback
    • A 1 second delay after each element interaction for slowed-down test execution
    • Caching of positive displayed and enabled state checks for performance optimization

All tests will inherit these settings automatically. The waiting configuration will also ignore NoSuchElementException exception and report "Element could not be found" error on time outs - these are inherited from the DEFAULT wait configuration.

ServiceLoader configuration

Java's ServiceLoader is a mechanism for dynamic service discovery and loading. In Kolibrium, automatic configuration discovery is enabled at runtime by scanning for and instantiating objects that extend AbstractSeleniumProjectConfiguration, applying their defined configuration values.

Once the configuration is created, we need to also configure metadata for the ServiceLoader by registering our implementation under META-INF/services folder.

Manual vs. AutoService approach

Manual Approach:

  • Requires manually creating service descriptor file.
  • Prone to human errors (typos, outdated paths).
  • Needs manual updates during refactoring.

AutoService Approach:

  • Automatically generates service descriptor.
  • Compile-time safety.
  • Reduces maintenance overhead.
  • Prevents configuration registration errors.

The key difference is automation and compile-time verification, making AutoService the recommended method for maintaining project-level configurations.

Manual approach

Create a file named as dev.kolibrium.core.selenium.configuration.AbstractSeleniumProjectConfiguration with content of

FQN.SeleniumConfiguration

under resources/META-INF/services directory, where FQN is the fully-qualified name of the object implementing AbstractSeleniumProjectConfiguration.

AutoService approach

To prevent manual service descriptor maintenance errors, we can leverage on a more robust solution: AutoService generates the metadata for any class annotated with @AutoService, avoiding typos, providing resistance to errors from refactoring, etc.

First, we need to register the Kotlin Symbol Processing (KSP) plugin and add the AutoService dependency.

plugins {
id("com.google.devtools.ksp") version "2.0.21-1.0.29"
// other plugins
}

dependencies {
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.2.0")
// other dependencies
}

After that, we just annotate our SeleniumConfiguration object as follows.

@AutoService(AbstractSeleniumProjectConfiguration::class)
object SeleniumConfiguration : AbstractSeleniumProjectConfiguration() {
// override desired properties
}