|
| 1 | +module Test.Main where |
| 2 | + |
| 3 | +import Prelude |
| 4 | + |
| 5 | +import Data.Maybe (Maybe(..)) |
| 6 | +import Data.Tuple (Tuple) |
| 7 | +import Data.Tuple.Nested ((/\)) |
| 8 | +import Effect (Effect) |
| 9 | +import Halogen.VDom (VDom, ElemName(..)) |
| 10 | +import Halogen.VDom as V |
| 11 | +import Halogen.VDom.DOM.Prop (Prop(..), PropValue, propFromBoolean, propFromInt, propFromNumber, propFromString) |
| 12 | +import Halogen.VDom.DOM.StringRenderer (render) |
| 13 | +import Test.Spec (describe, it) |
| 14 | +import Test.Spec.Assertions (shouldEqual) |
| 15 | +import Test.Spec.Reporter (consoleReporter) |
| 16 | +import Test.Spec.Runner.Node (runSpecAndExitProcess) |
| 17 | +import Unsafe.Coerce (unsafeCoerce) |
| 18 | + |
| 19 | +attr ∷ ∀ a. String → String → Prop a |
| 20 | +attr key value = Attribute Nothing key value |
| 21 | + |
| 22 | +class IsPropValue v where |
| 23 | + toPropValue :: v -> PropValue |
| 24 | + |
| 25 | +instance isPropValueString :: IsPropValue String where |
| 26 | + toPropValue = propFromString |
| 27 | + |
| 28 | +instance isPropValueBoolean :: IsPropValue Boolean where |
| 29 | + toPropValue = propFromBoolean |
| 30 | + |
| 31 | +instance isPropValueInt :: IsPropValue Int where |
| 32 | + toPropValue = propFromInt |
| 33 | + |
| 34 | +instance isPropValueNumber :: IsPropValue Number where |
| 35 | + toPropValue = propFromNumber |
| 36 | + |
| 37 | +prop :: forall a v. IsPropValue v => String -> v -> Prop a |
| 38 | +prop key value = Property key (toPropValue value) |
| 39 | + |
| 40 | +infixr 1 attr as := |
| 41 | +infixr 1 prop as .= |
| 42 | + |
| 43 | +elem ∷ ∀ a. String → Array (Prop a) → Array (VDom (Array (Prop a)) a) → VDom (Array (Prop a)) a |
| 44 | +elem n a c = V.Elem Nothing (ElemName n) a (unsafeCoerce c) |
| 45 | + |
| 46 | +keyed ∷ ∀ a. String → Array (Prop a) → Array (Tuple String (VDom (Array (Prop a)) a)) → VDom (Array (Prop a)) a |
| 47 | +keyed n a c = V.Keyed Nothing (ElemName n) a (unsafeCoerce c) |
| 48 | + |
| 49 | +text ∷ ∀ a. String → VDom (Array (Prop Void)) a |
| 50 | +text a = V.Text a |
| 51 | + |
| 52 | +main ∷ Effect Unit |
| 53 | +main = runSpecAndExitProcess [ consoleReporter ] do |
| 54 | + it "works" do |
| 55 | + let |
| 56 | + html ∷ VDom (Array (Prop Void)) Void |
| 57 | + html = |
| 58 | + elem "div" [ "className" .= "container", "id" := "root" ] |
| 59 | + [ elem "label" [ "htmlFor" .= "username" ] |
| 60 | + [ text "Username" ] |
| 61 | + , elem "input" [ "id" := "input" ] [] |
| 62 | + , elem "a" [ "href" := "index" ] [ text "Inbox" ] |
| 63 | + , keyed "div" [] |
| 64 | + [ "0" /\ elem "span" [] [ text "0" ] |
| 65 | + , "1" /\ elem "span" [] [ text "1" ] |
| 66 | + ] |
| 67 | + ] |
| 68 | + render absurd html `shouldEqual` """<div class="container" id="root"><label for="username">Username</label><input id="input"/><a href="index">Inbox</a><div><span>0</span><span>1</span></div></div>""" |
| 69 | + it "should not escape links in prop attrs" do |
| 70 | + let |
| 71 | + html ∷ VDom (Array (Prop Void)) Void |
| 72 | + html = |
| 73 | + elem "script" |
| 74 | + [ "async" := "" |
| 75 | + , "type" := "application/javascript" |
| 76 | + , "src" := "chunks/defaultVendors~main-063769ad5d827b791041.js" |
| 77 | + ] |
| 78 | + [] |
| 79 | + render absurd html `shouldEqual` """<script async="" type="application/javascript" src="chunks/defaultVendors~main-063769ad5d827b791041.js"></script>""" |
| 80 | + it "but should escape links prop names" do |
| 81 | + let |
| 82 | + html ∷ VDom (Array (Prop Void)) Void |
| 83 | + html = |
| 84 | + elem "script" |
| 85 | + [ "application/javascript" := "type" |
| 86 | + , "chunks/defaultVendors~main-063769ad5d827b791041.js" := "src" |
| 87 | + ] |
| 88 | + [] |
| 89 | + render absurd html `shouldEqual` """<script application/javascript="type" chunks/defaultVendors~main-063769ad5d827b791041.js="src"></script>""" |
| 90 | + it "but should render int, boolean, number props" do |
| 91 | + let |
| 92 | + html ∷ VDom (Array (Prop Void)) Void |
| 93 | + html = |
| 94 | + elem "input" |
| 95 | + [ "maxLength" .= 255 -- Int |
| 96 | + , "step" .= 0.5 -- Number |
| 97 | + , "disabled" .= true -- Boolean |
| 98 | + , "readonly" .= false -- Boolean |
| 99 | + ] |
| 100 | + [] |
| 101 | + render absurd html `shouldEqual` """<input maxLength="255" step="0.5" disabled/>""" |
| 102 | + describe "escape chars" do |
| 103 | + let |
| 104 | + mkElem char = elem ("div" <> char <> "test") [ "attr" <> char <> "key" := "attr" <> char <> "val", "prop" <> char <> "key" .= "prop" <> char <> "val" ] [] |
| 105 | + mkTest char expected = it char $ render absurd (mkElem char) `shouldEqual` expected |
| 106 | + |
| 107 | + mkTest "\"" """<divtest attr"key="attr"val" prop"key="prop"val"></divtest>""" |
| 108 | + mkTest "'" """<divtest attr'key="attr'val" prop'key="prop'val"></divtest>""" |
| 109 | + mkTest "/" """<divtest attr/key="attr/val" prop/key="prop/val"></divtest>""" |
| 110 | + mkTest "\\" """<divtest attr\key="attr\val" prop\key="prop\val"></divtest>""" |
| 111 | + mkTest "`" """<divtest attr`key="attr`val" prop`key="prop`val"></divtest>""" |
| 112 | + mkTest "?" """<divtest attr?key="attr?val" prop?key="prop?val"></divtest>""" |
| 113 | + mkTest "!" """<divtest attr!key="attr!val" prop!key="prop!val"></divtest>""" |
| 114 | + mkTest "@" """<divtest attr@key="attr@val" prop@key="prop@val"></divtest>""" |
| 115 | + mkTest "#" """<divtest attr#key="attr#val" prop#key="prop#val"></divtest>""" |
| 116 | + mkTest "$" """<divtest attr$key="attr$val" prop$key="prop$val"></divtest>""" |
0 commit comments