{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Text.MMarkSpec (spec) where
import Data.Aeson
import Data.Char
import Data.List.NonEmpty (NonEmpty (..))
import Data.Monoid
import Data.Text (Text)
import Test.Hspec
import Test.Hspec.Megaparsec
import Text.MMark (MMarkErr (..))
import Text.MMark.Extension (Inline (..))
import Text.MMark.TestUtils
import Text.Megaparsec (ErrorFancy (..), Stream)
import qualified Control.Foldl as L
import qualified Data.List.NonEmpty as NE
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified Text.MMark as MMark
import qualified Text.MMark.Extension as Ext
-- NOTE This test suite is mostly based on (sometimes altered) examples from
-- the Common Mark specification. We use the version 0.28 (2017-08-01),
-- which can be found online here:
--
--
foo\tbaz\t\tbim\n
\n"
it "CM2" $
" \tfoo\tbaz\t\tbim" ==->
"foo\tbaz\t\tbim\n
\n"
it "CM3" $
" a\ta\n ὐ\ta" ==->
"a\ta\nὐ\ta\n
\n"
it "CM4" $
" - foo\n\n\tbar" ==->
"foo
\nbar
\nfoo
\n bar\n
\n\n\n" it "CM7" $ "-\t\tfoo" ==-> "\nfoo\n
foo\n
\nfoo\nbar\n
\n"
it "CM9" $
" - foo\n - bar\n\t - baz" ==->
"+++
\n" it "CM15" $ "===" ==-> "===
\n" it "CM16" $ let s = "--\n**\n__\n" in s ~-> errFancy 3 (nonFlanking "**") it "CM17" $ " ***\n ***\n ***" ==-> "***\n
\n"
it "CM19" $
let s = "Foo\n ***\n"
in s ~-> errFancy 8 (nonFlanking "***")
it "CM20" $
"_____________________________________" ==->
"-
\n" it "CM27" $ "- foo\n***\n- bar" ==-> "Foo
\nbar
\n" it "CM29" $ "Foo\n---\nbar" ==-> "Foo
\nbar
\n" it "CM30" $ "* Foo\n* * *\n* Bar" ==-> "## foo
\n" it "CM36" $ "# foo *bar* \\*baz\\*" ==-> "# foo\n
\n"
it "CM40" $
"foo\n # bar" ==-> "foo\n# bar
\n" it "CM41" $ "## foo ##\n ### bar ###" ==-> "Foo bar
\nBar foo
\n" it "CM49" $ let s = "## \n#\n### ###" in s ~~-> [ err 3 (utok '\n' <> elabel "heading character" <> ews) , err 5 (utok '\n' <> etok '#' <> ews) ] context "4.3 Setext headings" $ do -- NOTE we do not support them, the tests have been adjusted -- accordingly. it "CM50" $ "Foo *bar*\n=========\n\nFoo *bar*\n---------" ==-> "Foo bar\n=========
\nFoo bar
\nFoo bar\nbaz\n====
\n" it "CM52" $ "Foo\n-------------------------\n\nFoo\n=" ==-> "Foo
\nFoo\n=
\n" it "CM53" $ " Foo\n---\n\n Foo\n-----\n\n Foo\n ===" ==-> "Foo
\nFoo
\nFoo\n===
\n" it "CM54" $ " Foo\n ---\n\n Foo\n---" ==-> "Foo\n---\n\nFoo\n
\nFoo
\nFoo\n---
\n" it "CM57" $ "Foo\n= =\n\nFoo\n--- -" ==-> "Foo\n= =
\nFoo
\nFoo
\nFoo\\
\n\n\nFoo
\n
\n\nfoo
\n
bar\n===
\n" it "CM63" $ "- Foo\n---" ==-> "Foo\nBar
\nBar
\nBaz
\n" it "CM66" $ "\n====" ==-> "====
\n" it "CM67" $ "---\n---" ==-> "" -- thinks that it's got a YAML block it "CM68" $ "- foo\n-----" ==-> "foo\n
\n\n\nfoo
\n
> foo
\nFoo
\nbar
\nbaz
\n" it "CM73" $ "Foo\nbar\n\n---\n\nbaz" ==-> "Foo\nbar
\nbaz
\n" it "CM74" $ "Foo\nbar\n* * *\nbaz" ==-> "Foo\nbar
\nbaz
\n" it "CM75" $ "Foo\nbar\n\\---\nbaz" ==-> "Foo\nbar\n---\nbaz
\n" context "4.4 Indented code blocks" $ do it "CM76" $ " a simple\n indented code block" ==-> "a simple\n indented code block\n
\n"
it "CM77" $
" - foo\n\n bar" ==->
"foo
\nbar
\nfoo
\n<a/>\n*hi*\n\n- one\n
\n"
it "CM80" $
" chunk1\n\n chunk2\n \n \n \n chunk3" ==->
"chunk1\n\nchunk2\n\n\n\nchunk3\n
\n"
it "CM81" $
" chunk1\n \n chunk2" ==->
"chunk1\n \n chunk2\n
\n"
it "CM82" $
"Foo\n bar\n" ==->
"Foo\nbar
\n" it "CM83" $ " foo\nbar" ==-> "foo\n
\nbar
\n" it "CM84" $ "# Heading\n foo\nHeading\n------\n foo\n----\n" ==-> "foo\n
\nHeading
\nfoo\n
\n foo\nbar\n
\n"
it "CM86" $
"\n \n foo\n \n" ==->
"foo\n
\n"
it "CM87" $
" foo " ==->
"foo \n
\n"
context "4.5 Fenced code blocks" $ do
it "CM88" $
"```\n<\n >\n```" ==->
"<\n >\n
\n"
it "CM89" $
"~~~\n<\n >\n~~~" ==->
"<\n >\n
\n"
it "CM90" $
"``\nfoo\n``\n" ==->
"foo
aaa\n~~~\n
\n"
it "CM92" $
"~~~\naaa\n```\n~~~" ==->
"aaa\n```\n
\n"
it "CM93" $
"````\naaa\n```\n``````" ==->
"aaa\n```\n
\n"
it "CM94" $
"~~~~\naaa\n~~~\n~~~~" ==->
"aaa\n~~~\n
\n"
it "CM95" $
let s = "```"
in s ~-> err 3 (ueib <> etok '`' <> ecsc)
it "CM96" $
let s = "`````\n\n```\naaa\n"
in s ~-> err 15
(ueof <> elabel "closing code fence" <> elabel "code block content")
it "CM97" $
let s = "> ```\n> aaa\n\nbbb\n"
in s ~-> err 17 (ueof <> elabel "closing code fence" <> elabel "code block content")
it "CM98" $
"```\n\n \n```" ==->
"\n \n
\n"
it "CM99" $
"```\n```" ==->
"
\n"
it "CM100" $
" ```\n aaa\naaa\n```" ==->
"aaa\naaa\n
\n"
it "CM101" $
" ```\naaa\n aaa\naaa\n ```" ==->
"aaa\naaa\naaa\n
\n"
it "CM102" $
" ```\n aaa\n aaa\n aaa\n ```" ==->
"aaa\n aaa\naaa\n
\n"
it "CM103" $
" ```\n aaa\n ```" ==->
"```\naaa\n```\n
\n"
it "CM104" $
"```\naaa\n ```" ==->
"aaa\n
\n"
it "CM105" $
" ```\naaa\n ```" ==->
"aaa\n
\n"
it "CM106" $
let s = "```\naaa\n ```\n"
in s ~-> err 16
(ueof <> elabel "closing code fence" <> elabel "code block content")
it "CM107" $
"``` ```\naaa" ==->
"\naaa
foo
\nbar\n
\nbaz
\n" it "CM110" $ "foo\n---\n~~~\nbar\n~~~\n# baz" ==-> "foo
\nbar\n
\ndef foo(x)\n return 3\nend\n
\n"
it "CM112" $
"~~~~ ruby startline=3 $%@#$\ndef foo(x)\n return 3\nend\n~~~~~~~" ==->
"def foo(x)\n return 3\nend\n
\n"
it "CM113" $
"````;\n````" ==->
"
\n"
it "CM114" $
"``` aa ```\nfoo" ==->
"aa
\nfoo
``` aaa\n
\n"
context "4.6 HTML blocks" $
-- NOTE We do not support HTML blocks, see the readme.
return ()
context "4.7 Link reference definitions" $ do
it "CM159" $
"[foo]: /url \"title\"\n\n[foo]" ==->
"\n"
it "CM160" $
" [foo]: \n /url \n 'the title' \n\n[foo]" ==->
"\n"
it "CM161" $
let s = "[Foo bar\\]]:my_(url) 'title (with parens)'\n\n[Foo bar\\]]"
in s ~~->
[ err 19 (utoks ") " <> euric <> elabel "newline" <> ews)
, errFancy 45 (couldNotMatchRef "Foo bar]" [])
]
it "CM162" $
"[Foo bar]:\nbar
\n" it "CM174" $ let s = "[foo]: /url \"title\" ok" in s ~-> err 20 (utoks "ok" <> elabel "newline" <> ews) it "CM175" $ let s = "[foo]: /url\n\"title\" ok\n" in s ~-> err 20 (utoks "ok" <> elabel "newline" <> ews) it "CM176" $ " [foo]: /url \"title\"" ==-> "[foo]: /url "title"\n
\n"
it "CM177" $
"```\n[foo]: /url\n```" ==->
"[foo]: /url\n
\n"
it "CM178" $
let s = "Foo\n[bar]: /baz\n\n[bar]\n"
in s ~~->
[ errFancy 5 (couldNotMatchRef "bar" [])
, errFancy 18 (couldNotMatchRef "bar" []) ]
it "CM179" $
"# [Foo]\n[foo]: /url\n> bar" ==->
"\n\n" it "CM180" $ "[foo]: /foo-url \"foo\"\n[bar]: /bar-url\n \"bar\"\n[baz]: /baz-url\n\n[foo],\n[bar],\n[baz]" ==-> "\n" it "CM181" $ "[foo]\n\n> [foo]: /url" ==-> "\nbar
\n
\n\n" context "4.8 Paragraphs" $ do it "CM182" $ "aaa\n\nbbb" ==-> "
aaa
\nbbb
\n" it "CM183" $ "aaa\nbbb\n\nccc\nddd" ==-> "aaa\nbbb
\nccc\nddd
\n" it "CM184" $ "aaa\n\n\nbbb" ==-> "aaa
\nbbb
\n" it "CM185" $ " aaa\n bbb" ==-> "aaa\nbbb
\n" it "CM186" $ "aaa\n bbb\n ccc" ==-> "aaa\nbbb\nccc
\n" it "CM187" $ " aaa\nbbb" ==-> "aaa\nbbb
\n" it "CM188" $ " aaa\nbbb" ==-> "aaa\n
\nbbb
\n" it "CM189" $ "aaa \nbbb " ==-> "aaa\nbbb
\n" context "4.9 Blank lines" $ it "CM190" $ " \n\naaa\n \n\n# aaa\n\n " ==-> "aaa
\n\n\n" it "CM192" $ "># Foo\n bar\n baz" ==-> "Foo
\nbar\nbaz
\n
\n\n" it "CM193" $ " > # Foo\n bar\n baz" ==-> "Foo
\nbar\nbaz
\n
\n\n" it "CM194" $ " > # Foo\n > bar\n > baz" ==-> "Foo
\nbar\nbaz
\n
> # Foo\n> bar\n> baz\n
\n"
it "CM195" $
"> # Foo\n> bar\nbaz" ==->
"\n\nFoo
\n
\n\nbar
\n
baz
\n" it "CM196" $ "> bar\nbaz\n> foo" ==-> "\n\nbar
\n
baz
\n\n\n" it "CM197" $ "> foo\n---" ==-> "foo
\n
\n\nfoo
\n
\n\n\n
\n- \nfoo\n
\n
\n\n" it "CM200" $ "> ```\nfoo\n```" ==-> "\nfoo\n
bar
\n
\n\n" it "CM201" $ "> foo\n - bar" ==-> "\nfoo\n
\n\n" it "CM202" $ ">" ==-> "foo
\n\n
\n- \nbar\n
\n
\n\n" it "CM203" $ ">\n> \n> " ==-> "
\n\n
\n\n
\n\n" it "CM204" $ ">\n foo\n " ==-> "
\n\n" it "CM205" $ "> foo\n\n> bar" ==-> "foo
\n
\n\nfoo
\n
\n\n" it "CM206" $ "> foo\n bar" ==-> "bar
\n
\n\n" it "CM207" $ "> foo\n\n bar" ==-> "foo\nbar
\n
\n\n" it "CM208" $ "foo\n> bar" ==-> "foo
\nbar
\n
foo
\n\n\n" it "CM209" $ "> aaa\n***\n> bbb" ==-> "bar
\n
\n\naaa
\n
\n\n" it "CM210" $ "> bar\n baz" ==-> "bbb
\n
\n\n" it "CM211" $ "> bar\n\nbaz" ==-> "bar\nbaz
\n
\n\nbar
\n
baz
\n" it "CM212" $ "> bar\n\nbaz" ==-> "\n\nbar
\n
baz
\n" it "CM213" $ "> > > foo\nbar" ==-> "\n\n\n\n\n\nfoo
\n
bar
\n" it "CM214" $ ">>> foo\n bar\n baz" ==-> "\n\n" it "CM215" $ "> code\n\n> not code" ==-> "\n\n\n\nfoo\nbar\nbaz
\n
\n\n\ncode\n
\n\n" context "5.2 List items" $ do it "CM216" $ "A paragraph\nwith two lines.\n\n indented code\n\n> A block quote." ==-> "not code
\n
A paragraph\nwith two lines.
\nindented code\n
\n\n\n" it "CM217" $ "1. A paragraph\n with two lines.\n\n indented code\n\n > A block quote." ==-> "A block quote.
\n
A paragraph\nwith two lines.
\nindented code\n
\n\n\nA block quote.
\n
two
\n" it "CM219" $ "- one\n\n two" ==-> "one
\ntwo
\n two\n
\n"
it "CM221" $
" - one\n\n two" ==->
"one
\ntwo
\n\n\n" it "CM223" $ ">>- one\n\n two" ==-> "\n\n\n
\n- \none\n
\ntwo
\n
\n\n" it "CM224" $ "-one\n\n2.two" ==-> "\n\n\n
\n- \n
\none
\ntwo
\n
-one
\n2.two
\n" it "CM225" $ "- foo\n\n\n bar" ==-> "foo
\nbar
\nfoo
\nbar\n
\nbaz
\n\n\nbam
\n
Foo
\nbar\n\n\nbaz\n
\n-1. not ok
\n" it "CM233" $ "- foo\n\n bar" ==-> "foo
\nbar\n
\nfoo
\nbar\n
\nindented code\n
\nparagraph
\nmore code\n
\n"
it "CM236" $
"1. indented code\n\n paragraph\n\n more code" ==->
"indented code\n
\nparagraph
\nmore code\n
\n indented code\n
\nparagraph
\nmore code\n
\nfoo
\nbar
\n" it "CM239" $ "- foo\n\n bar" ==-> "bar
\n" it "CM240" $ "- foo\n\n bar" ==-> "foo
\nbar
\nfoo
\nbar\n
\nbaz\n
\nfoo
\n" it "CM243b" $ "1.\n\n foo" ==-> "foo
\n" it "CM244" $ "- foo\n-\n- bar" ==-> "foo
\nfoo
\nA paragraph\nwith two lines.
\nindented code\n
\n\n\nA block quote.
\n
A paragraph\nwith two lines.
\nindented code\n
\n\n\nA block quote.
\n
A paragraph\nwith two lines.
\nindented code\n
\n\n\nA block quote.
\n
1. A paragraph\n with two lines.\n\n indented code\n\n > A block quote.\n
\n"
it "CM253" $
" 1. A paragraph\nwith two lines.\n\n indented code\n\n > A block quote." ==->
"with two lines.
\n indented code\n\n > A block quote.\n
\n"
it "CM254" $
" 1. A paragraph\n with two lines." ==->
"with two lines.\n
\n"
it "CM255" $
"> 1. > Blockquote\ncontinued here." ==->
"\n\n\n
\n- \n
\n\n\nBlockquote
\n
continued here.
\n" it "CM256" $ "> 1. > Blockquote\n continued here." ==-> "\n\n" it "CM257" $ "- foo\n - bar\n - baz\n - boo" ==-> "\n
\n- \n
\n\n\nBlockquote
\ncontinued here.
\n
Bar
\nbaz
\nFoo
\nThe number of windows in my house is
\nThe number of windows in my house is
\nfoo
\nbar
\nbaz
\nbaz
\nbim
\n<!-- -->
\nfoo
\nnotcode
\nfoo
\n<!-- -->
\ncode\n
\n"
it "CM273" $
"- a\n - b\n - c\n - d\n - e\n - f\n - g\n - h\n- i" ==->
"a
\nb
\nc
\na
\nb
\nc
\na
\nc
\na
\nb
\nc
\nd
\na
\nb
\nd
\na
\nb\n\n\n
\nc
\nb
\nc
\na
\n\n\nb
\n
\n\n
c
\na
\n\n\nb
\n
c\n
\nd
\nfoo\n
\nbar
\n!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~
\n" it "CM290" $ "\\\t\\A\\a\\ \\3\\φ\\«" ==-> "\\\t\\A\\a\\ \\3\\φ\\«
\n" it "CM291" $ "\\*not emphasized\\*\n\\*not emphasized*\n<br/> not a tag\n[not a link](/foo)\n`not code`\n1. not a list\n* not a list\n# not a heading\n[foo]: /url "not a reference"
\n" it "CM292" $ let s = "\\\\*emphasis*" in s ~-> errFancy 2 (nonFlanking "*") it "CM293" $ "foo\\\nbar" ==-> "foo
\nbar
\\[\\`
\\[\\]\n
\n"
it "CM296" $
"~~~\n\\[\\]\n~~~" ==->
"\\[\\]\n
\n"
it "CM297" $
"<a href="/bar/)">
\n" it "CM299" $ let s = "[foo](/bar\\* \"ti\\*tle\")" in s ~-> err 10 (utok '\\' <> euric <> euri) it "CM300" $ let s = "[foo]\n\n[foo]: /bar\\* \"ti\\*tle\"" in s ~~-> [ errFancy 1 (couldNotMatchRef "foo" []) , err 18 (utok '\\' <> euric <> euri) ] it "CM301" $ "``` foo\\+bar\nfoo\n```" ==-> "foo\n
\n"
context "6.2 Entity and numeric character references" $ do
it "CM302" $
" & © Æ Ď\n¾ ℋ ⅆ\n∲ ≧̸" ==->
"& © Æ Ď\n¾ ℋ ⅆ\n∲ ≧̸
\n" it "CM303a" $ "# Ӓ Ϡ" ==-> "# Ӓ Ϡ
\n" it "CM303b" $ "" ~-> errFancy 0 (invalidNumChar 98765432) it "CM303c" $ "" ~-> errFancy 0 (invalidNumChar 0) it "CM304" $ "" ആ ಫ" ==-> "" ആ ಫ
\n" it "CM305a" $ " " ==-> " 
\n" it "CM305b" $ let s = "&x;" in s ~-> errFancy 0 (unknownEntity "x") it "CM305c" $ let s = "" in s ~-> err 2 (utok ';' <> etok 'x' <> etok 'X' <> elabel "integer") it "CM305d" $ let s = "" in s ~-> err 3 (utok ';' <> elabel "hexadecimal integer") it "CM305e" $ let s = "&ThisIsNotDefined;" in s ~-> errFancy 0 (unknownEntity "ThisIsNotDefined") it "CM305f" $ "&hi?;" ==-> "&hi?;
\n" it "CM306" $ "©" ==-> "©
\n" it "CM307" $ let s = "&MadeUpEntity;" in s ~-> errFancy 0 (unknownEntity "MadeUpEntity") it "CM308" $ "" ==-> "<a href="\246\246.html">
\n" it "CM309" $ "[foo](/föö \"föö\")" ==-> "\n" it "CM310" $ "[foo]\n\n[foo]: /föö \"föö\"" ==-> "\n" it "CM311" $ "``` föö\nfoo\n```" ==-> "foo\n
\n"
it "CM312" $
"`föö`" ==->
"föö
föfö\n
\n"
context "6.3 Code spans" $ do
it "CM314" $
"`foo`" ==-> "foo
foo ` bar
``
foo
foo bar baz
a b
foo `` bar
<a href="">
<http://foo.bar.baz>
foo bar
\n" it "CM332" $ let s = "a * foo bar*\n" in s ~-> errFancy 2 (nonFlanking "*") it "CM333" $ let s = "a*\"foo\"*\n" in s ~-> errFancy 1 (nonFlanking "*") it "CM334" $ let s = "* a *\n" in s ~-> errFancy 0 (nonFlanking "*") it "CM335" $ let s = "foo*bar*\n" in s ~-> errFancy 3 (nonFlanking "*") it "CM336" $ let s = "5*6*78\n" in s ~-> errFancy 1 (nonFlanking "*") it "CM337" $ "_foo bar_" ==-> "foo bar
\n" it "CM338" $ let s = "_ foo bar_\n" in s ~-> errFancy 0 (nonFlanking "_") it "CM339" $ let s = "a_\"foo\"_\n" in s ~-> errFancy 1 (nonFlanking "_") it "CM340" $ let s = "foo_bar_\n" in s ~-> errFancy 3 (nonFlanking "_") it "CM341" $ let s = "5_6_78\n" in s ~-> errFancy 1 (nonFlanking "_") it "CM342" $ let s = "пристаням_стремятся_\n" in s ~-> errFancy 9 (nonFlanking "_") it "CM343" $ let s = "aa_\"bb\"_cc\n" in s ~-> errFancy 2 (nonFlanking "_") it "CM344" $ let s = "foo-_(bar)_\n" in s ~-> errFancy 4 (nonFlanking "_") it "CM345" $ let s = "_foo*\n" in s ~-> err 4 (utok '*' <> etok '_' <> eic) it "CM346" $ let s = "*foo bar *\n" in s ~-> errFancy 9 (nonFlanking "*") it "CM347" $ let s = "*foo bar\n*\n" in s ~-> err 8 (ueib <> etok '*' <> eic) it "CM348" $ let s = "*(*foo)\n" in s ~-> err 7 (ueib <> etok '*' <> eic) it "CM349" $ "*(*foo*)*" ==-> "(foo)
\n" it "CM350" $ let s = "*foo*bar\n" in s ~-> errFancy 4 (nonFlanking "*") it "CM351" $ let s = "_foo bar _\n" in s ~-> errFancy 9 (nonFlanking "_") it "CM352" $ let s = "_(_foo)" in s ~-> err 7 (ueib <> etok '_' <> eic) it "CM353" $ "_(_foo_)_" ==-> "(foo)
\n" it "CM354" $ let s = "_foo_bar\n" in s ~-> errFancy 4 (nonFlanking "_") it "CM355" $ let s = "_пристаням_стремятся\n" in s ~-> errFancy 10 (nonFlanking "_") it "CM356" $ let s = "_foo_bar_baz_\n" in s ~-> errFancy 4 (nonFlanking "_") it "CM357" $ "_(bar\\)_.\n" ==-> "(bar).
\n" it "CM358" $ "**foo bar**\n" ==-> "foo bar
\n" it "CM359" $ let s = "** foo bar**\n" in s ~-> errFancy 0 (nonFlanking "**") it "CM360" $ let s = "a**\"foo\"**\n" in s ~-> errFancy 1 (nonFlanking "**") it "CM361" $ let s = "foo**bar**\n" in s ~-> errFancy 3 (nonFlanking "**") it "CM362" $ "__foo bar__" ==-> "foo bar
\n" it "CM363" $ let s = "__ foo bar__\n" in s ~-> errFancy 0 (nonFlanking "__") it "CM364" $ let s = "__\nfoo bar__\n" in s ~-> errFancy 0 (nonFlanking "__") it "CM365" $ let s = "a__\"foo\"__\n" in s ~-> errFancy 1 (nonFlanking "__") it "CM366" $ let s = "foo__bar__\n" in s ~-> errFancy 3 (nonFlanking "__") it "CM367" $ let s = "5__6__78\n" in s ~-> errFancy 1 (nonFlanking "__") it "CM368" $ let s = "пристаням__стремятся__\n" in s ~-> errFancy 9 (nonFlanking "__") it "CM369" $ "__foo, __bar__, baz__" ==-> "foo, bar, baz
\n" it "CM370" $ "foo-__\\(bar)__" ==-> "foo-(bar)
\n" it "CM371" $ let s = "**foo bar **\n" in s ~-> errFancy 10 (nonFlanking "**") it "CM372" $ let s = "**(**foo)\n" in s ~-> err 9 (ueib <> etoks "**" <> eic) it "CM373" $ "*(**foo**)*" ==-> "(foo)
\n" it "CM374" $ "**Gomphocarpus (*Gomphocarpus physocarpus*, syn.\n*Asclepias physocarpa*)**" ==-> "Gomphocarpus (Gomphocarpus physocarpus, syn.\nAsclepias physocarpa)
\n" it "CM375" $ "**foo \"*bar*\" foo**" ==-> "foo "bar" foo
\n" it "CM376" $ let s = "**foo**bar\n" in s ~-> errFancy 5 (nonFlanking "**") it "CM377" $ let s = "__foo bar __\n" in s ~-> errFancy 10 (nonFlanking "__") it "CM378" $ let s = "__(__foo)\n" in s ~-> err 9 (ueib <> etoks "__" <> eic) it "CM379" $ "_(__foo__)_" ==-> "(foo)
\n" it "CM380" $ let s = "__foo__bar\n" in s ~-> errFancy 5 (nonFlanking "__") it "CM381" $ let s = "__пристаням__стремятся\n" in s ~-> errFancy 11 (nonFlanking "__") it "CM382" $ "__foo\\_\\_bar\\_\\_baz__" ==-> "foo__bar__baz
\n" it "CM383" $ "__(bar\\)__." ==-> "(bar).
\n" it "CM384" $ "*foo [bar](/url)*" ==-> "foo bar
\n" it "CM385" $ "*foo\nbar*" ==-> "foo\nbar
\n" it "CM386" $ "_foo __bar__ baz_" ==-> "foo bar baz
\n" it "CM387" $ "_foo _bar_ baz_" ==-> "foo bar baz
\n" it "CM388" $ let s = "__foo_ bar_" in s ~-> err 5 (utoks "_ " <> etoks "__" <> eic) it "CM389" $ "*foo *bar**" ==-> "foo bar
\n" it "CM390" $ "*foo **bar** baz*" ==-> "foo bar baz
\n" it "CM391" $ let s = "*foo**bar**baz*\n" in s ~-> errFancy 5 (nonFlanking "*") it "CM392" $ "***foo** bar*\n" ==-> "foo bar
\n" it "CM393" $ "*foo **bar***\n" ==-> "foo bar
\n" it "CM394" $ let s = "*foo**bar***\n" in s ~-> errFancy 5 (nonFlanking "*") it "CM395" $ "*foo **bar *baz* bim** bop*\n" ==-> "foo bar baz bim bop
\n" it "CM396" $ "*foo [*bar*](/url)*\n" ==-> "foo bar
\n" it "CM397" $ let s = "** is not an empty emphasis\n" in s ~-> errFancy 0 (nonFlanking "**") it "CM398" $ let s = "**** is not an empty strong emphasis\n" in s ~-> errFancy 0 (nonFlanking "****") it "CM399" $ "**foo [bar](/url)**" ==-> "foo bar
\n" it "CM400" $ "**foo\nbar**" ==-> "foo\nbar
\n" it "CM401" $ "__foo _bar_ baz__" ==-> "foo bar baz
\n" it "CM402" $ "__foo __bar__ baz__" ==-> "foo bar baz
\n" it "CM403" $ "____foo__ bar__" ==-> "foo bar
\n" it "CM404" $ "**foo **bar****" ==-> "foo bar
\n" it "CM405" $ "**foo *bar* baz**" ==-> "foo bar baz
\n" it "CM406" $ let s = "**foo*bar*baz**\n" in s ~-> err 5 (utoks "*b" <> etoks "**" <> eic) it "CM407" $ "***foo* bar**" ==-> "foo bar
\n" it "CM408" $ "**foo *bar***" ==-> "foo bar
\n" it "CM409" $ "**foo *bar **baz**\nbim* bop**" ==-> "foo bar baz\nbim bop
\n" it "CM410" $ "**foo [*bar*](/url)**" ==-> "foo bar
\n" it "CM411" $ let s = "__ is not an empty emphasis\n" in s ~-> errFancy 0 (nonFlanking "__") it "CM412" $ let s = "____ is not an empty strong emphasis\n" in s ~-> errFancy 0 (nonFlanking "____") it "CM413" $ let s = "foo ***\n" in s ~-> errFancy 4 (nonFlanking "***") it "CM414" $ "foo *\\**" ==-> "foo *
\n" it "CM415" $ "foo *\\_*\n" ==-> "foo _
\n" it "CM416" $ let s = "foo *****\n" in s ~-> errFancy 8 (nonFlanking "*") it "CM417" $ "foo **\\***" ==-> "foo *
\n" it "CM418" $ "foo **\\_**\n" ==-> "foo _
\n" it "CM419" $ let s = "**foo*\n" in s ~-> err 5 (utok '*' <> etoks "**" <> eic) it "CM420" $ let s = "*foo**\n" in s ~-> errFancy 5 (nonFlanking "*") it "CM421" $ let s = "***foo**\n" in s ~-> err 8 (ueib <> etok '*' <> eic) it "CM422" $ let s = "****foo*\n" in s ~-> err 7 (utok '*' <> etoks "**" <> eic) it "CM423" $ let s = "**foo***\n" in s ~-> errFancy 7 (nonFlanking "*") it "CM424" $ let s = "*foo****\n" in s ~-> errFancy 5 (nonFlanking "***") it "CM425" $ let s = "foo ___\n" in s ~-> errFancy 4 (nonFlanking "___") it "CM426" $ "foo _\\__" ==-> "foo _
\n" it "CM427" $ "foo _\\*_" ==-> "foo *
\n" it "CM428" $ let s = "foo _____\n" in s ~-> errFancy 8 (nonFlanking "_") it "CM429" $ "foo __\\___" ==-> "foo _
\n" it "CM430" $ "foo __\\*__" ==-> "foo *
\n" it "CM431" $ let s = "__foo_\n" in s ~-> err 5 (utok '_' <> etoks "__" <> eic) it "CM432" $ let s = "_foo__\n" in s ~-> errFancy 5 (nonFlanking "_") it "CM433" $ let s = "___foo__\n" in s ~-> err 8 (ueib <> etok '_' <> eic) it "CM434" $ let s = "____foo_\n" in s ~-> err 7 (utok '_' <> etoks "__" <> eic) it "CM435" $ let s = "__foo___\n" in s ~-> errFancy 7 (nonFlanking "_") it "CM436" $ let s = "_foo____\n" in s ~-> errFancy 5 (nonFlanking "___") it "CM437" $ "**foo**" ==-> "foo
\n" it "CM438" $ "*_foo_*" ==-> "foo
\n" it "CM439" $ "__foo__" ==-> "foo
\n" it "CM440" $ "_*foo*_" ==-> "foo
\n" it "CM441" $ "****foo****" ==-> "foo
\n" it "CM442" $ "____foo____" ==-> "foo
\n" it "CM443" $ "******foo******" ==-> "foo
\n" it "CM444" $ "***foo***" ==-> "foo
\n" it "CM445" $ "_____foo_____" ==-> "foo
\n" it "CM446" $ let s = "*foo _bar* baz_\n" in s ~-> err 9 (utok '*' <> etok '_' <> eic) it "CM447" $ let s = "*foo __bar *baz bim__ bam*\n" in s ~-> err 19 (utok '_' <> etok '*' <> eic) it "CM448" $ let s = "**foo **bar baz**\n" in s ~-> err 17 (ueib <> etoks "**" <> eic) it "CM449" $ let s = "*foo *bar baz*\n" in s ~-> err 14 (ueib <> etok '*' <> eic) it "CM450" $ let s = "*[bar*](/url)\n" in s ~-> err 5 (utok '*' <> etok ']' <> eic) it "CM451" $ let s = "_foo [bar_](/url)\n" in s ~-> err 9 (utok '_' <> etok ']' <> eic) it "CM452" $ let s = "*\n" in s ~-> errFancy 23 (nonFlanking "*") it "CM453" $ let s = "**" in s ~-> errFancy 11 (nonFlanking "**") it "CM454" $ let s = "__\n" in s ~-> errFancy 11 (nonFlanking "__") it "CM455" $ "*a `*`*" ==-> "a *
a _
link))
\n" it "CM469" $ let s = "[link](foo\\(and\\(bar\\))" in s ~-> err 10 (utok '\\' <> euric <> euri) it "CM470" $ "[link](foo<http://example.com/?search=>
\n" it "CM498" $ "[foo][bar]\n\n[bar]: /url \"title\"" ==-> "\n" it "CM499" $ let s = "[link [foo [bar]]][ref]\n\n[ref]: /uri" in s ~-> err 6 (utok '[' <> etok ']' <> eic) it "CM500" $ "[link \\[bar][ref]\n\n[ref]: /uri" ==-> "\n" it "CM501" $ "[link *foo **bar** `#`*][ref]\n\n[ref]: /uri" ==-> "\n" it "CM502" $ "[![moon](moon.jpg)][ref]\n\n[ref]: /uri" ==-> "\n" it "CM503" $ let s = "[foo [bar](/uri)][ref]\n\n[ref]: /uri" in s ~-> err 5 (utok '[' <> etok ']' <> eic) it "CM504" $ let s = "[foo *bar [baz][ref]*][ref]\n\n[ref]: /uri" in s ~-> err 10 (utok '[' <> etok '*' <> eic) it "CM505" $ let s = "*[foo*][ref]\n\n[ref]: /uri" in s ~-> err 5 (utok '*' <> etok ']' <> eic) it "CM506" $ let s = "[foo *bar][ref]\n\n[ref]: /uri" in s ~-> err 9 (utok ']' <> etok '*' <> eic) it "CM507" $ "[foofoo<http://example.com/?search=>
\n" it "CM510" $ "[foo][BaR]\n\n[bar]: /url \"title\"" ==-> "\n" it "CM511" $ "[Толпой][Толпой] is a Russian word.\n\n[ТОЛПОЙ]: /url" ==-> "Толпой is a Russian word.
\n" it "CM512" $ "[Foo\n bar]: /url\n\n[Baz][Foo bar]" ==-> "\n" it "CM513" $ let s = "[foo] [bar]\n\n[bar]: /url \"title\"" in s ~-> errFancy 1 (couldNotMatchRef "foo" []) it "CM514" $ let s = "[foo]\n[bar]\n\n[bar]: /url \"title\"" in s ~-> errFancy 1 (couldNotMatchRef "foo" []) it "CM515" $ let s = "[foo]: /url1\n\n[foo]: /url2\n\n[bar][foo]" in s ~-> errFancy 15 (duplicateRef "foo") it "CM516" $ "[bar][foo\\!]\n\n[foo!]: /url" ==-> "\n" it "CM517" $ let s = "[foo][ref[]\n\n[ref[]: /uri" in s ~~-> [ err 9 (utok '[' <> etoks "" <> etok '&' <> etok ']' <> elabel "escaped character") , err 17 (utok '[' <> etok ']' <> eic) ] it "CM518" $ let s = "[foo][ref[bar]]\n\n[ref[bar]]: /uri" in s ~~-> [ err 9 (utok '[' <> etoks "" <> etok '&' <> etok ']' <> elabel "escaped character") , err 21 (utok '[' <> etok ']' <> eic) ] it "CM519" $ let s = "[[[foo]]]\n\n[[[foo]]]: /url" in s ~~-> [ err 1 (utok '[' <> eic) , err 12 (utok '[' <> eic) ] it "CM520" $ "[foo][ref\\[]\n\n[ref\\[]: /uri" ==-> "\n" it "CM521" $ "[bar\\\\]: /uri\n\n[bar\\\\]" ==-> "\n" it "CM522" $ let s = "[]\n\n[]: /uri" in s ~~-> [ err 1 (utok ']' <> eic) , err 5 (utok ']' <> eic) ] it "CM523" $ let s = "[\n ]\n\n[\n ]: /uri" in s ~~-> [ errFancy 1 (couldNotMatchRef "" []) , errFancy 7 (couldNotMatchRef "" []) ] it "CM524" $ "[foo][]\n\n[foo]: /url \"title\"" ==-> "\n" it "CM525" $ let s = "[*foo* bar][]\n\n[*foo* bar]: /url \"title\"" in s ~-> errFancy 1 (couldNotMatchRef "foo bar" ["*foo* bar"]) it "CM526" $ "[Foo][]\n\n[foo]: /url \"title\"" ==-> "\n" it "CM527" $ let s = "[foo] \n[]\n\n[foo]: /url \"title\"" in s ~-> err 8 (utok ']' <> eic) it "CM528" $ "[foo]\n\n[foo]: /url \"title\"" ==-> "\n" it "CM529" $ let s = "[*foo* bar]\n\n[*foo* bar]: /url \"title\"" in s ~-> errFancy 1 (couldNotMatchRef "foo bar" ["*foo* bar"]) it "CM530" $ let s = "[[*foo* bar]]\n\n[*foo* bar]: /url \"title\"" in s ~-> err 1 (utok '[' <> eic) it "CM531" $ let s = "[[bar [foo]\n\n[foo]: /url" in s ~-> err 1 (utok '[' <> eic) it "CM532" $ "[Foo]\n\n[foo]: /url \"title\"" ==-> "\n" it "CM533" $ "[foo] bar\n\n[foo]: /url" ==-> "foo bar
\n" it "CM534" $ let s = "\\[foo]\n\n[foo]: /url \"title\"" in s ~-> err 5 (utok ']' <> eeib <> eic) it "CM535" $ let s = "[foo*]: /url\n\n*[foo*]" in s ~-> err 19 (utok '*' <> etok ']' <> eic) it "CM536" $ "[foo][bar]\n\n[foo]: /url1\n[bar]: /url2" ==-> "\n" it "CM537" $ "[foo][]\n\n[foo]: /url1" ==-> "\n" it "CM538" $ let s = "[foo]()\n\n[foo]: /url1" in s ~-> err 6 (utok ')' <> etok '<' <> elabel "URI" <> ews) it "CM539" $ let s = "[foo](not a link)\n\n[foo]: /url1" in s ~-> err 10 (utok 'a' <> etok '"' <> etok '\'' <> etok '(' <> etok ')' <> ews) it "CM540" $ let s = "[foo][bar][baz]\n\n[baz]: /url" in s ~-> errFancy 6 (couldNotMatchRef "bar" ["baz"]) it "CM541" $ "[foo][bar][baz]\n\n[baz]: /url1\n[bar]: /url2" ==-> "\n" it "CM542" $ let s = "[foo][bar][baz]\n\n[baz]: /url1\n[foo]: /url2" in s ~-> errFancy 6 (couldNotMatchRef "bar" ["baz"]) context "6.6 Images" $ do it "CM543" $ "![foo](/url \"title\")" ==-> "\n" it "CM544" $ "![foo *bar*](train.jpg \"train & tracks\")" ==-> "\n" it "CM545" $ let s = "![foo ![bar](/url)](/url2)\n" in s ~-> err 6 (utok '!' <> etok ']' <> eic) it "CM546" $ "![foo [bar](/url)](/url2)" ==-> "\n" it "CM547" $ let s = "![foo *bar*][]\n\n[foo *bar*]: train.jpg \"train & tracks\"\n" in s ~-> errFancy 2 (couldNotMatchRef "foo bar" ["foo *bar*"]) it "CM548" $ "![foo *bar*][foobar]\n\n[FOOBAR]: train.jpg \"train & tracks\"" ==-> "\n" it "CM549" $ "![foo](train.jpg)" ==-> "\n" it "CM550" $ "My ![foo bar](/path/to/train.jpg \"title\" )" ==-> "My
\n" it "CM551" $ "![foo](![foo]
\n" it "CM564" $ "\\![foo]\n\n[foo]: /url \"title\"" ==-> "!foo
\n" context "6.7 Autolinks" $ do it "CM565" $ "http://foo.bar.baz/test?q=hello&id=22&boolean
\n" it "CM567" $ "<http://foo.bar/baz bim>
\n" it "CM574" $ "<http://example.com/[>
\n" it "CM575" $ "<foo+@bar.example.com>
\n" it "CM578" $ "<>" ==-> "<>
\n" it "CM579" $ "< http://foo.bar >" ==-> "< http://foo.bar >
\n" it "CM580" $ "http://example.com
\n" it "CM583" $ "foo@bar.example.com" ==-> "foo@bar.example.com
\n" context "6.8 Raw HTML" $ -- NOTE We do not support raw HTML, see the readme. return () context "6.9 Hard line breaks" $ do -- NOTE We currently do not support hard line breaks represented in -- markup as two spaces before newline. it "CM605" $ "foo \nbaz" ==-> "foo\nbaz
\n" it "CM606" $ "foo\\\nbaz\n" ==-> "foo
\nbaz
foo\nbaz
\n" it "CM608" $ "foo \n bar" ==-> "foo\nbar
\n" it "CM609" $ "foo\\\n bar" ==-> "foo
\nbar
foo\nbar
\n" it "CM611" $ "*foo\\\nbar*" ==-> "foo
\nbar
code span
code\\ span
<a href="foo\nbar">
\n" it "CM615" $ "" ==-> "<a href="foo
\nbar">
foo\\
\n" it "CM617" $ "foo " ==-> "foo
\n" it "CM618" $ "### foo\\" ==-> "foo\nbaz
\n" it "CM621" $ "foo \n baz" ==-> "foo\nbaz
\n" context "6.11 Textual content" $ do it "CM622" $ "hello $.;'there" ==-> "hello $.;'there
\n" it "CM623" $ "Foo χρῆν" ==-> "Foo χρῆν
\n" it "CM624" $ "Multiple spaces" ==-> "Multiple spaces
\n" -- NOTE I don't test these so extensively because they share -- implementation with emphasis and strong emphasis which are thoroughly -- tested already. context "strikeout" $ do it "works in simplest form" $ "It's ~~bad~~ news." ==-> "It's bad news.
It's bad news.
It's bad news.
It's bad news.
It's bad news.
\n" it "combines with emphasis" $ "**It's ~bad~** news." ==-> "It's bad news.
\n" context "superscript" $ do it "works in simplest form" $ "It's ^bad^ news." ==-> "It's bad news.
\n" it "combines with emphasis" $ "**It's ^bad^** news." ==-> "It's bad news.
\n" it "a composite, complex example" $ "***Something ~~~is not~~ going~ ^so well^** today*." ==-> "Something is not going so well today.
Foo |
---|
foo |
Foo | Bar\nab- | ---
\n" it "demands that number of columns in rows match number of columns in header" $ (let s = "Foo | Bar | Baz\n--- | --- | ---\nfoo | bar" in s ~-> err 41 (ulabel "end of table block" <> etok '|' <> eic)) >> (let s = "Foo | Bar | Baz\n--- | --- | ---\nfoo | bar\n\nHere it goes." in s ~-> err 41 (utok '\n' <> etok '|' <> eic)) it "recognizes escaped pipes" $ "Foo \\| | Bar\n--- | ---\nfoo | \\|" ==-> "Foo | | Bar |
---|---|
foo | | |
Foo | Bar |
---|---|
*foo* | bar |
|Foo| | |Bar| |
---|---|
foo | bar |
Foo | Bar |
---|---|
|foo| | |bar| |
Foo Bar
| Bar\n--- | ---\nfoo | bar
Foo | Bar |
---|
Foo | Bar |
---|---|
foo | bar |
Here goes a paragraph.
\n" it "is capable of reporting a parse error per cell" $ let s = "Foo | *Bar\n--- | ----\n_foo | bar_" in s ~~-> [ err 10 (ueib <> etok '*' <> eic) , err 26 (ueib <> etok '_' <> eic) , errFancy 32 (nonFlanking "_") ] it "tables have higher precedence than unordered lists" $ do "+ foo | bar\n------|----\n" ==-> "+ foo | bar |
---|
+ foo | bar |
---|
1. foo | bar |
---|
1. foo | bar |
---|
foo | bar |
---|
foo | bar |
---|
Here we go...
\n" describe "useExtensions" $ it "applies extensions in the right order" $ do doc <- mkDoc "Here we go." let exts = [ append_ext "3" , append_ext "2" , append_ext "1" ] toText (MMark.useExtensions exts doc) `shouldBe` "Here we go.123
\n" describe "runScanner and scanner" $ it "extracts information from markdown document" $ do doc <- mkDoc "Here we go, pals." let n = MMark.runScanner doc (length_scan (const True)) n `shouldBe` 17 describe "combining of scanners" $ it "combines scanners" $ do doc <- mkDoc "Here we go, pals." let scan = (,,) <$> length_scan (const True) <*> length_scan isSpace <*> length_scan isPunctuation r = MMark.runScanner doc scan r `shouldBe` (17, 3, 2) describe "projectYaml" $ do context "when document does not contain a YAML section" $ it "returns Nothing" $ do doc <- mkDoc "Here we go." MMark.projectYaml doc `shouldBe` Nothing context "when document contains a YAML section" $ do context "when it is valid" $ do let r = object [ "x" .= Number 100 , "y" .= Number 200 ] it "returns the YAML section (1)" $ do doc <- mkDoc "---\nx: 100\ny: 200\n---\nHere we go." MMark.projectYaml doc `shouldBe` Just r it "returns the YAML section (2)" $ do doc <- mkDoc "---\nx: 100\ny: 200\n---\n\n" MMark.projectYaml doc `shouldBe` Just r context "when it is invalid" $ do let mappingErr = fancy . ErrorCustom . YamlParseError $ "mapping values are not allowed in this context" it "signal correct parse error" $ let s = "---\nx: 100\ny: x:\n---\nHere we go." in s ~-> errFancy 15 mappingErr it "does not choke and can report more parse errors" $ let s = "---\nx: 100\ny: x:\n---\nHere we *go." in s ~~-> [ errFancy 15 mappingErr , err 33 (ueib <> etok '*' <> eic) ] ---------------------------------------------------------------------------- -- Testing extensions -- | Append given text to all 'Plain' blocks. append_ext :: Text -> MMark.Extension append_ext y = Ext.inlineTrans $ \case Plain x -> Plain (x <> y) other -> other ---------------------------------------------------------------------------- -- Testing scanners -- | Scan total number of characters satisfying a predicate in all 'Plain' -- inlines. length_scan :: (Char -> Bool) -> L.Fold (Ext.Block (NonEmpty Inline)) Int length_scan p = Ext.scanner 0 $ \n block -> getSum $ Sum n <> foldMap (foldMap f) block where f (Plain txt) = (Sum . T.length) (T.filter p txt) f _ = mempty ---------------------------------------------------------------------------- -- For testing with documents loaded externally -- | Load a complete markdown document from an external file and compare the -- final HTML rendering with contents of another file. withFiles :: FilePath -- ^ Markdown document -> FilePath -- ^ HTML document containing the correct result -> Expectation withFiles input output = do i <- TIO.readFile input o <- TIO.readFile output i ==-> o ---------------------------------------------------------------------------- -- Helpers -- | Unexpected end of inline block. ueib :: Stream s => ET s ueib = ulabel "end of inline block" -- | Expecting end of inline block. eeib :: Stream s => ET s eeib = elabel "end of inline block" -- | Expecting end of URI. euri :: Stream s => ET s euri = elabel "end of URI" -- | Expecting inline content. eic :: Stream s => ET s eic = elabel "inline content" -- | Expecting white space. ews :: Stream s => ET s ews = elabel "white space" -- | Expecting code span content. ecsc :: Stream s => ET s ecsc = elabel "code span content" -- | Expecting common URI components. euric :: ET Text euric = mconcat [ etok '#' , etok '%' , etok '/' , etok ':' , etok '?' , etok '@' , elabel "sub-delimiter" , elabel "unreserved character" ] -- | Error component complaining that the given 'Text' is not in left- or -- right- flanking position. nonFlanking :: Text -> EF MMarkErr nonFlanking = fancy . ErrorCustom . NonFlankingDelimiterRun . NE.fromList . T.unpack -- | Error component complaining that the given starting index of an ordered -- list is too big. indexTooBig :: Word -> EF MMarkErr indexTooBig = fancy . ErrorCustom . ListStartIndexTooBig -- | Error component complaining about non-consecutive indices in an ordered -- list. indexNonCons :: Word -> Word -> EF MMarkErr indexNonCons actual expected = fancy . ErrorCustom $ ListIndexOutOfOrder actual expected -- | Error component complaining about a missing link\/image reference. couldNotMatchRef :: Text -> [Text] -> EF MMarkErr couldNotMatchRef name names = fancy . ErrorCustom $ CouldNotFindReferenceDefinition name names -- | Error component complaining about a duplicate reference definition. duplicateRef :: Text -> EF MMarkErr duplicateRef = fancy . ErrorCustom . DuplicateReferenceDefinition -- | Error component complaining about an invalid numeric character. invalidNumChar :: Int -> EF MMarkErr invalidNumChar = fancy . ErrorCustom . InvalidNumericCharacter -- | Error component complaining about an unknown HTML5 entity name. unknownEntity :: Text -> EF MMarkErr unknownEntity = fancy . ErrorCustom . UnknownHtmlEntityName