Yeah, I got lucky in my work (see sibling comment https://news.ycombinator.com/item?id=45207880).
For me, it was PageObjects all the way down, which "just composed".
Something like this:
(defprotocol IPageObject
"Each PageObject MUST implement the IPageObject protocol."
(page-object [this])
(exists? [this])
(visible? [this]))
And then an implementation like this: (defrecord Checkbox [target-css]
IPageObject
(page-object [this]
this)
(exists? [this]
;; webdriver check if target-css exists
)
(visible? [this]
;; webdriver check if target-css is visible
)
Selectable
(select [this]
;; webdriver select the target
)
(deselect [this]
;; webdriver undo selection
)
(selected? [this]
;; webdriver return true if target selected
)
Value
(get-value [this]
;; webdriver return current selection state
(selected? this)))
Thanks to Records (stateless, type-identified, hash-map semantics), the PageObject model composes arbitrarily. i.e. A Page is itself a PageObject containing a tree of arbitrarily nested PageObjects... i.e. PageObjects all the way down.Solid gold!
(edit: x-link to sibling comment)