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:
AbstractDecorator– Base type that decorates the SeleniumSearchContextcontract (bothWebDriverandWebElement) so chaining is preserved across nested searchesDecoratorManager– Manages test-level decorators per thread and applies them in insertion order- Concrete decorators – Specialized implementations for specific behaviors (e.g., highlighting, slow motion, logging, caching)
- Optional interaction callbacks – Decorators that implement
InteractionAwarecan receiveWebDriverinteraction events; Kolibrium wires them through a single SeleniumEventFiringDecoratorwith 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
clickandsendKeyswith 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
WebDrivercalls for state checks - Caches only positive results (
truevalues) - 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.decoratorson 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
SearchContextis 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 theWebDriverboundary and does not wrap elements returned byfindElement(s). Kolibrium uses it only to register a singleWebDriverListenermultiplexer soInteractionAwaredecorators can observe interactions.WebDriverDecorator: It can decorateWebElementand propagate wrapping, but Kolibrium chooses not to rely on it because:- It is marked
@Betaand 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 bothWebDriverandWebElement, independent of Selenium’s decorator internals.
Instead, Kolibrium:
- Wraps the
SearchContextcontract directly so both top‑level (WebDriver) and nested (WebElement) searches — single and multiple — are consistently decorated and chainable. - Optionally integrates Selenium interaction callbacks for
InteractionAwaredecorators via a singleEventFiringDecoratorwith a listener multiplexer.
Benefits of using Kolibrium's decorators
- Separation of Concerns: Keep test logic separate from cross-cutting concerns.
- Reusability: Apply the same enhancements across multiple tests.
- Thread Safety: Decorators are managed per-thread for parallel test execution.
- Dynamic Application: Add or remove behaviors at runtime.
- 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.