diff options
Diffstat (limited to 'test')
25 files changed, 3247 insertions, 1864 deletions
| diff --git a/test/Tests/Old.hs b/test/Tests/Old.hs index bbd51ee98..b82251a56 100644 --- a/test/Tests/Old.hs +++ b/test/Tests/Old.hs @@ -162,6 +162,12 @@ tests = [ testGroup "markdown"            [ test "reader" ["-r", "creole", "-w", "native", "-s"]              "creole-reader.txt" "creole-reader.native"            ] +        , testGroup "custom writer" +          [ test "basic" ["-f", "native", "-t", "../data/sample.lua"] +            "testsuite.native" "writer.custom" +          , test "tables" ["-f", "native", "-t", "../data/sample.lua"] +            "tables.native" "tables.custom" +          ]          ]  -- makes sure file is fully closed after reading diff --git a/test/Tests/Readers/Docx.hs b/test/Tests/Readers/Docx.hs index 6d91c36ae..5710a388f 100644 --- a/test/Tests/Readers/Docx.hs +++ b/test/Tests/Readers/Docx.hs @@ -171,6 +171,10 @@ tests = [ testGroup "inlines"              "inline code in subscript and superscript"              "docx/verbatim_subsuper.docx"              "docx/verbatim_subsuper.native" +          , testCompare +            "inlines inside of Structured Document Tags" +            "docx/sdt_elements.docx" +            "docx/sdt_elements.native"            ]          , testGroup "blocks"            [ testCompare diff --git a/test/Tests/Readers/Org.hs b/test/Tests/Readers/Org.hs index dbd56c880..de7f14e32 100644 --- a/test/Tests/Readers/Org.hs +++ b/test/Tests/Readers/Org.hs @@ -1,1871 +1,16 @@  {-# LANGUAGE OverloadedStrings #-}  module Tests.Readers.Org (tests) where -import Data.List (intersperse) -import Data.Text (Text) -import qualified Data.Text as T -import Test.Tasty -import Tests.Helpers -import Text.Pandoc -import Text.Pandoc.Builder -import Text.Pandoc.Shared (underlineSpan) - -org :: Text -> Pandoc -org = purely $ readOrg def{ readerExtensions = getDefaultExtensions "org" } - -orgSmart :: Text -> Pandoc -orgSmart = purely $ readOrg def { readerExtensions = -                     enableExtension Ext_smart $ getDefaultExtensions "org" } - -infix 4 =: -(=:) :: ToString c -     => String -> (Text, c) -> TestTree -(=:) = test org - -spcSep :: [Inlines] -> Inlines -spcSep = mconcat . intersperse space - -simpleTable' :: Int -             -> [Blocks] -             -> [[Blocks]] -             -> Blocks -simpleTable' n = table "" (replicate n (AlignDefault, 0.0)) - --- | Create a span for the given tag. -tagSpan :: String -> Inlines -tagSpan t = spanWith ("", ["tag"], [("tag-name", t)]) . smallcaps $ str t +import Test.Tasty (TestTree, testGroup) +import qualified Tests.Readers.Org.Block as Block +import qualified Tests.Readers.Org.Directive as Directive +import qualified Tests.Readers.Org.Inline as Inline +import qualified Tests.Readers.Org.Meta as Meta  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") - -      , "Emphasized Strong preceded by space" =: -          " */super/*" =?> -          para (strong . emph $ "super") - -      , "Underline" =: -          "_underline_" =?> -          para (underlineSpan $ "underline") - -      , "Strikeout" =: -          "+Kill Bill+" =?> -          para (strikeout . spcSep $ [ "Kill", "Bill" ]) - -      , "Verbatim" =: -          "=Robot.rock()=" =?> -          para (code "Robot.rock()") - -      , "Code" =: -          "~word for word~" =?> -          para (code "word for word") - -      , "Math $..$" =: -          "$E=mc^2$" =?> -           para (math "E=mc^2") - -      , "Math $$..$$" =: -          "$$E=mc^2$$" =?> -          para (displayMath "E=mc^2") - -      , "Math \\[..\\]" =: -          "\\[E=ℎν\\]" =?> -          para (displayMath "E=ℎν") - -      , "Math \\(..\\)" =: -          "\\(σ_x σ_p ≥ \\frac{ℏ}{2}\\)" =?> -          para (math "σ_x σ_p ≥ \\frac{ℏ}{2}") - -      , "Symbol" =: -          "A * symbol" =?> -          para (str "A" <> space <> str "*" <> space <> "symbol") - -      , "Superscript simple expression" =: -          "2^-λ" =?> -          para (str "2" <> superscript "-λ") - -      , "Superscript multi char" =: -          "2^{n-1}" =?> -          para (str "2" <> superscript "n-1") - -      , "Subscript simple expression" =: -          "a_n" =?> -          para (str "a" <> subscript "n") - -      , "Subscript multi char" =: -          "a_{n+1}" =?> -          para (str "a" <> subscript "n+1") - -      , "Linebreak" =: -          "line \\\\ \nbreak" =?> -          para ("line" <> linebreak <> "break") - -      , "Inline note" =: -          "[fn::Schreib mir eine E-Mail]" =?> -          para (note $ para "Schreib mir eine E-Mail") - -      , "Markup-chars not occuring on word break are symbols" =: -          T.unlines [ "this+that+ +so+on" -                    , "seven*eight* nine*" -                    , "+not+funny+" -                    ] =?> -          para ("this+that+ +so+on" <> softbreak <> -                "seven*eight* nine*" <> softbreak <> -                strikeout "not+funny") - -      , "No empty markup" =: -          "// ** __ <> == ~~ $$" =?> -          para (spcSep [ "//", "**", "__", "<>", "==", "~~", "$$" ]) - -      , "Adherence to Org's rules for markup borders" =: -          "/t/& a/ / ./r/ (*l*) /e/! /b/." =?> -          para (spcSep [ emph $ "t/&" <> space <> "a" -                       , "/" -                       , "./r/" -                       , "(" <> strong "l" <> ")" -                       , emph "e" <> "!" -                       , emph "b" <> "." -                       ]) - -      , "Quotes are allowed border chars" =: -          "/'yep/ *sure\"*" =?> -          para (emph "'yep" <> space <> strong "sure\"") - -      , "Spaces are forbidden border chars" =: -          "/nada /" =?> -          para "/nada /" - -      , "Markup should work properly after a blank line" =: -        T.unlines ["foo", "", "/bar/"] =?> -        (para $ text "foo") <> (para $ emph $ text "bar") - -      , "Inline math must stay within three lines" =: -          T.unlines [ "$a", "b", "c$", "$d", "e", "f", "g$" ] =?> -          para ((math "a\nb\nc") <> softbreak <> -                "$d" <> softbreak <> "e" <> softbreak <> -                "f" <> softbreak <> "g$") - -      , "Single-character math" =: -          "$a$ $b$! $c$?" =?> -          para (spcSep [ math "a" -                       , "$b$!" -                       , (math "c") <> "?" -                       ]) - -      , "Markup may not span more than two lines" =: -          "/this *is +totally\nnice+ not*\nemph/" =?> -          para ("/this" <> space <> -                  strong ("is" <> space <> -                          strikeout ("totally" <> -                            softbreak <> "nice") <> -                          space <> "not") <> -                  softbreak <> "emph/") - -      , "Sub- and superscript expressions" =: -         T.unlines [ "a_(a(b)(c)d)" -                   , "e^(f(g)h)" -                   , "i_(jk)l)" -                   , "m^()n" -                   , "o_{p{q{}r}}" -                   , "s^{t{u}v}" -                   , "w_{xy}z}" -                   , "1^{}2" -                   , "3_{{}}" -                   , "4^(a(*b(c*)d))" -                   ] =?> -         para (mconcat $ intersperse softbreak -                      [ "a" <> subscript "(a(b)(c)d)" -                      , "e" <> superscript "(f(g)h)" -                      , "i" <> (subscript "(jk)") <> "l)" -                      , "m" <> (superscript "()") <> "n" -                      , "o" <> subscript "p{q{}r}" -                      , "s" <> superscript "t{u}v" -                      , "w" <> (subscript "xy") <> "z}" -                      , "1" <> (superscript "") <> "2" -                      , "3" <> subscript "{}" -                      , "4" <> superscript ("(a(" <> strong "b(c" <> ")d))") -                      ]) -      , "Verbatim text can contain equal signes (=)" =: -          "=is_subst = True=" =?> -          para (code "is_subst = True") - -      , testGroup "Images" -        [ "Image" =: -            "[[./sunset.jpg]]" =?> -            (para $ image "./sunset.jpg" "" "") - -        , "Image with explicit file: prefix" =: -            "[[file:sunrise.jpg]]" =?> -            (para $ image "sunrise.jpg" "" "") - -        , "Multiple images within a paragraph" =: -            T.unlines [ "[[file:sunrise.jpg]]" -                      , "[[file:sunset.jpg]]" -                      ] =?> -            (para $ (image "sunrise.jpg" "" "") -                 <> softbreak -                 <> (image "sunset.jpg" "" "")) - -        , "Image with html attributes" =: -            T.unlines [ "#+ATTR_HTML: :width 50%" -                      , "[[file:guinea-pig.gif]]" -                      ] =?> -            (para $ imageWith ("", [], [("width", "50%")]) "guinea-pig.gif" "" "") -        ] - -      , "Explicit link" =: -          "[[http://zeitlens.com/][pseudo-random /nonsense/]]" =?> -          (para $ link "http://zeitlens.com/" "" -                       ("pseudo-random" <> space <> emph "nonsense")) - -      , "Self-link" =: -          "[[http://zeitlens.com/]]" =?> -          (para $ link "http://zeitlens.com/" "" "http://zeitlens.com/") - -      , "Absolute file link" =: -          "[[/url][hi]]" =?> -          (para $ link "file:///url" "" "hi") - -      , "Link to file in parent directory" =: -          "[[../file.txt][moin]]" =?> -          (para $ link "../file.txt" "" "moin") - -      , "Empty link (for gitit interop)" =: -          "[[][New Link]]" =?> -          (para $ link "" "" "New Link") - -      , "Image link" =: -          "[[sunset.png][file:dusk.svg]]" =?> -          (para $ link "sunset.png" "" (image "dusk.svg" "" "")) - -      , "Image link with non-image target" =: -          "[[http://example.com][./logo.png]]" =?> -          (para $ link "http://example.com" "" (image "./logo.png" "" "")) - -      , "Plain link" =: -          "Posts on http://zeitlens.com/ can be funny at times." =?> -          (para $ spcSep [ "Posts", "on" -                         , link "http://zeitlens.com/" "" "http://zeitlens.com/" -                         , "can", "be", "funny", "at", "times." -                         ]) - -      , "Angle link" =: -          "Look at <http://moltkeplatz.de> for fnords." =?> -          (para $ spcSep [ "Look", "at" -                         , link "http://moltkeplatz.de" "" "http://moltkeplatz.de" -                         , "for", "fnords." -                         ]) - -      , "Absolute file link" =: -          "[[file:///etc/passwd][passwd]]" =?> -          (para $ link "file:///etc/passwd" "" "passwd") - -      , "File link" =: -          "[[file:target][title]]" =?> -          (para $ link "target" "" "title") - -      , "Anchor" =: -          "<<anchor>> Link here later." =?> -          (para $ spanWith ("anchor", [], []) mempty <> -                  "Link" <> space <> "here" <> space <> "later.") - -      , "Inline code block" =: -          "src_emacs-lisp{(message \"Hello\")}" =?> -          (para $ codeWith ( "" -                           , [ "commonlisp" ] -                           , [ ("org-language", "emacs-lisp") ]) -                           "(message \"Hello\")") - -      , "Inline code block with arguments" =: -          "src_sh[:export both :results output]{echo 'Hello, World'}" =?> -          (para $ codeWith ( "" -                           , [ "bash" ] -                           , [ ("org-language", "sh") -                             , ("export", "both") -                             , ("results", "output") -                             ] -                           ) -                           "echo 'Hello, World'") - -      , "Inline code block with toggle" =: -          "src_sh[:toggle]{echo $HOME}" =?> -          (para $ codeWith ( "" -                           , [ "bash" ] -                           , [ ("org-language", "sh") -                             , ("toggle", "yes") -                             ] -                           ) -                           "echo $HOME") - -      , "Citation" =: -          "[@nonexistent]" =?> -          let citation = Citation -                         { citationId = "nonexistent" -                         , citationPrefix = [] -                         , citationSuffix = [] -                         , citationMode = NormalCitation -                         , citationNoteNum = 0 -                         , citationHash = 0} -          in (para $ cite [citation] "[@nonexistent]") - -      , "Citation containing text" =: -          "[see @item1 p. 34-35]" =?> -          let citation = Citation -                         { citationId = "item1" -                         , citationPrefix = [Str "see"] -                         , citationSuffix = [Space ,Str "p.",Space,Str "34-35"] -                         , citationMode = NormalCitation -                         , citationNoteNum = 0 -                         , citationHash = 0} -          in (para $ cite [citation] "[see @item1 p. 34-35]") - -      , "Org-ref simple citation" =: -        "cite:pandoc" =?> -        let citation = Citation -                       { citationId = "pandoc" -                       , citationPrefix = mempty -                       , citationSuffix = mempty -                       , citationMode = AuthorInText -                       , citationNoteNum = 0 -                       , citationHash = 0 -                       } -        in (para $ cite [citation] "cite:pandoc") - -      , "Org-ref simple citation with underscores" =: -        "cite:pandoc_org_ref" =?> -        let citation = Citation -                       { citationId = "pandoc_org_ref" -                       , citationPrefix = mempty -                       , citationSuffix = mempty -                       , citationMode = AuthorInText -                       , citationNoteNum = 0 -                       , citationHash = 0 -                       } -        in (para $ cite [citation] "cite:pandoc_org_ref") - -      , "Org-ref simple citation succeeded by comma" =: -        "cite:pandoc," =?> -        let citation = Citation -                       { citationId = "pandoc" -                       , citationPrefix = mempty -                       , citationSuffix = mempty -                       , citationMode = AuthorInText -                       , citationNoteNum = 0 -                       , citationHash = 0 -                       } -        in (para $ cite [citation] "cite:pandoc" <> str ",") - -      , "Org-ref simple citation succeeded by dot" =: -        "cite:pandoc." =?> -        let citation = Citation -                       { citationId = "pandoc" -                       , citationPrefix = mempty -                       , citationSuffix = mempty -                       , citationMode = AuthorInText -                       , citationNoteNum = 0 -                       , citationHash = 0 -                       } -        in (para $ cite [citation] "cite:pandoc" <> str ".") - -      , "Org-ref simple citation succeeded by colon" =: -        "cite:pandoc:" =?> -        let citation = Citation -                       { citationId = "pandoc" -                       , citationPrefix = mempty -                       , citationSuffix = mempty -                       , citationMode = AuthorInText -                       , citationNoteNum = 0 -                       , citationHash = 0 -                       } -        in (para $ cite [citation] "cite:pandoc" <> str ":") - -      , "Org-ref simple citep citation" =: -        "citep:pandoc" =?> -        let citation = Citation -                       { citationId = "pandoc" -                       , citationPrefix = mempty -                       , citationSuffix = mempty -                       , citationMode = NormalCitation -                       , citationNoteNum = 0 -                       , citationHash = 0 -                       } -        in (para $ cite [citation] "citep:pandoc") - -      , "Org-ref extended citation" =: -        "[[citep:Dominik201408][See page 20::, for example]]" =?> -        let citation = Citation -                       { citationId = "Dominik201408" -                       , citationPrefix = toList "See page 20" -                       , citationSuffix = toList ", for example" -                       , citationMode = NormalCitation -                       , citationNoteNum = 0 -                       , citationHash = 0 -                       } -        in (para $ cite [citation] "[[citep:Dominik201408][See page 20::, for example]]") - -      , testGroup "Berkeley-style citations" $ -        let pandocCite = Citation -              { citationId = "Pandoc" -              , citationPrefix = mempty -              , citationSuffix = mempty -              , citationMode = NormalCitation -              , citationNoteNum = 0 -              , citationHash = 0 -              } -            pandocInText = pandocCite { citationMode = AuthorInText } -            dominikCite = Citation -              { citationId = "Dominik201408" -              , citationPrefix = mempty -              , citationSuffix = mempty -              , citationMode = NormalCitation -              , citationNoteNum = 0 -              , citationHash = 0 -              } -            dominikInText = dominikCite { citationMode = AuthorInText } -        in [ -            "Berkeley-style in-text citation" =: -              "See @Dominik201408." =?> -                (para $ "See " -                      <> cite [dominikInText] "@Dominik201408" -                      <> ".") - -          , "Berkeley-style parenthetical citation list" =: -              "[(cite): see; @Dominik201408;also @Pandoc; and others]" =?> -              let pandocCite'  = pandocCite { -                                   citationPrefix = toList "also" -                                 , citationSuffix = toList "and others" -                                 } -                  dominikCite' = dominikCite { -                                   citationPrefix = toList "see" -                                 } -              in (para $ cite [dominikCite', pandocCite'] "") - -          , "Berkeley-style plain citation list" =: -              "[cite: See; @Dominik201408; and @Pandoc; and others]" =?> -              let pandocCite' = pandocInText { -                                  citationPrefix = toList "and" -                                } -              in (para $ "See " -                      <> cite [dominikInText] "" -                      <> "," <> space -                      <> cite [pandocCite'] "" -                      <> "," <> space <> "and others") -        ] - -      , "Inline LaTeX symbol" =: -          "\\dots" =?> -          para "…" - -      , "Inline LaTeX command" =: -          "\\textit{Emphasised}" =?> -          para (emph "Emphasised") - -      , "Inline LaTeX command with spaces" =: -          "\\emph{Emphasis mine}" =?> -          para (emph "Emphasis mine") - -      , "Inline LaTeX math symbol" =: -          "\\tau" =?> -          para (emph "τ") - -      , "Unknown inline LaTeX command" =: -          "\\notacommand{foo}" =?> -          para (rawInline "latex" "\\notacommand{foo}") - -      , "Export snippet" =: -          "@@html:<kbd>M-x org-agenda</kbd>@@" =?> -          para (rawInline "html" "<kbd>M-x org-agenda</kbd>") - -      , "MathML symbol in LaTeX-style" =: -          "There is a hackerspace in Lübeck, Germany, called nbsp (unicode symbol: '\\nbsp')." =?> -          para ("There is a hackerspace in Lübeck, Germany, called nbsp (unicode symbol: ' ').") - -      , "MathML symbol in LaTeX-style, including braces" =: -          "\\Aacute{}stor" =?> -          para "Ástor" - -      , "MathML copy sign" =: -          "\\copy" =?> -          para "©" - -      , "MathML symbols, space separated" =: -          "\\ForAll \\Auml" =?> -          para "∀ Ä" - -      , "LaTeX citation" =: -          "\\cite{Coffee}" =?> -          let citation = Citation -                         { citationId = "Coffee" -                         , citationPrefix = [] -                         , citationSuffix = [] -                         , citationMode = NormalCitation -                         , citationNoteNum = 0 -                         , citationHash = 0} -          in (para . cite [citation] $ rawInline "latex" "\\cite{Coffee}") - -      , "Macro" =: -          T.unlines [ "#+MACRO: HELLO /Hello, $1/" -                    , "{{{HELLO(World)}}}" -                    ] =?> -          para (emph "Hello, World") - -      , "Macro repeting its argument" =: -          T.unlines [ "#+MACRO: HELLO $1$1" -                    , "{{{HELLO(moin)}}}" -                    ] =?> -          para "moinmoin" - -      , "Macro called with too few arguments" =: -          T.unlines [ "#+MACRO: HELLO Foo $1 $2 Bar" -                    , "{{{HELLO()}}}" -                    ] =?> -          para "Foo Bar" -      ] - -  , testGroup "Meta Information" $ -      [ "Comment" =: -          "# Nothing to see here" =?> -          (mempty::Blocks) - -      , "Not a comment" =: -          "#-tag" =?> -          para "#-tag" - -      , "Comment surrounded by Text" =: -          T.unlines [ "Before" -                    , "# Comment" -                    , "After" -                    ] =?> -          mconcat [ para "Before" -                  , para "After" -                  ] - -      , "Title" =: -        "#+TITLE: Hello, World" =?> -        let titleInline = toList $ "Hello," <> space <> "World" -            meta = setMeta "title" (MetaInlines titleInline) $ nullMeta -        in Pandoc meta mempty - -      , "Author" =: -        "#+author: Albert /Emacs-Fanboy/ Krewinkel" =?> -        let author = toList . spcSep $ [ "Albert", emph "Emacs-Fanboy", "Krewinkel" ] -            meta = setMeta "author" (MetaList [MetaInlines author]) $ nullMeta -        in Pandoc meta mempty - -      , "Multiple authors" =: -        "#+author: James Dewey Watson, Francis Harry Compton Crick " =?> -        let watson = MetaInlines $ toList "James Dewey Watson" -            crick = MetaInlines $ toList "Francis Harry Compton Crick" -            meta = setMeta "author" (MetaList [watson, crick]) $ nullMeta -        in Pandoc meta mempty - -      , "Date" =: -        "#+Date: Feb. *28*, 2014" =?> -        let date = toList . spcSep $ [ "Feb.", (strong "28") <> ",", "2014" ] -            meta = setMeta "date" (MetaInlines date) $ nullMeta -        in Pandoc meta mempty - -      , "Description" =: -        "#+DESCRIPTION: Explanatory text" =?> -        let description = "Explanatory text" -            meta = setMeta "description" (MetaString description) $ nullMeta -        in Pandoc meta mempty - -      , "Properties drawer" =: -          T.unlines [ "  :PROPERTIES:" -                    , "  :setting: foo" -                    , "  :END:" -                    ] =?> -          (mempty::Blocks) - -      , "LaTeX_headers options are translated to header-includes" =: -          "#+LaTeX_header: \\usepackage{tikz}" =?> -          let latexInlines = rawInline "latex" "\\usepackage{tikz}" -              inclList = MetaList [MetaInlines (toList latexInlines)] -              meta = setMeta "header-includes" inclList nullMeta -          in Pandoc meta mempty - -      , "LaTeX_class option is translated to documentclass" =: -          "#+LATEX_CLASS: article" =?> -          let meta = setMeta "documentclass" (MetaString "article") nullMeta -          in Pandoc meta mempty - -      , "LaTeX_class_options is translated to classoption" =: -          "#+LATEX_CLASS_OPTIONS: [a4paper]" =?> -          let meta = setMeta "classoption" (MetaString "a4paper") nullMeta -          in Pandoc meta mempty - -      , "LaTeX_class_options is translated to classoption" =: -          "#+html_head: <meta/>" =?> -          let html = rawInline "html" "<meta/>" -              inclList = MetaList [MetaInlines (toList html)] -              meta = setMeta "header-includes" inclList nullMeta -          in Pandoc meta mempty - -      , "later meta definitions take precedence" =: -          T.unlines [ "#+AUTHOR: this will not be used" -                    , "#+author: Max" -                    ] =?> -          let author = MetaInlines [Str "Max"] -              meta = setMeta "author" (MetaList [author]) $ nullMeta -          in Pandoc meta mempty - -      , "Logbook drawer" =: -          T.unlines [ "  :LogBook:" -                    , "  - State \"DONE\"       from \"TODO\"       [2014-03-03 Mon 11:00]" -                    , "  :END:" -                    ] =?> -          (mempty::Blocks) - -      , "Drawer surrounded by text" =: -          T.unlines [ "Before" -                    , ":PROPERTIES:" -                    , ":END:" -                    , "After" -                    ] =?> -          para "Before" <> para "After" - -      , "Drawer markers must be the only text in the line" =: -          T.unlines [ "  :LOGBOOK: foo" -                    , "  :END: bar" -                    ] =?> -          para (":LOGBOOK: foo" <> softbreak <> ":END: bar") - -      , "Drawers can be arbitrary" =: -          T.unlines [ ":FOO:" -                    , "/bar/" -                    , ":END:" -                    ] =?> -          divWith (mempty, ["FOO", "drawer"], mempty) (para $ emph "bar") - -      , "Anchor reference" =: -          T.unlines [ "<<link-here>> Target." -                    , "" -                    , "[[link-here][See here!]]" -                    ] =?> -          (para (spanWith ("link-here", [], []) mempty <> "Target.") <> -           para (link "#link-here" "" ("See" <> space <> "here!"))) - -      , "Search links are read as emph" =: -          "[[Wally][Where's Wally?]]" =?> -          (para (emph $ "Where's" <> space <> "Wally?")) - -      , "Link to nonexistent anchor" =: -          T.unlines [ "<<link-here>> Target." -                    , "" -                    , "[[link$here][See here!]]" -                    ] =?> -          (para (spanWith ("link-here", [], []) mempty <> "Target.") <> -           para (emph ("See" <> space <> "here!"))) - -      , "Link abbreviation" =: -          T.unlines [ "#+LINK: wp https://en.wikipedia.org/wiki/%s" -                    , "[[wp:Org_mode][Wikipedia on Org-mode]]" -                    ] =?> -          (para (link "https://en.wikipedia.org/wiki/Org_mode" "" -                      ("Wikipedia" <> space <> "on" <> space <> "Org-mode"))) - -      , "Link abbreviation, defined after first use" =: -          T.unlines [ "[[zl:non-sense][Non-sense articles]]" -                    , "#+LINK: zl http://zeitlens.com/tags/%s.html" -                    ] =?> -          (para (link "http://zeitlens.com/tags/non-sense.html" "" -                      ("Non-sense" <> space <> "articles"))) - -      , "Link abbreviation, URL encoded arguments" =: -          T.unlines [ "#+link: expl http://example.com/%h/foo" -                    , "[[expl:Hello, World!][Moin!]]" -                    ] =?> -          (para (link "http://example.com/Hello%2C%20World%21/foo" "" "Moin!")) - -      , "Link abbreviation, append arguments" =: -          T.unlines [ "#+link: expl http://example.com/" -                    , "[[expl:foo][bar]]" -                    ] =?> -          (para (link "http://example.com/foo" "" "bar")) - - -      , 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 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 "Basic Blocks" $ -      [ "Paragraph" =: -          "Paragraph\n" =?> -          para "Paragraph" - -      , testGroup "headers" $ -        [ "First Level Header" =: -            "* Headline\n" =?> -            headerWith ("headline", [], []) 1 "Headline" - -        , "Third Level Header" =: -            "*** Third Level Headline\n" =?> -            headerWith ("third-level-headline", [], []) -                       3 -                       ("Third" <> space <> "Level" <> space <> "Headline") - -        , "Compact Headers with Paragraph" =: -            T.unlines [ "* First Level" -                      , "** Second Level" -                      , "   Text" -                      ] =?> -            mconcat [ headerWith ("first-level", [], []) -                                 1 -                                 ("First" <> space <> "Level") -                    , headerWith ("second-level", [], []) -                                 2 -                                 ("Second" <> space <> "Level") -                    , para "Text" -                    ] - -        , "Separated Headers with Paragraph" =: -            T.unlines [ "* First Level" -                      , "" -                      , "** Second Level" -                      , "" -                      , "   Text" -                      ] =?> -            mconcat [ headerWith ("first-level", [], []) -                                 1 -                                 ("First" <> space <> "Level") -                    , headerWith ("second-level", [], []) -                                 2 -                                 ("Second" <> space <> "Level") -                    , para "Text" -                    ] - -        , "Headers not preceded by a blank line" =: -            T.unlines [ "** eat dinner" -                      , "Spaghetti and meatballs tonight." -                      , "** walk dog" -                      ] =?> -            mconcat [ headerWith ("eat-dinner", [], []) -                                 2 -                                 ("eat" <> space <> "dinner") -                    , para $ spcSep [ "Spaghetti", "and", "meatballs", "tonight." ] -                    , headerWith ("walk-dog", [], []) -                                 2 -                                 ("walk" <> space <> "dog") -                    ] - -        , testGroup "Todo keywords" -          [ "Header with known todo keyword" =: -              "* TODO header" =?> -              let todoSpan = spanWith ("", ["todo", "TODO"], []) "TODO" -              in headerWith ("header", [], []) 1 (todoSpan <> space <> "header") - -          , "Header marked as done" =: -              "* DONE header" =?> -              let todoSpan = spanWith ("", ["done", "DONE"], []) "DONE" -              in headerWith ("header", [], []) 1 (todoSpan <> space <> "header") - -          , "Header with unknown todo keyword" =: -              "* WAITING header" =?> -              headerWith ("waiting-header", [], []) 1 "WAITING header" - -          , "Custom todo keywords" =: -              T.unlines [ "#+TODO: WAITING CANCELLED" -                        , "* WAITING compile" -                        , "* CANCELLED lunch" -                        ] =?> -              let todoSpan = spanWith ("", ["todo", "WAITING"], []) "WAITING" -                  doneSpan = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" -              in headerWith ("compile", [], []) 1 (todoSpan <> space <> "compile") -              <> headerWith ("lunch", [], []) 1 (doneSpan <> space <> "lunch") - -          , "Custom todo keywords with multiple done-states" =: -              T.unlines [ "#+TODO: WAITING | DONE CANCELLED " -                        , "* WAITING compile" -                        , "* CANCELLED lunch" -                        , "* DONE todo-feature" -                        ] =?> -              let waiting = spanWith ("", ["todo", "WAITING"], []) "WAITING" -                  cancelled = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" -                  done = spanWith ("", ["done", "DONE"], []) "DONE" -              in headerWith ("compile", [], []) 1 (waiting <> space <> "compile") -              <> headerWith ("lunch", [], []) 1 (cancelled <> space <> "lunch") -              <> headerWith ("todo-feature", [], []) 1 (done <> space <> "todo-feature") -          ] - -        , "Tagged headers" =: -            T.unlines [ "* Personal       :PERSONAL:" -                      , "** Call Mom      :@PHONE:" -                      , "** Call John     :@PHONE:JOHN: " -                      ] =?> -            mconcat [ headerWith ("personal", [], []) -                                 1 -                                 ("Personal " <> tagSpan "PERSONAL") -                    , headerWith ("call-mom", [], []) -                                 2 -                                 ("Call Mom " <> tagSpan "@PHONE") -                    , headerWith ("call-john", [], []) -                                 2 -                                 ("Call John " <> tagSpan "@PHONE" <> "\160" <> tagSpan "JOHN") -                    ] - -        , "Untagged header containing colons" =: -            "* This: is not: tagged" =?> -            headerWith ("this-is-not-tagged", [], []) 1 "This: is not: tagged" - -        , "Header starting with strokeout text" =: -            T.unlines [ "foo" -                      , "" -                      , "* +thing+ other thing" -                      ] =?> -            mconcat [ para "foo" -                    , headerWith ("thing-other-thing", [], []) -                                 1 -                                 ((strikeout "thing") <> " other thing") -                    ] - -        , "Comment Trees" =: -            T.unlines [ "* COMMENT A comment tree" -                      , "  Not much going on here" -                      , "** This will be dropped" -                      , "* Comment tree above" -                      ] =?> -            headerWith ("comment-tree-above", [], []) 1 "Comment tree above" - -        , "Nothing but a COMMENT header" =: -            "* COMMENT Test" =?> -            (mempty::Blocks) - -        , "Tree with :noexport:" =: -            T.unlines [ "* Should be ignored :archive:noexport:old:" -                      , "** Old stuff" -                      , "   This is not going to be exported" -                      ] =?> -            (mempty::Blocks) - -        , "Subtree with :noexport:" =: -            T.unlines [ "* Exported" -                      , "** This isn't exported :noexport:" -                      , "*** This neither" -                      , "** But this is" -                      ] =?> -            mconcat [ headerWith ("exported", [], []) 1 "Exported" -                    , headerWith ("but-this-is", [], []) 2 "But this is" -                    ] - -        , "Preferences are treated as header attributes" =: -            T.unlines [ "* foo" -                      , "  :PROPERTIES:" -                      , "  :custom_id: fubar" -                      , "  :bar: baz" -                      , "  :END:" -                      ] =?> -            headerWith ("fubar", [], [("bar", "baz")]) 1 "foo" - - -        , "Headers marked with a unnumbered property get a class of the same name" =: -            T.unlines [ "* Not numbered" -                      , "  :PROPERTIES:" -                      , "  :UNNUMBERED: t" -                      , "  :END:" -                      ] =?> -            headerWith ("not-numbered", ["unnumbered"], []) 1 "Not numbered" -      ] -      , "Paragraph starting with an asterisk" =: -          "*five" =?> -          para "*five" - -      , "Paragraph containing asterisk at beginning of line" =: -          T.unlines [ "lucky" -                    , "*star" -                    ] =?> -          para ("lucky" <> softbreak <> "*star") - -      , "Example block" =: -          T.unlines [ ": echo hello" -                    , ": echo dear tester" -                    ] =?> -          codeBlockWith ("", ["example"], []) "echo hello\necho dear tester\n" - -      , "Example block surrounded by text" =: -          T.unlines [ "Greetings" -                    , ": echo hello" -                    , ": echo dear tester" -                    , "Bye" -                    ] =?> -          mconcat [ para "Greetings" -                  , codeBlockWith ("", ["example"], []) -                                  "echo hello\necho dear tester\n" -                  , para "Bye" -                  ] - -      , "Horizontal Rule" =: -          T.unlines [ "before" -                    , "-----" -                    , "after" -                    ] =?> -          mconcat [ para "before" -                  , horizontalRule -                  , para "after" -                  ] - -      , "Not a Horizontal Rule" =: -          "----- em and en dash" =?> -          para "\8212\8211 em and en dash" - -      , "Comment Block" =: -          T.unlines [ "#+BEGIN_COMMENT" -                    , "stuff" -                    , "bla" -                    , "#+END_COMMENT"] =?> -          (mempty::Blocks) - -      , testGroup "Figures" $ -        [ "Figure" =: -            T.unlines [ "#+caption: A very courageous man." -                      , "#+name: goodguy" -                      , "[[file:edward.jpg]]" -                      ] =?> -            para (image "edward.jpg" "fig:goodguy" "A very courageous man.") - -        , "Figure with no name" =: -            T.unlines [ "#+caption: I've been through the desert on this" -                      , "[[file:horse.png]]" -                      ] =?> -            para (image "horse.png" "fig:" "I've been through the desert on this") - -        , "Figure with `fig:` prefix in name" =: -            T.unlines [ "#+caption: Used as a metapher in evolutionary biology." -                      , "#+name: fig:redqueen" -                      , "[[./the-red-queen.jpg]]" -                      ] =?> -            para (image "./the-red-queen.jpg" "fig:redqueen" -                        "Used as a metapher in evolutionary biology.") - -        , "Figure with HTML attributes" =: -            T.unlines [ "#+CAPTION: mah brain just explodid" -                      , "#+NAME: lambdacat" -                      , "#+ATTR_HTML: :style color: blue :role button" -                      , "[[file:lambdacat.jpg]]" -                      ] =?> -            let kv = [("style", "color: blue"), ("role", "button")] -                name = "fig:lambdacat" -                caption = "mah brain just explodid" -            in para (imageWith (mempty, mempty, kv) "lambdacat.jpg" name caption) - -        , "Labelled figure" =: -            T.unlines [ "#+CAPTION: My figure" -                      , "#+LABEL: fig:myfig" -                      , "[[file:blub.png]]" -                      ] =?> -            let attr = ("fig:myfig", mempty, mempty) -            in para (imageWith attr "blub.png" "fig:" "My figure") - -        , "Figure with empty caption" =: -            T.unlines [ "#+CAPTION:" -                      , "[[file:guess.jpg]]" -                      ] =?> -            para (image "guess.jpg" "fig:" "") -        ] - -      , testGroup "Footnotes" -        [ "Footnote" =: -            T.unlines [ "A footnote[1]" -                      , "" -                      , "[1] First paragraph" -                      , "" -                      , "second paragraph" -                      ] =?> -            para (mconcat -                  [ "A", space, "footnote" -                  , note $ mconcat [ para ("First" <> space <> "paragraph") -                                   , para ("second" <> space <> "paragraph") -                                   ] -                  ]) - -        , "Two footnotes" =: -            T.unlines [ "Footnotes[fn:1][fn:2]" -                      , "" -                      , "[fn:1] First note." -                      , "" -                      , "[fn:2] Second note." -                      ] =?> -            para (mconcat -                  [ "Footnotes" -                  , note $ para ("First" <> space <> "note.") -                  , note $ para ("Second" <> space <> "note.") -                  ]) - -        , "Emphasized text before footnote" =: -            T.unlines [ "/text/[fn:1]" -                      , "" -                      , "[fn:1] unicorn" -                      ] =?> -            para (mconcat -                 [ emph "text" -                 , note . para $ "unicorn" -                 ]) - -        , "Footnote that starts with emphasized text" =: -            T.unlines [ "text[fn:1]" -                      , "" -                      , "[fn:1] /emphasized/" -                      ] =?> -            para (mconcat -                 [ "text" -                 , note . para $ emph "emphasized" -                 ]) - -        , "Footnote followed by header" =: -            T.unlines [ "Another note[fn:yay]" -                      , "" -                      , "[fn:yay] This is great!" -                      , "" -                      , "** Headline" -                      ] =?> -            mconcat -            [ para (mconcat -                    [ "Another", space, "note" -                    , note $ para ("This" <> space <> "is" <> space <> "great!") -                    ]) -            , headerWith ("headline", [], []) 2 "Headline" -            ] - -        , "Footnote followed by two blank lines" =: -            T.unlines [ "footnote[fn:blanklines]" -                      , "" -                      , "[fn:blanklines] followed by blank lines" -                      , "" -                      , "" -                      , "next" -                      ] =?> -            mconcat -            [ para ("footnote" <> note (para "followed by blank lines")) -            , para "next" -            ] -        ] -      ] - -  , 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" -                     ] - -      , "Unindented *" =: -          ("- Item1\n" <> -           "* Item2\n") =?> -          bulletList [ plain "Item1" -                     ] <> -          headerWith ("item2", [], []) 1 "Item2" - -      , "Multi-line Bullet Lists" =: -          ("- *Fat\n" <> -           "  Tony*\n" <> -           "- /Sideshow\n" <> -           " Bob/") =?> -          bulletList [ plain $ strong ("Fat" <> softbreak <> "Tony") -                     , plain $ emph ("Sideshow" <> softbreak <> "Bob") -                     ] - -      , "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" -                       , bulletList [ plain ("One" <> space <> -                                             "More" <> space <> -                                             "Time") -                                    , plain ("Harder," <> space <> -                                             "Better," <> space <> -                                             "Faster," <> space <> -                                             "Stronger") -                                    ] -                       ] -                     , mconcat -                       [ plain "Homework" -                       , bulletList [ plain ("Around" <> space <> -                                             "the" <> space <> -                                             "World") -                                    ] -                       ] -                     , mconcat -                       [ plain ("Human" <> space <> "After" <> space <> "All") -                       , bulletList [ plain "Technologic" -                                    , plain ("Robot" <> space <> "Rock") -                                    ] -                       ] -                     ] - -      , "Bullet List with Decreasing Indent" =: -           ("  - Discovery\n\ -            \ - Human After All\n") =?> -           mconcat [ bulletList [ plain "Discovery" ] -                   , bulletList [ plain ("Human" <> space <> "After" <> space <> "All")] -                   ] - -      , "Header follows Bullet List" =: -          ("  - Discovery\n\ -           \  - Human After All\n\ -           \* Homework") =?> -          mconcat [ bulletList [ plain "Discovery" -                               , plain ("Human" <> space <> "After" <> space <> "All") -                               ] -                  , headerWith ("homework", [], []) 1 "Homework" -                  ] - -      , "Bullet List Unindented with trailing Header" =: -          ("- Discovery\n\ -           \- Homework\n\ -           \* NotValidListItem") =?> -          mconcat [ bulletList [ plain "Discovery" -                               , plain "Homework" -                               ] -                  , headerWith ("notvalidlistitem", [], []) 1 "NotValidListItem" -                  ] - -      , "Empty bullet points" =: -          T.unlines [ "-" -                    , "- " -                    ] =?> -          bulletList [ plain "", plain "" ] - -      , "Simple Ordered List" =: -          ("1. Item1\n" <> -           "2. Item2\n") =?> -          let listStyle = (1, DefaultStyle, DefaultDelim) -              listStructure = [ plain "Item1" -                              , plain "Item2" -                              ] -          in orderedListWith listStyle listStructure - -      , "Simple Ordered List with Parens" =: -          ("1) Item1\n" <> -           "2) Item2\n") =?> -          let listStyle = (1, DefaultStyle, DefaultDelim) -              listStructure = [ plain "Item1" -                              , plain "Item2" -                              ] -          in orderedListWith listStyle listStructure - -      , "Indented Ordered List" =: -          (" 1. Item1\n" <> -           " 2. Item2\n") =?> -          let listStyle = (1, DefaultStyle, DefaultDelim) -              listStructure = [ plain "Item1" -                              , plain "Item2" -                              ] -          in orderedListWith listStyle listStructure - -      , "Empty ordered list item" =: -          T.unlines [ "1." -                    , "3. " -                    ] =?> -          orderedList [ plain "", plain "" ] - -      , "Nested Ordered Lists" =: -          ("1. One\n" <> -           "   1. One-One\n" <> -           "   2. One-Two\n" <> -           "2. Two\n" <> -           "   1. Two-One\n"<> -           "   2. 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" <> -           "  1. Org\n") =?> -          bulletList [ (plain "Emacs") <> -                       (orderedList [ plain "Org"]) -                     ] - -      , "Bullet List in Ordered List" =: -          ("1. GNU\n" <> -           "   - Freedom\n") =?> -          orderedList [ (plain "GNU") <> bulletList [ (plain "Freedom") ] ] - -      , "Definition List" =: -          T.unlines [ "- PLL :: phase-locked loop" -                    , "- TTL ::" -                    , "  transistor-transistor logic" -                    , "- PSK :: phase-shift keying" -                    , "" -                    , "  a digital modulation scheme" -                    ] =?> -          definitionList [ ("PLL", [ plain $ "phase-locked" <> space <> "loop" ]) -                         , ("TTL", [ plain $ "transistor-transistor" <> space <> -                                               "logic" ]) -                         , ("PSK", [ mconcat -                                     [ para $ "phase-shift" <> space <> "keying" -                                     , para $ spcSep [ "a", "digital" -                                                     , "modulation", "scheme" ] -                                     ] -                                   ]) -                         ] -      , "Definition list with multi-word term" =: -        " - Elijah Wood :: He plays Frodo" =?> -         definitionList [ ("Elijah" <> space <> "Wood", [plain $ "He" <> space <> "plays" <> space <> "Frodo"])] -      , "Compact definition list" =: -           T.unlines [ "- ATP :: adenosine 5' triphosphate" -                     , "- DNA :: deoxyribonucleic acid" -                     , "- PCR :: polymerase chain reaction" -                     , "" -                     ] =?> -          definitionList -          [ ("ATP", [ plain $ spcSep [ "adenosine", "5'", "triphosphate" ] ]) -          , ("DNA", [ plain $ spcSep [ "deoxyribonucleic", "acid" ] ]) -          , ("PCR", [ plain $ spcSep [ "polymerase", "chain", "reaction" ] ]) -          ] - -      , "Definition List With Trailing Header" =: -          "- definition :: list\n\ -          \- cool :: defs\n\ -          \* header" =?> -          mconcat [ definitionList [ ("definition", [plain "list"]) -                                   , ("cool", [plain "defs"]) -                                   ] -                  , headerWith ("header", [], []) 1 "header" -                  ] - -      , "Definition lists double-colon markers must be surrounded by whitespace" =: -          "- std::cout" =?> -          bulletList [ plain "std::cout" ] - -      , "Loose bullet list" =: -         T.unlines [ "- apple" -                   , "" -                   , "- orange" -                   , "" -                   , "- peach" -                   ] =?> -          bulletList [ para "apple" -                     , para "orange" -                     , para "peach" -                     ] - -      , "Recognize preceding paragraphs in non-list contexts" =: -          T.unlines [ "CLOSED: [2015-10-19 Mon 15:03]" -                    , "- Note taken on [2015-10-19 Mon 13:24]" -                    ] =?> -          mconcat [ para "CLOSED: [2015-10-19 Mon 15:03]" -                  , bulletList [ plain "Note taken on [2015-10-19 Mon 13:24]" ] -                  ] -      ] - -  , 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 between Paragraphs" =: -          T.unlines [ "Before" -                    , "| One | Two |" -                    , "After" -                    ] =?> -          mconcat [ para "Before" -                  , simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] -                  , para "After" -                  ] - -      , "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 with final hline" =: -          T.unlines [ "| cervisiae    | domesticated |" -                    , "| paradoxus    | wild         |" -                    , "|--------------+--------------|" -                    ] =?> -          simpleTable' 2 mempty -                [ [ plain "cervisiae", plain "domesticated" ] -                 , [ plain "paradoxus", plain "wild" ] -                ] - -      , "Table in a box" =: -          T.unlines [ "|---------|---------|" -                    , "| static  | Haskell |" -                    , "| dynamic | Lisp    |" -                    , "|---------+---------|" -                    ] =?> -          simpleTable' 2 mempty -                [ [ plain "static", plain "Haskell" ] -                , [ plain "dynamic", plain "Lisp" ] -                ] - -      , "Table with empty cells" =: -          "|||c|" =?> -          simpleTable' 3 mempty [[mempty, mempty, plain "c"]] - -      , "Table with empty rows" =: -          T.unlines [ "| first  |" -                    , "|        |" -                    , "| third  |" -                    ] =?> -          simpleTable' 1 mempty [[plain "first"], [mempty], [plain "third"]] - -      , "Table with alignment row" =: -          T.unlines [ "| Numbers | Text | More |" -                    , "| <c>     | <r>  |      |" -                    , "| 1       | One  | foo  |" -                    , "| 2       | Two  | bar  |" -                    ] =?> -          table "" (zip [AlignCenter, AlignRight, AlignDefault] [0, 0, 0]) -                [] -                [ [ 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" ]) - -      , "Missing pipe at end of row" =: -          "|incomplete-but-valid" =?> -          simpleTable' 1 mempty [ [ plain "incomplete-but-valid" ] ] - -      , "Table with differing row lengths" =: -          T.unlines [ "| Numbers | Text " -                    , "|-" -                    , "| <c>     | <r>  |" -                    , "| 1       | One  | foo  |" -                    , "| 2" -                    ] =?> -          table "" (zip [AlignCenter, AlignRight] [0, 0]) -                [ plain "Numbers", plain "Text" ] -                [ [ plain "1" , plain "One" , plain "foo" ] -                , [ plain "2" ] -                ] - -      , "Table with caption" =: -          T.unlines [ "#+CAPTION: Hitchhiker's Multiplication Table" -                    , "| x |  6 |" -                    , "| 9 | 42 |" -                    ] =?> -          table "Hitchhiker's Multiplication Table" -                [(AlignDefault, 0), (AlignDefault, 0)] -                [] -                [ [ plain "x", plain "6" ] -                , [ plain "9", plain "42" ] -                ] -      ] - -    , testGroup "Blocks and fragments" -      [ "Source block" =: -           T.unlines [ "  #+BEGIN_SRC haskell" -                     , "  main = putStrLn greeting" -                     , "    where greeting = \"moin\"" -                     , "  #+END_SRC" ] =?> -           let attr' = ("", ["haskell"], []) -               code' = "main = putStrLn greeting\n" <> -                       "  where greeting = \"moin\"\n" -           in codeBlockWith attr' code' - -      , "Source block with indented code" =: -           T.unlines [ "  #+BEGIN_SRC haskell" -                     , "    main = putStrLn greeting" -                     , "      where greeting = \"moin\"" -                     , "  #+END_SRC" ] =?> -           let attr' = ("", ["haskell"], []) -               code' = "main = putStrLn greeting\n" <> -                       "  where greeting = \"moin\"\n" -           in codeBlockWith attr' code' - -      , "Source block with tab-indented code" =: -           T.unlines [ "\t#+BEGIN_SRC haskell" -                     , "\tmain = putStrLn greeting" -                     , "\t  where greeting = \"moin\"" -                     , "\t#+END_SRC" ] =?> -           let attr' = ("", ["haskell"], []) -               code' = "main = putStrLn greeting\n" <> -                       "  where greeting = \"moin\"\n" -           in codeBlockWith attr' code' - -      , "Empty source block" =: -           T.unlines [ "  #+BEGIN_SRC haskell" -                     , "  #+END_SRC" ] =?> -           let attr' = ("", ["haskell"], []) -               code' = "" -           in codeBlockWith attr' code' - -      , "Source block between paragraphs" =: -           T.unlines [ "Low German greeting" -                     , "  #+BEGIN_SRC haskell" -                     , "  main = putStrLn greeting" -                     , "    where greeting = \"Moin!\"" -                     , "  #+END_SRC" ] =?> -           let attr' = ("", ["haskell"], []) -               code' = "main = putStrLn greeting\n" <> -                        "  where greeting = \"Moin!\"\n" -           in mconcat [ para $ spcSep [ "Low", "German", "greeting"  ] -                      , codeBlockWith attr' code' -                      ] -      , "Source block with babel arguments" =: -           T.unlines [ "#+BEGIN_SRC emacs-lisp :exports both" -                     , "(progn (message \"Hello, World!\")" -                     , "       (+ 23 42))" -                     , "#+END_SRC" ] =?> -           let classes = [ "commonlisp" ] -- as kate doesn't know emacs-lisp syntax -               params = [ ("org-language", "emacs-lisp") -                        , ("exports", "both") -                        ] -               code' = unlines [ "(progn (message \"Hello, World!\")" -                               , "       (+ 23 42))" ] -           in codeBlockWith ("", classes, params) code' - -      , "Source block with results and :exports both" =: -           T.unlines [ "#+BEGIN_SRC emacs-lisp :exports both" -                     , "(progn (message \"Hello, World!\")" -                     , "       (+ 23 42))" -                     , "#+END_SRC" -                     , "" -                     , "#+RESULTS:" -                     , ": 65"] =?> -           let classes = [ "commonlisp" ] -               params = [ ("org-language", "emacs-lisp") -                        , ("exports", "both") -                        ] -               code' = unlines [ "(progn (message \"Hello, World!\")" -                               , "       (+ 23 42))" ] -               results' = "65\n" -           in codeBlockWith ("", classes, params) code' -              <> -              codeBlockWith ("", ["example"], []) results' - -      , "Source block with results and :exports code" =: -           T.unlines [ "#+BEGIN_SRC emacs-lisp :exports code" -                     , "(progn (message \"Hello, World!\")" -                     , "       (+ 23 42))" -                     , "#+END_SRC" -                     , "" -                     , "#+RESULTS:" -                     , ": 65" ] =?> -           let classes = [ "commonlisp" ] -               params = [ ("org-language", "emacs-lisp") -                        , ("exports", "code") -                        ] -               code' = unlines [ "(progn (message \"Hello, World!\")" -                               , "       (+ 23 42))" ] -           in codeBlockWith ("", classes, params) code' - -      , "Source block with results and :exports results" =: -           T.unlines [ "#+BEGIN_SRC emacs-lisp :exports results" -                     , "(progn (message \"Hello, World!\")" -                     , "       (+ 23 42))" -                     , "#+END_SRC" -                     , "" -                     , "#+RESULTS:" -                     , ": 65" ] =?> -           let results' = "65\n" -           in codeBlockWith ("", ["example"], []) results' - -      , "Source block with results and :exports none" =: -           T.unlines [ "#+BEGIN_SRC emacs-lisp :exports none" -                     , "(progn (message \"Hello, World!\")" -                     , "       (+ 23 42))" -                     , "#+END_SRC" -                     , "" -                     , "#+RESULTS:" -                     , ": 65" ] =?> -           (mempty :: Blocks) - -      , "Source block with toggling header arguments" =: -        T.unlines [ "#+BEGIN_SRC sh :noeval" -                  , "echo $HOME" -                  , "#+END_SRC" -                  ] =?> -        let classes = [ "bash" ] -            params = [ ("org-language", "sh"), ("noeval", "yes") ] -        in codeBlockWith ("", classes, params) "echo $HOME\n" - -      , "Source block with line number switch" =: -        T.unlines [ "#+BEGIN_SRC sh -n 10" -                  , ":() { :|:& };:" -                  , "#+END_SRC" -                  ] =?> -        let classes = [ "bash", "numberLines" ] -            params = [ ("org-language", "sh"), ("startFrom", "10") ] -        in codeBlockWith ("", classes, params) ":() { :|:& };:\n" - -      , "Source block with multi-word parameter values" =: -        T.unlines [ "#+BEGIN_SRC dot :cmdline -Kdot -Tpng " -                  , "digraph { id [label=\"ID\"] }" -                  , "#+END_SRC" -                  ] =?> -        let classes = [ "dot" ] -            params = [ ("cmdline", "-Kdot -Tpng") ] -        in codeBlockWith ("", classes, params) "digraph { id [label=\"ID\"] }\n" - -      , "Example block" =: -           T.unlines [ "#+begin_example" -                     , "A chosen representation of" -                     , "a rule." -                     , "#+eND_exAMPle" -                     ] =?> -           codeBlockWith ("", ["example"], []) -                         "A chosen representation of\na rule.\n" - -      , "HTML block" =: -           T.unlines [ "#+BEGIN_HTML" -                     , "<aside>HTML5 is pretty nice.</aside>" -                     , "#+END_HTML" -                     ] =?> -           rawBlock "html" "<aside>HTML5 is pretty nice.</aside>\n" - -      , "Quote block" =: -           T.unlines [ "#+BEGIN_QUOTE" -                     , "/Niemand/ hat die Absicht, eine Mauer zu errichten!" -                     , "#+END_QUOTE" -                     ] =?> -           blockQuote (para (spcSep [ emph "Niemand", "hat", "die", "Absicht," -                                    , "eine", "Mauer", "zu", "errichten!" -                                    ])) - -      , "Verse block" =: -          T.unlines [ "The first lines of Goethe's /Faust/:" -                    , "#+begin_verse" -                    , "Habe nun, ach! Philosophie," -                    , "Juristerei und Medizin," -                    , "Und leider auch Theologie!" -                    , "Durchaus studiert, mit heißem Bemühn." -                    , "#+end_verse" -                    ] =?> -          mconcat -          [ para $ spcSep [ "The", "first", "lines", "of" -                          , "Goethe's", emph "Faust" <> ":"] -          , lineBlock -              [ "Habe nun, ach! Philosophie," -              , "Juristerei und Medizin," -              , "Und leider auch Theologie!" -              , "Durchaus studiert, mit heißem Bemühn." -              ] -          ] - -      , "Verse block with blank lines" =: -          T.unlines [ "#+BEGIN_VERSE" -                    , "foo" -                    , "" -                    , "bar" -                    , "#+END_VERSE" -                    ] =?> -          lineBlock [ "foo", mempty, "bar" ] - -      , "Verse block with varying indentation" =: -          T.unlines [ "#+BEGIN_VERSE" -                    , "  hello darkness" -                    , "my old friend" -                    , "#+END_VERSE" -                    ] =?> -          lineBlock [ "\160\160hello darkness", "my old friend" ] - -      , "Raw block LaTeX" =: -          T.unlines [ "#+BEGIN_LaTeX" -                    , "The category $\\cat{Set}$ is adhesive." -                    , "#+END_LaTeX" -                    ] =?> -          rawBlock "latex" "The category $\\cat{Set}$ is adhesive.\n" - -      , "Raw LaTeX line" =: -          "#+LATEX: \\let\\foo\\bar" =?> -          rawBlock "latex" "\\let\\foo\\bar" - -      , "Raw Beamer line" =: -          "#+beamer: \\pause" =?> -          rawBlock "beamer" "\\pause" - -      , "Raw HTML line" =: -          "#+HTML: <aside>not important</aside>" =?> -          rawBlock "html" "<aside>not important</aside>" - -      , "Export block HTML" =: -          T.unlines [ "#+BEGIN_export html" -                    , "<samp>Hello, World!</samp>" -                    , "#+END_export" -                    ] =?> -          rawBlock "html" "<samp>Hello, World!</samp>\n" - -      , "LaTeX fragment" =: -          T.unlines [ "\\begin{equation}" -                    , "X_i = \\begin{cases}" -                    , "      G_{\\alpha(i)} & \\text{if }\\alpha(i-1) = \\alpha(i)\\\\" -                    , "      C_{\\alpha(i)} & \\text{otherwise}" -                    , "      \\end{cases}" -                    , "\\end{equation}" -                    ] =?> -          rawBlock "latex" -                   (unlines [ "\\begin{equation}" -                            , "X_i = \\begin{cases}" -                            , "      G_{\\alpha(i)} & \\text{if }\\alpha(i-1) =" <> -                              " \\alpha(i)\\\\" -                            , "      C_{\\alpha(i)} & \\text{otherwise}" -                            , "      \\end{cases}" -                            , "\\end{equation}" -                            ]) - -      , "Code block with caption" =: -          T.unlines [ "#+CAPTION: Functor laws in Haskell" -                    , "#+NAME: functor-laws" -                    , "#+BEGIN_SRC haskell" -                    , "fmap id = id" -                    , "fmap (p . q) = (fmap p) . (fmap q)" -                    , "#+END_SRC" -                    ] =?> -          divWith -             nullAttr -             (mappend -              (plain $ spanWith ("", ["label"], []) -                                (spcSep [ "Functor", "laws", "in", "Haskell" ])) -              (codeBlockWith ("functor-laws", ["haskell"], []) -                             (unlines [ "fmap id = id" -                                      , "fmap (p . q) = (fmap p) . (fmap q)" -                                      ]))) - -      , "Convert blank lines in blocks to single newlines" =: -          T.unlines [ "#+begin_html" -                    , "" -                    , "<span>boring</span>" -                    , "" -                    , "#+end_html" -                    ] =?> -          rawBlock "html" "\n<span>boring</span>\n\n" - -      , "Accept `ATTR_HTML` attributes for generic block" =: -          T.unlines [ "#+ATTR_HTML: :title hello, world :id test :class fun code" -                    , "#+BEGIN_TEST" -                    , "nonsense" -                    , "#+END_TEST" -                    ] =?> -          let attr = ("test", ["fun", "code", "TEST"], [("title", "hello, world")]) -          in divWith attr (para "nonsense") - -      , "Non-letter chars in source block parameters" =: -          T.unlines [ "#+BEGIN_SRC C :tangle xxxx.c :city Zürich" -                    , "code body" -                    , "#+END_SRC" -                    ] =?> -          let params  = [ ("org-language", "C") -                        , ("tangle", "xxxx.c") -                        , ("city", "Zürich") -                        ] -          in codeBlockWith ( "", ["c"], params) "code body\n" -      ] - -    , testGroup "Smart punctuation" -      [ test orgSmart "quote before ellipses" -        ("'...hi'" -         =?> para (singleQuoted "…hi")) - -      , test orgSmart "apostrophe before emph" -        ("D'oh! A l'/aide/!" -         =?> para ("D’oh! A l’" <> emph "aide" <> "!")) - -      , test orgSmart "apostrophe in French" -        ("À l'arrivée de la guerre, le thème de l'«impossibilité du socialisme»" -         =?> para "À l’arrivée de la guerre, le thème de l’«impossibilité du socialisme»") - -      , test orgSmart "Quotes cannot occur at the end of emphasized text" -        ("/say \"yes\"/" =?> -         para ("/say" <> space <> doubleQuoted "yes" <> "/")) - -      , test orgSmart "Dashes are allowed at the borders of emphasis'" -        ("/foo---/" =?> -         para (emph "foo—")) - -      , test orgSmart "Single quotes can be followed by emphasized text" -        ("Singles on the '/meat market/'" =?> -         para ("Singles on the " <> (singleQuoted $ emph "meat market"))) - -      , test orgSmart "Double quotes can be followed by emphasized text" -        ("Double income, no kids: \"/DINK/\"" =?> -         para ("Double income, no kids: " <> (doubleQuoted $ emph "DINK"))) -      ] +  [ testGroup "Inlines" Inline.tests +  , testGroup "Basic Blocks" Block.tests +  , testGroup "Meta Information" Meta.tests +  , testGroup "Directives" Directive.tests    ] diff --git a/test/Tests/Readers/Org/Block.hs b/test/Tests/Readers/Org/Block.hs new file mode 100644 index 000000000..15dc63554 --- /dev/null +++ b/test/Tests/Readers/Org/Block.hs @@ -0,0 +1,192 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block (tests) where + +import Test.Tasty (TestTree, testGroup) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc.Builder +import qualified Data.Text as T +import qualified Tests.Readers.Org.Block.CodeBlock as CodeBlock +import qualified Tests.Readers.Org.Block.Figure as Figure +import qualified Tests.Readers.Org.Block.Header as Header +import qualified Tests.Readers.Org.Block.List as List +import qualified Tests.Readers.Org.Block.Table as Table + +tests :: [TestTree] +tests = +  [ "Paragraph" =: +      "Paragraph\n" =?> +      para "Paragraph" + +  , "Paragraph starting with an asterisk" =: +      "*five" =?> +      para "*five" + +  , "Paragraph containing asterisk at beginning of line" =: +      T.unlines [ "lucky" +                , "*star" +                ] =?> +      para ("lucky" <> softbreak <> "*star") + +  , "Example block" =: +      T.unlines [ ": echo hello" +                , ": echo dear tester" +                ] =?> +      codeBlockWith ("", ["example"], []) "echo hello\necho dear tester\n" + +  , "Example block surrounded by text" =: +      T.unlines [ "Greetings" +                , ": echo hello" +                , ": echo dear tester" +                , "Bye" +                ] =?> +      mconcat [ para "Greetings" +              , codeBlockWith ("", ["example"], []) +                              "echo hello\necho dear tester\n" +              , para "Bye" +              ] + +  , "Horizontal Rule" =: +      T.unlines [ "before" +                , "-----" +                , "after" +                ] =?> +      mconcat [ para "before" +              , horizontalRule +              , para "after" +              ] + +  , "Not a Horizontal Rule" =: +      "----- em and en dash" =?> +      para "\8212\8211 em and en dash" + +  , "Comment Block" =: +      T.unlines [ "#+BEGIN_COMMENT" +                , "stuff" +                , "bla" +                , "#+END_COMMENT"] =?> +      (mempty::Blocks) + +  , testGroup "Blocks and fragments" +    [ "HTML block" =: +      T.unlines [ "#+BEGIN_HTML" +                , "<aside>HTML5 is pretty nice.</aside>" +                , "#+END_HTML" +                ] =?> +      rawBlock "html" "<aside>HTML5 is pretty nice.</aside>\n" + +    , "Quote block" =: +      T.unlines [ "#+BEGIN_QUOTE" +                , "/Niemand/ hat die Absicht, eine Mauer zu errichten!" +                , "#+END_QUOTE" +                ] =?> +      blockQuote (para (spcSep [ emph "Niemand", "hat", "die", "Absicht," +                               , "eine", "Mauer", "zu", "errichten!" +                               ])) + +    , "Verse block" =: +      T.unlines [ "The first lines of Goethe's /Faust/:" +                , "#+begin_verse" +                , "Habe nun, ach! Philosophie," +                , "Juristerei und Medizin," +                , "Und leider auch Theologie!" +                , "Durchaus studiert, mit heißem Bemühn." +                , "#+end_verse" +                ] =?> +      mconcat +        [ para $ spcSep [ "The", "first", "lines", "of" +                        , "Goethe's", emph "Faust" <> ":"] +        , lineBlock +          [ "Habe nun, ach! Philosophie," +          , "Juristerei und Medizin," +          , "Und leider auch Theologie!" +          , "Durchaus studiert, mit heißem Bemühn." +          ] +        ] + +    , "Verse block with blank lines" =: +      T.unlines [ "#+BEGIN_VERSE" +                , "foo" +                , "" +                , "bar" +                , "#+END_VERSE" +                ] =?> +      lineBlock [ "foo", mempty, "bar" ] + +    , "Verse block with varying indentation" =: +      T.unlines [ "#+BEGIN_VERSE" +                , "  hello darkness" +                , "my old friend" +                , "#+END_VERSE" +                ] =?> +      lineBlock [ "\160\160hello darkness", "my old friend" ] + +    , "Raw block LaTeX" =: +      T.unlines [ "#+BEGIN_LaTeX" +                , "The category $\\cat{Set}$ is adhesive." +                , "#+END_LaTeX" +                ] =?> +      rawBlock "latex" "The category $\\cat{Set}$ is adhesive.\n" + +    , "Raw LaTeX line" =: +      "#+LATEX: \\let\\foo\\bar" =?> +      rawBlock "latex" "\\let\\foo\\bar" + +    , "Raw Beamer line" =: +      "#+beamer: \\pause" =?> +      rawBlock "beamer" "\\pause" + +    , "Raw HTML line" =: +      "#+HTML: <aside>not important</aside>" =?> +      rawBlock "html" "<aside>not important</aside>" + +    , "Export block HTML" =: +      T.unlines [ "#+BEGIN_export html" +                , "<samp>Hello, World!</samp>" +                , "#+END_export" +                ] =?> +      rawBlock "html" "<samp>Hello, World!</samp>\n" + +    , "LaTeX fragment" =: +      T.unlines [ "\\begin{equation}" +                , "X_i = \\begin{cases}" +                , "      G_{\\alpha(i)} & \\text{if }\\alpha(i-1) = \\alpha(i)\\\\" +                , "      C_{\\alpha(i)} & \\text{otherwise}" +                , "      \\end{cases}" +                , "\\end{equation}" +                ] =?> +      rawBlock "latex" +      (unlines [ "\\begin{equation}" +               , "X_i = \\begin{cases}" +               , "      G_{\\alpha(i)} & \\text{if }\\alpha(i-1) =" <> +                 " \\alpha(i)\\\\" +               , "      C_{\\alpha(i)} & \\text{otherwise}" +               , "      \\end{cases}" +               , "\\end{equation}" +               ]) + +    , "Convert blank lines in blocks to single newlines" =: +      T.unlines [ "#+begin_html" +                , "" +                , "<span>boring</span>" +                , "" +                , "#+end_html" +                ] =?> +      rawBlock "html" "\n<span>boring</span>\n\n" + +    , "Accept `ATTR_HTML` attributes for generic block" =: +      T.unlines [ "#+ATTR_HTML: :title hello, world :id test :class fun code" +                , "#+BEGIN_TEST" +                , "nonsense" +                , "#+END_TEST" +                ] =?> +      let attr = ("test", ["fun", "code", "TEST"], [("title", "hello, world")]) +      in divWith attr (para "nonsense") +    ] + +  , testGroup "Headers" Header.tests +  , testGroup "Figures" Figure.tests +  , testGroup "Lists" List.tests +  , testGroup "CodeBlocks" CodeBlock.tests +  , testGroup "Tables" Table.tests +  ] diff --git a/test/Tests/Readers/Org/Block/CodeBlock.hs b/test/Tests/Readers/Org/Block/CodeBlock.hs new file mode 100644 index 000000000..8fa822089 --- /dev/null +++ b/test/Tests/Readers/Org/Block/CodeBlock.hs @@ -0,0 +1,194 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block.CodeBlock (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc.Builder +import qualified Data.Text as T + +tests :: [TestTree] +tests = +  [ "Source block" =: +       T.unlines [ "  #+BEGIN_SRC haskell" +                 , "  main = putStrLn greeting" +                 , "    where greeting = \"moin\"" +                 , "  #+END_SRC" ] =?> +       let attr' = ("", ["haskell"], []) +           code' = "main = putStrLn greeting\n" <> +                   "  where greeting = \"moin\"\n" +       in codeBlockWith attr' code' + +  , "Source block with indented code" =: +       T.unlines [ "  #+BEGIN_SRC haskell" +                 , "    main = putStrLn greeting" +                 , "      where greeting = \"moin\"" +                 , "  #+END_SRC" ] =?> +       let attr' = ("", ["haskell"], []) +           code' = "main = putStrLn greeting\n" <> +                   "  where greeting = \"moin\"\n" +       in codeBlockWith attr' code' + +  , "Source block with tab-indented code" =: +       T.unlines [ "\t#+BEGIN_SRC haskell" +                 , "\tmain = putStrLn greeting" +                 , "\t  where greeting = \"moin\"" +                 , "\t#+END_SRC" ] =?> +       let attr' = ("", ["haskell"], []) +           code' = "main = putStrLn greeting\n" <> +                   "  where greeting = \"moin\"\n" +       in codeBlockWith attr' code' + +  , "Empty source block" =: +       T.unlines [ "  #+BEGIN_SRC haskell" +                 , "  #+END_SRC" ] =?> +       let attr' = ("", ["haskell"], []) +           code' = "" +       in codeBlockWith attr' code' + +  , "Source block between paragraphs" =: +       T.unlines [ "Low German greeting" +                 , "  #+BEGIN_SRC haskell" +                 , "  main = putStrLn greeting" +                 , "    where greeting = \"Moin!\"" +                 , "  #+END_SRC" ] =?> +       let attr' = ("", ["haskell"], []) +           code' = "main = putStrLn greeting\n" <> +                    "  where greeting = \"Moin!\"\n" +       in mconcat [ para $ spcSep [ "Low", "German", "greeting"  ] +                  , codeBlockWith attr' code' +                  ] +  , "Source block with babel arguments" =: +       T.unlines [ "#+BEGIN_SRC emacs-lisp :exports both" +                 , "(progn (message \"Hello, World!\")" +                 , "       (+ 23 42))" +                 , "#+END_SRC" ] =?> +       let classes = [ "commonlisp" ] -- as kate doesn't know emacs-lisp syntax +           params = [ ("org-language", "emacs-lisp") +                    , ("exports", "both") +                    ] +           code' = unlines [ "(progn (message \"Hello, World!\")" +                           , "       (+ 23 42))" ] +       in codeBlockWith ("", classes, params) code' + +  , "Source block with results and :exports both" =: +       T.unlines [ "#+BEGIN_SRC emacs-lisp :exports both" +                 , "(progn (message \"Hello, World!\")" +                 , "       (+ 23 42))" +                 , "#+END_SRC" +                 , "" +                 , "#+RESULTS:" +                 , ": 65"] =?> +       let classes = [ "commonlisp" ] +           params = [ ("org-language", "emacs-lisp") +                    , ("exports", "both") +                    ] +           code' = unlines [ "(progn (message \"Hello, World!\")" +                           , "       (+ 23 42))" ] +           results' = "65\n" +       in codeBlockWith ("", classes, params) code' +          <> +          codeBlockWith ("", ["example"], []) results' + +  , "Source block with results and :exports code" =: +       T.unlines [ "#+BEGIN_SRC emacs-lisp :exports code" +                 , "(progn (message \"Hello, World!\")" +                 , "       (+ 23 42))" +                 , "#+END_SRC" +                 , "" +                 , "#+RESULTS:" +                 , ": 65" ] =?> +       let classes = [ "commonlisp" ] +           params = [ ("org-language", "emacs-lisp") +                    , ("exports", "code") +                    ] +           code' = unlines [ "(progn (message \"Hello, World!\")" +                           , "       (+ 23 42))" ] +       in codeBlockWith ("", classes, params) code' + +  , "Source block with results and :exports results" =: +       T.unlines [ "#+BEGIN_SRC emacs-lisp :exports results" +                 , "(progn (message \"Hello, World!\")" +                 , "       (+ 23 42))" +                 , "#+END_SRC" +                 , "" +                 , "#+RESULTS:" +                 , ": 65" ] =?> +       let results' = "65\n" +       in codeBlockWith ("", ["example"], []) results' + +  , "Source block with results and :exports none" =: +       T.unlines [ "#+BEGIN_SRC emacs-lisp :exports none" +                 , "(progn (message \"Hello, World!\")" +                 , "       (+ 23 42))" +                 , "#+END_SRC" +                 , "" +                 , "#+RESULTS:" +                 , ": 65" ] =?> +       (mempty :: Blocks) + +  , "Source block with toggling header arguments" =: +    T.unlines [ "#+BEGIN_SRC sh :noeval" +              , "echo $HOME" +              , "#+END_SRC" +              ] =?> +    let classes = [ "bash" ] +        params = [ ("org-language", "sh"), ("noeval", "yes") ] +    in codeBlockWith ("", classes, params) "echo $HOME\n" + +  , "Source block with line number switch" =: +    T.unlines [ "#+BEGIN_SRC sh -n 10" +              , ":() { :|:& };:" +              , "#+END_SRC" +              ] =?> +    let classes = [ "bash", "numberLines" ] +        params = [ ("org-language", "sh"), ("startFrom", "10") ] +    in codeBlockWith ("", classes, params) ":() { :|:& };:\n" + +  , "Source block with multi-word parameter values" =: +    T.unlines [ "#+BEGIN_SRC dot :cmdline -Kdot -Tpng " +              , "digraph { id [label=\"ID\"] }" +              , "#+END_SRC" +              ] =?> +    let classes = [ "dot" ] +        params = [ ("cmdline", "-Kdot -Tpng") ] +    in codeBlockWith ("", classes, params) "digraph { id [label=\"ID\"] }\n" + +  , "Example block" =: +       T.unlines [ "#+begin_example" +                 , "A chosen representation of" +                 , "a rule." +                 , "#+eND_exAMPle" +                 ] =?> +       codeBlockWith ("", ["example"], []) +                     "A chosen representation of\na rule.\n" + +  , "Code block with caption" =: +      T.unlines [ "#+CAPTION: Functor laws in Haskell" +                , "#+NAME: functor-laws" +                , "#+BEGIN_SRC haskell" +                , "fmap id = id" +                , "fmap (p . q) = (fmap p) . (fmap q)" +                , "#+END_SRC" +                ] =?> +      divWith +         nullAttr +         (mappend +          (plain $ spanWith ("", ["label"], []) +                            (spcSep [ "Functor", "laws", "in", "Haskell" ])) +          (codeBlockWith ("functor-laws", ["haskell"], []) +                         (unlines [ "fmap id = id" +                                  , "fmap (p . q) = (fmap p) . (fmap q)" +                                  ]))) + +  , "Non-letter chars in source block parameters" =: +      T.unlines [ "#+BEGIN_SRC C :tangle xxxx.c :city Zürich" +                , "code body" +                , "#+END_SRC" +                ] =?> +      let params  = [ ("org-language", "C") +                    , ("tangle", "xxxx.c") +                    , ("city", "Zürich") +                    ] +      in codeBlockWith ( "", ["c"], params) "code body\n" +  ] diff --git a/test/Tests/Readers/Org/Block/Figure.hs b/test/Tests/Readers/Org/Block/Figure.hs new file mode 100644 index 000000000..cae6ef179 --- /dev/null +++ b/test/Tests/Readers/Org/Block/Figure.hs @@ -0,0 +1,57 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block.Figure (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:)) +import Text.Pandoc.Builder (image, imageWith, para) +import qualified Data.Text as T + +tests :: [TestTree] +tests = +  [ "Figure" =: +      T.unlines [ "#+caption: A very courageous man." +                , "#+name: goodguy" +                , "[[file:edward.jpg]]" +                ] =?> +      para (image "edward.jpg" "fig:goodguy" "A very courageous man.") + +  , "Figure with no name" =: +      T.unlines [ "#+caption: I've been through the desert on this" +                , "[[file:horse.png]]" +                ] =?> +      para (image "horse.png" "fig:" "I've been through the desert on this") + +  , "Figure with `fig:` prefix in name" =: +      T.unlines [ "#+caption: Used as a metapher in evolutionary biology." +                , "#+name: fig:redqueen" +                , "[[./the-red-queen.jpg]]" +                ] =?> +      para (image "./the-red-queen.jpg" "fig:redqueen" +                  "Used as a metapher in evolutionary biology.") + +  , "Figure with HTML attributes" =: +      T.unlines [ "#+CAPTION: mah brain just explodid" +                , "#+NAME: lambdacat" +                , "#+ATTR_HTML: :style color: blue :role button" +                , "[[file:lambdacat.jpg]]" +                ] =?> +      let kv = [("style", "color: blue"), ("role", "button")] +          name = "fig:lambdacat" +          caption = "mah brain just explodid" +      in para (imageWith (mempty, mempty, kv) "lambdacat.jpg" name caption) + +  , "Labelled figure" =: +      T.unlines [ "#+CAPTION: My figure" +                , "#+LABEL: fig:myfig" +                , "[[file:blub.png]]" +                ] =?> +      let attr = ("fig:myfig", mempty, mempty) +      in para (imageWith attr "blub.png" "fig:" "My figure") + +  , "Figure with empty caption" =: +      T.unlines [ "#+CAPTION:" +                , "[[file:guess.jpg]]" +                ] =?> +      para (image "guess.jpg" "fig:" "") +  ] diff --git a/test/Tests/Readers/Org/Block/Header.hs b/test/Tests/Readers/Org/Block/Header.hs new file mode 100644 index 000000000..d895c86e2 --- /dev/null +++ b/test/Tests/Readers/Org/Block/Header.hs @@ -0,0 +1,182 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block.Header (tests) where + +import Test.Tasty (TestTree, testGroup) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep, tagSpan) +import Text.Pandoc.Builder +import qualified Data.Text as T + +tests :: [TestTree] +tests = +  [ "First Level Header" =: +      "* Headline\n" =?> +      headerWith ("headline", [], []) 1 "Headline" + +  , "Third Level Header" =: +      "*** Third Level Headline\n" =?> +      headerWith ("third-level-headline", [], []) +                 3 +                 ("Third" <> space <> "Level" <> space <> "Headline") + +  , "Compact Headers with Paragraph" =: +      T.unlines [ "* First Level" +                , "** Second Level" +                , "   Text" +                ] =?> +      mconcat [ headerWith ("first-level", [], []) +                           1 +                           ("First" <> space <> "Level") +              , headerWith ("second-level", [], []) +                           2 +                           ("Second" <> space <> "Level") +              , para "Text" +              ] + +  , "Separated Headers with Paragraph" =: +      T.unlines [ "* First Level" +                , "" +                , "** Second Level" +                , "" +                , "   Text" +                ] =?> +      mconcat [ headerWith ("first-level", [], []) +                           1 +                           ("First" <> space <> "Level") +              , headerWith ("second-level", [], []) +                           2 +                           ("Second" <> space <> "Level") +              , para "Text" +              ] + +  , "Headers not preceded by a blank line" =: +      T.unlines [ "** eat dinner" +                , "Spaghetti and meatballs tonight." +                , "** walk dog" +                ] =?> +      mconcat [ headerWith ("eat-dinner", [], []) +                           2 +                           ("eat" <> space <> "dinner") +              , para $ spcSep [ "Spaghetti", "and", "meatballs", "tonight." ] +              , headerWith ("walk-dog", [], []) +                           2 +                           ("walk" <> space <> "dog") +              ] + +  , testGroup "Todo keywords" +    [ "Header with known todo keyword" =: +        "* TODO header" =?> +        let todoSpan = spanWith ("", ["todo", "TODO"], []) "TODO" +        in headerWith ("header", [], []) 1 (todoSpan <> space <> "header") + +    , "Header marked as done" =: +        "* DONE header" =?> +        let todoSpan = spanWith ("", ["done", "DONE"], []) "DONE" +        in headerWith ("header", [], []) 1 (todoSpan <> space <> "header") + +    , "Header with unknown todo keyword" =: +        "* WAITING header" =?> +        headerWith ("waiting-header", [], []) 1 "WAITING header" + +    , "Custom todo keywords" =: +        T.unlines [ "#+TODO: WAITING CANCELLED" +                  , "* WAITING compile" +                  , "* CANCELLED lunch" +                  ] =?> +        let todoSpan = spanWith ("", ["todo", "WAITING"], []) "WAITING" +            doneSpan = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" +        in headerWith ("compile", [], []) 1 (todoSpan <> space <> "compile") +        <> headerWith ("lunch", [], []) 1 (doneSpan <> space <> "lunch") + +    , "Custom todo keywords with multiple done-states" =: +        T.unlines [ "#+TODO: WAITING | DONE CANCELLED " +                  , "* WAITING compile" +                  , "* CANCELLED lunch" +                  , "* DONE todo-feature" +                  ] =?> +        let waiting = spanWith ("", ["todo", "WAITING"], []) "WAITING" +            cancelled = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" +            done = spanWith ("", ["done", "DONE"], []) "DONE" +        in headerWith ("compile", [], []) 1 (waiting <> space <> "compile") +        <> headerWith ("lunch", [], []) 1 (cancelled <> space <> "lunch") +        <> headerWith ("todo-feature", [], []) 1 (done <> space <> "todo-feature") +    ] + +  , "Tagged headers" =: +      T.unlines [ "* Personal       :PERSONAL:" +                , "** Call Mom      :@PHONE:" +                , "** Call John     :@PHONE:JOHN: " +                ] =?> +      mconcat [ headerWith ("personal", [], []) +                           1 +                           ("Personal " <> tagSpan "PERSONAL") +              , headerWith ("call-mom", [], []) +                           2 +                           ("Call Mom " <> tagSpan "@PHONE") +              , headerWith ("call-john", [], []) +                           2 +                           ("Call John " <> tagSpan "@PHONE" <> "\160" <> tagSpan "JOHN") +              ] + +  , "Untagged header containing colons" =: +      "* This: is not: tagged" =?> +      headerWith ("this-is-not-tagged", [], []) 1 "This: is not: tagged" + +  , "Header starting with strokeout text" =: +      T.unlines [ "foo" +                , "" +                , "* +thing+ other thing" +                ] =?> +      mconcat [ para "foo" +              , headerWith ("thing-other-thing", [], []) +                           1 +                           ((strikeout "thing") <> " other thing") +              ] + +  , "Comment Trees" =: +      T.unlines [ "* COMMENT A comment tree" +                , "  Not much going on here" +                , "** This will be dropped" +                , "* Comment tree above" +                ] =?> +      headerWith ("comment-tree-above", [], []) 1 "Comment tree above" + +  , "Nothing but a COMMENT header" =: +      "* COMMENT Test" =?> +      (mempty::Blocks) + +  , "Tree with :noexport:" =: +      T.unlines [ "* Should be ignored :archive:noexport:old:" +                , "** Old stuff" +                , "   This is not going to be exported" +                ] =?> +      (mempty::Blocks) + +  , "Subtree with :noexport:" =: +      T.unlines [ "* Exported" +                , "** This isn't exported :noexport:" +                , "*** This neither" +                , "** But this is" +                ] =?> +      mconcat [ headerWith ("exported", [], []) 1 "Exported" +              , headerWith ("but-this-is", [], []) 2 "But this is" +              ] + +  , "Preferences are treated as header attributes" =: +      T.unlines [ "* foo" +                , "  :PROPERTIES:" +                , "  :custom_id: fubar" +                , "  :bar: baz" +                , "  :END:" +                ] =?> +      headerWith ("fubar", [], [("bar", "baz")]) 1 "foo" + + +  , "Headers marked with a unnumbered property get a class of the same name" =: +      T.unlines [ "* Not numbered" +                , "  :PROPERTIES:" +                , "  :UNNUMBERED: t" +                , "  :END:" +                ] =?> +      headerWith ("not-numbered", ["unnumbered"], []) 1 "Not numbered" +  ] diff --git a/test/Tests/Readers/Org/Block/List.hs b/test/Tests/Readers/Org/Block/List.hs new file mode 100644 index 000000000..32bb13294 --- /dev/null +++ b/test/Tests/Readers/Org/Block/List.hs @@ -0,0 +1,244 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block.List (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc.Builder +import qualified Data.Text as T + +tests :: [TestTree] +tests = +  [ "Simple Bullet Lists" =: +      ("- Item1\n" <> +       "- Item2\n") =?> +      bulletList [ plain "Item1" +                 , plain "Item2" +                 ] + +  , "Indented Bullet Lists" =: +      ("   - Item1\n" <> +       "   - Item2\n") =?> +      bulletList [ plain "Item1" +                 , plain "Item2" +                 ] + +  , "Unindented *" =: +      ("- Item1\n" <> +       "* Item2\n") =?> +      bulletList [ plain "Item1" +                 ] <> +      headerWith ("item2", [], []) 1 "Item2" + +  , "Multi-line Bullet Lists" =: +      ("- *Fat\n" <> +       "  Tony*\n" <> +       "- /Sideshow\n" <> +       " Bob/") =?> +      bulletList [ plain $ strong ("Fat" <> softbreak <> "Tony") +                 , plain $ emph ("Sideshow" <> softbreak <> "Bob") +                 ] + +  , "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" +                   , bulletList [ plain ("One" <> space <> +                                         "More" <> space <> +                                         "Time") +                                , plain ("Harder," <> space <> +                                         "Better," <> space <> +                                         "Faster," <> space <> +                                         "Stronger") +                                ] +                   ] +                 , mconcat +                   [ plain "Homework" +                   , bulletList [ plain ("Around" <> space <> +                                         "the" <> space <> +                                         "World") +                                ] +                   ] +                 , mconcat +                   [ plain ("Human" <> space <> "After" <> space <> "All") +                   , bulletList [ plain "Technologic" +                                , plain ("Robot" <> space <> "Rock") +                                ] +                   ] +                 ] + +  , "Bullet List with Decreasing Indent" =: +       ("  - Discovery\n\ +        \ - Human After All\n") =?> +       mconcat [ bulletList [ plain "Discovery" ] +               , bulletList [ plain ("Human" <> space <> "After" <> space <> "All")] +               ] + +  , "Header follows Bullet List" =: +      ("  - Discovery\n\ +       \  - Human After All\n\ +       \* Homework") =?> +      mconcat [ bulletList [ plain "Discovery" +                           , plain ("Human" <> space <> "After" <> space <> "All") +                           ] +              , headerWith ("homework", [], []) 1 "Homework" +              ] + +  , "Bullet List Unindented with trailing Header" =: +      ("- Discovery\n\ +       \- Homework\n\ +       \* NotValidListItem") =?> +      mconcat [ bulletList [ plain "Discovery" +                           , plain "Homework" +                           ] +              , headerWith ("notvalidlistitem", [], []) 1 "NotValidListItem" +              ] + +  , "Empty bullet points" =: +      T.unlines [ "-" +                , "- " +                ] =?> +      bulletList [ plain "", plain "" ] + +  , "Simple Ordered List" =: +      ("1. Item1\n" <> +       "2. Item2\n") =?> +      let listStyle = (1, DefaultStyle, DefaultDelim) +          listStructure = [ plain "Item1" +                          , plain "Item2" +                          ] +      in orderedListWith listStyle listStructure + +  , "Simple Ordered List with Parens" =: +      ("1) Item1\n" <> +       "2) Item2\n") =?> +      let listStyle = (1, DefaultStyle, DefaultDelim) +          listStructure = [ plain "Item1" +                          , plain "Item2" +                          ] +      in orderedListWith listStyle listStructure + +  , "Indented Ordered List" =: +      (" 1. Item1\n" <> +       " 2. Item2\n") =?> +      let listStyle = (1, DefaultStyle, DefaultDelim) +          listStructure = [ plain "Item1" +                          , plain "Item2" +                          ] +      in orderedListWith listStyle listStructure + +  , "Empty ordered list item" =: +      T.unlines [ "1." +                , "3. " +                ] =?> +      orderedList [ plain "", plain "" ] + +  , "Nested Ordered Lists" =: +      ("1. One\n" <> +       "   1. One-One\n" <> +       "   2. One-Two\n" <> +       "2. Two\n" <> +       "   1. Two-One\n"<> +       "   2. 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" <> +       "  1. Org\n") =?> +      bulletList [ (plain "Emacs") <> +                   (orderedList [ plain "Org"]) +                 ] + +  , "Bullet List in Ordered List" =: +      ("1. GNU\n" <> +       "   - Freedom\n") =?> +      orderedList [ (plain "GNU") <> bulletList [ (plain "Freedom") ] ] + +  , "Definition List" =: +      T.unlines [ "- PLL :: phase-locked loop" +                , "- TTL ::" +                , "  transistor-transistor logic" +                , "- PSK :: phase-shift keying" +                , "" +                , "  a digital modulation scheme" +                ] =?> +      definitionList [ ("PLL", [ plain $ "phase-locked" <> space <> "loop" ]) +                     , ("TTL", [ plain $ "transistor-transistor" <> space <> +                                           "logic" ]) +                     , ("PSK", [ mconcat +                                 [ para $ "phase-shift" <> space <> "keying" +                                 , para $ spcSep [ "a", "digital" +                                                 , "modulation", "scheme" ] +                                 ] +                               ]) +                     ] +  , "Definition list with multi-word term" =: +    " - Elijah Wood :: He plays Frodo" =?> +     definitionList [ ("Elijah" <> space <> "Wood", [plain $ "He" <> space <> "plays" <> space <> "Frodo"])] +  , "Compact definition list" =: +       T.unlines [ "- ATP :: adenosine 5' triphosphate" +                 , "- DNA :: deoxyribonucleic acid" +                 , "- PCR :: polymerase chain reaction" +                 , "" +                 ] =?> +      definitionList +      [ ("ATP", [ plain $ spcSep [ "adenosine", "5'", "triphosphate" ] ]) +      , ("DNA", [ plain $ spcSep [ "deoxyribonucleic", "acid" ] ]) +      , ("PCR", [ plain $ spcSep [ "polymerase", "chain", "reaction" ] ]) +      ] + +  , "Definition List With Trailing Header" =: +      "- definition :: list\n\ +      \- cool :: defs\n\ +      \* header" =?> +      mconcat [ definitionList [ ("definition", [plain "list"]) +                               , ("cool", [plain "defs"]) +                               ] +              , headerWith ("header", [], []) 1 "header" +              ] + +  , "Definition lists double-colon markers must be surrounded by whitespace" =: +      "- std::cout" =?> +      bulletList [ plain "std::cout" ] + +  , "Loose bullet list" =: +     T.unlines [ "- apple" +               , "" +               , "- orange" +               , "" +               , "- peach" +               ] =?> +      bulletList [ para "apple" +                 , para "orange" +                 , para "peach" +                 ] + +  , "Recognize preceding paragraphs in non-list contexts" =: +      T.unlines [ "CLOSED: [2015-10-19 Mon 15:03]" +                , "- Note taken on [2015-10-19 Mon 13:24]" +                ] =?> +      mconcat [ para "CLOSED: [2015-10-19 Mon 15:03]" +              , bulletList [ plain "Note taken on [2015-10-19 Mon 13:24]" ] +              ] +  ] diff --git a/test/Tests/Readers/Org/Block/Table.hs b/test/Tests/Readers/Org/Block/Table.hs new file mode 100644 index 000000000..db6e756f8 --- /dev/null +++ b/test/Tests/Readers/Org/Block/Table.hs @@ -0,0 +1,150 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block.Table (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc.Builder +import qualified Data.Text as T + +simpleTable' :: Int +             -> [Blocks] +             -> [[Blocks]] +             -> Blocks +simpleTable' n = table "" (replicate n (AlignDefault, 0.0)) + +tests :: [TestTree] +tests = +  [ "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 between Paragraphs" =: +      T.unlines [ "Before" +                , "| One | Two |" +                , "After" +                ] =?> +      mconcat [ para "Before" +              , simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] +              , para "After" +              ] + +  , "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 with final hline" =: +      T.unlines [ "| cervisiae    | domesticated |" +                , "| paradoxus    | wild         |" +                , "|--------------+--------------|" +                ] =?> +      simpleTable' 2 mempty +            [ [ plain "cervisiae", plain "domesticated" ] +             , [ plain "paradoxus", plain "wild" ] +            ] + +  , "Table in a box" =: +      T.unlines [ "|---------|---------|" +                , "| static  | Haskell |" +                , "| dynamic | Lisp    |" +                , "|---------+---------|" +                ] =?> +      simpleTable' 2 mempty +            [ [ plain "static", plain "Haskell" ] +            , [ plain "dynamic", plain "Lisp" ] +            ] + +  , "Table with empty cells" =: +      "|||c|" =?> +      simpleTable' 3 mempty [[mempty, mempty, plain "c"]] + +  , "Table with empty rows" =: +      T.unlines [ "| first  |" +                , "|        |" +                , "| third  |" +                ] =?> +      simpleTable' 1 mempty [[plain "first"], [mempty], [plain "third"]] + +  , "Table with alignment row" =: +      T.unlines [ "| Numbers | Text | More |" +                , "| <c>     | <r>  |      |" +                , "| 1       | One  | foo  |" +                , "| 2       | Two  | bar  |" +                ] =?> +      table "" (zip [AlignCenter, AlignRight, AlignDefault] [0, 0, 0]) +            [] +            [ [ 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" ]) + +  , "Missing pipe at end of row" =: +      "|incomplete-but-valid" =?> +      simpleTable' 1 mempty [ [ plain "incomplete-but-valid" ] ] + +  , "Table with differing row lengths" =: +      T.unlines [ "| Numbers | Text " +                , "|-" +                , "| <c>     | <r>  |" +                , "| 1       | One  | foo  |" +                , "| 2" +                ] =?> +      table "" (zip [AlignCenter, AlignRight] [0, 0]) +            [ plain "Numbers", plain "Text" ] +            [ [ plain "1" , plain "One" , plain "foo" ] +            , [ plain "2" ] +            ] + +  , "Table with caption" =: +      T.unlines [ "#+CAPTION: Hitchhiker's Multiplication Table" +                , "| x |  6 |" +                , "| 9 | 42 |" +                ] =?> +      table "Hitchhiker's Multiplication Table" +            [(AlignDefault, 0), (AlignDefault, 0)] +            [] +            [ [ plain "x", plain "6" ] +            , [ plain "9", plain "42" ] +            ] +  ] diff --git a/test/Tests/Readers/Org/Directive.hs b/test/Tests/Readers/Org/Directive.hs new file mode 100644 index 000000000..862315ef3 --- /dev/null +++ b/test/Tests/Readers/Org/Directive.hs @@ -0,0 +1,199 @@ +{-# LANGUAGE OverloadedStrings #-} +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) +  where +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 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 "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" +      (T.unlines [ "#+include: \"level3.org\" :minlevel 1" ] =?> +       headerWith ("level3", [], []) 1 "Level3") + +    , 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" +         ] +      ) +    ] +  ] diff --git a/test/Tests/Readers/Org/Inline.hs b/test/Tests/Readers/Org/Inline.hs new file mode 100644 index 000000000..cb50ba630 --- /dev/null +++ b/test/Tests/Readers/Org/Inline.hs @@ -0,0 +1,516 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Inline (tests) where + +import Data.List (intersperse) +import Test.Tasty (TestTree, testGroup) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc +import Text.Pandoc.Builder +import Text.Pandoc.Shared (underlineSpan) +import qualified Data.Text as T +import qualified Tests.Readers.Org.Inline.Note as Note +import qualified Tests.Readers.Org.Inline.Smart as Smart + +tests :: [TestTree] +tests = +  [ "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") + +  , "Emphasized Strong preceded by space" =: +      " */super/*" =?> +      para (strong . emph $ "super") + +  , "Underline" =: +      "_underline_" =?> +      para (underlineSpan $ "underline") + +  , "Strikeout" =: +      "+Kill Bill+" =?> +      para (strikeout . spcSep $ [ "Kill", "Bill" ]) + +  , "Verbatim" =: +      "=Robot.rock()=" =?> +      para (code "Robot.rock()") + +  , "Code" =: +      "~word for word~" =?> +      para (code "word for word") + +  , "Math $..$" =: +      "$E=mc^2$" =?> +       para (math "E=mc^2") + +  , "Math $$..$$" =: +      "$$E=mc^2$$" =?> +      para (displayMath "E=mc^2") + +  , "Math \\[..\\]" =: +      "\\[E=ℎν\\]" =?> +      para (displayMath "E=ℎν") + +  , "Math \\(..\\)" =: +      "\\(σ_x σ_p ≥ \\frac{ℏ}{2}\\)" =?> +      para (math "σ_x σ_p ≥ \\frac{ℏ}{2}") + +  , "Symbol" =: +      "A * symbol" =?> +      para (str "A" <> space <> str "*" <> space <> "symbol") + +  , "Superscript simple expression" =: +      "2^-λ" =?> +      para (str "2" <> superscript "-λ") + +  , "Superscript multi char" =: +      "2^{n-1}" =?> +      para (str "2" <> superscript "n-1") + +  , "Subscript simple expression" =: +      "a_n" =?> +      para (str "a" <> subscript "n") + +  , "Subscript multi char" =: +      "a_{n+1}" =?> +      para (str "a" <> subscript "n+1") + +  , "Linebreak" =: +      "line \\\\ \nbreak" =?> +      para ("line" <> linebreak <> "break") + +  , "Inline note" =: +      "[fn::Schreib mir eine E-Mail]" =?> +      para (note $ para "Schreib mir eine E-Mail") + +  , "Markup-chars not occuring on word break are symbols" =: +      T.unlines [ "this+that+ +so+on" +                , "seven*eight* nine*" +                , "+not+funny+" +                ] =?> +      para ("this+that+ +so+on" <> softbreak <> +            "seven*eight* nine*" <> softbreak <> +            strikeout "not+funny") + +  , "No empty markup" =: +      "// ** __ <> == ~~ $$" =?> +      para (spcSep [ "//", "**", "__", "<>", "==", "~~", "$$" ]) + +  , "Adherence to Org's rules for markup borders" =: +      "/t/& a/ / ./r/ (*l*) /e/! /b/." =?> +      para (spcSep [ emph $ "t/&" <> space <> "a" +                   , "/" +                   , "./r/" +                   , "(" <> strong "l" <> ")" +                   , emph "e" <> "!" +                   , emph "b" <> "." +                   ]) + +  , "Quotes are allowed border chars" =: +      "/'yep/ *sure\"*" =?> +      para (emph "'yep" <> space <> strong "sure\"") + +  , "Spaces are forbidden border chars" =: +      "/nada /" =?> +      para "/nada /" + +  , "Markup should work properly after a blank line" =: +    T.unlines ["foo", "", "/bar/"] =?> +    (para $ text "foo") <> (para $ emph $ text "bar") + +  , "Inline math must stay within three lines" =: +      T.unlines [ "$a", "b", "c$", "$d", "e", "f", "g$" ] =?> +      para ((math "a\nb\nc") <> softbreak <> +            "$d" <> softbreak <> "e" <> softbreak <> +            "f" <> softbreak <> "g$") + +  , "Single-character math" =: +      "$a$ $b$! $c$?" =?> +      para (spcSep [ math "a" +                   , "$b$!" +                   , (math "c") <> "?" +                   ]) + +  , "Markup may not span more than two lines" =: +      "/this *is +totally\nnice+ not*\nemph/" =?> +      para ("/this" <> space <> +              strong ("is" <> space <> +                      strikeout ("totally" <> +                        softbreak <> "nice") <> +                      space <> "not") <> +              softbreak <> "emph/") + +  , "Sub- and superscript expressions" =: +     T.unlines [ "a_(a(b)(c)d)" +               , "e^(f(g)h)" +               , "i_(jk)l)" +               , "m^()n" +               , "o_{p{q{}r}}" +               , "s^{t{u}v}" +               , "w_{xy}z}" +               , "1^{}2" +               , "3_{{}}" +               , "4^(a(*b(c*)d))" +               ] =?> +     para (mconcat $ intersperse softbreak +                  [ "a" <> subscript "(a(b)(c)d)" +                  , "e" <> superscript "(f(g)h)" +                  , "i" <> (subscript "(jk)") <> "l)" +                  , "m" <> (superscript "()") <> "n" +                  , "o" <> subscript "p{q{}r}" +                  , "s" <> superscript "t{u}v" +                  , "w" <> (subscript "xy") <> "z}" +                  , "1" <> (superscript "") <> "2" +                  , "3" <> subscript "{}" +                  , "4" <> superscript ("(a(" <> strong "b(c" <> ")d))") +                  ]) +  , "Verbatim text can contain equal signes (=)" =: +      "=is_subst = True=" =?> +      para (code "is_subst = True") + +  , testGroup "Images" +    [ "Image" =: +        "[[./sunset.jpg]]" =?> +        (para $ image "./sunset.jpg" "" "") + +    , "Image with explicit file: prefix" =: +        "[[file:sunrise.jpg]]" =?> +        (para $ image "sunrise.jpg" "" "") + +    , "Multiple images within a paragraph" =: +        T.unlines [ "[[file:sunrise.jpg]]" +                  , "[[file:sunset.jpg]]" +                  ] =?> +        (para $ (image "sunrise.jpg" "" "") +             <> softbreak +             <> (image "sunset.jpg" "" "")) + +    , "Image with html attributes" =: +        T.unlines [ "#+ATTR_HTML: :width 50%" +                  , "[[file:guinea-pig.gif]]" +                  ] =?> +        (para $ imageWith ("", [], [("width", "50%")]) "guinea-pig.gif" "" "") +    ] + +  , "Explicit link" =: +      "[[http://zeitlens.com/][pseudo-random /nonsense/]]" =?> +      (para $ link "http://zeitlens.com/" "" +                   ("pseudo-random" <> space <> emph "nonsense")) + +  , "Self-link" =: +      "[[http://zeitlens.com/]]" =?> +      (para $ link "http://zeitlens.com/" "" "http://zeitlens.com/") + +  , "Absolute file link" =: +      "[[/url][hi]]" =?> +      (para $ link "file:///url" "" "hi") + +  , "Link to file in parent directory" =: +      "[[../file.txt][moin]]" =?> +      (para $ link "../file.txt" "" "moin") + +  , "Empty link (for gitit interop)" =: +      "[[][New Link]]" =?> +      (para $ link "" "" "New Link") + +  , "Image link" =: +      "[[sunset.png][file:dusk.svg]]" =?> +      (para $ link "sunset.png" "" (image "dusk.svg" "" "")) + +  , "Image link with non-image target" =: +      "[[http://example.com][./logo.png]]" =?> +      (para $ link "http://example.com" "" (image "./logo.png" "" "")) + +  , "Plain link" =: +      "Posts on http://zeitlens.com/ can be funny at times." =?> +      (para $ spcSep [ "Posts", "on" +                     , link "http://zeitlens.com/" "" "http://zeitlens.com/" +                     , "can", "be", "funny", "at", "times." +                     ]) + +  , "Angle link" =: +      "Look at <http://moltkeplatz.de> for fnords." =?> +      (para $ spcSep [ "Look", "at" +                     , link "http://moltkeplatz.de" "" "http://moltkeplatz.de" +                     , "for", "fnords." +                     ]) + +  , "Absolute file link" =: +      "[[file:///etc/passwd][passwd]]" =?> +      (para $ link "file:///etc/passwd" "" "passwd") + +  , "File link" =: +      "[[file:target][title]]" =?> +      (para $ link "target" "" "title") + +  , "Anchor" =: +      "<<anchor>> Link here later." =?> +      (para $ spanWith ("anchor", [], []) mempty <> +              "Link" <> space <> "here" <> space <> "later.") + +  , "Inline code block" =: +      "src_emacs-lisp{(message \"Hello\")}" =?> +      (para $ codeWith ( "" +                       , [ "commonlisp" ] +                       , [ ("org-language", "emacs-lisp") ]) +                       "(message \"Hello\")") + +  , "Inline code block with arguments" =: +      "src_sh[:export both :results output]{echo 'Hello, World'}" =?> +      (para $ codeWith ( "" +                       , [ "bash" ] +                       , [ ("org-language", "sh") +                         , ("export", "both") +                         , ("results", "output") +                         ] +                       ) +                       "echo 'Hello, World'") + +  , "Inline code block with toggle" =: +      "src_sh[:toggle]{echo $HOME}" =?> +      (para $ codeWith ( "" +                       , [ "bash" ] +                       , [ ("org-language", "sh") +                         , ("toggle", "yes") +                         ] +                       ) +                       "echo $HOME") + +  , "Citation" =: +      "[@nonexistent]" =?> +      let citation = Citation +                     { citationId = "nonexistent" +                     , citationPrefix = [] +                     , citationSuffix = [] +                     , citationMode = NormalCitation +                     , citationNoteNum = 0 +                     , citationHash = 0} +      in (para $ cite [citation] "[@nonexistent]") + +  , "Citation containing text" =: +      "[see @item1 p. 34-35]" =?> +      let citation = Citation +                     { citationId = "item1" +                     , citationPrefix = [Str "see"] +                     , citationSuffix = [Space ,Str "p.",Space,Str "34-35"] +                     , citationMode = NormalCitation +                     , citationNoteNum = 0 +                     , citationHash = 0} +      in (para $ cite [citation] "[see @item1 p. 34-35]") + +  , "Org-ref simple citation" =: +    "cite:pandoc" =?> +    let citation = Citation +                   { citationId = "pandoc" +                   , citationPrefix = mempty +                   , citationSuffix = mempty +                   , citationMode = AuthorInText +                   , citationNoteNum = 0 +                   , citationHash = 0 +                   } +    in (para $ cite [citation] "cite:pandoc") + +  , "Org-ref simple citation with underscores" =: +    "cite:pandoc_org_ref" =?> +    let citation = Citation +                   { citationId = "pandoc_org_ref" +                   , citationPrefix = mempty +                   , citationSuffix = mempty +                   , citationMode = AuthorInText +                   , citationNoteNum = 0 +                   , citationHash = 0 +                   } +    in (para $ cite [citation] "cite:pandoc_org_ref") + +  , "Org-ref simple citation succeeded by comma" =: +    "cite:pandoc," =?> +    let citation = Citation +                   { citationId = "pandoc" +                   , citationPrefix = mempty +                   , citationSuffix = mempty +                   , citationMode = AuthorInText +                   , citationNoteNum = 0 +                   , citationHash = 0 +                   } +    in (para $ cite [citation] "cite:pandoc" <> str ",") + +  , "Org-ref simple citation succeeded by dot" =: +    "cite:pandoc." =?> +    let citation = Citation +                   { citationId = "pandoc" +                   , citationPrefix = mempty +                   , citationSuffix = mempty +                   , citationMode = AuthorInText +                   , citationNoteNum = 0 +                   , citationHash = 0 +                   } +    in (para $ cite [citation] "cite:pandoc" <> str ".") + +  , "Org-ref simple citation succeeded by colon" =: +    "cite:pandoc:" =?> +    let citation = Citation +                   { citationId = "pandoc" +                   , citationPrefix = mempty +                   , citationSuffix = mempty +                   , citationMode = AuthorInText +                   , citationNoteNum = 0 +                   , citationHash = 0 +                   } +    in (para $ cite [citation] "cite:pandoc" <> str ":") + +  , "Org-ref simple citep citation" =: +    "citep:pandoc" =?> +    let citation = Citation +                   { citationId = "pandoc" +                   , citationPrefix = mempty +                   , citationSuffix = mempty +                   , citationMode = NormalCitation +                   , citationNoteNum = 0 +                   , citationHash = 0 +                   } +    in (para $ cite [citation] "citep:pandoc") + +  , "Org-ref extended citation" =: +    "[[citep:Dominik201408][See page 20::, for example]]" =?> +    let citation = Citation +                   { citationId = "Dominik201408" +                   , citationPrefix = toList "See page 20" +                   , citationSuffix = toList ", for example" +                   , citationMode = NormalCitation +                   , citationNoteNum = 0 +                   , citationHash = 0 +                   } +    in (para $ cite [citation] "[[citep:Dominik201408][See page 20::, for example]]") + +  , testGroup "Berkeley-style citations" $ +    let pandocCite = Citation +          { citationId = "Pandoc" +          , citationPrefix = mempty +          , citationSuffix = mempty +          , citationMode = NormalCitation +          , citationNoteNum = 0 +          , citationHash = 0 +          } +        pandocInText = pandocCite { citationMode = AuthorInText } +        dominikCite = Citation +          { citationId = "Dominik201408" +          , citationPrefix = mempty +          , citationSuffix = mempty +          , citationMode = NormalCitation +          , citationNoteNum = 0 +          , citationHash = 0 +          } +        dominikInText = dominikCite { citationMode = AuthorInText } +    in [ +        "Berkeley-style in-text citation" =: +          "See @Dominik201408." =?> +            (para $ "See " +                  <> cite [dominikInText] "@Dominik201408" +                  <> ".") + +      , "Berkeley-style parenthetical citation list" =: +          "[(cite): see; @Dominik201408;also @Pandoc; and others]" =?> +          let pandocCite'  = pandocCite { +                               citationPrefix = toList "also" +                             , citationSuffix = toList "and others" +                             } +              dominikCite' = dominikCite { +                               citationPrefix = toList "see" +                             } +          in (para $ cite [dominikCite', pandocCite'] "") + +      , "Berkeley-style plain citation list" =: +          "[cite: See; @Dominik201408; and @Pandoc; and others]" =?> +          let pandocCite' = pandocInText { +                              citationPrefix = toList "and" +                            } +          in (para $ "See " +                  <> cite [dominikInText] "" +                  <> "," <> space +                  <> cite [pandocCite'] "" +                  <> "," <> space <> "and others") +    ] + +  , "Inline LaTeX symbol" =: +      "\\dots" =?> +      para "…" + +  , "Inline LaTeX command" =: +      "\\textit{Emphasised}" =?> +      para (emph "Emphasised") + +  , "Inline LaTeX command with spaces" =: +      "\\emph{Emphasis mine}" =?> +      para (emph "Emphasis mine") + +  , "Inline LaTeX math symbol" =: +      "\\tau" =?> +      para (emph "τ") + +  , "Unknown inline LaTeX command" =: +      "\\notacommand{foo}" =?> +      para (rawInline "latex" "\\notacommand{foo}") + +  , "Export snippet" =: +      "@@html:<kbd>M-x org-agenda</kbd>@@" =?> +      para (rawInline "html" "<kbd>M-x org-agenda</kbd>") + +  , "MathML symbol in LaTeX-style" =: +      "There is a hackerspace in Lübeck, Germany, called nbsp (unicode symbol: '\\nbsp')." =?> +      para ("There is a hackerspace in Lübeck, Germany, called nbsp (unicode symbol: ' ').") + +  , "MathML symbol in LaTeX-style, including braces" =: +      "\\Aacute{}stor" =?> +      para "Ástor" + +  , "MathML copy sign" =: +      "\\copy" =?> +      para "©" + +  , "MathML symbols, space separated" =: +      "\\ForAll \\Auml" =?> +      para "∀ Ä" + +  , "LaTeX citation" =: +      "\\cite{Coffee}" =?> +      let citation = Citation +                     { citationId = "Coffee" +                     , citationPrefix = [] +                     , citationSuffix = [] +                     , citationMode = NormalCitation +                     , citationNoteNum = 0 +                     , citationHash = 0} +      in (para . cite [citation] $ rawInline "latex" "\\cite{Coffee}") + +  , "Macro" =: +      T.unlines [ "#+MACRO: HELLO /Hello, $1/" +                , "{{{HELLO(World)}}}" +                ] =?> +      para (emph "Hello, World") + +  , "Macro repeting its argument" =: +      T.unlines [ "#+MACRO: HELLO $1$1" +                , "{{{HELLO(moin)}}}" +                ] =?> +      para "moinmoin" + +  , "Macro called with too few arguments" =: +      T.unlines [ "#+MACRO: HELLO Foo $1 $2 Bar" +                , "{{{HELLO()}}}" +                ] =?> +      para "Foo Bar" + +  , testGroup "Footnotes" Note.tests +  , testGroup "Smart punctuation" Smart.tests +  ] diff --git a/test/Tests/Readers/Org/Inline/Note.hs b/test/Tests/Readers/Org/Inline/Note.hs new file mode 100644 index 000000000..46416d7d8 --- /dev/null +++ b/test/Tests/Readers/Org/Inline/Note.hs @@ -0,0 +1,87 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Inline.Note (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:)) +import Text.Pandoc.Builder +import qualified Data.Text as T + +tests :: [TestTree] +tests = +  [ "Footnote" =: +      T.unlines [ "A footnote[1]" +                , "" +                , "[1] First paragraph" +                , "" +                , "second paragraph" +                ] =?> +      para (mconcat +            [ "A", space, "footnote" +            , note $ mconcat [ para ("First" <> space <> "paragraph") +                             , para ("second" <> space <> "paragraph") +                             ] +            ]) + +  , "Two footnotes" =: +      T.unlines [ "Footnotes[fn:1][fn:2]" +                , "" +                , "[fn:1] First note." +                , "" +                , "[fn:2] Second note." +                ] =?> +      para (mconcat +            [ "Footnotes" +            , note $ para ("First" <> space <> "note.") +            , note $ para ("Second" <> space <> "note.") +            ]) + +  , "Emphasized text before footnote" =: +      T.unlines [ "/text/[fn:1]" +                , "" +                , "[fn:1] unicorn" +                ] =?> +      para (mconcat +           [ emph "text" +           , note . para $ "unicorn" +           ]) + +  , "Footnote that starts with emphasized text" =: +      T.unlines [ "text[fn:1]" +                , "" +                , "[fn:1] /emphasized/" +                ] =?> +      para (mconcat +           [ "text" +           , note . para $ emph "emphasized" +           ]) + +  , "Footnote followed by header" =: +      T.unlines [ "Another note[fn:yay]" +                , "" +                , "[fn:yay] This is great!" +                , "" +                , "** Headline" +                ] =?> +      mconcat +      [ para (mconcat +              [ "Another", space, "note" +              , note $ para ("This" <> space <> "is" <> space <> "great!") +              ]) +      , headerWith ("headline", [], []) 2 "Headline" +      ] + +  , "Footnote followed by two blank lines" =: +      T.unlines [ "footnote[fn:blanklines]" +                , "" +                , "[fn:blanklines] followed by blank lines" +                , "" +                , "" +                , "next" +                ] =?> +      mconcat +      [ para ("footnote" <> note (para "followed by blank lines")) +      , para "next" +      ] +  ] +   diff --git a/test/Tests/Readers/Org/Inline/Smart.hs b/test/Tests/Readers/Org/Inline/Smart.hs new file mode 100644 index 000000000..7a5e653cf --- /dev/null +++ b/test/Tests/Readers/Org/Inline/Smart.hs @@ -0,0 +1,46 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Inline.Smart (tests) where + +import Data.Text (Text) +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>), purely, test) +import Text.Pandoc (ReaderOptions (readerExtensions), +                    Extension (Ext_smart), def, enableExtension, +                    getDefaultExtensions, readOrg) +import Text.Pandoc.Builder + +orgSmart :: Text -> Pandoc +orgSmart = purely $ +  let extensionsSmart = enableExtension Ext_smart (getDefaultExtensions "org") +  in readOrg def{ readerExtensions = extensionsSmart } + +tests :: [TestTree] +tests = +  [ test orgSmart "quote before ellipses" +    ("'...hi'" +     =?> para (singleQuoted "…hi")) + +  , test orgSmart "apostrophe before emph" +    ("D'oh! A l'/aide/!" +     =?> para ("D’oh! A l’" <> emph "aide" <> "!")) + +  , test orgSmart "apostrophe in French" +    ("À l'arrivée de la guerre, le thème de l'«impossibilité du socialisme»" +     =?> para "À l’arrivée de la guerre, le thème de l’«impossibilité du socialisme»") + +  , test orgSmart "Quotes cannot occur at the end of emphasized text" +    ("/say \"yes\"/" =?> +     para ("/say" <> space <> doubleQuoted "yes" <> "/")) + +  , test orgSmart "Dashes are allowed at the borders of emphasis'" +    ("/foo---/" =?> +     para (emph "foo—")) + +  , test orgSmart "Single quotes can be followed by emphasized text" +    ("Singles on the '/meat market/'" =?> +     para ("Singles on the " <> (singleQuoted $ emph "meat market"))) + +  , test orgSmart "Double quotes can be followed by emphasized text" +    ("Double income, no kids: \"/DINK/\"" =?> +     para ("Double income, no kids: " <> (doubleQuoted $ emph "DINK"))) +  ] diff --git a/test/Tests/Readers/Org/Meta.hs b/test/Tests/Readers/Org/Meta.hs new file mode 100644 index 000000000..3ad6f5d8b --- /dev/null +++ b/test/Tests/Readers/Org/Meta.hs @@ -0,0 +1,173 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Meta (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc +import Text.Pandoc.Builder +import qualified Data.Text as T + +tests :: [TestTree] +tests = +  [ "Comment" =: +      "# Nothing to see here" =?> +      (mempty::Blocks) + +  , "Not a comment" =: +      "#-tag" =?> +      para "#-tag" + +  , "Comment surrounded by Text" =: +      T.unlines [ "Before" +                , "# Comment" +                , "After" +                ] =?> +      mconcat [ para "Before" +              , para "After" +              ] + +  , "Title" =: +    "#+TITLE: Hello, World" =?> +    let titleInline = toList $ "Hello," <> space <> "World" +        meta = setMeta "title" (MetaInlines titleInline) $ nullMeta +    in Pandoc meta mempty + +  , "Author" =: +    "#+author: John /Emacs-Fanboy/ Doe" =?> +    let author = toList . spcSep $ [ "John", emph "Emacs-Fanboy", "Doe" ] +        meta = setMeta "author" (MetaList [MetaInlines author]) $ nullMeta +    in Pandoc meta mempty + +  , "Multiple authors" =: +    "#+author: James Dewey Watson, Francis Harry Compton Crick " =?> +    let watson = MetaInlines $ toList "James Dewey Watson" +        crick = MetaInlines $ toList "Francis Harry Compton Crick" +        meta = setMeta "author" (MetaList [watson, crick]) $ nullMeta +    in Pandoc meta mempty + +  , "Date" =: +    "#+Date: Feb. *28*, 2014" =?> +    let date = toList . spcSep $ [ "Feb.", (strong "28") <> ",", "2014" ] +        meta = setMeta "date" (MetaInlines date) $ nullMeta +    in Pandoc meta mempty + +  , "Description" =: +    "#+DESCRIPTION: Explanatory text" =?> +    let description = "Explanatory text" +        meta = setMeta "description" (MetaString description) $ nullMeta +    in Pandoc meta mempty + +  , "Properties drawer" =: +      T.unlines [ "  :PROPERTIES:" +                , "  :setting: foo" +                , "  :END:" +                ] =?> +      (mempty::Blocks) + +  , "LaTeX_headers options are translated to header-includes" =: +      "#+LaTeX_header: \\usepackage{tikz}" =?> +      let latexInlines = rawInline "latex" "\\usepackage{tikz}" +          inclList = MetaList [MetaInlines (toList latexInlines)] +          meta = setMeta "header-includes" inclList nullMeta +      in Pandoc meta mempty + +  , "LaTeX_class option is translated to documentclass" =: +      "#+LATEX_CLASS: article" =?> +      let meta = setMeta "documentclass" (MetaString "article") nullMeta +      in Pandoc meta mempty + +  , "LaTeX_class_options is translated to classoption" =: +      "#+LATEX_CLASS_OPTIONS: [a4paper]" =?> +      let meta = setMeta "classoption" (MetaString "a4paper") nullMeta +      in Pandoc meta mempty + +  , "LaTeX_class_options is translated to classoption" =: +      "#+html_head: <meta/>" =?> +      let html = rawInline "html" "<meta/>" +          inclList = MetaList [MetaInlines (toList html)] +          meta = setMeta "header-includes" inclList nullMeta +      in Pandoc meta mempty + +  , "later meta definitions take precedence" =: +      T.unlines [ "#+AUTHOR: this will not be used" +                , "#+author: Max" +                ] =?> +      let author = MetaInlines [Str "Max"] +          meta = setMeta "author" (MetaList [author]) $ nullMeta +      in Pandoc meta mempty + +  , "Logbook drawer" =: +      T.unlines [ "  :LogBook:" +                , "  - State \"DONE\"       from \"TODO\"       [2014-03-03 Mon 11:00]" +                , "  :END:" +                ] =?> +      (mempty::Blocks) + +  , "Drawer surrounded by text" =: +      T.unlines [ "Before" +                , ":PROPERTIES:" +                , ":END:" +                , "After" +                ] =?> +      para "Before" <> para "After" + +  , "Drawer markers must be the only text in the line" =: +      T.unlines [ "  :LOGBOOK: foo" +                , "  :END: bar" +                ] =?> +      para (":LOGBOOK: foo" <> softbreak <> ":END: bar") + +  , "Drawers can be arbitrary" =: +      T.unlines [ ":FOO:" +                , "/bar/" +                , ":END:" +                ] =?> +      divWith (mempty, ["FOO", "drawer"], mempty) (para $ emph "bar") + +  , "Anchor reference" =: +      T.unlines [ "<<link-here>> Target." +                , "" +                , "[[link-here][See here!]]" +                ] =?> +      (para (spanWith ("link-here", [], []) mempty <> "Target.") <> +       para (link "#link-here" "" ("See" <> space <> "here!"))) + +  , "Search links are read as emph" =: +      "[[Wally][Where's Wally?]]" =?> +      (para (emph $ "Where's" <> space <> "Wally?")) + +  , "Link to nonexistent anchor" =: +      T.unlines [ "<<link-here>> Target." +                , "" +                , "[[link$here][See here!]]" +                ] =?> +      (para (spanWith ("link-here", [], []) mempty <> "Target.") <> +       para (emph ("See" <> space <> "here!"))) + +  , "Link abbreviation" =: +      T.unlines [ "#+LINK: wp https://en.wikipedia.org/wiki/%s" +                , "[[wp:Org_mode][Wikipedia on Org-mode]]" +                ] =?> +      (para (link "https://en.wikipedia.org/wiki/Org_mode" "" +                  ("Wikipedia" <> space <> "on" <> space <> "Org-mode"))) + +  , "Link abbreviation, defined after first use" =: +      T.unlines [ "[[zl:non-sense][Non-sense articles]]" +                , "#+LINK: zl http://zeitlens.com/tags/%s.html" +                ] =?> +      (para (link "http://zeitlens.com/tags/non-sense.html" "" +                  ("Non-sense" <> space <> "articles"))) + +  , "Link abbreviation, URL encoded arguments" =: +      T.unlines [ "#+link: expl http://example.com/%h/foo" +                , "[[expl:Hello, World!][Moin!]]" +                ] =?> +      (para (link "http://example.com/Hello%2C%20World%21/foo" "" "Moin!")) + +  , "Link abbreviation, append arguments" =: +      T.unlines [ "#+link: expl http://example.com/" +                , "[[expl:foo][bar]]" +                ] =?> +      (para (link "http://example.com/foo" "" "bar")) +  ] diff --git a/test/Tests/Readers/Org/Shared.hs b/test/Tests/Readers/Org/Shared.hs new file mode 100644 index 000000000..5e8f6dd54 --- /dev/null +++ b/test/Tests/Readers/Org/Shared.hs @@ -0,0 +1,29 @@ +module Tests.Readers.Org.Shared +  ( (=:) +  , org +  , spcSep +  , tagSpan +  ) where + +import Data.List (intersperse) +import Data.Text (Text) +import Tests.Helpers (ToString, purely, test) +import Test.Tasty (TestTree) +import Text.Pandoc (Pandoc, ReaderOptions (readerExtensions), +                    def, getDefaultExtensions, readOrg) +import Text.Pandoc.Builder (Inlines, smallcaps, space, spanWith, str) + +org :: Text -> Pandoc +org = purely $ readOrg def{ readerExtensions = getDefaultExtensions "org" } + +infix 4 =: +(=:) :: ToString c +     => String -> (Text, c) -> TestTree +(=:) = test org + +spcSep :: [Inlines] -> Inlines +spcSep = mconcat . intersperse space + +-- | Create a span for the given tag. +tagSpan :: String -> Inlines +tagSpan t = spanWith ("", ["tag"], [("tag-name", t)]) . smallcaps $ str t diff --git a/test/Tests/Writers/Powerpoint.hs b/test/Tests/Writers/Powerpoint.hs new file mode 100644 index 000000000..46ebd77bd --- /dev/null +++ b/test/Tests/Writers/Powerpoint.hs @@ -0,0 +1,101 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Tests.Writers.Powerpoint (tests) where + +import Control.Exception (throwIO) +import Text.Pandoc +import Text.Pandoc.Builder +import Test.Tasty +import Test.Tasty.HUnit +import Codec.Archive.Zip +import Data.List (isPrefixOf, isSuffixOf) + +----- Number of Slides ----------- + +numberOfSlides :: WriterOptions -> Pandoc -> IO Int +numberOfSlides opts pd = do +  mbs <- runIO $ +        do setUserDataDir $ Just "../data" +           writePowerpoint opts pd +  case mbs of +       Left e   -> throwIO e +       Right bs -> do +         let archive = toArchive bs +         return $ +           length $ +           filter (isSuffixOf ".xml") $ +           filter (isPrefixOf "ppt/slides/slide") $ +           filesInArchive archive + +testNumberOfSlides :: TestName -> Int -> WriterOptions -> Pandoc -> TestTree +testNumberOfSlides name n opts pd = +  testCase name $ do +    n' <- numberOfSlides opts pd +    n' @=? n + +numSlideTests :: TestTree +numSlideTests = testGroup "Number of slides in output" +  [ testNumberOfSlides +    "simple one-slide deck" 1 +    def +    (doc $ para "foo") +  , testNumberOfSlides +    "with metadata (header slide)" 2 +    def +    (setTitle "My Title" $ doc $ para "foo") +  , testNumberOfSlides +    "With h1 slide (using default slide-level)" 2 +    def +    (doc $ header 1 "Header" <> para "foo") +  , testNumberOfSlides +    "With h2 slide (using default slide-level)" 2 +    def +    (doc $ header 1 "Header" <> header 2 "subeader" <> para "foo") +  , testNumberOfSlides +    "With h1 slide (using default slide-level)" 2 +    def +    (doc $ header 1 "Header" <> para "foo") +  , testNumberOfSlides +    "With h2 slide (using default slide-level)" 2 +    def +    (doc $ header 1 "Header" <> header 2 "subeader" <> para "foo") +  , testNumberOfSlides +    "With image slide, no header" 3 +    def +    (doc $ +      para "first slide" <> +      (para $ image "lalune.jpg" "" "") <> +      para "foo") +  , testNumberOfSlides +    "With image slide, header" 3 +    def +    (doc $ +      para "first slide" <> +      header 2 "image header" <> +      (para $ image "lalune.jpg" "" "") <> +      para "foo") +  , testNumberOfSlides +    "With table, no header" 3 +    def +    (doc $ +     para "first slide" <> +     (simpleTable [para "foo" <> para "bar"] [[para "this" <> para "that"]]) <> +     para "foo") +  , testNumberOfSlides +    "With table, header" 3 +    def +    (doc $ +     para "first slide" <> +     header 2 "table header" <> +     (simpleTable [para "foo" <> para "bar"] [[para "this" <> para "that"]]) <> +     para "foo") +  , testNumberOfSlides +    "hrule" 2 +    def +    (doc $ +     para "first slide" <> horizontalRule <> para "last slide") +  ] + + +tests :: [TestTree] +tests = [numSlideTests] diff --git a/test/command/4162.md b/test/command/4162.md new file mode 100644 index 000000000..d88e1ec4e --- /dev/null +++ b/test/command/4162.md @@ -0,0 +1,10 @@ +``` +% pandoc -f html -t native +<div class="line-block">hi<br /><br> + there</div> +^D +[LineBlock + [[Str "hi"] + ,[] + ,[Str "\160there"]]] +``` diff --git a/test/command/4183.md b/test/command/4183.md new file mode 100644 index 000000000..c18320882 --- /dev/null +++ b/test/command/4183.md @@ -0,0 +1,32 @@ +``` +% pandoc -f html -t native +<figure> +  <img src="foo" alt="bar"> +</figure> +^D +[Para [Image ("",[],[]) [] ("foo","fig:")]] +``` + +``` +% pandoc -f html -t native +<figure> +  <img src="foo" alt="bar"> +  <figcaption> +    <div> +      baz +    </div> +  </figcaption> +</figure> +^D +[Para [Image ("",[],[]) [Str "baz"] ("foo","fig:")]] +``` + +``` +% pandoc -f html -t native +<figure> +  <img src="foo"> +  <figcaption><p><em>baz</em></p></figcaption> +</figure> +^D +[Para [Image ("",[],[]) [Emph [Str "baz"]] ("foo","fig:")]] +``` diff --git a/test/command/4208.md b/test/command/4208.md new file mode 100644 index 000000000..9bc519d90 --- /dev/null +++ b/test/command/4208.md @@ -0,0 +1,18 @@ +``` +% pandoc -t latex +What is a _piffle_? Mark the correct answer(s): + +\begin{TAB}(@)[6pt]{|l|c|}{|c|c|c|} +(a) a subnormal woffle                      &  $\Box$  \\ +(b) an infinite-dimensional baffle          &  $\Box$  \\ +(c) an inverted first-order triffle         &  $\Box$  \\ +\end{TAB} +^D +What is a \emph{piffle}? Mark the correct answer(s): + +\begin{TAB}(@)[6pt]{|l|c|}{|c|c|c|} +(a) a subnormal woffle                      &  $\Box$  \\ +(b) an infinite-dimensional baffle          &  $\Box$  \\ +(c) an inverted first-order triffle         &  $\Box$  \\ +\end{TAB} +``` diff --git a/test/docx/sdt_elements.docx b/test/docx/sdt_elements.docxBinary files differ new file mode 100644 index 000000000..9356a6b40 --- /dev/null +++ b/test/docx/sdt_elements.docx diff --git a/test/docx/sdt_elements.native b/test/docx/sdt_elements.native new file mode 100644 index 000000000..7f7768728 --- /dev/null +++ b/test/docx/sdt_elements.native @@ -0,0 +1,10 @@ +[Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0] + [[] + ,[] + ,[]] + [[[Plain [Strong [Str "col1Header"]]] +  ,[Plain [Strong [Str "col2Header"]]] +  ,[Plain [Strong [Str "col3Header"]]]] + ,[[Plain [Str "col1",Space,Str "content"]] +  ,[Plain [Str "Body",Space,Str "copy"]] +  ,[Plain [Str "col3",Space,Str "content"]]]]] diff --git a/test/tables.custom b/test/tables.custom new file mode 100644 index 000000000..410b68d3f --- /dev/null +++ b/test/tables.custom @@ -0,0 +1,201 @@ +<p>Simple table with caption:</p> + +<table> +<caption>Demonstration of simple table syntax.</caption> +<tr class="header"> +<th align="right">Right</th> +<th align="left">Left</th> +<th align="center">Center</th> +<th align="left">Default</th> +</tr> +<tr class="odd"> +<td align="right">12</td> +<td align="left">12</td> +<td align="center">12</td> +<td align="left">12</td> +</tr> +<tr class="even"> +<td align="right">123</td> +<td align="left">123</td> +<td align="center">123</td> +<td align="left">123</td> +</tr> +<tr class="odd"> +<td align="right">1</td> +<td align="left">1</td> +<td align="center">1</td> +<td align="left">1</td> +</tr> +</table + +<p>Simple table without caption:</p> + +<table> +<tr class="header"> +<th align="right">Right</th> +<th align="left">Left</th> +<th align="center">Center</th> +<th align="left">Default</th> +</tr> +<tr class="odd"> +<td align="right">12</td> +<td align="left">12</td> +<td align="center">12</td> +<td align="left">12</td> +</tr> +<tr class="even"> +<td align="right">123</td> +<td align="left">123</td> +<td align="center">123</td> +<td align="left">123</td> +</tr> +<tr class="odd"> +<td align="right">1</td> +<td align="left">1</td> +<td align="center">1</td> +<td align="left">1</td> +</tr> +</table + +<p>Simple table indented two spaces:</p> + +<table> +<caption>Demonstration of simple table syntax.</caption> +<tr class="header"> +<th align="right">Right</th> +<th align="left">Left</th> +<th align="center">Center</th> +<th align="left">Default</th> +</tr> +<tr class="odd"> +<td align="right">12</td> +<td align="left">12</td> +<td align="center">12</td> +<td align="left">12</td> +</tr> +<tr class="even"> +<td align="right">123</td> +<td align="left">123</td> +<td align="center">123</td> +<td align="left">123</td> +</tr> +<tr class="odd"> +<td align="right">1</td> +<td align="left">1</td> +<td align="center">1</td> +<td align="left">1</td> +</tr> +</table + +<p>Multiline table with caption:</p> + +<table> +<caption>Here’s the caption. +It may span multiple lines.</caption> +<col width="15%" /> +<col width="14%" /> +<col width="16%" /> +<col width="34%" /> +<tr class="header"> +<th align="center">Centered +Header</th> +<th align="left">Left +Aligned</th> +<th align="right">Right +Aligned</th> +<th align="left">Default aligned</th> +</tr> +<tr class="odd"> +<td align="center">First</td> +<td align="left">row</td> +<td align="right">12.0</td> +<td align="left">Example of a row that spans +multiple lines.</td> +</tr> +<tr class="even"> +<td align="center">Second</td> +<td align="left">row</td> +<td align="right">5.0</td> +<td align="left">Here’s another one. Note +the blank line between rows.</td> +</tr> +</table + +<p>Multiline table without caption:</p> + +<table> +<col width="15%" /> +<col width="14%" /> +<col width="16%" /> +<col width="34%" /> +<tr class="header"> +<th align="center">Centered +Header</th> +<th align="left">Left +Aligned</th> +<th align="right">Right +Aligned</th> +<th align="left">Default aligned</th> +</tr> +<tr class="odd"> +<td align="center">First</td> +<td align="left">row</td> +<td align="right">12.0</td> +<td align="left">Example of a row that spans +multiple lines.</td> +</tr> +<tr class="even"> +<td align="center">Second</td> +<td align="left">row</td> +<td align="right">5.0</td> +<td align="left">Here’s another one. Note +the blank line between rows.</td> +</tr> +</table + +<p>Table without column headers:</p> + +<table> +<tr class="odd"> +<td align="right">12</td> +<td align="left">12</td> +<td align="center">12</td> +<td align="right">12</td> +</tr> +<tr class="even"> +<td align="right">123</td> +<td align="left">123</td> +<td align="center">123</td> +<td align="right">123</td> +</tr> +<tr class="odd"> +<td align="right">1</td> +<td align="left">1</td> +<td align="center">1</td> +<td align="right">1</td> +</tr> +</table + +<p>Multiline table without column headers:</p> + +<table> +<col width="15%" /> +<col width="14%" /> +<col width="16%" /> +<col width="34%" /> +<tr class="odd"> +<td align="center">First</td> +<td align="left">row</td> +<td align="right">12.0</td> +<td align="left">Example of a row that spans +multiple lines.</td> +</tr> +<tr class="even"> +<td align="center">Second</td> +<td align="left">row</td> +<td align="right">5.0</td> +<td align="left">Here’s another one. Note +the blank line between rows.</td> +</tr> +</table + diff --git a/test/tables.latex b/test/tables.latex index 7e3d9613d..759b35dfa 100644 --- a/test/tables.latex +++ b/test/tables.latex @@ -136,6 +136,7 @@ Table without column headers:  \begin{longtable}[]{@{}rlcr@{}}  \toprule +\endhead  12 & 12 & 12 & 12\tabularnewline  123 & 123 & 123 & 123\tabularnewline  1 & 1 & 1 & 1\tabularnewline @@ -146,6 +147,7 @@ Multiline table without column headers:  \begin{longtable}[]{@{}clrl@{}}  \toprule +\endhead  \begin{minipage}[t]{0.13\columnwidth}\centering  First\strut  \end{minipage} & \begin{minipage}[t]{0.12\columnwidth}\raggedright diff --git a/test/test-pandoc.hs b/test/test-pandoc.hs index ff852ee0e..4cf1a952d 100644 --- a/test/test-pandoc.hs +++ b/test/test-pandoc.hs @@ -33,6 +33,7 @@ import qualified Tests.Writers.Muse  import qualified Tests.Writers.Native  import qualified Tests.Writers.Org  import qualified Tests.Writers.Plain +import qualified Tests.Writers.Powerpoint  import qualified Tests.Writers.RST  import qualified Tests.Writers.TEI  import Text.Pandoc.Shared (inDirectory) @@ -57,6 +58,7 @@ tests = testGroup "pandoc tests" [ Tests.Command.tests            , testGroup "TEI" Tests.Writers.TEI.tests            , testGroup "Muse" Tests.Writers.Muse.tests            , testGroup "FB2" Tests.Writers.FB2.tests +          , testGroup "PowerPoint" Tests.Writers.Powerpoint.tests            ]          , testGroup "Readers"            [ testGroup "LaTeX" Tests.Readers.LaTeX.tests diff --git a/test/writer.custom b/test/writer.custom new file mode 100644 index 000000000..b32d777de --- /dev/null +++ b/test/writer.custom @@ -0,0 +1,783 @@ +<p>This is a set of tests for pandoc. Most of them are adapted from +John Gruber’s markdown test suite.</p> + +<hr/> + +<h1 id="headers">Headers</h1> + +<h2 id="level-2-with-an-embedded-link">Level 2 with an <a href='/url' title=''>embedded link</a></h2> + +<h3 id="level-3-with-emphasis">Level 3 with <em>emphasis</em></h3> + +<h4 id="level-4">Level 4</h4> + +<h5 id="level-5">Level 5</h5> + +<h1 id="level-1">Level 1</h1> + +<h2 id="level-2-with-emphasis">Level 2 with <em>emphasis</em></h2> + +<h3 id="level-3">Level 3</h3> + +<p>with no blank line</p> + +<h2 id="level-2">Level 2</h2> + +<p>with no blank line</p> + +<hr/> + +<h1 id="paragraphs">Paragraphs</h1> + +<p>Here’s a regular paragraph.</p> + +<p>In Markdown 1.0.0 and earlier. Version +8. This line turns into a list item. +Because a hard-wrapped line in the +middle of a paragraph looked like a +list item.</p> + +<p>Here’s one with a bullet. +* criminey.</p> + +<p>There should be a hard line break<br/>here.</p> + +<hr/> + +<h1 id="block-quotes">Block Quotes</h1> + +<p>E-mail style:</p> + +<blockquote> +<p>This is a block quote. +It is pretty short.</p> +</blockquote> + +<blockquote> +<p>Code in a block quote:</p> + +<pre><code>sub status { +    print "working"; +}</code></pre> + +<p>A list:</p> + +<ol> +<li>item one</li> +<li>item two</li> +</ol> + +<p>Nested block quotes:</p> + +<blockquote> +<p>nested</p> +</blockquote> + +<blockquote> +<p>nested</p> +</blockquote> +</blockquote> + +<p>This should not be a block quote: 2 +> 1.</p> + +<p>And a following paragraph.</p> + +<hr/> + +<h1 id="code-blocks">Code Blocks</h1> + +<p>Code:</p> + +<pre><code>---- (should be four hyphens) + +sub status { +    print "working"; +} + +this code block is indented by one tab</code></pre> + +<p>And:</p> + +<pre><code>    this code block is indented by two tabs + +These should not be escaped:  \$ \\ \> \[ \{</code></pre> + +<hr/> + +<h1 id="lists">Lists</h1> + +<h2 id="unordered">Unordered</h2> + +<p>Asterisks tight:</p> + +<ul> +<li>asterisk 1</li> +<li>asterisk 2</li> +<li>asterisk 3</li> +</ul> + +<p>Asterisks loose:</p> + +<ul> +<li><p>asterisk 1</p></li> +<li><p>asterisk 2</p></li> +<li><p>asterisk 3</p></li> +</ul> + +<p>Pluses tight:</p> + +<ul> +<li>Plus 1</li> +<li>Plus 2</li> +<li>Plus 3</li> +</ul> + +<p>Pluses loose:</p> + +<ul> +<li><p>Plus 1</p></li> +<li><p>Plus 2</p></li> +<li><p>Plus 3</p></li> +</ul> + +<p>Minuses tight:</p> + +<ul> +<li>Minus 1</li> +<li>Minus 2</li> +<li>Minus 3</li> +</ul> + +<p>Minuses loose:</p> + +<ul> +<li><p>Minus 1</p></li> +<li><p>Minus 2</p></li> +<li><p>Minus 3</p></li> +</ul> + +<h2 id="ordered">Ordered</h2> + +<p>Tight:</p> + +<ol> +<li>First</li> +<li>Second</li> +<li>Third</li> +</ol> + +<p>and:</p> + +<ol> +<li>One</li> +<li>Two</li> +<li>Three</li> +</ol> + +<p>Loose using tabs:</p> + +<ol> +<li><p>First</p></li> +<li><p>Second</p></li> +<li><p>Third</p></li> +</ol> + +<p>and using spaces:</p> + +<ol> +<li><p>One</p></li> +<li><p>Two</p></li> +<li><p>Three</p></li> +</ol> + +<p>Multiple paragraphs:</p> + +<ol> +<li><p>Item 1, graf one.</p> + +<p>Item 1. graf two. The quick brown fox jumped over the lazy dog’s +back.</p></li> +<li><p>Item 2.</p></li> +<li><p>Item 3.</p></li> +</ol> + +<h2 id="nested">Nested</h2> + +<ul> +<li>Tab + +<ul> +<li>Tab + +<ul> +<li>Tab</li> +</ul></li> +</ul></li> +</ul> + +<p>Here’s another:</p> + +<ol> +<li>First</li> +<li>Second: + +<ul> +<li>Fee</li> +<li>Fie</li> +<li>Foe</li> +</ul></li> +<li>Third</li> +</ol> + +<p>Same thing but with paragraphs:</p> + +<ol> +<li><p>First</p></li> +<li><p>Second:</p> + +<ul> +<li>Fee</li> +<li>Fie</li> +<li>Foe</li> +</ul></li> +<li><p>Third</p></li> +</ol> + +<h2 id="tabs-and-spaces">Tabs and spaces</h2> + +<ul> +<li><p>this is a list item +indented with tabs</p></li> +<li><p>this is a list item +indented with spaces</p> + +<ul> +<li><p>this is an example list item +indented with tabs</p></li> +<li><p>this is an example list item +indented with spaces</p></li> +</ul></li> +</ul> + +<h2 id="fancy-list-markers">Fancy list markers</h2> + +<ol> +<li>begins with 2</li> +<li><p>and now 3</p> + +<p>with a continuation</p> + +<ol> +<li>sublist with roman numerals, +starting with 4</li> +<li>more items + +<ol> +<li>a subsublist</li> +<li>a subsublist</li> +</ol></li> +</ol></li> +</ol> + +<p>Nesting:</p> + +<ol> +<li>Upper Alpha + +<ol> +<li>Upper Roman. + +<ol> +<li>Decimal start with 6 + +<ol> +<li>Lower alpha with paren</li> +</ol></li> +</ol></li> +</ol></li> +</ol> + +<p>Autonumbering:</p> + +<ol> +<li>Autonumber.</li> +<li>More. + +<ol> +<li>Nested.</li> +</ol></li> +</ol> + +<p>Should not be a list item:</p> + +<p>M.A. 2007</p> + +<p>B. Williams</p> + +<hr/> + +<h1 id="definition-lists">Definition Lists</h1> + +<p>Tight using spaces:</p> + +<dl> +<dt>apple</dt> +<dd>red fruit</dd> +<dt>orange</dt> +<dd>orange fruit</dd> +<dt>banana</dt> +<dd>yellow fruit</dd> +</dl> + +<p>Tight using tabs:</p> + +<dl> +<dt>apple</dt> +<dd>red fruit</dd> +<dt>orange</dt> +<dd>orange fruit</dd> +<dt>banana</dt> +<dd>yellow fruit</dd> +</dl> + +<p>Loose:</p> + +<dl> +<dt>apple</dt> +<dd><p>red fruit</p></dd> +<dt>orange</dt> +<dd><p>orange fruit</p></dd> +<dt>banana</dt> +<dd><p>yellow fruit</p></dd> +</dl> + +<p>Multiple blocks with italics:</p> + +<dl> +<dt><em>apple</em></dt> +<dd><p>red fruit</p> + +<p>contains seeds, +crisp, pleasant to taste</p></dd> +<dt><em>orange</em></dt> +<dd><p>orange fruit</p> + +<pre><code>{ orange code block }</code></pre> + +<blockquote> +<p>orange block quote</p> +</blockquote></dd> +</dl> + +<p>Multiple definitions, tight:</p> + +<dl> +<dt>apple</dt> +<dd>red fruit</dd> +<dd>computer</dd> +<dt>orange</dt> +<dd>orange fruit</dd> +<dd>bank</dd> +</dl> + +<p>Multiple definitions, loose:</p> + +<dl> +<dt>apple</dt> +<dd><p>red fruit</p></dd> +<dd><p>computer</p></dd> +<dt>orange</dt> +<dd><p>orange fruit</p></dd> +<dd><p>bank</p></dd> +</dl> + +<p>Blank line after term, indented marker, alternate markers:</p> + +<dl> +<dt>apple</dt> +<dd><p>red fruit</p></dd> +<dd><p>computer</p></dd> +<dt>orange</dt> +<dd><p>orange fruit</p> + +<ol> +<li>sublist</li> +<li>sublist</li> +</ol></dd> +</dl> + +<h1 id="html-blocks">HTML Blocks</h1> + +<p>Simple block on one line:</p> + +<div> +foo</div> + +<p>And nested without indentation:</p> + +<div> +<div> +<div> +<p>foo</p></div></div> + +<div> +bar</div></div> + +<p>Interpreted markdown in a table:</p> + +<table> + +<tr> + +<td> + +This is <em>emphasized</em> + +</td> + +<td> + +And this is <strong>strong</strong> + +</td> + +</tr> + +</table> + +<script type="text/javascript">document.write('This *should not* be interpreted as markdown');</script> + +<p>Here’s a simple block:</p> + +<div> +<p>foo</p></div> + +<p>This should be a code block, though:</p> + +<pre><code><div> +    foo +</div></code></pre> + +<p>As should this:</p> + +<pre><code><div>foo</div></code></pre> + +<p>Now, nested:</p> + +<div> +<div> +<div> +foo</div></div></div> + +<p>This should just be an HTML comment:</p> + +<!-- Comment --> + +<p>Multiline:</p> + +<!-- +Blah +Blah +--> + +<!-- +    This is another comment. +--> + +<p>Code block:</p> + +<pre><code><!-- Comment --></code></pre> + +<p>Just plain comment, with trailing spaces on the line:</p> + +<!-- foo --> + +<p>Code:</p> + +<pre><code><hr /></code></pre> + +<p>Hr’s:</p> + +<hr> + +<hr /> + +<hr /> + +<hr> + +<hr /> + +<hr /> + +<hr class="foo" id="bar" /> + +<hr class="foo" id="bar" /> + +<hr class="foo" id="bar"> + +<hr/> + +<h1 id="inline-markup">Inline Markup</h1> + +<p>This is <em>emphasized</em>, and so <em>is this</em>.</p> + +<p>This is <strong>strong</strong>, and so <strong>is this</strong>.</p> + +<p>An <em><a href='/url' title=''>emphasized link</a></em>.</p> + +<p><strong><em>This is strong and em.</em></strong></p> + +<p>So is <strong><em>this</em></strong> word.</p> + +<p><strong><em>This is strong and em.</em></strong></p> + +<p>So is <strong><em>this</em></strong> word.</p> + +<p>This is code: <code>></code>, <code>$</code>, <code>\</code>, <code>\$</code>, <code><html></code>.</p> + +<p><del>This is <em>strikeout</em>.</del></p> + +<p>Superscripts: a<sup>bc</sup>d a<sup><em>hello</em></sup> a<sup>hello there</sup>.</p> + +<p>Subscripts: H<sub>2</sub>O, H<sub>23</sub>O, H<sub>many of them</sub>O.</p> + +<p>These should not be superscripts or subscripts, +because of the unescaped spaces: a^b c^d, a~b c~d.</p> + +<hr/> + +<h1 id="smart-quotes-ellipses-dashes">Smart quotes, ellipses, dashes</h1> + +<p> said the spider. </p> + +<p>, , and  are letters.</p> + +<p>  and  are names of trees. +So is </p> + +<p> Were you alive in the +70’s?</p> + +<p>Here is some quoted  and a .</p> + +<p>Some dashes: one—two — three—four — five.</p> + +<p>Dashes between numbers: 5–7, 255–66, 1987–1999.</p> + +<p>Ellipses…and…and….</p> + +<hr/> + +<h1 id="latex">LaTeX</h1> + +<ul> +<li></li> +<li>\(2+2=4\)</li> +<li>\(x \in y\)</li> +<li>\(\alpha \wedge \omega\)</li> +<li>\(223\)</li> +<li>\(p\)-Tree</li> +<li>Here’s some display math: +\[\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}\]</li> +<li>Here’s one that has a line break in it: \(\alpha + \omega \times x^2\).</li> +</ul> + +<p>These shouldn’t be math:</p> + +<ul> +<li>To get the famous equation, write <code>$e = mc^2$</code>.</li> +<li>$22,000 is a <em>lot</em> of money. So is $34,000. +(It worked if  is emphasized.)</li> +<li>Shoes ($20) and socks ($5).</li> +<li>Escaped <code>$</code>: $73 <em>this should be emphasized</em> 23$.</li> +</ul> + +<p>Here’s a LaTeX table:</p> + + + +<hr/> + +<h1 id="special-characters">Special Characters</h1> + +<p>Here is some unicode:</p> + +<ul> +<li>I hat: Î</li> +<li>o umlaut: ö</li> +<li>section: §</li> +<li>set membership: ∈</li> +<li>copyright: ©</li> +</ul> + +<p>AT&T has an ampersand in their name.</p> + +<p>AT&T is another way to write it.</p> + +<p>This & that.</p> + +<p>4 < 5.</p> + +<p>6 > 5.</p> + +<p>Backslash: \</p> + +<p>Backtick: `</p> + +<p>Asterisk: *</p> + +<p>Underscore: _</p> + +<p>Left brace: {</p> + +<p>Right brace: }</p> + +<p>Left bracket: [</p> + +<p>Right bracket: ]</p> + +<p>Left paren: (</p> + +<p>Right paren: )</p> + +<p>Greater-than: ></p> + +<p>Hash: #</p> + +<p>Period: .</p> + +<p>Bang: !</p> + +<p>Plus: +</p> + +<p>Minus: -</p> + +<hr/> + +<h1 id="links">Links</h1> + +<h2 id="explicit">Explicit</h2> + +<p>Just a <a href='/url/' title=''>URL</a>.</p> + +<p><a href='/url/' title='title'>URL and title</a>.</p> + +<p><a href='/url/' title='title preceded by two spaces'>URL and title</a>.</p> + +<p><a href='/url/' title='title preceded by a tab'>URL and title</a>.</p> + +<p><a href='/url/' title='title with "quotes" in it'>URL and title</a></p> + +<p><a href='/url/' title='title with single quotes'>URL and title</a></p> + +<p><a href='/url/with_underscore' title=''>with_underscore</a></p> + +<p><a href='mailto:nobody@nowhere.net' title=''>Email link</a></p> + +<p><a href='' title=''>Empty</a>.</p> + +<h2 id="reference">Reference</h2> + +<p>Foo <a href='/url/' title=''>bar</a>.</p> + +<p>With <a href='/url/' title=''>embedded [brackets]</a>.</p> + +<p><a href='/url/' title=''>b</a> by itself should be a link.</p> + +<p>Indented <a href='/url' title=''>once</a>.</p> + +<p>Indented <a href='/url' title=''>twice</a>.</p> + +<p>Indented <a href='/url' title=''>thrice</a>.</p> + +<p>This should [not][] be a link.</p> + +<pre><code>[not]: /url</code></pre> + +<p>Foo <a href='/url/' title='Title with "quotes" inside'>bar</a>.</p> + +<p>Foo <a href='/url/' title='Title with "quote" inside'>biz</a>.</p> + +<h2 id="with-ampersands">With ampersands</h2> + +<p>Here’s a <a href='http://example.com/?foo=1&bar=2' title=''>link with an ampersand in the URL</a>.</p> + +<p>Here’s a link with an amersand in the link text: <a href='http://att.com/' title='AT&T'>AT&T</a>.</p> + +<p>Here’s an <a href='/script?foo=1&bar=2' title=''>inline link</a>.</p> + +<p>Here’s an <a href='/script?foo=1&bar=2' title=''>inline link in pointy braces</a>.</p> + +<h2 id="autolinks">Autolinks</h2> + +<p>With an ampersand: <a href='http://example.com/?foo=1&bar=2' title=''>http://example.com/?foo=1&bar=2</a></p> + +<ul> +<li>In a list?</li> +<li><a href='http://example.com/' title=''>http://example.com/</a></li> +<li>It should.</li> +</ul> + +<p>An e-mail address: <a href='mailto:nobody@nowhere.net' title=''>nobody@nowhere.net</a></p> + +<blockquote> +<p>Blockquoted: <a href='http://example.com/' title=''>http://example.com/</a></p> +</blockquote> + +<p>Auto-links should not occur here: <code><http://example.com/></code></p> + +<pre><code>or here: <http://example.com/></code></pre> + +<hr/> + +<h1 id="images">Images</h1> + +<p>From  by Georges Melies (1902):</p> + +<div class="figure"> +<img src="lalune.jpg" title="fig:Voyage dans la Lune"/> +<p class="caption">lalune</p> +</div> + +<p>Here is a movie <img src='movie.jpg' title=''/> icon.</p> + +<hr/> + +<h1 id="footnotes">Footnotes</h1> + +<p>Here is a footnote reference,<a id="fnref1" href="#fn1"><sup>1</sup></a> and another.<a id="fnref2" href="#fn2"><sup>2</sup></a> +This should <em>not</em> be a footnote reference, because it +contains a space.[^my note] Here is an inline note.<a id="fnref3" href="#fn3"><sup>3</sup></a></p> + +<blockquote> +<p>Notes can go in quotes.<a id="fnref4" href="#fn4"><sup>4</sup></a></p> +</blockquote> + +<ol> +<li>And in list items.<a id="fnref5" href="#fn5"><sup>5</sup></a></li> +</ol> + +<p>This paragraph should not be part of the note, as it is not indented.</p> +<ol class="footnotes"> +<li id="fn1"><p>Here is the footnote. It can go anywhere after the footnote +reference. It need not be placed at the end of the document. <a href="#fnref1">↩</a></p></li> +<li id="fn2"><p>Here’s the long note. This one contains multiple +blocks.</p> + +<p>Subsequent blocks are indented to show that they belong to the +footnote (as with list items).</p> + +<pre><code>  { <code> }</code></pre> + +<p>If you want, you can indent every line, but you can also be +lazy and just indent the first line of each block. <a href="#fnref2">↩</a></p></li> +<li id="fn3"><p>This +is <em>easier</em> to type. Inline notes may contain +<a href='http://google.com' title=''>links</a> and <code>]</code> verbatim characters, +as well as [bracketed text]. <a href="#fnref3">↩</a></p></li> +<li id="fn4"><p>In quote. <a href="#fnref4">↩</a></p></li> +<li id="fn5"><p>In list. <a href="#fnref5">↩</a></p></li> +</ol> + | 
