content: {-# LANGUAGE OverloadedStrings #-}
  {- |
     Module      : Tests.Readers.Org.Directive
     Copyright   : © 2014-2024 Albert Krewinkel
     License     : GNU GPL, version 2 or above
  
     Maintainer  : Albert Krewinkel <albert+pandoc@tarleb.com>
     Stability   : alpha
     Portability : portable
  
  Tests parsing of org directives (like @#+OPTIONS@).
  -}
  module Tests.Readers.Org.Directive (tests) where
  
  import Data.Time (UTCTime (UTCTime), secondsToDiffTime)
  import Data.Time.Calendar (Day (ModifiedJulianDay))
  import Test.Tasty (TestTree, testGroup)
  import Tests.Helpers ((=?>), ToString, purely, test)
  import Tests.Readers.Org.Shared ((=:), tagSpan)
  import Text.Pandoc
  import Text.Pandoc.Builder
  import qualified Data.ByteString as BS
  import qualified Data.Text as T
  
  testWithFiles :: (ToString c)
                => [(FilePath, BS.ByteString)]
                -> String         -- ^ name of test case
                -> (T.Text, c)    -- ^ (input, expected value)
                -> TestTree
  testWithFiles fileDefs = test (orgWithFiles fileDefs)
  
  orgWithFiles :: [(FilePath, BS.ByteString)] -> T.Text -> Pandoc
  orgWithFiles fileDefs input =
    let readOrg' = readOrg def{ readerExtensions = getDefaultExtensions "org" }
    in flip purely input $ \inp -> do
      modifyPureState (\st -> st { stFiles = files fileDefs })
      readOrg' inp
  
  
  files :: [(FilePath, BS.ByteString)] -> FileTree
  files fileDefs =
    let dummyTime = UTCTime (ModifiedJulianDay 125) (secondsToDiffTime 0)
    in foldr (\(fp, bs) -> insertInFileTree fp (FileInfo dummyTime bs))
        mempty fileDefs
  
  tests :: [TestTree]
  tests =
    [ testGroup "export options"
      [ "disable simple sub/superscript syntax" =:
          T.unlines [ "#+OPTIONS: ^:nil"
                    , "a^b"
                    ] =?>
          para "a^b"
  
      , "directly select drawers to be exported" =:
          T.unlines [ "#+OPTIONS: d:(\"IMPORTANT\")"
                    , ":IMPORTANT:"
                    , "23"
                    , ":END:"
                    , ":BORING:"
                    , "very boring"
                    , ":END:"
                    ] =?>
          divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "23")
  
      , "exclude drawers from being exported" =:
          T.unlines [ "#+OPTIONS: d:(not \"BORING\")"
                    , ":IMPORTANT:"
                    , "5"
                    , ":END:"
                    , ":BORING:"
                    , "very boring"
                    , ":END:"
                    ] =?>
          divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "5")
  
      , "don't include archive trees" =:
          T.unlines [ "#+OPTIONS: arch:nil"
                    , "* old  :ARCHIVE:"
                    ] =?>
          (mempty ::Blocks)
  
      , "include complete archive trees" =:
          T.unlines [ "#+OPTIONS: arch:t"
                    , "* old  :ARCHIVE:"
                    , "  boring"
                    ] =?>
          mconcat [ headerWith ("old", [], mempty) 1
                               ("old" <> space <> tagSpan "ARCHIVE")
                  , para "boring"
                  ]
  
      , "include archive tree header only" =:
          T.unlines [ "#+OPTIONS: arch:headline"
                    , "* old  :ARCHIVE:"
                    , "  boring"
                    ] =?>
          headerWith ("old", [], mempty) 1 ("old" <> space <> tagSpan "ARCHIVE")
  
      , "limit headline depth" =:
          T.unlines [ "#+OPTIONS: H:2"
                    , "* top-level section"
                    , "** subsection"
                    , "*** list item 1"
                    , "*** list item 2"
                    ] =?>
          mconcat [ headerWith ("top-level-section", [], [])    1 "top-level section"
                  , headerWith ("subsection", [], []) 2 "subsection"
                  , orderedList [ para "list item 1", para "list item 2" ]
                  ]
  
      , "turn all headlines into lists" =:
          T.unlines [ "#+OPTIONS: H:0"
                    , "first block"
                    , "* top-level section 1"
                    , "** subsection"
                    , "* top-level section 2"
                    ] =?>
          mconcat [ para "first block"
                  , orderedList
                    [ para "top-level section 1" <>
                       orderedList [ para "subsection" ]
                    , para "top-level section 2" ]
                  ]
  
      , "preserve linebreaks as hard breaks" =:
          T.unlines [ "#+OPTIONS: \\n:t"
                    , "first"
                    , "second"
                    ] =?>
          para ("first" <> linebreak <> "second")
  
      , "disable author export" =:
          T.unlines [ "#+OPTIONS: author:nil"
                    , "#+AUTHOR: ShyGuy"
                    ] =?>
          Pandoc nullMeta mempty
  
      , "disable creator export" =:
          T.unlines [ "#+OPTIONS: creator:nil"
                    , "#+creator: The Architect"
                    ] =?>
          Pandoc nullMeta mempty
  
      , "disable email export" =:
          T.unlines [ "#+OPTIONS: email:nil"
                    , "#+email: no-mail-please@example.com"
                    ] =?>
          Pandoc nullMeta mempty
  
      , "disable MathML-like entities" =:
          T.unlines [ "#+OPTIONS: e:nil"
                    , "Icelandic letter: \\thorn"
                    ] =?>
          para "Icelandic letter: \\thorn"
  
      , testGroup "Option f"
        [ "disable inline footnotes" =:
          T.unlines [ "#+OPTIONS: f:nil"
                    , "Funny![fn:funny:or not]"
                    ] =?>
          para "Funny!"
  
        , "disable reference footnotes" =:
          T.unlines [ "#+OPTIONS: f:nil"
                    , "Burn everything[fn:1] down!"
                    , ""
                    , "[fn:2] Not quite everything."
                    ] =?>
          para "Burn everything down!"
        ]
  
      , "disable inclusion of todo keywords" =:
          T.unlines [ "#+OPTIONS: todo:nil"
                    , "** DONE todo export"
                    ] =?>
          headerWith ("todo-export", [], []) 2 "todo export"
  
      , "remove tags from headlines" =:
          T.unlines [ "#+OPTIONS: tags:nil"
                    , "* Headline :hello:world:"
                    ] =?>
          headerWith ("headline", [], mempty) 1 "Headline"
  
      , testGroup "LaTeX"
        [ testGroup "Include LaTeX fragments"
          [ "Inline command" =:
            T.unlines [ "#+OPTIONS: tex:t"
                      , "Hello \\emph{Name}"
                      ] =?>
            para ("Hello" <> space <> rawInline "latex" "\\emph{Name}")
  
          , "Alpha" =:
            T.unlines [ "#+OPTIONS: tex:t"
                      , "\\alpha"
                      ] =?>
            para "α"
  
          , "equation environment" =:
            "#+OPTIONS: tex:t\n\
            \\\begin{equation}\n\
            \f(x) = x^2\n\
            \\\end{equation}"
            =?>
            para (rawInline "latex"
                   "\\begin{equation}\n\
                   \f(x) = x^2\n\
                   \\\end{equation}")
          ]
  
        , testGroup "Ignore LaTeX fragments"
          [ "Inline command" =:
            T.unlines [ "#+OPTIONS: tex:nil"
                      , "Hello \\emph{Emphasised}"
                      ] =?>
            para "Hello"
  
          , "MathML symbol (alpha)" =:
            T.unlines [ "#+OPTIONS: tex:nil"
                      , "\\alpha"
                      ] =?>
            para "α"
  
          , "equation environment" =:
            T.unlines [ "#+OPTIONS: tex:nil"
                      , "\\begin{equation}"
                      , "f(x) = x^2"
                      , "\\end{equation}"
                      ] =?>
            (para mempty)
          ]
  
        , testGroup "Verbatim LaTeX"
          [ "Inline command" =:
            T.unlines [ "#+OPTIONS: tex:verbatim"
                      , "Hello \\emph{Emphasised}"
                      ] =?>
            para "Hello \\emph{Emphasised}"
  
          , "MathML symbol (alpha)" =:
            T.unlines [ "#+OPTIONS: tex:verbatim"
                      , "\\alpha"
                      ] =?>
            para "α"
  
          , "equation environment" =:
            T.unlines [ "#+OPTIONS: tex:verbatim"
                      , "\\begin{equation}"
                      , "f(x) = x^2"
                      , "\\end{equation}"
                      ] =?>
            para (str "\\begin{equation}" <> softbreak <>
                  text "f(x) = x^2" <> softbreak <>
                  str "\\end{equation}")
          ]
        ]
  
      , testGroup "planning information"
        [ "include planning info after headlines" =:
          T.unlines [ "#+OPTIONS: p:t"
                    , "* important"
                    , "  DEADLINE: <2018-10-01 Mon> SCHEDULED: <2018-09-15 Sat>"
                    ] =?>
          mconcat [ headerWith ("important", mempty, mempty) 1 "important"
                  , plain $ strong "DEADLINE:"
                         <> space
                         <> emph (str "<2018-10-01 Mon>")
                         <> space
                         <> strong "SCHEDULED:"
                         <> space
                         <> emph (str "<2018-09-15 Sat>")
                  ]
  
        , "empty planning info is not included" =:
          T.unlines [ "#+OPTIONS: p:t"
                    , "* Wichtig"
                    ] =?>
          headerWith ("wichtig", mempty, mempty) 1 "Wichtig"
        ]
  
      , testGroup "Option |"
        [ "disable export of tables" =:
          T.unlines [ "#+OPTIONS: |:nil"
                    , "| chair |"
                    ] =?>
          (mempty :: Blocks)
        ]
  
      , testGroup "unknown options"
        [ "unknown options are ignored" =:
            T.unlines [ "#+OPTIONS: does-not-exist:t "] =?>
            (mempty :: Pandoc)
  
        , "highlighting after unknown option" =:
            T.unlines [ "#+OPTIONS: nope"
                      , "/yup/"
                      ] =?>
            para (emph "yup")
  
        , "unknown option interleaved with known" =:
            T.unlines [ "#+OPTIONS: tags:nil foo:bar todo:nil"
                      , "* DONE ignore things  :easy:"
                      ] =?>
            headerWith ("ignore-things", [], mempty) 1 "ignore things"
        ]
      ]
  
    , testGroup "Include"
      [ testWithFiles [("./other.org", "content of other file\n")]
        "file inclusion"
        (T.unlines [ "#+include: \"other.org\"" ] =?>
         plain "content of other file")
  
      , testWithFiles [("./world.org", "World\n\n")]
        "Included file belongs to item"
        (T.unlines [ "- Hello,\n  #+include: \"world.org\"" ] =?>
         bulletList [para "Hello," <> para "World"])
  
      , testWithFiles [("./level3.org", "*** Level3\n\n")]
        "Default include preserves level"
        (T.unlines [ "#+include: \"level3.org\"" ] =?>
         headerWith ("level3", [], []) 3 "Level3")
  
      , testWithFiles [("./level3.org", "*** Level3\n\n")]
        "Minlevel shifts level leftward"
        (T.unlines [ "#+include: \"level3.org\" :minlevel 1" ] =?>
         headerWith ("level3", [], []) 1 "Level3")
  
      , testWithFiles [("./level1.org", "* Level1\n\n")]
        "Minlevel shifts level rightward"
        (T.unlines [ "#+include: \"level1.org\" :minlevel 3" ] =?>
         headerWith ("level1", [], []) 3 "Level1")
  
      , testWithFiles [("./src.hs", "putStrLn outString\n")]
        "Include file as source code snippet"
        (T.unlines [ "#+include: \"src.hs\" src haskell" ] =?>
         codeBlockWith ("", ["haskell"], []) "putStrLn outString\n")
  
      , testWithFiles [("./export-latex.org", "\\emph{Hello}\n")]
        "Include file as export snippet"
        (T.unlines [ "#+include: \"export-latex.org\" export latex" ] =?>
         rawBlock "latex" "\\emph{Hello}\n")
  
      , testWithFiles [("./subdir/foo-bar.latex", "foo\n"),
                       ("./hello.lisp", "(print \"Hello!\")\n")
                      ]
        "include directive is limited to one line"
        (T.unlines [ "#+INCLUDE: \"hello.lisp\" src lisp"
                   , "#+include: \"subdir/foo-bar.latex\" export latex"
                   , "bar"
                   ] =?>
         mconcat
           [ codeBlockWith ("", ["lisp"], []) "(print \"Hello!\")\n"
           , rawBlock "latex" "foo\n"
           , para "bar"
           ]
        )
      ]
    ]
  