diff of 9a7004f18303b19aafe20bbe37869ba705877655

9a7004f18303b19aafe20bbe37869ba705877655
diff --git a/src/murja-newui/newui.lisp b/src/murja-newui/newui.lisp
index 73f6f33..9e9be25 100644
--- a/src/murja-newui/newui.lisp
+++ b/src/murja-newui/newui.lisp
@@ -1,6 +1,6 @@
 (defpackage murja.newui
   (:use :cl)
-  (:export :*server*)
+  (:export :*server* :@newui :c :with-state)
   (:local-nicknames (:json :com.inuoe.jzon))
   (:import-from :cl-hash-util :hash))
 
@@ -100,10 +100,17 @@
 (defmethod render ((s string))
   s)
 
+(defparameter *single-element-tags* (list :link)
+  "Contains tags that don't expand to <:tag attrs>children</:tag> but instead into <:tag attrs />")
+
 (defmethod render ((c component))
   (with-slots (tag attrs children) c
-    (format nil "<~a ~{~a=\"~a\"~^ ~}>~%  ~{~a~}~%</~a>~%"
-	    tag attrs (mapcar (lambda (kid) (render kid)) children) tag)))
+    (if (member tag *single-element-tags*)
+	(format nil "<~a~{ ~a=\"~a\"~^~} />" tag attrs)	
+	(format nil "~a~&
+<~a~{ ~a=\"~a\"~^~}>~%  ~{~a~}~%</~a>~%"
+		(if (equalp tag :html) "<!DOCTYPE html>" "")
+		tag attrs (mapcar (lambda (kid) (render kid)) children) tag))))
 
 (defmethod rerender ((c component))
   (with-slots (attrs) c
@@ -147,7 +154,8 @@
 	  "(with-state (~{(~a ~a)~})
   ~a)" (alexandria:hash-table-plist (state-map s)) (root-component s)))
 
-(defmacro with-state (bindings &rest body)
+(defmacro with-state (bindings (&key (root-id (random 123456)))
+		      &body body)
   (let* ((rewritten-symbols (map 'list #'first bindings))
 	 (values (map 'list #'second bindings))
 	 (_ 
@@ -165,7 +173,7 @@
 		     ((member element rewritten-symbols) `(get-state current-state (quote ,element)))
 		     (t element))))
       `(let ((current-state (make-instance 'state :state (alexandria:plist-hash-table (list ,@actual-bindings-plist))))
-	     (root-component-id (format nil "id~d" (random 123456))))
+	     (root-component-id (format nil "id~a" ,root-id)))
 	 (setf (root-component current-state)
 	       ,@(rewrite body))
 	 (with-slots (attrs) (root-component current-state)
diff --git a/test/newui-tests.lisp b/test/newui-tests.lisp
new file mode 100644
index 0000000..c00b73d
--- /dev/null
+++ b/test/newui-tests.lisp
@@ -0,0 +1,91 @@
+(defpackage murja.tests.newui
+  (:use :cl :fiveam :murja.newui)  
+  (:import-from :murja.tests :prepare-db-and-server :drakma->string :url :main-suite :prepare-db-and-server))
+
+(in-package :murja.tests.newui)
+
+(in-suite main-suite)
+
+;; testaa sulkeutuvien tagien sulkeutuminen
+;; html:n doctypet, että niitä on vain 1 kerran, ei enempää ei vähempää
+
+
+;; https://rosettacode.org/wiki/Count_occurrences_of_a_substring#Common_Lisp
+(defun count-sub (str pat)
+  (loop with z = 0 with s = 0 while s do
+	(when (setf s (search pat str :start2 s))
+	  (incf z) (incf s (length pat)))
+	finally (return z)))
+
+(def-test html-generator-tests ()
+  ;; basic html transformations
+  (is (string= (render (c :p (:id "lol") "Sisältö"))
+	       "
+<P ID=\"lol\">
+  Sisältö
+</P>
+"))
+
+  ;; children are rendered somewhat sensibly 
+  (is (string= (render (c :body ()
+			  (c :h1 (:id "lol" :class "header") "HEADER")
+			  (c :article (:id "lol2") "article")))
+	       "
+<BODY>
+  
+<H1 ID=\"lol\" CLASS=\"header\">
+  HEADER
+</H1>
+
+<ARTICLE ID=\"lol2\">
+  article
+</ARTICLE>
+
+</BODY>
+"))
+
+  ;; with-state doesn't screw up rendering 
+  (is (string= (render (c :body ()
+			  (c :h1 (:id "lol" :class "header") "HEADER")
+			  (with-state ((example-variable "Example content")) (:root-id "testitila")
+			    (c :article (:id "lol2") example-variable))))
+	       "
+<BODY>
+  
+<H1 ID=\"lol\" CLASS=\"header\">
+  HEADER
+</H1>
+
+<ARTICLE ID=\"idtestitila\" ID=\"lol2\">
+  Example content
+</ARTICLE>
+
+</BODY>
+"))
+
+  ;; basic with-state renders too 
+  (is (string= (render (with-state ((example-variable "Example content")) (:root-id "testitila")
+			 (c :article (:id "lol2") example-variable)))
+	       "
+<ARTICLE ID=\"idtestitila\" ID=\"lol2\">
+  Example content
+</ARTICLE>
+"))
+
+
+  ;; doctype tests 
+  (let ((html-result (render (c :html (:lang "en") (c :body () (c :p () "Testing"))))))
+    (is (equalp 1 (count-sub html-result "DOCTYPE html"))))
+
+  ;; :link is rendered as <link .../> instead of <link> ... </link>
+
+  (is (string= "<LINK HREF=\"/resources/murja.css\" REL=\"stylesheet\" TYPE=\"text/css\" />"
+	       (render (c :link (:href "/resources/murja.css" :rel "stylesheet" :type "text/css") "random child that shouldn't be")))))
+
+  
+	       
+	       
+	       
+
+;; (fiveam:explain! 
+;;  (fiveam:run 'html-generator-tests))