blaze-html 0.4.3.2 → 0.4.3.3
raw patch · 6 files changed
+347/−8 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- blaze-html.cabal +7/−1
- src/Text/Blaze/Internal.hs +0/−7
- tests/Text/Blaze/Tests.hs +178/−0
- tests/Text/Blaze/Tests/Cases.hs +107/−0
- tests/Text/Blaze/Tests/Util.hs +31/−0
- tests/Util/Tests.hs +24/−0
blaze-html.cabal view
@@ -1,5 +1,5 @@ Name: blaze-html-Version: 0.4.3.2+Version: 0.4.3.3 Homepage: http://jaspervdj.be/blaze Bug-Reports: http://github.com/jaspervdj/blaze-html/issues License: BSD3@@ -59,6 +59,12 @@ Hs-source-dirs: src tests Main-is: TestSuite.hs Ghc-options: -Wall++ Other-modules:+ Text.Blaze.Tests+ Text.Blaze.Tests.Cases+ Text.Blaze.Tests.Util+ Util.Tests Build-depends: HUnit >= 1.2 && < 1.3,
src/Text/Blaze/Internal.hs view
@@ -173,13 +173,6 @@ newtype Attribute = Attribute (forall a. HtmlM a -> HtmlM a) instance Monoid Attribute where- -- Usually function composition does not form a monoid because we can't- -- prove that- --- -- > f . (g . h) == (f . g) . h- --- -- but since the functions in 'Attribute' can only apply attributes, this- -- is not a problem here. mempty = Attribute id Attribute f `mappend` Attribute g = Attribute (g . f)
+ tests/Text/Blaze/Tests.hs view
@@ -0,0 +1,178 @@+{-# LANGUAGE OverloadedStrings, TypeSynonymInstances, FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Text.Blaze.Tests+ ( tests+ ) where++import Prelude hiding (div, id)+import Data.Monoid (mempty)+import Control.Monad (replicateM)+import Control.Applicative ((<$>))+import Data.Word (Word8)+import Data.Char (ord)+import Data.List (isInfixOf)++import qualified Data.ByteString as SB+import qualified Data.ByteString.Lazy.Char8 as LBC+import qualified Data.ByteString.Lazy as LB+import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck++import Text.Blaze.Html5 hiding (map)+import Text.Blaze.Html5.Attributes (id, class_, name)+import Text.Blaze.Internal+import Text.Blaze.Tests.Util++tests :: [Test]+tests = [ testProperty "left identity Monoid law" monoidLeftIdentity+ , testProperty "right identity Monoid law" monoidRightIdentity+ , testProperty "associativity Monoid law" monoidAssociativity+ , testProperty "mconcat Monoid law" monoidConcat+ , testProperty "post escaping characters" postEscapingCharacters+ , testProperty "valid UTF-8" isValidUtf8+ , testProperty "external </ sequence" externalEndSequence+ , testProperty "well nested <>" wellNestedBrackets+ , testProperty "unsafeByteString id" unsafeByteStringId+ ]++-- | The left identity Monoid law.+--+monoidLeftIdentity :: Html -> Bool+monoidLeftIdentity h = (return () >> h) == h++-- | The right identity Monoid law.+--+monoidRightIdentity :: Html -> Bool+monoidRightIdentity h = (h >> return ()) == h++-- | The associativity Monoid law.+--+monoidAssociativity :: Html -> Html -> Html -> Bool+monoidAssociativity x y z = (x >> (y >> z)) == ((x >> y) >> z)++-- | Concatenation Monoid law.+--+monoidConcat :: [Html] -> Bool+monoidConcat xs = sequence_ xs == foldr (>>) (return ()) xs++-- | Escaped content cannot contain certain characters.+--+postEscapingCharacters :: String -> Bool+postEscapingCharacters str =+ LB.all (`notElem` forbidden) $ renderUsingUtf8 (string str)+ where+ forbidden = map (fromIntegral . ord) "\"'<>"++-- | Check if the produced bytes are valid UTF-8+--+isValidUtf8 :: Html -> Bool+isValidUtf8 = isValidUtf8' . LB.unpack . renderUsingUtf8+ where+ isIn x y z = (x <= z) && (z <= y)+ isValidUtf8' :: [Word8] -> Bool+ isValidUtf8' [] = True+ isValidUtf8' (x:t)+ -- One byte+ | isIn 0x00 0x7f x = isValidUtf8' t+ -- Two bytes+ | isIn 0xc0 0xdf x = case t of+ (y:t') -> isIn 0x80 0xbf y && isValidUtf8' t'+ _ -> False+ -- Three bytes+ | isIn 0xe0 0xef x = case t of+ (y:z:t') -> all (isIn 0x80 0xbf) [y, z] && isValidUtf8' t'+ _ -> False+ -- Four bytes+ | isIn 0xf0 0xf7 x = case t of+ (y:z:u:t') -> all (isIn 0x80 0xbf) [y, z, u] && isValidUtf8' t'+ _ -> False+ | otherwise = False++-- | Rendering an unsafe bytestring should not do anything+--+unsafeByteStringId :: [Word8] -> Bool+unsafeByteStringId ws =+ LB.pack ws == renderUsingUtf8 (unsafeByteString $ SB.pack ws)++-- | Check if the "</" sequence does not appear in @<script>@ or @<style>@ tags.+--+externalEndSequence :: String -> Bool+externalEndSequence = not . isInfixOf "</" . LBC.unpack+ . renderUsingUtf8 . external . string++-- | Check that the "<>" characters are well-nested.+--+wellNestedBrackets :: Html -> Bool+wellNestedBrackets = wellNested False . LBC.unpack . renderUsingUtf8+ where+ wellNested isOpen [] = not isOpen+ wellNested isOpen (x:xs) = case x of+ '<' -> if isOpen then False else wellNested True xs+ '>' -> if isOpen then wellNested False xs else False+ _ -> wellNested isOpen xs++-- Show instance for the HTML type, so we can debug.+--+instance Show Html where+ show = show . renderUsingUtf8++-- Eq instance for the HTML type, so we can compare the results.+--+instance Eq Html where+ x == y = renderUsingString x == renderUsingString y+ && renderUsingText x == renderUsingText y+ && renderUsingUtf8 x == renderUsingUtf8 y+ -- Some cross-checks+ && renderUsingString x == renderUsingText y+ && renderUsingText x == renderUsingUtf8 y++-- Arbitrary instance for the HTML type.+--+instance Arbitrary Html where+ arbitrary = arbitraryHtml 4++-- | Auxiliary function for the arbitrary instance of the HTML type, used+-- to limit the depth and size of the type.+--+arbitraryHtml :: Int -- ^ Maximum depth.+ -> Gen Html -- ^ Resulting arbitrary HTML snippet.+arbitraryHtml depth = do + -- Choose the size (width) of this element.+ size <- choose (0, 3)++ -- Generate `size` new HTML snippets.+ children <- replicateM size arbitraryChild++ -- Return a concatenation of these children.+ return $ sequence_ children+ where+ -- Generate an arbitrary child. Do not take a parent when we have no depth+ -- left, obviously.+ arbitraryChild = do+ child <- oneof $ [arbitraryLeaf, arbitraryString, return mempty]+ ++ [arbitraryParent | depth > 0]++ -- Generate some attributes for the child.+ size <- choose (0, 4)+ attributes <- replicateM size arbitraryAttribute+ return $ foldl (!) child attributes++ -- Generate an arbitrary parent element.+ arbitraryParent = do+ parent <- elements [p, div, table]+ parent <$> arbitraryHtml (depth - 1)++ -- Generate an arbitrary leaf element.+ arbitraryLeaf = oneof $ map return [img, br, area]++ -- Generate arbitrary string element.+ arbitraryString = do+ s <- arbitrary+ return $ string s++ -- Generate an arbitrary HTML attribute.+ arbitraryAttribute = do+ attr <- elements [id, class_, name]+ value <- arbitrary+ return $ attr $ stringValue value
+ tests/Text/Blaze/Tests/Cases.hs view
@@ -0,0 +1,107 @@+-- | A whole bunch of simple test cases+--+{-# LANGUAGE OverloadedStrings #-}+module Text.Blaze.Tests.Cases+ ( tests+ ) where++import Prelude hiding (div, id)+import Control.Monad (forM_)+import Data.Monoid (mempty, mappend, mconcat)++import Data.Text (Text)+import Test.HUnit ((@=?))+import Test.Framework.Providers.HUnit (testCase)+import Test.Framework (Test)+import qualified Data.ByteString.Lazy.Char8 as LBC++import Text.Blaze+import Text.Blaze.Html5 hiding (map)+import qualified Text.Blaze.Html5 as H+import Text.Blaze.Html5.Attributes+import Text.Blaze.Tests.Util++-- | Type for a simple HTML test. This data type contains the expected output+-- and the HTML template.+--+data HtmlTest = HtmlTest LBC.ByteString Html++-- | Create tests from an HTML test+--+makeTests :: String -> HtmlTest -> [Test]+makeTests baseName (HtmlTest expected h) =+ [ testCase (baseName ++ " (String)") $ expected @=? renderUsingString h+ , testCase (baseName ++ " (Text)") $ expected @=? renderUsingText h+ , testCase (baseName ++ " (Utf8)") $ expected @=? renderUsingUtf8 h+ ]++-- | Actual tests+--+tests :: [Test]+tests = concatMap (uncurry makeTests) $ zip names+ -- Simple cases+ [ HtmlTest "<div id=\"foo\"><p>banana</p><span>banana</span></div>" $+ div ! id "foo" $ do+ p "banana"+ H.span "banana"++ , HtmlTest "<img src=\"foo.png\" alt=\"bar\">" $+ img ! src "foo.png" ! alt "bar"++ -- Escaping cases+ , HtmlTest ""&"" "\"&\""++ , HtmlTest "<img>" $ toHtml ("<img>" :: Text)++ , HtmlTest ""'"" "\"'\""++ , HtmlTest "<img src=\"&\">" $ img ! src "&"++ -- Pre-escaping cases+ , HtmlTest "<3 Haskell" $ preEscapedText "<3 Haskell"++ , HtmlTest "<script />" $ preEscapedString "<script />"++ , HtmlTest "<p class=\"'&!;\">bad</p>" $+ p ! class_ (preEscapedTextValue "'&!;") $ "bad"++ -- Unicode cases+ , HtmlTest "<span id=\"&\">\206\187</span>" $+ H.span ! id "&" $ "λ"++ , HtmlTest "\226\136\128x. x \226\136\136 A"+ "∀x. x ∈ A"++ , HtmlTest "$6, \226\130\172\&7.01, \194\163\&75"+ "$6, €7.01, £75"++ -- Control cases+ , HtmlTest "<li>4</li><li>5</li><li>6</li>" $+ forM_ [4 :: Int .. 6] (li . toHtml)++ , HtmlTest "<br><img><area>" $+ sequence_ [br, img, area]++ -- Attribute tests+ , HtmlTest "<p data-foo=\"bar\">A paragraph</p>" $+ p ! (dataAttribute "foo" "bar") $ "A paragraph"++ , HtmlTest "<p dojoType=\"select\">A paragraph</p>" $+ p ! (customAttribute "dojoType" "select") $ "A paragraph"++ , HtmlTest "<p>Hello</p>" $ p ! mempty $ "Hello"++ , HtmlTest "<img src=\"foo.png\" alt=\"foo\">" $+ img ! (src "foo.png" `mappend` alt "foo")++ -- ToHtml/ToValue tests+ , HtmlTest "12345678910" $ mconcat $ map toHtml [1 :: Int .. 10]++ , HtmlTest "<img src=\"funny-picture-4.png\">" $+ img ! src ("funny-picture-" `mappend` toValue (4 :: Integer)+ `mappend` ".png")++ , HtmlTest "abcdefghijklmnopqrstuvwxyz" $ forM_ ['a' .. 'z'] toHtml+ ]+ where+ names = map (("Test case " ++) . show) [1 :: Int ..]
+ tests/Text/Blaze/Tests/Util.hs view
@@ -0,0 +1,31 @@+-- | Utility functions for the blaze tests+--+module Text.Blaze.Tests.Util+ ( renderUsingString+ , renderUsingText+ , renderUsingUtf8+ ) where++import Text.Blaze.Html5 hiding (map)+import qualified Data.ByteString.Lazy as LB+import qualified Text.Blaze.Renderer.Utf8 as Utf8 (renderHtml)+import qualified Text.Blaze.Renderer.Text as Text (renderHtml)+import qualified Text.Blaze.Renderer.String as String (renderHtml)+import Blaze.ByteString.Builder as B (toLazyByteString)+import Blaze.ByteString.Builder.Char.Utf8 as B (fromString)+import Data.Text.Lazy.Encoding (encodeUtf8)++-- | Render HTML to an UTF-8 encoded ByteString using the String renderer+--+renderUsingString :: Html -> LB.ByteString+renderUsingString = toLazyByteString . fromString . String.renderHtml++-- | Render HTML to an UTF-8 encoded ByteString using the Text renderer+--+renderUsingText :: Html -> LB.ByteString+renderUsingText = encodeUtf8 . Text.renderHtml++-- | Render HTML to an UTF-8 encoded ByteString using the Utf8 renderer+--+renderUsingUtf8 :: Html -> LB.ByteString+renderUsingUtf8 = Utf8.renderHtml
+ tests/Util/Tests.hs view
@@ -0,0 +1,24 @@+module Util.Tests+ ( tests+ ) where++import Test.Framework+import Test.Framework.Providers.HUnit+import Test.HUnit hiding (Test)++import Util.Sanitize (sanitize)++tests :: [Test]+tests = [ testCase "sanitize case 1" sanitize1+ , testCase "sanitize case 2" sanitize2+ ]++-- | Simple sanitize test case+--+sanitize1 :: Assertion+sanitize1 = "class_" @=? sanitize "CLASS"++-- | Simple sanitize test case+--+sanitize2 :: Assertion+sanitize2 = "httpEquiv" @=? sanitize "http-equiv"