Skip to main content

Decorators framework

In the context of Selenium testing with Kolibrium, decorators are design pattern implementations that allow you to dynamically enhance the behavior of WebDriver and WebElement instances without modifying their core functionality. They follow the decorator pattern from software design to add supplementary behaviors to existing objects at runtime.

Core decorator concept

Decorators in Kolibrium wrap around standard Selenium WebDriver and WebElement objects to intercept and augment their methods. This enables you to add cross-cutting functionality that applies consistently across your test suite without cluttering your test code.

The decorator framework consists of:

  1. AbstractDecorator – Base type that decorates the Selenium SearchContext contract (both WebDriver and WebElement) so chaining is preserved across nested searches
  2. DecoratorManager – Manages test-level decorators per thread and applies them in insertion order
  3. Concrete decorators – Specialized implementations for specific behaviors (e.g., highlighting, slow motion, logging, caching)
  4. Optional interaction callbacks – Decorators that implement InteractionAware can receive WebDriver interaction events; Kolibrium wires them through a single Selenium EventFiringDecorator with a listener multiplexer

Available decorators

Kolibrium provides several built-in decorators.

SlowMotionDecorator

Adds configurable delays between Selenium operations, useful for:

  • Debugging tests visually
  • Creating demonstrations or recordings
  • Simulating human-like interaction timing

Example usage:

DecoratorManager.addDecorators(SlowMotionDecorator(2.seconds))

HighlighterDecorator

Visually highlights elements on the page by adding a colored border around them when they're found or interacted with:

  • Configurable border style, color, and width
  • Automatically cleans up previous highlights
  • Helps visualize which elements are being targeted during test execution

Example usage:

DecoratorManager.addDecorators(HighlighterDecorator(
style = BorderStyle.Solid,
color = Color.Blue,
width = 3
))

LoggerDecorator

Logs element discovery and basic interactions:

  • Traces findElement(s) calls and counts
  • Logs before click and sendKeys with a short preview of keys
  • Lightweight and suitable for troubleshooting

Example usage:

DecoratorManager.addDecorators(LoggerDecorator())

ElementStateCacheDecorator

Optimizes performance by caching positive state check results for elements:

  • Eliminates redundant WebDriver calls for state checks
  • Caches only positive results (true values)
  • Configurable for different state methods (displayed, enabled, selected)

Example usage:

DecoratorManager.addDecorators(ElementStateCacheDecorator(
cacheDisplayed = true,
cacheEnabled = true,
cacheSelected = false
))

Applying decorators

There are two ways to apply decorators:

  • At the test level via DecoratorManager
  • At the site level by overriding Site.decorators on your site class

Test-level vs site-level decorator precedence rule

Kolibrium applies this precedence rule to decide what decorators shall be used in tests:

  • If only site-level decorators exist (through Site.decorators), they will be used
  • If only test-level decorators exist (through DecoratorManager), they will be used
  • If both exist, test-level decorators take precedence; duplicates by class are de-duplicated with test-level winning
  • If neither exists, the original SearchContext is returned unchanged

Applying decorators at the test-level

Decorators can be applied at the test-level through the DecoratorManager:

@BeforeEach
fun setup() {
DecoratorManager.addDecorators( // Add decorators before the start of your tests
SlowMotionDecorator(1.seconds),
HighlighterDecorator(style = BorderStyle.Dashed, color = Color.Green, width = 5)
)
}

@AfterEach
fun cleanup() {
DecoratorManager.clearDecorators() // Clear decorators after tests execution
}

Decorators are applied in the order they're added, allowing you to combine multiple decorators to create a customized testing experience.

Scoped application with withDecorators

Prefer the scoped helper when you want decorators active only within a block. They are automatically cleared on exit (even if an exception is thrown):

DecoratorManager.withDecorators(
SlowMotionDecorator(1.seconds),
HighlighterDecorator(color = Color.Green, width = 5)
) {
// decorators active only within this block
}

Manual management is also available, but remember to clear them afterward to avoid cross‑test leakage:

DecoratorManager.addDecorators(SlowMotionDecorator(1.seconds))
// ... run tests ...
DecoratorManager.clearDecorators()

Why not implementing decorators with WebDriverDecorator or EventFiringDecorator from Selenium support library?

Short answer: Kolibrium deliberately does not use Selenium’s decorator framework for element wrapping; we only use a single EventFiringDecorator to dispatch interaction callbacks.

  • EventFiringDecorator: It operates at the WebDriver boundary and does not wrap elements returned by findElement(s). Kolibrium uses it only to register a single WebDriverListener multiplexer so InteractionAware decorators can observe interactions.
  • WebDriverDecorator: It can decorate WebElement and propagate wrapping, but Kolibrium chooses not to rely on it because:
  • It is marked @Beta and may change or be removed between releases.
  • It uses dynamic proxies/reflection, which adds overhead and stack noise and doesn’t align with our Kotlin‑first delegation approach.
  • Composition and precedence: we need deterministic merging of site‑level and test‑level decorators and class‑based de‑duplication; stacking Selenium decorators makes this harder and can lead to multiple nested driver proxies.
  • Our SearchContext‑first design keeps chaining explicit and consistent for both WebDriver and WebElement, independent of Selenium’s decorator internals.

Instead, Kolibrium:

  • Wraps the SearchContext contract directly so both top‑level (WebDriver) and nested (WebElement) searches — single and multiple — are consistently decorated and chainable.
  • Optionally integrates Selenium interaction callbacks for InteractionAware decorators via a single EventFiringDecorator with a listener multiplexer.

Benefits of using Kolibrium's decorators

  1. Separation of Concerns: Keep test logic separate from cross-cutting concerns.
  2. Reusability: Apply the same enhancements across multiple tests.
  3. Thread Safety: Decorators are managed per-thread for parallel test execution.
  4. Dynamic Application: Add or remove behaviors at runtime.
  5. Non-Invasive: Enhance functionality without modifying the core Selenium classes or requiring changes to their source code. This maintains compatibility with future Selenium updates and allows you to add behaviors without subclassing or replacing the original implementation.

Decorators provide a powerful way to enhance your Selenium tests with visual feedback, performance optimizations, and debugging capabilities while maintaining clean and modular test code.