Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

OP here. Ordinarily, I would agree with you, because PageObjects themselves are not composable, in languages belonging to the "Kingdom Of Nouns".

However, the following design, thanks to Clojure's language design, helped address a rather nasty situation.

A tightly scoped Domain Specific Language, over some Types of PageObjects, all of which compose arbitrarily (without breaking value semantics). So, if you wanted the `value` of a Modal box having all sorts of switches, form fields etc., you'd call `value` on it, and it would call `value` on all of its constituents, and return the immutable hash-map snapshot of whatever state it found.

  Cross-cutting concerns

  | v PageObject / DSL -> | open | close | open? | select | deselect | ... |
  |-----------------------+------+-------+-------+--------+----------+-----|
  | Dropdown              |      |       |       |        |          |     |
  | Checkbox              |      |       |       |        |          |     |
  | Switch                |      |       |       |        |          |     |
  | Modal                 |      |       |       |        |          |     |
  | SearchList            |      |       |       |        |          |     |
  | ...                   |      |       |       |        |          |     |
Concrete example (in the deck and demo linked below):

  (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)))
Deck: https://github.com/adityaathalye/slideware/blob/master/desig...

Talk + Demo: https://www.youtube.com/watch?v=hwoLON80ZzA&list=PLG4-zNACPC...

I also commented about it here: https://news.ycombinator.com/item?id=45161410 (Clojure's solution to the Expression problem).

That said, UI testing is a hot mess in general (especially for SPAs). I prefer to avoid automating anything but the "happy paths". IME, exploratory testing is better at sussing out corner cases, and "emergent" misbehaviours. So I do that, in addition to the "happy path" suites. Cue: James Bach et. al. https://www.satisfice.com/

Also I am warming up to server-generated HTML, because I can unit-test that, if I can use `hiccup` syntax + HTMX. In that case, I just make all request handlers spit out HTML fragments as Clojure data, and test those responses in my test suite.



Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: