@@ -1,1262 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−module Tests.Readers.Muse (tests) where
3−
4−import Data.List (intersperse)
5−import Data.Text (Text)
6−import qualified Data.Text as T
7−import Test.Tasty
8−import Test.Tasty.QuickCheck
9−import Tests.Helpers
10−import Text.Pandoc
11−import Text.Pandoc.Arbitrary ()
12−import Text.Pandoc.Builder
13−import Text.Pandoc.Shared (underlineSpan)
14−import Text.Pandoc.Walk (walk)
15−
16−amuse :: Text -> Pandoc
17−amuse = purely $ readMuse def { readerExtensions = extensionsFromList [Ext_amuse]}
18−
19−emacsMuse :: Text -> Pandoc
20−emacsMuse = purely $ readMuse def { readerExtensions = emptyExtensions }
21−
22−infix 4 =:
23−(=:) :: ToString c
24− => String -> (Text, c) -> TestTree
25−(=:) = test amuse
26−
27−spcSep :: [Inlines] -> Inlines
28−spcSep = mconcat . intersperse space
29−
30−-- Tables don't round-trip yet
31−--
32−makeRoundTrip :: Block -> Block
33−makeRoundTrip Table{} = Para [Str "table was here"]
34−makeRoundTrip (OrderedList (start, LowerAlpha, _) items) = OrderedList (start, Decimal, Period) items
35−makeRoundTrip (OrderedList (start, UpperAlpha, _) items) = OrderedList (start, Decimal, Period) items
36−makeRoundTrip x = x
37−
38−-- Demand that any AST produced by Muse reader and written by Muse writer can be read back exactly the same way.
39−-- Currently we remove tables and compare first rewrite to the second.
40−roundTrip :: Block -> Bool
41−roundTrip b = d' == d''
42− where d = walk makeRoundTrip $ Pandoc nullMeta [b]
43− d' = rewrite d
44− d'' = rewrite d'
45− rewrite = amuse . T.pack . (++ "\n") . T.unpack .
46− purely (writeMuse def { writerExtensions = extensionsFromList [Ext_amuse]
47− , writerWrapText = WrapPreserve
48− })
49−
50−tests :: [TestTree]
51−tests =
52− [ testGroup "Inlines"
53− [ "Plain String" =:
54− "Hello, World" =?>
55− para "Hello, World"
56−
57− , "Muse is not XML" =: "<" =?> para "<"
58−
59− , "Emphasis" =:
60− "*Foo bar*" =?>
61− para (emph . spcSep $ ["Foo", "bar"])
62−
63− , "Comma after closing *" =:
64− "Foo *bar*, baz" =?>
65− para ("Foo " <> emph "bar" <> ", baz")
66−
67− , "Letter after closing *" =:
68− "Foo *bar*x baz" =?>
69− para "Foo *bar*x baz"
70−
71− , "Letter before opening *" =:
72− "Foo x*bar* baz" =?>
73− para "Foo x*bar* baz"
74−
75− , "Emphasis tag" =:
76− "<em>Foo bar</em>" =?>
77− para (emph . spcSep $ ["Foo", "bar"])
78−
79− , "Strong" =:
80− "**Cider**" =?>
81− para (strong "Cider")
82−
83− , "Strong tag" =: "<strong>Strong</strong>" =?> para (strong "Strong")
84−
85− , "Strong Emphasis" =:
86− "***strength***" =?>
87− para (strong . emph $ "strength")
88−
89− , test emacsMuse "Underline"
90− ("_Underline_" =?> para (underlineSpan "Underline"))
91−
92− , "Superscript tag" =: "<sup>Superscript</sup>" =?> para (superscript "Superscript")
93−
94− , "Subscript tag" =: "<sub>Subscript</sub>" =?> para (subscript "Subscript")
95−
96− , "Strikeout tag" =: "<del>Strikeout</del>" =?> para (strikeout "Strikeout")
97−
98− , "Opening inline tags" =: "foo <em> bar <strong>baz" =?> para "foo <em> bar <strong>baz"
99−
100− , "Closing inline tags" =: "foo </em> bar </strong>baz" =?> para "foo </em> bar </strong>baz"
101−
102− , "Tag soup" =: "foo <em> bar </strong>baz" =?> para "foo <em> bar </strong>baz"
103−
104− -- Both inline tags must be within the same paragraph
105− , "No multiparagraph inline tags" =:
106− T.unlines [ "First line"
107− , "<em>Second line"
108− , ""
109− , "Fourth line</em>"
110− ] =?>
111− para "First line\n<em>Second line" <>
112− para "Fourth line</em>"
113−
114− , "Linebreak" =: "Line <br> break" =?> para ("Line" <> linebreak <> "break")
115−
116− , "Trailing whitespace inside paragraph" =:
117− T.unlines [ "First line " -- trailing whitespace here
118− , "second line"
119− ]
120− =?> para "First line\nsecond line"
121−
122− , "Non-breaking space" =: "Foo~~bar" =?> para "Foo\160bar"
123− , "Single ~" =: "Foo~bar" =?> para "Foo~bar"
124−
125− , testGroup "Code markup"
126− [ "Code" =: "=foo(bar)=" =?> para (code "foo(bar)")
127−
128− , "Not code" =: "a=b= =c=d" =?> para (text "a=b= =c=d")
129−
130− -- Emacs Muse 3.20 parses this as code, we follow Amusewiki
131− , "Not code if closing = is detached" =: "=this is not a code =" =?> para "=this is not a code ="
132−
133− , "Not code if opening = is detached" =: "= this is not a code=" =?> para "= this is not a code="
134−
135− , "Code if followed by comma" =:
136− "Foo =bar=, baz" =?>
137− para (text "Foo " <> code "bar" <> text ", baz")
138−
139− , "One character code" =: "=c=" =?> para (code "c")
140−
141− , "Three = characters is not a code" =: "===" =?> para "==="
142−
143− , "Multiline code markup" =:
144− "foo =bar\nbaz= end of code" =?>
145− para (text "foo " <> code "bar\nbaz" <> text " end of code")
146−
147−{- Emacs Muse 3.20 has a bug: it publishes
148− - <p>foo <code>bar
149− -
150− - baz</code> foo</p>
151− - which is displayed as one paragraph by browsers.
152− - We follow Amusewiki here and avoid joining paragraphs.
153− -}
154− , "No multiparagraph code" =:
155− T.unlines [ "foo =bar"
156− , ""
157− , "baz= foo"
158− ] =?>
159− para "foo =bar" <>
160− para "baz= foo"
161−
162− , "Code at the beginning of paragraph but not first column" =:
163− " - =foo=" =?> bulletList [ para $ code "foo" ]
164− ]
165−
166− , "Code tag" =: "<code>foo(bar)</code>" =?> para (code "foo(bar)")
167−
168− , "Verbatim tag" =: "*<verbatim>*</verbatim>*" =?> para (emph "*")
169−
170− , "Verbatim inside code" =: "<code><verbatim>foo</verbatim></code>" =?> para (code "<verbatim>foo</verbatim>")
171−
172− , "Verbatim tag after text" =: "Foo <verbatim>bar</verbatim>" =?> para "Foo bar"
173−
174− , "Class tag" =: "<class name=\"foo\">bar</class>" =?> para (spanWith ("", ["foo"], []) "bar")
175− , "Class tag without name" =: "<class>foobar</class>" =?> para (spanWith ("", [], []) "foobar")
176−
177− -- <em> tag should match with the last </em> tag, not verbatim one
178− , "Nested \"</em>\" inside em tag" =: "<em>foo<verbatim></em></verbatim>bar</em>" =?> para (emph "foo</em>bar")
179−
180− , testGroup "Links"
181− [ "Link without description" =:
182− "[[https://amusewiki.org/]]" =?>
183− para (link "https://amusewiki.org/" "" (str "https://amusewiki.org/"))
184− , "Link with description" =:
185− "[[https://amusewiki.org/][A Muse Wiki]]" =?>
186− para (link "https://amusewiki.org/" "" (text "A Muse Wiki"))
187− , "Image" =:
188− "[[image.jpg]]" =?>
189− para (image "image.jpg" "" mempty)
190− , "Image with description" =:
191− "[[image.jpg][Image]]" =?>
192− para (image "image.jpg" "" (text "Image"))
193− , "Image link" =:
194− "[[URL:image.jpg]]" =?>
195− para (link "image.jpg" "" (str "image.jpg"))
196− , "Image link with description" =:
197− "[[URL:image.jpg][Image]]" =?>
198− para (link "image.jpg" "" (text "Image"))
199− -- Implicit links are supported in Emacs Muse, but not in Amusewiki:
200− -- https://github.com/melmothx/text-amuse/issues/18
201− --
202− -- This test also makes sure '=' without whitespace is not treated as code markup
203− , "No implicit links" =: "http://example.org/index.php?action=view&id=1"
204− =?> para "http://example.org/index.php?action=view&id=1"
205− ]
206−
207− , testGroup "Literal"
208− [ test emacsMuse "Inline literal"
209− ("Foo<literal style=\"html\">lit</literal>bar" =?>
210− para (text "Foo" <> rawInline "html" "lit" <> text "bar"))
211− ]
212− ]
213−
214− , testGroup "Blocks"
215− [ testProperty "Round trip" roundTrip,
216− "Block elements end paragraphs" =:
217− T.unlines [ "First paragraph"
218− , "----"
219− , "Second paragraph"
220− ] =?> para (text "First paragraph") <> horizontalRule <> para (text "Second paragraph")
221− , testGroup "Horizontal rule"
222− [ "Less than 4 dashes is not a horizontal rule" =: "---" =?> para (text "---")
223− , "4 dashes is a horizontal rule" =: "----" =?> horizontalRule
224− , "5 dashes is a horizontal rule" =: "-----" =?> horizontalRule
225− , "4 dashes with spaces is a horizontal rule" =: "---- " =?> horizontalRule
226− ]
227− , testGroup "Paragraphs"
228− [ "Simple paragraph" =:
229− T.unlines [ "First line"
230− , "second line."
231− ] =?>
232− para "First line\nsecond line."
233− , "Indented paragraph" =:
234− T.unlines [ " First line"
235− , "second line."
236− ] =?>
237− para "First line\nsecond line."
238− -- Emacs Muse starts a blockquote on the second line.
239− -- We copy Amusewiki behavior and require a blank line to start a blockquote.
240− , "Indentation in the middle of paragraph" =:
241− T.unlines [ "First line"
242− , " second line"
243− , "third line"
244− ] =?>
245− para "First line\nsecond line\nthird line"
246− , "Quote" =:
247− " This is a quotation\n" =?>
248− blockQuote (para "This is a quotation")
249− , "Indentation does not indicate quote inside quote tag" =:
250− T.unlines [ "<quote>"
251− , " Not a nested quote"
252− , "</quote>"
253− ] =?>
254− blockQuote (para "Not a nested quote")
255− , "Multiline quote" =:
256− T.unlines [ " This is a quotation"
257− , " with a continuation"
258− ] =?>
259− blockQuote (para "This is a quotation\nwith a continuation")
260− , testGroup "Div"
261− [ "Div without id" =:
262− T.unlines [ "<div>"
263− , "Foo bar"
264− , "</div>"
265− ] =?>
266− divWith nullAttr (para "Foo bar")
267− , "Div with id" =:
268− T.unlines [ "<div id=\"foo\">"
269− , "Foo bar"
270− , "</div>"
271− ] =?>
272− divWith ("foo", [], []) (para "Foo bar")
273− ]
274− , "Verse" =:
275− T.unlines [ "> This is"
276− , "> First stanza"
277− , ">" -- Emacs produces verbatim ">" here, we follow Amusewiki
278− , "> And this is"
279− , "> Second stanza"
280− , ">"
281− , ""
282− , ">"
283− , ""
284− , "> Another verse"
285− , "> is here"
286− ] =?>
287− lineBlock [ "This is"
288− , "First stanza"
289− , ""
290− , "And this is"
291− , "\160\160Second stanza"
292− , ""
293− ] <>
294− lineBlock [ "" ] <>
295− lineBlock [ "Another verse"
296− , "\160\160\160is here"
297− ]
298− ]
299− , "Verse in list" =: " - > foo" =?> bulletList [ lineBlock [ "foo" ] ]
300− , "Verse line starting with emphasis" =: "> *foo* bar" =?> lineBlock [ emph "foo" <> text " bar" ]
301− , "Multiline verse in list" =:
302− T.unlines [ " - > foo"
303− , " > bar"
304− ] =?>
305− bulletList [ lineBlock [ "foo", "bar" ] ]
306− , "Paragraph after verse in list" =:
307− T.unlines [ " - > foo"
308− , " bar"
309− ] =?>
310− bulletList [ lineBlock [ "foo" ] <> para "bar" ]
311− , "Empty quote tag" =:
312− T.unlines [ "<quote>"
313− , "</quote>"
314− ]
315− =?> blockQuote mempty
316− , "Quote tag" =:
317− T.unlines [ "<quote>"
318− , "Hello, world"
319− , "</quote>"
320− ]
321− =?> blockQuote (para $ text "Hello, world")
322− , "Nested quote tag" =:
323− T.unlines [ "<quote>"
324− , "foo"
325− , "<quote>"
326− , "bar"
327− , "</quote>"
328− , "baz"
329− , "</quote>"
330− ] =?>
331− blockQuote (para "foo" <> blockQuote (para "bar") <> para "baz")
332− , "Indented quote inside list" =:
333− T.unlines [ " - <quote>"
334− , " foo"
335− , " </quote>"
336− ] =?>
337− bulletList [ blockQuote (para "foo") ]
338− , "Verse tag" =:
339− T.unlines [ "<verse>"
340− , ""
341− , "Foo bar baz"
342− , " One two three"
343− , ""
344− , "</verse>"
345− ] =?>
346− lineBlock [ ""
347− , text "Foo bar baz"
348− , text "\160\160One two three"
349− , ""
350− ]
351− , "Verse tag with empty line inside" =:
352− T.unlines [ "<verse>"
353− , ""
354− , "</verse>"
355− ] =?>
356− lineBlock [ "" ]
357− , testGroup "Example"
358− [ "Braces on separate lines" =:
359− T.unlines [ "{{{"
360− , "Example line"
361− , "}}}"
362− ] =?>
363− codeBlock "Example line"
364− , "Spaces after opening braces" =:
365− T.unlines [ "{{{ "
366− , "Example line"
367− , "}}}"
368− ] =?>
369− codeBlock "Example line"
370− , "One blank line in the beginning" =:
371− T.unlines [ "{{{"
372− , ""
373− , "Example line"
374− , "}}}"
375− ] =?>
376− codeBlock "\nExample line"
377− , "One blank line in the end" =:
378− T.unlines [ "{{{"
379− , "Example line"
380− , ""
381− , "}}}"
382− ] =?>
383− codeBlock "Example line\n"
384− -- Amusewiki requires braces to be on separate line,
385− -- this is an extension.
386− , "One line" =:
387− "{{{Example line}}}" =?>
388− codeBlock "Example line"
389− ]
390− , testGroup "Example tag"
391− [ "Tags on separate lines" =:
392− T.unlines [ "<example>"
393− , "Example line"
394− , "</example>"
395− ] =?>
396− codeBlock "Example line"
397− , "One line" =:
398− "<example>Example line</example>" =?>
399− codeBlock "Example line"
400− , "One blank line in the beginning" =:
401− T.unlines [ "<example>"
402− , ""
403− , "Example line"
404− , "</example>"
405− ] =?>
406− codeBlock "\nExample line"
407− , "One blank line in the end" =:
408− T.unlines [ "<example>"
409− , "Example line"
410− , ""
411− , "</example>"
412− ] =?>
413− codeBlock "Example line\n"
414− , "Example inside list" =:
415− T.unlines [ " - <example>"
416− , " foo"
417− , " </example>"
418− ] =?>
419− bulletList [ codeBlock "foo" ]
420− , "Empty example inside list" =:
421− T.unlines [ " - <example>"
422− , " </example>"
423− ] =?>
424− bulletList [ codeBlock "" ]
425− , "Example inside list with empty lines" =:
426− T.unlines [ " - <example>"
427− , " foo"
428− , " </example>"
429− , ""
430− , " bar"
431− , ""
432− , " <example>"
433− , " baz"
434− , " </example>"
435− ] =?>
436− bulletList [ codeBlock "foo" <> para "bar" <> codeBlock "baz" ]
437− , "Indented example inside list" =:
438− T.unlines [ " - <example>"
439− , " foo"
440− , " </example>"
441− ] =?>
442− bulletList [ codeBlock "foo" ]
443− , "Example inside definition list" =:
444− T.unlines [ " foo :: <example>"
445− , " bar"
446− , " </example>"
447− ] =?>
448− definitionList [ ("foo", [codeBlock "bar"]) ]
449− , "Example inside list definition with empty lines" =:
450− T.unlines [ " term :: <example>"
451− , " foo"
452− , " </example>"
453− , ""
454− , " bar"
455− , ""
456− , " <example>"
457− , " baz"
458− , " </example>"
459− ] =?>
460− definitionList [ ("term", [codeBlock "foo" <> para "bar" <> codeBlock "baz"]) ]
461− , "Example inside note" =:
462− T.unlines [ "Foo[1]"
463− , ""
464− , "[1] <example>"
465− , " bar"
466− , " </example>"
467− ] =?>
468− para ("Foo" <> note (codeBlock "bar"))
469− ]
470− , testGroup "Literal blocks"
471− [ test emacsMuse "Literal block"
472− (T.unlines [ "<literal style=\"latex\">"
473− , "\\newpage"
474− , "</literal>"
475− ] =?>
476− rawBlock "latex" "\\newpage")
477− ]
478− , "Center" =:
479− T.unlines [ "<center>"
480− , "Hello, world"
481− , "</center>"
482− ] =?>
483− para (text "Hello, world")
484− , "Right" =:
485− T.unlines [ "<right>"
486− , "Hello, world"
487− , "</right>"
488− ] =?>
489− para (text "Hello, world")
490− , testGroup "Comments"
491− [ "Comment tag" =: "<comment>\nThis is a comment\n</comment>" =?> (mempty::Blocks)
492− , "Line comment" =: "; Comment" =?> (mempty::Blocks)
493− , "Empty comment" =: ";" =?> (mempty::Blocks)
494− , "Text after empty comment" =: ";\nfoo" =?> para "foo" -- Make sure we don't consume newline while looking for whitespace
495− , "Not a comment (does not start with a semicolon)" =: " ; Not a comment" =?> para (text "; Not a comment")
496− , "Not a comment (has no space after semicolon)" =: ";Not a comment" =?> para (text ";Not a comment")
497− ]
498− , testGroup "Headers"
499− [ "Part" =:
500− "* First level" =?>
501− header 1 "First level"
502− , "Chapter" =:
503− "** Second level" =?>
504− header 2 "Second level"
505− , "Section" =:
506− "*** Third level" =?>
507− header 3 "Third level"
508− , "Subsection" =:
509− "**** Fourth level" =?>
510− header 4 "Fourth level"
511− , "Subsubsection" =:
512− "***** Fifth level" =?>
513− header 5 "Fifth level"
514− , "Whitespace is required after *" =: "**Not a header" =?> para "**Not a header"
515− , "No headers in footnotes" =:
516− T.unlines [ "Foo[1]"
517− , "[1] * Bar"
518− ] =?>
519− para (text "Foo" <>
520− note (para "* Bar"))
521− , "No headers in quotes" =:
522− T.unlines [ "<quote>"
523− , "* Hi"
524− , "</quote>"
525− ] =?>
526− blockQuote (para "* Hi")
527− , "Headers consume anchors" =:
528− T.unlines [ "** Foo"
529− , "#bar"
530− ] =?>
531− headerWith ("bar",[],[]) 2 "Foo"
532− , "Headers don't consume anchors separated with a blankline" =:
533− T.unlines [ "** Foo"
534− , ""
535− , "#bar"
536− ] =?>
537− header 2 "Foo" <>
538− para (spanWith ("bar", [], []) mempty)
539− , "Headers terminate lists" =:
540− T.unlines [ " - foo"
541− , "* bar"
542− ] =?>
543− bulletList [ para "foo" ] <>
544− header 1 "bar"
545− ]
546− , testGroup "Directives"
547− [ "Title" =:
548− "#title Document title" =?>
549− let titleInline = toList "Document title"
550− meta = setMeta "title" (MetaInlines titleInline) nullMeta
551− in Pandoc meta mempty
552− -- Emacs Muse documentation says that "You can use any combination
553− -- of uppercase and lowercase letters for directives",
554− -- but also allows '-', which is not documented, but used for disable-tables.
555− , test emacsMuse "Disable tables"
556− ("#disable-tables t" =?>
557− Pandoc (setMeta "disable-tables" (MetaInlines $ toList "t") nullMeta) mempty)
558− , "Multiple directives" =:
559− T.unlines [ "#title Document title"
560− , "#subtitle Document subtitle"
561− ] =?>
562− Pandoc (setMeta "title" (MetaInlines $ toList "Document title") $
563− setMeta "subtitle" (MetaInlines $ toList "Document subtitle") nullMeta) mempty
564− , "Multiline directive" =:
565− T.unlines [ "#title Document title"
566− , "#notes First line"
567− , "and second line"
568− , "#author Name"
569− ] =?>
570− Pandoc (setMeta "title" (MetaInlines $ toList "Document title") $
571− setMeta "notes" (MetaInlines $ toList "First line\nand second line") $
572− setMeta "author" (MetaInlines $ toList "Name") nullMeta) mempty
573− ]
574− , testGroup "Anchors"
575− [ "Anchor" =:
576− T.unlines [ "; A comment to make sure anchor is not parsed as a directive"
577− , "#anchor Target"
578− ] =?>
579− para (spanWith ("anchor", [], []) mempty <> "Target")
580− , "Anchor cannot start with a number" =:
581− T.unlines [ "; A comment to make sure anchor is not parsed as a directive"
582− , "#0notanchor Target"
583− ] =?>
584− para "#0notanchor Target"
585− , "Not anchor if starts with a space" =:
586− " #notanchor Target" =?>
587− para "#notanchor Target"
588− , "Anchor inside a paragraph" =:
589− T.unlines [ "Paragraph starts here"
590− , "#anchor and ends here."
591− ] =?>
592− para ("Paragraph starts here\n" <> spanWith ("anchor", [], []) mempty <> "and ends here.")
593− ]
594− , testGroup "Footnotes"
595− [ "Simple footnote" =:
596− T.unlines [ "Here is a footnote[1]."
597− , ""
598− , "[1] Footnote contents"
599− ] =?>
600− para (text "Here is a footnote" <>
601− note (para "Footnote contents") <>
602− str ".")
603− , "Recursive footnote" =:
604− T.unlines [ "Start recursion here[1]"
605− , ""
606− , "[1] Recursion continues here[1]"
607− ] =?>
608− para (text "Start recursion here" <>
609− note (para "Recursion continues here[1]"))
610− , "No zero footnotes" =:
611− T.unlines [ "Here is a footnote[0]."
612− , ""
613− , "[0] Footnote contents"
614− ] =?>
615− para "Here is a footnote[0]." <>
616− para "[0] Footnote contents"
617− , "Footnotes can't start with zero" =:
618− T.unlines [ "Here is a footnote[01]."
619− , ""
620− , "[01] Footnote contents"
621− ] =?>
622− para "Here is a footnote[01]." <>
623− para "[01] Footnote contents"
624− , testGroup "Multiparagraph footnotes"
625− [ "Amusewiki multiparagraph footnotes" =:
626− T.unlines [ "Multiparagraph[1] footnotes[2]"
627− , ""
628− , "[1] First footnote paragraph"
629− , ""
630− , " Second footnote paragraph"
631− , "with continuation"
632− , ""
633− , "Not a note"
634− , "[2] Second footnote"
635− ] =?>
636− para (text "Multiparagraph" <>
637− note (para "First footnote paragraph" <>
638− para "Second footnote paragraph\nwith continuation") <>
639− text " footnotes" <>
640− note (para "Second footnote")) <>
641− para (text "Not a note")
642−
643− -- Verse requires precise indentation, so it is good to test indentation requirements
644− , "Note continuation with verse" =:
645− T.unlines [ "Foo[1]"
646− , ""
647− , "[1] Bar"
648− , ""
649− , " > Baz"
650− ] =?>
651− para ("Foo" <> note (para "Bar" <> lineBlock ["Baz"]))
652− , test emacsMuse "Emacs multiparagraph footnotes"
653− (T.unlines
654− [ "First footnote reference[1] and second footnote reference[2]."
655− , ""
656− , "[1] First footnote paragraph"
657− , ""
658− , "Second footnote"
659− , "paragraph"
660− , ""
661− , "[2] Third footnote paragraph"
662− , ""
663− , "Fourth footnote paragraph"
664− ] =?>
665− para (text "First footnote reference" <>
666− note (para "First footnote paragraph" <>
667− para "Second footnote\nparagraph") <>
668− text " and second footnote reference" <>
669− note (para "Third footnote paragraph" <>
670− para "Fourth footnote paragraph") <>
671− text "."))
672− ]
673− ]
674− ]
675− , testGroup "Tables"
676− [ "Two cell table" =:
677− "One | Two" =?>
678− table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
679− []
680− [[plain "One", plain "Two"]]
681− , "Table with multiple words" =:
682− "One two | three four" =?>
683− table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
684− []
685− [[plain "One two", plain "three four"]]
686− , "Not a table" =:
687− "One| Two" =?>
688− para (text "One| Two")
689− , "Not a table again" =:
690− "One |Two" =?>
691− para (text "One |Two")
692− , "Two line table" =:
693− T.unlines
694− [ "One | Two"
695− , "Three | Four"
696− ] =?>
697− table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
698− []
699− [[plain "One", plain "Two"],
700− [plain "Three", plain "Four"]]
701− , "Table with one header" =:
702− T.unlines
703− [ "First || Second"
704− , "Third | Fourth"
705− ] =?>
706− table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
707− [plain "First", plain "Second"]
708− [[plain "Third", plain "Fourth"]]
709− , "Table with two headers" =:
710− T.unlines
711− [ "First || header"
712− , "Second || header"
713− , "Foo | bar"
714− ] =?>
715− table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
716− [plain "First", plain "header"]
717− [[plain "Second", plain "header"],
718− [plain "Foo", plain "bar"]]
719− , "Header and footer reordering" =:
720− T.unlines
721− [ "Foo ||| bar"
722− , "Baz || foo"
723− , "Bar | baz"
724− ] =?>
725− table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
726− [plain "Baz", plain "foo"]
727− [[plain "Bar", plain "baz"],
728− [plain "Foo", plain "bar"]]
729− , "Table with caption" =:
730− T.unlines
731− [ "Foo || bar || baz"
732− , "First | row | here"
733− , "Second | row | there"
734− , "|+ Table caption +|"
735− ] =?>
736− table (text "Table caption") (replicate 3 (AlignDefault, 0.0))
737− [plain "Foo", plain "bar", plain "baz"]
738− [[plain "First", plain "row", plain "here"],
739− [plain "Second", plain "row", plain "there"]]
740− , "Caption without table" =:
741− "|+ Foo bar baz +|" =?>
742− table (text "Foo bar baz") [] [] []
743− , "Table indented with space" =:
744− T.unlines
745− [ " Foo | bar"
746− , " Baz | foo"
747− , " Bar | baz"
748− ] =?>
749− table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
750− []
751− [[plain "Foo", plain "bar"],
752− [plain "Baz", plain "foo"],
753− [plain "Bar", plain "baz"]]
754− , "Empty cells" =:
755− T.unlines
756− [ " | Foo"
757− , " |"
758− , " bar |"
759− , " || baz"
760− ] =?>
761− table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
762− [plain "", plain "baz"]
763− [[plain "", plain "Foo"],
764− [plain "", plain ""],
765− [plain "bar", plain ""]]
766− ]
767− , testGroup "Lists"
768− [ "Bullet list" =:
769− T.unlines
770− [ " - Item1"
771− , ""
772− , " - Item2"
773− ] =?>
774− bulletList [ para "Item1"
775− , para "Item2"
776− ]
777− , "Ordered list" =:
778− T.unlines
779− [ " 1. Item1"
780− , ""
781− , " 2. Item2"
782− ] =?>
783− orderedListWith (1, Decimal, Period) [ para "Item1"
784− , para "Item2"
785− ]
786− , "Ordered list with implicit numbers" =:
787− T.unlines
788− [ " 1. Item1"
789− , ""
790− , " 1. Item2"
791− , ""
792− , " 1. Item3"
793− ] =?>
794− orderedListWith (1, Decimal, Period) [ para "Item1"
795− , para "Item2"
796− , para "Item3"
797− ]
798− , "Ordered list with roman numerals" =:
799− T.unlines
800− [ " i. First"
801− , " ii. Second"
802− , " iii. Third"
803− , " iv. Fourth"
804− ] =?>
805− orderedListWith (1, LowerRoman, Period) [ para "First"
806− , para "Second"
807− , para "Third"
808− , para "Fourth"
809− ]
810− , "Bullet list with empty items" =:
811− T.unlines
812− [ " -"
813− , ""
814− , " - Item2"
815− ] =?>
816− bulletList [ mempty
817− , para "Item2"
818− ]
819− , "Ordered list with empty items" =:
820− T.unlines
821− [ " 1."
822− , ""
823− , " 2."
824− , ""
825− , " 3. Item3"
826− ] =?>
827− orderedListWith (1, Decimal, Period) [ mempty
828− , mempty
829− , para "Item3"
830− ]
831− , "Bullet list with last item empty" =:
832− T.unlines
833− [ " -"
834− , ""
835− , "foo"
836− ] =?>
837− bulletList [ mempty ] <>
838− para "foo"
839− , testGroup "Nested lists"
840− [ "Nested bullet list" =:
841− T.unlines [ " - Item1"
842− , " - Item2"
843− , " - Item3"
844− , " - Item4"
845− , " - Item5"
846− , " - Item6"
847− ] =?>
848− bulletList [ para "Item1" <>
849− bulletList [ para "Item2" <>
850− bulletList [ para "Item3" ]
851− , para "Item4" <>
852− bulletList [ para "Item5" ]
853− ]
854− , para "Item6"
855− ]
856− , "Nested ordered list" =:
857− T.unlines [ " 1. Item1"
858− , " 1. Item2"
859− , " 1. Item3"
860− , " 2. Item4"
861− , " 1. Item5"
862− , " 2. Item6"
863− ] =?>
864− orderedListWith (1, Decimal, Period) [ para "Item1" <>
865− orderedListWith (1, Decimal, Period) [ para "Item2" <>
866− orderedListWith (1, Decimal, Period) [ para "Item3" ]
867− , para "Item4" <>
868− orderedListWith (1, Decimal, Period) [ para "Item5" ]
869− ]
870− , para "Item6"
871− ]
872− , "Mixed nested list" =:
873− T.unlines
874− [ " - Item1"
875− , " - Item2"
876− , " - Item3"
877− , " - Item4"
878− , " 1. Nested"
879− , " 2. Ordered"
880− , " 3. List"
881− ] =?>
882− bulletList [ mconcat [ para "Item1"
883− , bulletList [ para "Item2"
884− , para "Item3"
885− ]
886− ]
887− , mconcat [ para "Item4"
888− , orderedListWith (1, Decimal, Period) [ para "Nested"
889− , para "Ordered"
890− , para "List"
891− ]
892− ]
893− ]
894− , "Text::Amuse includes only one space in list marker" =:
895− T.unlines
896− [ " - First item"
897− , " - Nested item"
898− ] =?>
899− bulletList [ para "First item" <> bulletList [ para "Nested item"]]
900− ]
901− , "List continuation" =:
902− T.unlines
903− [ " - a"
904− , ""
905− , " b"
906− , ""
907− , " c"
908− ] =?>
909− bulletList [ mconcat [ para "a"
910− , para "b"
911− , para "c"
912− ]
913− ]
914− , "List continuation afeter nested list" =:
915− T.unlines
916− [ " - - foo"
917− , ""
918− , " bar"
919− ] =?>
920− bulletList [ bulletList [ para "foo" ] <>
921− para "bar"
922− ]
923− -- Emacs Muse allows to separate lists with two or more blank lines.
924− -- Text::Amuse (Amusewiki engine) always creates a single list as of version 0.82.
925− -- pandoc follows Emacs Muse behavior
926− , testGroup "Blank lines"
927− [ "Blank lines between list items are not required" =:
928− T.unlines
929− [ " - Foo"
930− , " - Bar"
931− ] =?>
932− bulletList [ para "Foo"
933− , para "Bar"
934− ]
935− , "One blank line between list items is allowed" =:
936− T.unlines
937− [ " - Foo"
938− , ""
939− , " - Bar"
940− ] =?>
941− bulletList [ para "Foo"
942− , para "Bar"
943− ]
944− , "Two blank lines separate lists" =:
945− T.unlines
946− [ " - Foo"
947− , ""
948− , ""
949− , " - Bar"
950− ] =?>
951− bulletList [ para "Foo" ] <> bulletList [ para "Bar" ]
952− , "No blank line after multiline first item" =:
953− T.unlines
954− [ " - Foo"
955− , " bar"
956− , " - Baz"
957− ] =?>
958− bulletList [ para "Foo\nbar"
959− , para "Baz"
960− ]
961− , "One blank line after multiline first item" =:
962− T.unlines
963− [ " - Foo"
964− , " bar"
965− , ""
966− , " - Baz"
967− ] =?>
968− bulletList [ para "Foo\nbar"
969− , para "Baz"
970− ]
971− , "Two blank lines after multiline first item" =:
972− T.unlines
973− [ " - Foo"
974− , " bar"
975− , ""
976− , ""
977− , " - Baz"
978− ] =?>
979− bulletList [ para "Foo\nbar" ] <> bulletList [ para "Baz" ]
980− , "No blank line after list continuation" =:
981− T.unlines
982− [ " - Foo"
983− , ""
984− , " bar"
985− , " - Baz"
986− ] =?>
987− bulletList [ para "Foo" <> para "bar"
988− , para "Baz"
989− ]
990− , "One blank line after list continuation" =:
991− T.unlines
992− [ " - Foo"
993− , ""
994− , " bar"
995− , ""
996− , " - Baz"
997− ] =?>
998− bulletList [ para "Foo" <> para "bar"
999− , para "Baz"
1000− ]
1001− , "Two blank lines after list continuation" =:
1002− T.unlines
1003− [ " - Foo"
1004− , ""
1005− , " bar"
1006− , ""
1007− , ""
1008− , " - Baz"
1009− ] =?>
1010− bulletList [ para "Foo" <> para "bar" ] <> bulletList [ para "Baz" ]
1011− , "No blank line after blockquote" =:
1012− T.unlines
1013− [ " - <quote>"
1014− , " foo"
1015− , " </quote>"
1016− , " - bar"
1017− ] =?>
1018− bulletList [ blockQuote $ para "foo", para "bar" ]
1019− , "One blank line after blockquote" =:
1020− T.unlines
1021− [ " - <quote>"
1022− , " foo"
1023− , " </quote>"
1024− , ""
1025− , " - bar"
1026− ] =?>
1027− bulletList [ blockQuote $ para "foo", para "bar" ]
1028− , "Two blank lines after blockquote" =:
1029− T.unlines
1030− [ " - <quote>"
1031− , " foo"
1032− , " </quote>"
1033− , ""
1034− , ""
1035− , " - bar"
1036− ] =?>
1037− bulletList [ blockQuote $ para "foo" ] <> bulletList [ para "bar" ]
1038− , "No blank line after verse" =:
1039− T.unlines
1040− [ " - > foo"
1041− , " - bar"
1042− ] =?>
1043− bulletList [ lineBlock [ "foo" ], para "bar" ]
1044− , "One blank line after verse" =:
1045− T.unlines
1046− [ " - > foo"
1047− , ""
1048− , " - bar"
1049− ] =?>
1050− bulletList [ lineBlock [ "foo" ], para "bar" ]
1051− , "Two blank lines after verse" =:
1052− T.unlines
1053− [ " - > foo"
1054− , ""
1055− , ""
1056− , " - bar"
1057− ] =?>
1058− bulletList [ lineBlock [ "foo" ] ] <> bulletList [ para "bar" ]
1059− ]
1060− -- Test that definition list requires a leading space.
1061− -- Emacs Muse does not require a space, we follow Amusewiki here.
1062− , "Not a definition list" =:
1063− T.unlines
1064− [ "First :: second"
1065− , "Foo :: bar"
1066− ] =?>
1067− para "First :: second\nFoo :: bar"
1068− , test emacsMuse "Emacs Muse definition list"
1069− (T.unlines
1070− [ "First :: second"
1071− , "Foo :: bar"
1072− ] =?>
1073− definitionList [ ("First", [ para "second" ])
1074− , ("Foo", [ para "bar" ])
1075− ])
1076− , "Definition list" =:
1077− T.unlines
1078− [ " First :: second"
1079− , " Foo :: bar"
1080− ] =?>
1081− definitionList [ ("First", [ para "second" ])
1082− , ("Foo", [ para "bar" ])
1083− ]
1084− , "Definition list term cannot include newline" =:
1085− T.unlines
1086− [ " Foo" -- "Foo" is not a part of the definition list term
1087− , " Bar :: baz"
1088− ] =?>
1089− para "Foo" <>
1090− definitionList [ ("Bar", [ para "baz" ]) ]
1091− , "One-line definition list" =: " foo :: bar" =?>
1092− definitionList [ ("foo", [ para "bar" ]) ]
1093− , "Definition list term may include single colon" =:
1094− " foo:bar :: baz" =?>
1095− definitionList [ ("foo:bar", [ para "baz" ]) ]
1096− , "Definition list term with emphasis" =: " *Foo* :: bar\n" =?>
1097− definitionList [ (emph "Foo", [ para "bar" ]) ]
1098− , "Definition list term with :: inside code" =: " foo <code> :: </code> :: bar <code> :: </code> baz\n" =?>
1099− definitionList [ ("foo " <> code " :: ", [ para $ "bar " <> code " :: " <> " baz" ]) ]
1100− , "Multi-line definition lists" =:
1101− T.unlines
1102− [ " First term :: Definition of first term"
1103− , "and its continuation."
1104− , " Second term :: Definition of second term."
1105− ] =?>
1106− definitionList [ ("First term", [ para "Definition of first term\nand its continuation." ])
1107− , ("Second term", [ para "Definition of second term." ])
1108− ]
1109− , test emacsMuse "Multi-line definition lists from Emacs Muse manual"
1110− (T.unlines
1111− [ "Term1 ::"
1112− , " This is a first definition"
1113− , " And it has two lines;"
1114− , "no, make that three."
1115− , ""
1116− , "Term2 :: This is a second definition"
1117− ] =?>
1118− definitionList [ ("Term1", [ para "This is a first definition\nAnd it has two lines;\nno, make that three."])
1119− , ("Term2", [ para "This is a second definition"])
1120− ])
1121− -- Text::Amuse requires indentation with one space
1122− , "Multi-line definition lists from Emacs Muse manual with initial space" =:
1123− (T.unlines
1124− [ " Term1 ::"
1125− , " This is a first definition"
1126− , " And it has two lines;"
1127− , "no, make that three."
1128− , ""
1129− , " Term2 :: This is a second definition"
1130− ] =?>
1131− definitionList [ ("Term1", [ para "This is a first definition\nAnd it has two lines;\nno, make that three."])
1132− , ("Term2", [ para "This is a second definition"])
1133− ])
1134− , "One-line nested definition list" =:
1135− " Foo :: bar :: baz" =?>
1136− definitionList [ ("Foo", [ definitionList [ ("bar", [ para "baz" ])]])]
1137− , "Nested definition list" =:
1138− T.unlines
1139− [ " First :: Second :: Third"
1140− , " Fourth :: Fifth :: Sixth"
1141− , " Seventh :: Eighth"
1142− ] =?>
1143− definitionList [ ("First", [ definitionList [ ("Second", [ para "Third" ]),
1144− ("Fourth", [ definitionList [ ("Fifth", [ para "Sixth"] ) ] ] ) ] ] )
1145− , ("Seventh", [ para "Eighth" ])
1146− ]
1147− , testGroup "Definition lists with multiple descriptions"
1148− [ "Correctly indented second description" =:
1149− T.unlines
1150− [ " First term :: first description"
1151− , " :: second description"
1152− ] =?>
1153− definitionList [ ("First term", [ para "first description"
1154− , para "second description"
1155− ])
1156− ]
1157− , "Incorrectly indented second description" =:
1158− T.unlines
1159− [ " First term :: first description"
1160− , " :: second description"
1161− ] =?>
1162− definitionList [ ("First term", [ para "first description" ])
1163− , ("", [ para "second description" ])
1164− ]
1165− ]
1166− , "Two blank lines separate definition lists" =:
1167− T.unlines
1168− [ " First :: list"
1169− , ""
1170− , ""
1171− , " Second :: list"
1172− ] =?>
1173− definitionList [ ("First", [ para "list" ]) ] <>
1174− definitionList [ ("Second", [ para "list" ]) ]
1175− -- Headers in first column of list continuation are not allowed
1176− , "No headers in list continuation" =:
1177− T.unlines
1178− [ " - Foo"
1179− , ""
1180− , " * Bar"
1181− ] =?>
1182− bulletList [ mconcat [ para "Foo"
1183− , para "* Bar"
1184− ]
1185− ]
1186− , "Bullet list inside a tag" =:
1187− T.unlines
1188− [ "<quote>"
1189− , " - First"
1190− , ""
1191− , " - Second"
1192− , ""
1193− , " - Third"
1194− , "</quote>"
1195− ] =?>
1196− blockQuote (bulletList [ para "First"
1197− , para "Second"
1198− , para "Third"
1199− ])
1200− , "Ordered list inside a tag" =:
1201− T.unlines
1202− [ "<quote>"
1203− , " 1. First"
1204− , ""
1205− , " 2. Second"
1206− , ""
1207− , " 3. Third"
1208− , "</quote>"
1209− ] =?>
1210− blockQuote (orderedListWith (1, Decimal, Period) [ para "First"
1211− , para "Second"
1212− , para "Third"
1213− ])
1214− -- Regression test for a bug caught by round-trip test
1215− , "Do not consume whitespace while looking for end tag" =:
1216− T.unlines
1217− [ "<quote>"
1218− , " - <quote>"
1219− , " foo"
1220− , " </quote>"
1221− , " bar" -- Do not consume whitespace while looking for arbitraritly indented </quote>
1222− , "</quote>"
1223− ] =?>
1224− blockQuote (bulletList [ blockQuote $ para "foo" ] <> para "bar")
1225−
1226− , "Unclosed quote tag" =:
1227− T.unlines
1228− [ "<quote>"
1229− , "<verse>"
1230− , "</quote>"
1231− , "</verse>"
1232− ] =?>
1233− para "<quote>" <> lineBlock [ "</quote>" ]
1234−
1235− , "Unclosed quote tag inside list" =:
1236− T.unlines
1237− [ " - <quote>"
1238− , " <verse>"
1239− , " </quote>"
1240− , " </verse>"
1241− ] =?>
1242− bulletList [ para "<quote>" <> lineBlock [ "</quote>" ] ]
1243−
1244− -- Allowing indented closing tags is dangerous,
1245− -- as they may terminate lists
1246− , "No indented closing tags" =:
1247− T.unlines
1248− [ "<quote>"
1249− , ""
1250− , " - Foo"
1251− , ""
1252− , " </quote>"
1253− , ""
1254− , " bar"
1255− , ""
1256− , " <verse>"
1257− , " </quote>"
1258− , " </verse>"
1259− ] =?>
1260− para "<quote>" <> bulletList [ para "Foo" <> para "</quote>" <> para "bar" <> lineBlock [ "</quote>" ] ]
1261− ]
1262− ]