{-# LANGUAGE OverloadedStrings #-} module Text.Markdown.UnlitSpec (main, spec) where import Test.Hspec import Test.QuickCheck import Data.String.Builder import System.Environment import Control.Exception import System.Exit import System.IO.Silently import System.IO import System.IO.Temp (withSystemTempFile) import System.Directory import qualified Control.Exception as E import Text.Markdown.Unlit main :: IO () main = hspec spec withTempFile :: (FilePath -> IO ()) -> IO () withTempFile action = withSystemTempFile "hspec" $ \f h -> do hClose h action f `E.finally` removeFile f spec :: Spec spec = do describe "run" $ do it "prints a usage message" $ do withProgName "foo" $ do (r, Left (ExitFailure 1)) <- hCapture [stderr] (try $ run []) r `shouldBe` "usage: foo [selector] -h label infile outfile\n" it "unlits code marked with .haskell by default (unless it is marked with .ignore as well)" $ do withTempFile $ \infile -> withTempFile $ \outfile -> do writeFile infile . build $ do "~~~ {.haskell}" "some code" "~~~" "~~~ {.haskell .ignore}" "some other code" "~~~" run ["-h", "Foo.lhs", infile, outfile] readFile outfile `shouldReturn` (build $ do "#line 2 \"Foo.lhs\"" "some code" ) it "can be customized" $ do withTempFile $ \infile -> withTempFile $ \outfile -> do writeFile infile . build $ do "~~~ {.foo}" "some code" "" "~~~" "~~~ {.bar}" "some other code" "~~~" run ["bar", "-h", "Foo.lhs", infile, outfile] readFile outfile `shouldReturn` (build $ do "#line 6 \"Foo.lhs\"" "some other code" ) describe "parseSelector" $ do it "parses + as :&:" $ do parseSelector "foo+bar+baz" `shouldBe` Just ("foo" :&: "bar" :&: "baz") it "parses whitespace as :|:" $ do parseSelector "foo bar baz" `shouldBe` Just ("foo" :|: "bar" :|: "baz") it "parses ! as Not" $ do parseSelector "foo+!bar+baz" `shouldBe` Just ("foo" :&: Not "bar" :&: "baz") it "can handle a combination of :&: and :|:" $ do parseSelector "foo+bar baz+bar" `shouldBe` Just ("foo" :&: "bar" :|: "baz" :&: "bar") it "is total" $ do property $ \xs -> parseSelector xs `seq` True describe "unlit" $ do it "can be used to unlit everything with a specified class" $ do unlit "Foo.lhs" "foo" . build $ do "~~~ {.foo}" "foo" "~~~" "~~~ {.bar}" "bar" "~~~" `shouldBe` (build $ do "#line 2 \"Foo.lhs\"" "foo" ) it "can handle Not" $ do unlit "Foo.lhs" (Not "foo") . build $ do "~~~ {.foo}" "1" "~~~" "~~~ {.bar}" "2" "~~~" `shouldBe` (build $ do "#line 5 \"Foo.lhs\"" "2" ) it "can handle :&:" $ do unlit "Foo.lhs" ("foo" :&: "bar") . build $ do "~~~ {.foo}" "some code" "~~~" "~~~ {.foo .bar}" "some other code" "~~~" `shouldBe` (build $ do "#line 5 \"Foo.lhs\"" "some other code" ) it "can handle :|:" $ do unlit "Foo.lhs" ("foo" :|: "bar") . build $ do "~~~ {.foo}" "foo" "~~~" "~~~ {.bar}" "bar" "~~~" `shouldBe` (build $ do "#line 2 \"Foo.lhs\"" "foo" "#line 5 \"Foo.lhs\"" "bar" ) it "can handle a combination of :&: and :|:" $ do unlit "Foo.lhs" ("foo" :&: "bar" :|: "foo" :&: "baz") . build $ do "~~~ {.foo .bar}" "one" "~~~" "~~~ {.foo .baz}" "two" "~~~" "~~~ {.bar .baz}" "two" "~~~" `shouldBe` (build $ do "#line 2 \"Foo.lhs\"" "one" "#line 5 \"Foo.lhs\"" "two" ) it "can handle a combination of :&: and Not" $ do unlit "Foo.lhs" ("foo" :&: Not "bar" :&: "baz") . build $ do "~~~ {.foo}" "1" "~~~" "~~~ {.foo .bar}" "2" "~~~" "~~~ {.foo .baz}" "3" "~~~" "~~~ {.foo .bar .baz}" "4" "~~~" `shouldBe` (build $ do "#line 8 \"Foo.lhs\"" "3" ) describe "parse" $ do it "yields an empty list on empty input" $ do parse "" `shouldBe` [] it "parses a code block" $ do map codeBlockContent . parse . build $ do "some text" "~~~" "some" "code" "~~~" "some other text" `shouldBe` [["some", "code"]] it "accepts backticks as fence" $ do parse . build $ do "``` {.haskell .ignore}" "some" "code" "```" `shouldBe` [CodeBlock ["haskell", "ignore"] ["some", "code"] 2] it "parses an indented code block" $ do map codeBlockContent . parse . build $ do "1. some text" " ~~~" " some" " code" " ~~~" "2. some other text" `shouldBe` [["some", " code"]] it "parses an empty code block" $ do map codeBlockContent . parse . build $ do "some text" "~~~" "~~~" "some other text" `shouldBe` [[]] it "attaches classes to code blocks" $ do map codeBlockClasses . parse . build $ do "~~~ {.haskell .literate}" "some code" "~~~" `shouldBe` [["haskell", "literate"]] it "attaches classes to indented code blocks" $ do map codeBlockClasses . parse . build $ do "1. some text" " ~~~ {.haskell .literate}" " some code" " ~~~" "2. some other text" `shouldBe` [["haskell", "literate"]] it "attaches source locations to code blocks" $ do map codeBlockStartLine . parse . build $ do "some text" "" "~~~" "some" "code" "~~~" "some other text" `shouldBe` [4] describe "parseClasses" $ do it "drops a leading dot" $ do parseClasses "~~~ {.foo .bar}" `shouldBe` ["foo", "bar"] it "treats dots as whitespace" $ do parseClasses "~~~ {foo.bar. ..}" `shouldBe` ["foo", "bar"] context "without {...}" $ do it "accepts single class" $ do parseClasses "```haskell" `shouldBe` ["haskell"] it "accepts multiple classes" $ do parseClasses "```.haskell .ignore" `shouldBe` ["haskell", "ignore"]