Back
html (clj)
(source)macro
(html options & content)
Render Clojure data structures to a string of HTML. Strings are **not**
automatically escaped, but must be manually escaped with the [[h]] function.
A literal option map may be specified as the first argument. It accepts the
following keys:
`:mode`
: One of `:html`, `:xhtml`, `:xml` or `:sgml` (defaults to `:xhtml`).
Controls how tags are rendered.
Examples
hiccup
(ns hiccup.form_test
(:require [clojure.test :refer :all]
[hiccup.core :refer [html]]
[hiccup.form :refer :all]))
(deftest test-hidden-field
(is (= (html (hidden-field :foo "bar"))
"<input id=\"foo\" name=\"foo\" type=\"hidden\" value=\"bar\" />")))
(deftest test-hidden-field-with-extra-atts
(is (= (html (hidden-field {:class "classy"} :foo "bar"))
"<input class=\"classy\" id=\"foo\" name=\"foo\" type=\"hidden\" value=\"bar\" />")))
(deftest test-text-field
(is (= (html (text-field :foo))
"<input id=\"foo\" name=\"foo\" type=\"text\" />")))
(deftest test-text-field-with-extra-atts
(is (= (html (text-field {:class "classy"} :foo "bar"))
"<input class=\"classy\" id=\"foo\" name=\"foo\" type=\"text\" value=\"bar\" />")))
(deftest test-check-box
(is (= (html (check-box :foo true))
(str "<input checked=\"checked\" id=\"foo\" name=\"foo\""
" type=\"checkbox\" value=\"true\" />"))))
(deftest test-check-box-with-extra-atts
(is (= (html (check-box {:class "classy"} :foo true 1))
(str "<input checked=\"checked\" class=\"classy\" id=\"foo\" name=\"foo\""
" type=\"checkbox\" value=\"1\" />"))))
(deftest test-password-field
(is (= (html (password-field :foo "bar"))
"<input id=\"foo\" name=\"foo\" type=\"password\" value=\"bar\" />")))
(deftest test-password-field-with-extra-atts
(is (= (html (password-field {:class "classy"} :foo "bar"))
"<input class=\"classy\" id=\"foo\" name=\"foo\" type=\"password\" value=\"bar\" />")))
(deftest test-email-field
(is (= (html (email-field :foo "bar"))
"<input id=\"foo\" name=\"foo\" type=\"email\" value=\"bar\" />")))
(deftest test-email-field-with-extra-atts
(is (= (html (email-field {:class "classy"} :foo "bar"))
"<input class=\"classy\" id=\"foo\" name=\"foo\" type=\"email\" value=\"bar\" />")))
(deftest test-radio-button
(is (= (html (radio-button :foo true 1))
(str "<input checked=\"checked\" id=\"foo-1\" name=\"foo\""
" type=\"radio\" value=\"1\" />"))))
(deftest test-radio-button-with-extra-atts
(is (= (html (radio-button {:class "classy"} :foo true 1))
(str "<input checked=\"checked\" class=\"classy\" id=\"foo-1\" name=\"foo\""
" type=\"radio\" value=\"1\" />"))))
(deftest test-select-options
(are [x y] (= (html x) y)
(select-options ["foo" "bar" "baz"])
"<option>foo</option><option>bar</option><option>baz</option>"
(select-options ["foo" "bar"] "bar")
"<option>foo</option><option selected=\"selected\">bar</option>"
(select-options [["Foo" 1] ["Bar" 2]])
"<option value=\"1\">Foo</option><option value=\"2\">Bar</option>"
(select-options [["Foo" [1 2]] ["Bar" [3 4]]])
(str "<optgroup label=\"Foo\"><option>1</option><option>2</option></optgroup>"
"<optgroup label=\"Bar\"><option>3</option><option>4</option></optgroup>")
(select-options [["Foo" [["bar" 1] ["baz" 2]]]])
(str "<optgroup label=\"Foo\"><option value=\"1\">bar</option>"
"<option value=\"2\">baz</option></optgroup>")
(select-options [["Foo" [1 2]]] 2)
(str "<optgroup label=\"Foo\"><option>1</option>"
"<option selected=\"selected\">2</option></optgroup>")))
(deftest test-drop-down
(let [options ["op1" "op2"]
selected "op1"
select-options (html (select-options options selected))]
(is (= (html (drop-down :foo options selected))
(str "<select id=\"foo\" name=\"foo\">" select-options "</select>")))))
(deftest test-drop-down-with-extra-atts
(let [options ["op1" "op2"]
selected "op1"
select-options (html (select-options options selected))]
(is (= (html (drop-down {:class "classy"} :foo options selected))
(str "<select class=\"classy\" id=\"foo\" name=\"foo\">"
select-options "</select>")))))
(deftest test-text-area
(is (= (html (text-area :foo "bar"))
"<textarea id=\"foo\" name=\"foo\">bar</textarea>")))
(deftest test-text-area-field-with-extra-atts
(is (= (html (text-area {:class "classy"} :foo "bar"))
"<textarea class=\"classy\" id=\"foo\" name=\"foo\">bar</textarea>")))
(deftest test-text-area-escapes
(is (= (html (text-area :foo "bar</textarea>"))
"<textarea id=\"foo\" name=\"foo\">bar</textarea></textarea>")))
(deftest test-file-field
(is (= (html (file-upload :foo))
"<input id=\"foo\" name=\"foo\" type=\"file\" />")))
(deftest test-file-field-with-extra-atts
(is (= (html (file-upload {:class "classy"} :foo))
(str "<input class=\"classy\" id=\"foo\" name=\"foo\""
" type=\"file\" />"))))
(deftest test-label
(is (= (html (label :foo "bar"))
"<label for=\"foo\">bar</label>")))
(deftest test-label-with-extra-atts
(is (= (html (label {:class "classy"} :foo "bar"))
"<label class=\"classy\" for=\"foo\">bar</label>")))
(deftest test-submit
(is (= (html (submit-button "bar"))
"<input type=\"submit\" value=\"bar\" />")))
(deftest test-submit-button-with-extra-atts
(is (= (html (submit-button {:class "classy"} "bar"))
"<input class=\"classy\" type=\"submit\" value=\"bar\" />")))
(deftest test-reset-button
(is (= (html (reset-button "bar"))
"<input type=\"reset\" value=\"bar\" />")))
(deftest test-reset-button-with-extra-atts
(is (= (html (reset-button {:class "classy"} "bar"))
"<input class=\"classy\" type=\"reset\" value=\"bar\" />")))
(deftest test-form-to
(is (= (html (form-to [:post "/path"] "foo" "bar"))
"<form action=\"/path\" method=\"POST\">foobar</form>")))
(deftest test-form-to-with-hidden-method
(is (= (html (form-to [:put "/path"] "foo" "bar"))
(str "<form action=\"/path\" method=\"POST\">"
"<input id=\"_method\" name=\"_method\" type=\"hidden\" value=\"PUT\" />"
"foobar</form>"))))
(deftest test-form-to-with-extr-atts
(is (= (html (form-to {:class "classy"} [:post "/path"] "foo" "bar"))
"<form action=\"/path\" class=\"classy\" method=\"POST\">foobar</form>")))
(deftest test-with-group
(testing "hidden-field"
(is (= (html (with-group :foo (hidden-field :bar "val")))
"<input id=\"foo-bar\" name=\"foo[bar]\" type=\"hidden\" value=\"val\" />")))
(testing "text-field"
(is (= (html (with-group :foo (text-field :bar)))
"<input id=\"foo-bar\" name=\"foo[bar]\" type=\"text\" />")))
(testing "checkbox"
(is (= (html (with-group :foo (check-box :bar)))
"<input id=\"foo-bar\" name=\"foo[bar]\" type=\"checkbox\" value=\"true\" />")))
(testing "password-field"
(is (= (html (with-group :foo (password-field :bar)))
"<input id=\"foo-bar\" name=\"foo[bar]\" type=\"password\" />")))
(testing "radio-button"
(is (= (html (with-group :foo (radio-button :bar false "val")))
"<input id=\"foo-bar-val\" name=\"foo[bar]\" type=\"radio\" value=\"val\" />")))
(testing "drop-down"
(is (= (html (with-group :foo (drop-down :bar [])))
(str "<select id=\"foo-bar\" name=\"foo[bar]\"></select>"))))
(testing "text-area"
(is (= (html (with-group :foo (text-area :bar)))
(str "<textarea id=\"foo-bar\" name=\"foo[bar]\"></textarea>"))))
(testing "file-upload"
(is (= (html (with-group :foo (file-upload :bar)))
"<input id=\"foo-bar\" name=\"foo[bar]\" type=\"file\" />")))
(testing "label"
(is (= (html (with-group :foo (label :bar "Bar")))
"<label for=\"foo-bar\">Bar</label>")))
(testing "multiple with-groups"
(is (= (html (with-group :foo (with-group :bar (text-field :baz))))
"<input id=\"foo-bar-baz\" name=\"foo[bar][baz]\" type=\"text\" />")))
(testing "multiple elements"
(is (= (html (with-group :foo (label :bar "Bar") (text-field :var)))
"<label for=\"foo-bar\">Bar</label><input id=\"foo-var\" name=\"foo[var]\" type=\"text\" />"))))
hiccup
(ns hiccup.core-test
(:require [clojure.test :refer :all]
[hiccup.core :refer :all]))
(deftest tag-names
(testing "basic tags"
(is (= (html [:div]) "<div></div>"))
(is (= (html ["div"]) "<div></div>"))
(is (= (html ['div]) "<div></div>")))
(testing "tag syntax sugar"
(is (= (html [:div#foo]) "<div id=\"foo\"></div>"))
(is (= (html [:div.foo]) "<div class=\"foo\"></div>"))
(is (= (html [:div.foo (str "bar" "baz")])
"<div class=\"foo\">barbaz</div>"))
(is (= (html [:div.a.b]) "<div class=\"a b\"></div>"))
(is (= (html [:div.a.b.c]) "<div class=\"a b c\"></div>"))
(is (= (html [:div#foo.bar.baz])
"<div class=\"bar baz\" id=\"foo\"></div>"))))
(deftest tag-contents
(testing "empty tags"
(is (= (html [:div]) "<div></div>"))
(is (= (html [:h1]) "<h1></h1>"))
(is (= (html [:script]) "<script></script>"))
(is (= (html [:text]) "<text></text>"))
(is (= (html [:a]) "<a></a>"))
(is (= (html [:iframe]) "<iframe></iframe>"))
(is (= (html [:title]) "<title></title>"))
(is (= (html [:section]) "<section></section>"))
(is (= (html [:select]) "<select></select>"))
(is (= (html [:object]) "<object></object>"))
(is (= (html [:video]) "<video></video>")))
(testing "void tags"
(is (= (html [:br]) "<br />"))
(is (= (html [:link]) "<link />"))
(is (= (html [:colgroup {:span 2}]) "<colgroup span=\"2\"></colgroup>"))
(is (= (html [:colgroup [:col]]) "<colgroup><col /></colgroup>")))
(testing "tags containing text"
(is (= (html [:text "Lorem Ipsum"]) "<text>Lorem Ipsum</text>")))
(testing "contents are concatenated"
(is (= (html [:body "foo" "bar"]) "<body>foobar</body>"))
(is (= (html [:body [:p] [:br]]) "<body><p></p><br /></body>")))
(testing "seqs are expanded"
(is (= (html [:body (list "foo" "bar")]) "<body>foobar</body>"))
(is (= (html (list [:p "a"] [:p "b"])) "<p>a</p><p>b</p>")))
(testing "keywords are turned into strings"
(is (= (html [:div :foo]) "<div>foo</div>")))
(testing "vecs don't expand - error if vec doesn't have tag name"
(is (thrown? IllegalArgumentException
(html (vector [:p "a"] [:p "b"])))))
(testing "tags can contain tags"
(is (= (html [:div [:p]]) "<div><p></p></div>"))
(is (= (html [:div [:b]]) "<div><b></b></div>"))
(is (= (html [:p [:span [:a "foo"]]])
"<p><span><a>foo</a></span></p>"))))
(deftest tag-attributes
(testing "tag with blank attribute map"
(is (= (html [:xml {}]) "<xml></xml>")))
(testing "tag with populated attribute map"
(is (= (html [:xml {:a 123}]) "<xml a=\"123\"></xml>"))
(is (= (html [:xml {:a 'sym}]) "<xml a=\"sym\"></xml>"))
(is (= (html [:xml {:a :kw}]) "<xml a=\"kw\"></xml>"))
(is (= (html [:xml {:a [:kw :ns/ns-kw "str" 3 'sym]}]) "<xml a=\"kw ns-kw str 3 sym\"></xml>"))
(is (= (html [:xml {:a "1", :b "2"}]) "<xml a=\"1\" b=\"2\"></xml>"))
(is (= (html [:img {"id" "foo"}]) "<img id=\"foo\" />"))
(is (= (html [:img {'id "foo"}]) "<img id=\"foo\" />"))
(is (= (html [:xml {:a "1", 'b "2", "c" "3"}])
"<xml a=\"1\" b=\"2\" c=\"3\"></xml>")))
(testing "attribute values are escaped"
(is (= (html [:div {:id "\""}]) "<div id=\""\"></div>")))
(testing "boolean attributes"
(is (= (html [:input {:type "checkbox" :checked true}])
"<input checked=\"checked\" type=\"checkbox\" />"))
(is (= (html [:input {:type "checkbox" :checked false}])
"<input type=\"checkbox\" />")))
(testing "nil attributes"
(is (= (html [:span {:class nil} "foo"])
"<span>foo</span>")))
(testing "resolving conflicts between attributes in the map and tag"
(is (= (html [:div.foo {:class "bar"} "baz"])
"<div class=\"foo bar\">baz</div>"))
(is (= (html [:div#bar.foo {:id "baq"} "baz"])
"<div class=\"foo\" id=\"baq\">baz</div>")))
(testing "tag with vector class"
(is (= (html [:div {:class [:bar]} "baz"])
"<div class=\"bar\">baz</div>"))
(is (= (html [:div.foo {:class ["bar"]} "baz"])
"<div class=\"foo bar\">baz</div>"))
(is (= (html [:div.foo {:class [:bar]} "baz"])
"<div class=\"foo bar\">baz</div>"))
(is (= (html [:div.foo {:class [:bar "box"]} "baz"])
"<div class=\"foo bar box\">baz</div>"))
(is (= (html [:div.foo {:class ["bar" "box"]} "baz"])
"<div class=\"foo bar box\">baz</div>"))
(is (= (html [:div.foo {:class [:bar :box]} "baz"])
"<div class=\"foo bar box\">baz</div>"))
(is (= (html [:div.foo {:class [nil :bar nil]} "baz"])
"<div class=\"foo bar\">baz</div>"))))
(deftest compiled-tags
(testing "tag content can be vars"
(is (= (let [x "foo"] (html [:span x])) "<span>foo</span>")))
(testing "tag content can be forms"
(is (= (html [:span (str (+ 1 1))]) "<span>2</span>"))
(is (= (html [:span ({:foo "bar"} :foo)]) "<span>bar</span>")))
(testing "attributes can contain vars"
(let [x "foo"]
(is (= (html [:xml {:x x}]) "<xml x=\"foo\"></xml>"))
(is (= (html [:xml {x "x"}]) "<xml foo=\"x\"></xml>"))
(is (= (html [:xml {:x x} "bar"]) "<xml x=\"foo\">bar</xml>"))))
(testing "attributes are evaluated"
(is (= (html [:img {:src (str "/foo" "/bar")}])
"<img src=\"/foo/bar\" />"))
(is (= (html [:div {:id (str "a" "b")} (str "foo")])
"<div id=\"ab\">foo</div>")))
(testing "type hints"
(let [string "x"]
(is (= (html [:span ^String string]) "<span>x</span>"))))
(testing "optimized forms"
(is (= (html [:ul (for [n (range 3)]
[:li n])])
"<ul><li>0</li><li>1</li><li>2</li></ul>"))
(is (= (html [:div (if true
[:span "foo"]
[:span "bar"])])
"<div><span>foo</span></div>"))
(is (= (html (let [x "foo"] [:span x]))
"<span>foo</span>"))
(is (= (html (when true [:span "true"]))
"<span>true</span>"))
(is (= (html (when false [:span "true"]))
"")))
(testing "values are evaluated only once"
(let [times-called (atom 0)
foo #(swap! times-called inc)]
(html [:div (foo)])
(is (= @times-called 1))))
(testing "defer evaluation of non-literal class names when combined with tag classes"
(let [x "attr-class"]
(is (= (html [:div.tag-class {:class x}])
"<div class=\"tag-class attr-class\"></div>")))))
(deftest render-modes
(testing "closed tag"
(is (= (html [:p] [:br]) "<p></p><br />"))
(is (= (html {:mode :xhtml} [:p] [:br]) "<p></p><br />"))
(is (= (html {:mode :html} [:p] [:br]) "<p></p><br>"))
(is (= (html {:mode :xml} [:p] [:br]) "<p /><br />"))
(is (= (html {:mode :sgml} [:p] [:br]) "<p><br>")))
(testing "boolean attributes"
(is (= (html {:mode :xml} [:input {:type "checkbox" :checked true}])
"<input checked=\"checked\" type=\"checkbox\" />"))
(is (= (html {:mode :sgml} [:input {:type "checkbox" :checked true}])
"<input checked type=\"checkbox\">")))
(testing "laziness and binding scope"
(is (= (html {:mode :sgml} [:html [:link] (list [:link])])
"<html><link><link></html>"))))
hiccup
(ns hiccup.middleware_test
(:require [clojure.test :refer :all]
[hiccup.core :refer [html]]
[hiccup.element :refer [link-to]]
[hiccup.middleware :refer :all]))
(defn test-handler [request]
{:status 200
:headers {"Content-Type" "text/html"}
:body (html [:html [:body (link-to "/bar" "bar")]])})
(deftest test-wrap-base-url
(let [resp ((wrap-base-url test-handler "/foo") {})]
(is (= (:body resp)
"<html><body><a href=\"/foo/bar\">bar</a></body></html>"))))