content: {-# LANGUAGE OverloadedStrings #-}
  {- |
     Module      : Tests.Readers.Txt2Tags
     Copyright   : © 2014-2024 John MacFarlane,
                   © 2014 Matthew Pickering
     License     : GNU GPL, version 2 or above
  
     Maintainer  : John MacFarlane <jgm@berkeley.edu>
     Stability   : alpha
     Portability : portable
  
  Tests for the Txt2Tags reader.
  -}
  module Tests.Readers.Txt2Tags (tests) where
  
  import Data.List (intersperse)
  import Data.Text (Text)
  import qualified Data.Text as T
  import Test.Tasty
  import Test.Tasty.HUnit (HasCallStack)
  import Tests.Helpers
  import Text.Pandoc
  import Text.Pandoc.Arbitrary ()
  import Text.Pandoc.Builder
  
  t2t :: Text -> Pandoc
  -- t2t = handleError . readTxt2Tags (T2TMeta "date" "mtime" "in" "out") def
  t2t = purely $ \s -> do
    setInputFiles ["in"]
    setOutputFile (Just "out")
    readTxt2Tags def s
  
  infix 4 =:
  (=:) :: (ToString c, HasCallStack)
       => String -> (Text, c) -> TestTree
  (=:) = test t2t
  
  spcSep :: [Inlines] -> Inlines
  spcSep = mconcat . intersperse space
  
  simpleTable' :: Int
               -> [Blocks]
               -> [[Blocks]]
               -> Blocks
  simpleTable' n = simpleTable'' $ replicate n (AlignCenter, ColWidthDefault)
  
  simpleTable'' :: [ColSpec] -> [Blocks] -> [[Blocks]] -> Blocks
  simpleTable'' spec headers rows
    = table emptyCaption
            spec
            (TableHead nullAttr $ toHeaderRow headers)
            [TableBody nullAttr 0 [] $ map toRow rows]
            (TableFoot nullAttr [])
    where
      toRow = Row nullAttr . map simpleCell
      toHeaderRow l = [toRow l | not (null l)]
  
  tests :: [TestTree]
  tests =
    [ testGroup "Inlines"
        [ "Plain String" =:
            "Hello, World" =?>
            para (spcSep [ "Hello,", "World" ])
  
        , "Emphasis" =:
            "//Planet Punk//" =?>
            para (emph . spcSep $ ["Planet", "Punk"])
  
        , "Strong" =:
            "**Cider**" =?>
            para (strong "Cider")
  
        , "Strong Emphasis" =:
            "//**strength**//" =?>
            para (emph . strong $ "strength")
  
        , "Strikeout" =:
            "--Kill Bill--" =?>
            para (strikeout . spcSep $ [ "Kill", "Bill" ])
  
        , "Verbatim" =:
            "``Robot.rock()``" =?>
            para (code "Robot.rock()")
  
        , "Symbol" =:
            "A * symbol" =?>
            para (str "A" <> space <> str "*" <> space <> "symbol")
  
        , "No empty markup" =:
            "//// **** ____ ---- ```` \"\"\"\" ''''" =?>
            para (spcSep [ "////", "****", "____", "----", "````", "\"\"\"\"", "''''" ])
  
        , "Inline markup is greedy" =:
            "***** ///// _____ ----- ````` \"\"\"\"\" '''''" =?>
            para (spcSep [strong "*", emph "/", underline "_"
                         , strikeout "-", code "`", text "\""
                         , rawInline "html" "'"])
        , "Markup must be greedy" =:
            "**********    //////////    __________    ----------    ``````````   \"\"\"\"\"\"\"\"\"\"   ''''''''''" =?>
                        para (spcSep [strong "******", emph "//////", underline "______"
                         , strikeout "------", code "``````", text "\"\"\"\"\"\""
                         , rawInline "html" "''''''"])
        , "Inlines must be glued" =:
            "** a** **a ** ** a **" =?>
            para (text "** a** **a ** ** a **")
  
        , "Macros: Date" =:
            "%%date" =?>
              para "1970-01-01"
        , "Macros: Mod Time" =:
            "%%mtime" =?>
              para (str "")
        , "Macros: Infile" =:
            "%%infile" =?>
              para "in"
        , "Macros: Outfile" =:
            "%%outfile" =?>
              para "out"
        , "Autolink" =:
            "http://www.google.com" =?>
              para (link "http://www.google.com" "" (str "http://www.google.com"))
        , "JPEG Image" =:
            "[image.jpg]" =?>
              para (image "image.jpg" "" mempty)
        , "PNG Image" =:
            "[image.png]" =?>
              para (image "image.png" "" mempty)
  
        , "Link" =:
            "[title http://google.com]" =?>
              para (link "http://google.com" "" (str "title"))
  
        , "Image link" =:
            "[[image.jpg] abc]" =?>
              para (link "abc" "" (image "image.jpg" "" mempty))
        , "Invalid link: No trailing space" =:
            "[title invalid ]" =?>
              para (text "[title invalid ]")
  
  
        ]
  
    , testGroup "Basic Blocks"
        ["Paragraph, lines grouped together" =:
            "A paragraph\n A blank line ends the \n current paragraph\n"
              =?> para "A paragraph\n A blank line ends the\n current paragraph"
        , "Paragraph, ignore leading and trailing spaces" =:
            "   Leading and trailing spaces are ignored.   \n" =?>
              para "Leading and trailing spaces are ignored."
        , "Comment line in paragraph" =:
            "A comment line can be placed inside a paragraph.\n% this comment will be ignored \nIt will not affect it.\n"
            =?> para "A comment line can be placed inside a paragraph.\nIt will not affect it."
        , "Paragraph" =:
            "Paragraph\n" =?>
            para "Paragraph"
  
        , "First Level Header" =:
            "+ Headline +\n" =?>
            header 1 "Headline"
  
        , "Third Level Header" =:
            "=== Third Level Headline ===\n" =?>
            header 3 ("Third" <> space <>
                      "Level" <> space <>
                      "Headline")
  
        , "Header with label" =:
            "= header =[label]" =?>
              headerWith ("label", [], []) 1 "header"
  
        , "Invalid header, mismatched delimiters" =:
            "== header =" =?>
              para (text "== header =")
  
        , "Invalid header, spaces in label" =:
            "== header ==[ haha ]" =?>
              para (text "== header ==[ haha ]")
  
        , "Invalid header, invalid label character" =:
            "== header ==[lab/el]" =?>
              para (text "== header ==[lab/el]")
        , "Headers not preceded by a blank line" =:
            T.unlines [ "++ eat dinner ++"
                    , "Spaghetti and meatballs tonight."
                    , "== walk dog =="
                    ] =?>
            mconcat [ header 2 ("eat" <> space <> "dinner")
                    , para $ spcSep [ "Spaghetti", "and", "meatballs", "tonight." ]
                    , header 2 ("walk" <> space <> "dog")
                    ]
  
        , "Paragraph starting with an equals" =:
            "=five" =?>
            para "=five"
  
        , "Paragraph containing asterisk at beginning of line" =:
            T.unlines [ "lucky"
                    , "*star"
                    ] =?>
            para ("lucky" <> softbreak <> "*star")
  
        , "Horizontal Rule" =:
            T.unlines [ "before"
                    , T.replicate 20 "-"
                    , T.replicate 20 "="
                    , T.replicate 20 "_"
                    , "after"
                    ] =?>
            mconcat [ para "before"
                    , horizontalRule
                    , horizontalRule
                    , horizontalRule
                    , para "after"
                    ]
  
        , "Comment Block" =:
            T.unlines [ "%%%"
                    , "stuff"
                    , "bla"
                    , "%%%"] =?>
            (mempty::Blocks)
  
  
      ]
  
    , testGroup "Lists"
        [ "Simple Bullet Lists" =:
            ("- Item1\n" <>
             "- Item2\n") =?>
            bulletList [ plain "Item1"
                       , plain "Item2"
                       ]
  
        , "Indented Bullet Lists" =:
            ("   - Item1\n" <>
             "   - Item2\n") =?>
            bulletList [ plain "Item1"
                       , plain "Item2"
                       ]
  
  
  
        , "Nested Bullet Lists" =:
            ("- Discovery\n" <>
             "  + One More Time\n" <>
             "  + Harder, Better, Faster, Stronger\n" <>
             "- Homework\n" <>
             "  + Around the World\n"<>
             "- Human After All\n" <>
             "  + Technologic\n" <>
             "  + Robot Rock\n") =?>
            bulletList [ mconcat
                         [ plain "Discovery"
                         , orderedList [ plain ("One" <> space <>
                                               "More" <> space <>
                                               "Time")
                                      , plain ("Harder," <> space <>
                                               "Better," <> space <>
                                               "Faster," <> space <>
                                               "Stronger")
                                      ]
                         ]
                       , mconcat
                         [ plain "Homework"
                         , orderedList [ plain ("Around" <> space <>
                                               "the" <> space <>
                                               "World")
                                      ]
                         ]
                       , mconcat
                         [ plain ("Human" <> space <> "After" <> space <> "All")
                         , orderedList [ plain "Technologic"
                                      , plain ("Robot" <> space <> "Rock")
                                      ]
                         ]
                       ]
  
        , "Simple Ordered List" =:
            ("+ Item1\n" <>
             "+ Item2\n") =?>
            let listStyle = (1, DefaultStyle, DefaultDelim)
                listStructure = [ plain "Item1"
                                , plain "Item2"
                                ]
            in orderedListWith listStyle listStructure
  
  
        , "Indented Ordered List" =:
            (" + Item1\n" <>
             " + Item2\n") =?>
            let listStyle = (1, DefaultStyle, DefaultDelim)
                listStructure = [ plain "Item1"
                                , plain "Item2"
                                ]
            in orderedListWith listStyle listStructure
  
        , "Nested Ordered Lists" =:
            ("+ One\n" <>
             "   + One-One\n" <>
             "   + One-Two\n" <>
             "+ Two\n" <>
             "   + Two-One\n"<>
             "   + Two-Two\n") =?>
            let listStyle = (1, DefaultStyle, DefaultDelim)
                listStructure = [ mconcat
                                  [ plain "One"
                                  , orderedList [ plain "One-One"
                                                , plain "One-Two"
                                                ]
                                  ]
                                , mconcat
                                  [ plain "Two"
                                  , orderedList [ plain "Two-One"
                                                , plain "Two-Two"
                                                ]
                                  ]
                                ]
            in orderedListWith listStyle listStructure
  
        , "Ordered List in Bullet List" =:
            ("- Emacs\n" <>
             "  + Org\n") =?>
            bulletList [ plain "Emacs" <>
                         orderedList [ plain "Org"]
                       ]
  
        , "Bullet List in Ordered List" =:
            ("+ GNU\n" <>
             "   - Freedom\n") =?>
            orderedList [ plain "GNU" <> bulletList [ plain "Freedom" ] ]
  
        , "Definition List" =:
            T.unlines [ ": PLL"
                    , "  phase-locked loop"
                    , ": TTL"
                    , "  transistor-transistor logic"
                    , ": PSK"
                    , "  a digital"
                    ] =?>
            definitionList [ ("PLL", [ plain $ "phase-locked" <> space <> "loop" ])
                           , ("TTL", [ plain $ "transistor-transistor" <> space <> "logic" ])
                           , ("PSK", [ plain $ "a" <> space <> "digital" ])
                           ]
  
  
        , "Loose bullet list" =:
            T.unlines [ "- apple"
                    , ""
                    , "- orange"
                    , ""
                    , "- peach"
                    ] =?>
            bulletList [ para "apple"
                       , para "orange"
                       , para "peach"
                       ]
        ]
  
    , testGroup "Tables"
        [ "Single cell table" =:
            "| Test " =?>
            simpleTable' 1 mempty [[plain "Test"]]
  
        , "Multi cell table" =:
            "| One | Two |" =?>
             simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ]
  
        , "Multi line table" =:
            T.unlines [ "| One |"
                    , "| Two |"
                    , "| Three |"
                    ] =?>
             simpleTable' 1 mempty
                          [ [ plain "One" ]
                          , [ plain "Two" ]
                          , [ plain "Three" ]
                          ]
  
        , "Empty table" =:
            "| |" =?>
            simpleTable' 1 mempty [[mempty]]
  
        , "Glider Table" =:
            T.unlines [ "| 1 | 0 | 0 |"
                    , "| 0 | 1 | 1 |"
                    , "| 1 | 1 | 0 |"
                    ] =?>
            simpleTable' 3 mempty
                         [ [ plain "1", plain "0", plain "0" ]
                         , [ plain "0", plain "1", plain "1" ]
                         , [ plain "1", plain "1", plain "0" ]
                         ]
  
  
        , "Table with Header" =:
            T.unlines [ "|| Species     | Status       |"
                    , "| cervisiae    | domesticated |"
                    , "| paradoxus | wild         |"
                    ] =?>
            simpleTable [ plain "Species", plain "Status" ]
                        [ [ plain "cervisiae", plain "domesticated" ]
                        , [ plain "paradoxus", plain "wild" ]
                        ]
  
        , "Table alignment determined by spacing" =:
            T.unlines [ "| Numbers |     Text | More    |"
                    , "| 1 |    One  |    foo  |"
                    , "| 2 |    Two  | bar  |"
                    ] =?>
            simpleTable''
              (zip
                [AlignCenter, AlignRight, AlignDefault]
                [ColWidthDefault, ColWidthDefault, ColWidthDefault])
              []
              [ [ plain "Numbers", plain "Text", plain "More" ]
              , [ plain "1"      , plain "One" , plain "foo"  ]
              , [ plain "2"      , plain "Two" , plain "bar"  ]
              ]
  
        , "Pipe within text doesn't start a table" =:
            "Ceci n'est pas une | pipe " =?>
            para (spcSep [ "Ceci", "n'est", "pas", "une", "|", "pipe" ])
  
  
        , "Table with differing row lengths" =:
            T.unlines [ "|| Numbers | Text "
                    , "| 1 | One  | foo  |"
                    , "| 2 "
                    ] =?>
            simpleTable''
              (zip
                [AlignCenter, AlignLeft, AlignLeft]
                [ColWidthDefault, ColWidthDefault, ColWidthDefault])
              [ plain "Numbers", plain "Text" , plain mempty ]
              [ [ plain "1"      , plain "One"  , plain "foo"  ]
              , [ plain "2"      , plain mempty , plain mempty  ]
              ]
  
        ]
  
      , testGroup "Blocks and fragments"
        [ "Source block" =:
             T.unlines [ "```"
                     , "main = putStrLn greeting"
                     , "  where greeting = \"moin\""
                     , "```" ] =?>
             let code' = "main = putStrLn greeting\n" <>
                         "  where greeting = \"moin\"\n"
             in codeBlock code'
  
        , "tagged block" =:
             T.unlines [ "'''"
                     , "<aside>HTML5 is pretty nice.</aside>"
                     , "'''"
                     ] =?>
             rawBlock "html" "<aside>HTML5 is pretty nice.</aside>\n"
  
        , "Quote block" =:
             T.unlines ["\t//Niemand// hat die Absicht, eine Mauer zu errichten!"
                     ] =?>
             blockQuote (para (spcSep [ emph "Niemand", "hat", "die", "Absicht,"
                                      , "eine", "Mauer", "zu", "errichten!"
                                      ]))
  
        ]
    ]
  