You wrote a function that returns hiccup, and now you want to test it.
But you don't care about all the bells and whistles,
only certain bits and pieces.
That's what lookup
helps you do:
Find the parts that matter to you.
Lookup is a testing library for hiccup-like data, and it helps you:
With tools.deps:
no.cjohansen/lookup {:mvn/version "2024.12.22"}
With Leiningen:
[no.cjohansen/lookup "2024.12.22"]
lookup.core/select
can be used to find hiccup nodes with a CSS selector. A
selector can be a single expression, like 'div
to find all divs, or a vector
representing a hierarchy, e.g. '[div button.btn]
finds all button
elements
with the class name "btn"
that are descendants of a div
element. The >
character can be used to specify direct children, e.g. '[ul > li]
will only
match li
elements with a direct ul
parent.
(require '[lookup.core :as lookup])
(def hiccup
[:div
[:ul
nil
[:li "B1"]
[:li.active
[:a {:href "#"} "C"]]
[:li "D1"]
[:li "E"]]
[:p "Paragraph 1"]
[:h1 "Heading"]
[:p "Paragraph 2"]])
(lookup/select '[ul > li a] hiccup)
;;=> [[:a {:href "#"} "C"]]
(lookup.core/select-one selector hiccup)
returns the first element matching
the selector.
Supported selector symbols:
'a
matches all anchor tags.'[form input]
matches all input tags nested inside a form.'[form > input]
matches all input tags that are direct children of a form.'[h1 + p]
matches all paragraphs that are direct siblings of an h1.'[h1 ~ p]
matches all paragraphs that are subsequent siblings of an h1.'div.foo
matches all div tags with "foo" in its class name.'.button
matches all elements with the "button" class.'div#content
matches the div with "content" as its id.':first-child
matches any element that is the first child.':last-child
matches any element that is the last child.'"meta[property]"
matches all meta tags with the property attribute.'"meta[property=og:title]"
matches all meta tags with the property
attribute set to "og:title".h1:has(a)
matches all h1 elements that contain the provided selectorAdditionally supports all attribute selector operators.
Hiccup is a flexible format, and when built with code it will often contain
noise such as nested lists of children, nil
s, classes in several places, etc.
lookup.core/normalize-hiccup
unifies all these things:
(require '[lookup.core :as lookup])
(lookup/normalize-hiccup
[:div
[:ul
nil
[:li {} "One"]
[:li.active
[:a {:href "#"} "Two"]]
[:li "Three"]
[:li "Four"]]
[:p {:class "text-sm fg-red"} "Paragraph 1"]
'([:h1 "Heading"]
[:p "Paragraph 2"])])
;;=>
;; [:div {}
;; [:ul {}
;; [:li {} "One"]
;; [:li {:class #{"active"}} [:a {:href "#"} "Two"]]
;; [:li {} "Three"]
;; [:li {} "Four"]]
;; [:p {:class #{"fg-red" "text-sm"}}
;; "Paragraph 1"]
;; [:h1 {} "Heading"]
;; [:p {} "Paragraph 2"]]
This form is ideal for further programmatic manipulation, as every node is
guaranteed to have an attribute map and a flat list of children. If you want
something that's better suited for human reading, employ :strip-empty-attrs?
:
(require '[lookup.core :as lookup])
(lookup/normalize-hiccup
[:div
[:ul
nil
[:li {} "One"]
[:li.active
[:a {:href "#"} "Two"]]
[:li "Three"]
[:li "Four"]]
[:p {:class "text-sm fg-red"} "Paragraph 1"]
'([:h1 "Heading"]
[:p "Paragraph 2"])]
{:strip-empty-attrs? true})
;;=>
;; [:div
;; [:ul
;; [:li "One"]
;; [:li {:class #{"active"}}
;; [:a {:href "#"} "Two"]]
;; [:li "Three"]
;; [:li "Four"]]
;; [:p {:class #{"fg-red" "text-sm"}}
;; "Paragraph 1"]
;; [:h1 "Heading"]
;; [:p "Paragraph 2"]]
To make assertions about the normalized hiccup, you can use lookup.core/attrs
and lookup.core/children
, which both take a hiccup form and returns the
corresponding details:
(require '[lookup.core :as lookup])
(def hiccup
[:p {:class "text-sm fg-red"}
"Paragraph " [:strong "1"]])
(lookup/attrs hiccup)
;;=> {:class "text-sm fg-red"}
(lookup/children hiccup)
;;=> ("Paragraph " [:strong "1"])
lookup.core/text
returns the text content of some hiccup:
(require '[lookup.core :as lookup])
(lookup/text [:h1 "Hello world"])
;;=> "Hello world"
(lookup/text
[:div
[:ul
nil
[:li {} "One"]
[:li.active
[:a {:href "#"} "Two"]]
[:li "Three"]
[:li "Four"]]
[:p {:class "text-sm fg-red"} "Paragraph 1"]
'([:h1 "Heading"]
[:p "Paragraph 2"])])
;;=> "One Two Three Four Paragraph 1 Heading Paragraph 2"
text
also works on collections of hiccup nodes, and can be used in conjunction
with select
:
(require '[lookup.core :as lookup])
(def hiccup
[:div
[:ul
nil
[:li "B1"]
[:li.active
[:a {:href "#"} "C"]]
[:li "D1"]
[:li "E"]]
[:p "Paragraph 1"]
[:h1 "Heading"]
[:p "Paragraph 2"]])
(->> hiccup
(lookup/select '[ul > li])
text)
;;=> "B1 C D1 E"
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close