require 'test/unit'
require 'creole'
require 'cgi'
class TestCreole < Test::Unit::TestCase
def tc(html, creole)
output = Creole.creolize(creole)
assert html === output, "Parsing: #{creole.inspect}\nExpected: #{html.inspect}\n Was: #{output.inspect}"
end
def run_file(file)
html = File.read(file.sub('.creole', '.html'))
output = Creole.creolize(File.read(file))
puts html
puts output
assert html === output, "Parsing #{file} failed"
end
def escape_html(html)
CGI::escapeHTML(html)
end
def test_bold
# Creole1.0: Bold can be used inside paragraphs
tc "
This is bold
", "This **is** bold"
tc "This is bold and boldish
", "This **is** bold and **bold**ish"
# Creole1.0: Bold can be used inside list items
tc "", "* This is **bold**"
# Creole1.0: Bold can be used inside table cells
tc("",
"|This is **bold**|")
# Creole1.0: Links can appear inside bold text:
tc("A bold link: http://wikicreole.org/ nice!
",
"A bold link: **http://wikicreole.org/ nice!**")
# Creole1.0: Bold will end at the end of paragraph
tc "This is bold
", "This **is bold"
# Creole1.0: Bold will end at the end of list items
tc("",
"* Item **bold\n* Item normal")
# Creole1.0: Bold will end at the end of table cells
tc("",
"|Item **bold|Another **bold")
# Creole1.0: Bold should not cross paragraphs
tc("This is
bold maybe
",
"This **is\n\nbold** maybe")
# Creole1.0-Implied: Bold should be able to cross lines
tc "This is bold
", "This **is\nbold**"
end
def test_italic
# Creole1.0: Italic can be used inside paragraphs
tc("This is italic
",
"This //is// italic")
tc("This is italic and italicish
",
"This //is// italic and //italic//ish")
# Creole1.0: Italic can be used inside list items
tc "", "* This is //italic//"
# Creole1.0: Italic can be used inside table cells
tc("",
"|This is //italic//|")
# Creole1.0: Links can appear inside italic text:
tc("A italic link: http://wikicreole.org/ nice!
",
"A italic link: //http://wikicreole.org/ nice!//")
# Creole1.0: Italic will end at the end of paragraph
tc "This is italic
", "This //is italic"
# Creole1.0: Italic will end at the end of list items
tc("",
"* Item //italic\n* Item normal")
# Creole1.0: Italic will end at the end of table cells
tc("Item italic | Another italic |
",
"|Item //italic|Another //italic")
# Creole1.0: Italic should not cross paragraphs
tc("This is
italic maybe
",
"This //is\n\nitalic// maybe")
# Creole1.0-Implied: Italic should be able to cross lines
tc "This is italic
", "This //is\nitalic//"
end
def test_bold_italics
# Creole1.0: By example
tc "bold italics
", "**//bold italics//**"
# Creole1.0: By example
tc "bold italics
", "//**bold italics**//"
# Creole1.0: By example
tc "This is also good.
", "//This is **also** good.//"
end
def test_headings
# Creole1.0: Only three differed sized levels of heading are required.
tc "Heading 1
", "= Heading 1 ="
tc "Heading 2
", "== Heading 2 =="
tc "Heading 3
", "=== Heading 3 ==="
# WARNING: Optional feature, not specified in creole 1.0
tc "Heading 4
", "==== Heading 4 ===="
tc "Heading 5
", "===== Heading 5 ====="
tc "Heading 6
", "====== Heading 6 ======"
# Creole1.0: Closing (right-side) equal signs are optional
tc "Heading 1
", "=Heading 1"
tc "Heading 2
", "== Heading 2"
tc "Heading 3
", " === Heading 3"
# Creole1.0: Closing (right-side) equal signs don't need to be balanced and don't impact the kind of heading generated
tc "Heading 1
", "=Heading 1 ==="
tc "Heading 2
", "== Heading 2 ="
tc "Heading 3
", " === Heading 3 ==========="
# Creole1.0: Whitespace is allowed before the left-side equal signs.
tc "Heading 1
", " \t= Heading 1 ="
tc "Heading 2
", " \t== Heading 2 =="
# Creole1.0: Only white-space characters are permitted after the closing equal signs.
tc "Heading 1
", " = Heading 1 = "
tc "Heading 2
", " == Heading 2 == \t "
# WARNING: !!Creole1.0 doesn't specify if text after closing equal signs
# !!becomes part of the heading or invalidates the entire heading.
# tc " == Heading 2 == foo
", " == Heading 2 == foo"
tc "Heading 2 == foo
", " == Heading 2 == foo"
# Creole1.0-Implied: Line must start with equal sign
tc "foo = Heading 1 =
", "foo = Heading 1 ="
end
def test_links
# Creole1.0: Links
tc "link
", "[[link]]"
# Creole1.0: Links can appear in paragraphs (i.e. inline item)
tc "Hello, world
", "Hello, [[world]]"
# Creole1.0: Named links
tc "Go to my page
", "[[MyBigPage|Go to my page]]"
# Creole1.0: URLs
tc "http://www.wikicreole.org/
", "[[http://www.wikicreole.org/]]"
# Creole1.0: Free-standing URL's should be turned into links
tc "http://www.wikicreole.org/
", "http://www.wikicreole.org/"
# Creole1.0: Single punctuation characters at the end of URLs
# should not be considered a part of the URL.
[',','.','?','!',':',';','\'','"'].each { |punct|
esc_punct = escape_html(punct)
tc "http://www.wikicreole.org/#{esc_punct}
", "http://www.wikicreole.org/#{punct}"
}
# Creole1.0: Nameds URLs (by example)
tc("Visit the WikiCreole website
",
"[[http://www.wikicreole.org/|Visit the WikiCreole website]]")
# WARNING: Parsing markup within a link is optional
tc "**Weird** //Stuff//
", "[[Weird Stuff|**Weird** //Stuff//]]"
# Inside bold
tc "link
", "**[[link]]**"
# Whitespace inside [[ ]] should be ignored
tc("link
", "[[ link ]]")
tc("link me
", "[[ link me ]]")
tc("dot.com
", "[[ http://dot.com/ \t| \t dot.com ]]")
tc("dot.com
", "[[ http://dot.com/ | dot.com ]]")
end
def test_paragraph
# Creole1.0: One or more blank lines end paragraphs.
tc "This is my text.
This is more text.
", "This is\nmy text.\n\nThis is\nmore text."
tc "This is my text.
This is more text.
", "This is\nmy text.\n\n\nThis is\nmore text."
tc "This is my text.
This is more text.
", "This is\nmy text.\n\n\n\nThis is\nmore text."
# Creole1.0: A list end paragraphs too.
tc "Hello
", "Hello\n* Item\n"
# Creole1.0: A table end paragraphs too.
tc "Hello
", "Hello\n|Cell|"
# Creole1.0: A nowiki end paragraphs too.
tc "Hello
nowiki
", "Hello\n{{{\nnowiki\n}}}\n"
# WARNING: A heading ends a paragraph (not specced)
tc "Hello
Heading
", "Hello\n= Heading =\n"
end
def test_linebreak
# Creole1.0: \\ (wiki-style) for line breaks.
tc "This is the first line,
and this is the second.
", "This is the first line,\\\\and this is the second."
end
def test_unordered_lists
# Creole1.0: List items begin with a * at the beginning of a line.
# Creole1.0: An item ends at the next *
tc "", "* Item 1\n *Item 2\n *\t\tItem 3\n"
# Creole1.0: Whitespace is optional before and after the *.
tc("",
" * Item 1\n*Item 2\n \t*\t\tItem 3\n")
# Creole1.0: A space is required if if the list element starts with bold text.
tc("", "***Item 1")
tc("", "* **Item 1")
# Creole1.0: An item ends at blank line
tc("Par
", "* Item\n\nPar\n")
# Creole1.0: An item ends at a heading
tc("Heading
", "* Item\n= Heading =\n")
# Creole1.0: An item ends at a table
tc("", "* Item\n|Cell|\n")
# Creole1.0: An item ends at a nowiki block
tc("Code
", "* Item\n{{{\nCode\n}}}\n")
# Creole1.0: An item can span multiple lines
tc("- The quick brown fox jumps over lazy dog.
- Humpty Dumpty sat on a wall.
",
"* The quick\nbrown fox\n\tjumps over\nlazy dog.\n*Humpty Dumpty\nsat\t\non a wall.")
# Creole1.0: An item can contain line breaks
tc("- The quick brown
fox jumps over lazy dog.
",
"* The quick brown\\\\fox jumps over lazy dog.")
# Creole1.0: Nested
tc "", "* Item 1\n **Item 2\n *\t\tItem 3\n"
# Creole1.0: Nested up to 5 levels
tc("",
"*Item 1\n**Item 2\n***Item 3\n****Item 4\n*****Item 5\n")
# Creole1.0: ** immediatly following a list element will be treated as a nested unordered element.
tc("",
"*Hello,\nWorld!\n**Not bold\n")
# Creole1.0: ** immediatly following a list element will be treated as a nested unordered element.
tc("- Hello, World!
",
"#Hello,\nWorld!\n**Not bold\n")
# Creole1.0: [...] otherwise it will be treated as the beginning of bold text.
tc("Not bold
",
"*Hello,\nWorld!\n\n**Not bold\n")
end
def test_ordered_lists
# Creole1.0: List items begin with a * at the beginning of a line.
# Creole1.0: An item ends at the next *
tc "- Item 1
- Item 2
- Item 3
", "# Item 1\n #Item 2\n #\t\tItem 3\n"
# Creole1.0: Whitespace is optional before and after the #.
tc("- Item 1
- Item 2
- Item 3
",
" # Item 1\n#Item 2\n \t#\t\tItem 3\n")
# Creole1.0: A space is required if if the list element starts with bold text.
tc("- Item 1
", "###Item 1")
tc("- Item 1
", "# **Item 1")
# Creole1.0: An item ends at blank line
tc("- Item
Par
", "# Item\n\nPar\n")
# Creole1.0: An item ends at a heading
tc("- Item
Heading
", "# Item\n= Heading =\n")
# Creole1.0: An item ends at a table
tc("- Item
", "# Item\n|Cell|\n")
# Creole1.0: An item ends at a nowiki block
tc("- Item
Code
", "# Item\n{{{\nCode\n}}}\n")
# Creole1.0: An item can span multiple lines
tc("- The quick brown fox jumps over lazy dog.
- Humpty Dumpty sat on a wall.
",
"# The quick\nbrown fox\n\tjumps over\nlazy dog.\n#Humpty Dumpty\nsat\t\non a wall.")
# Creole1.0: An item can contain line breaks
tc("- The quick brown
fox jumps over lazy dog.
",
"# The quick brown\\\\fox jumps over lazy dog.")
# Creole1.0: Nested
tc "- Item 1
- Item 2
- Item 3
", "# Item 1\n ##Item 2\n #\t\tItem 3\n"
# Creole1.0: Nested up to 5 levels
tc("- Item 1
- Item 2
- Item 3
- Item 4
- Item 5
",
"#Item 1\n##Item 2\n###Item 3\n####Item 4\n#####Item 5\n")
# Creole1.0_Infered: The two-bullet rule only applies to **.
tc("- Item
", "##Item")
end
def test_ordered_lists2
tc "- Item 1
- Item 2
- Item 3
", "# Item 1\n #Item 2\n #\t\tItem 3\n"
# Nested
tc "- Item 1
- Item 2
- Item 3
", "# Item 1\n ##Item 2\n #\t\tItem 3\n"
# Multiline
tc "- Item 1 on multiple lines
", "# Item 1\non multiple lines"
end
def test_ambiguity_mixed_lists
# ol following ul
tc("- oitem
", "*uitem\n#oitem\n")
# ul following ol
tc("- uitem
", "#uitem\n*oitem\n")
# 2ol following ul
tc("", "*uitem\n##oitem\n")
# 2ul following ol
tc("- uitem
", "#uitem\n**oitem\n")
# 3ol following 3ul
tc("", "***uitem\n###oitem\n")
# 2ul following 2ol
tc("- uitem
", "##uitem\n**oitem\n")
# ol following 2ol
tc("- oitem1
- oitem2
", "##oitem1\n#oitem2\n")
# ul following 2ol
tc("- oitem1
", "##oitem1\n*oitem2\n")
end
def test_ambiguity_italics_and_url
# Uncommon URL schemes should not be parsed as URLs
tc("This is what can go wrong:this should be an italic text.
",
"This is what can go wrong://this should be an italic text//.")
# A link inside italic text
tc("How about a link, like http://example.org, in italic text?
",
"How about //a link, like http://example.org, in italic// text?")
# Another test from Creole Wiki
tc("Formatted fruits, for example:apples, oranges, pears ...
",
"Formatted fruits, for example://apples//, oranges, **pears** ...")
tc("Blablabala (http://blub.de)
",
"Blablabala (http://blub.de)")
end
def test_ambiguity_bold_and_lists
tc " bold text
", "** bold text **"
tc " bold text
", " ** bold text **"
end
def test_nowiki
# ... works as block
tc "Hello
", "{{{\nHello\n}}}\n"
# ... works inline
tc "Hello world.
", "Hello {{{world}}}."
tc "Hello world.
", "{{{Hello}}} {{{world}}}."
# Creole1.0: No wiki markup is interpreted inbetween
tc "**Hello**
", "{{{\n**Hello**\n}}}\n"
# Creole1.0: Leading whitespaces are not permitted
tc(" {{{ Hello }}}
", " {{{\nHello\n}}}")
tc("{{{ Hello }}}
", "{{{\nHello\n }}}")
# Assumed: Should preserve whitespace
tc(" \t Hello, \t \n \t World \t
",
"{{{\n \t Hello, \t \n \t World \t \n}}}\n")
# In preformatted blocks ... one leading space is removed
tc("nowikiblock\n}}}
", "{{{\nnowikiblock\n }}}\n}}}\n")
# In inline nowiki, any trailing closing brace is included in the span
tc("this is nowiki}
", "this is {{{nowiki}}}}")
tc("this is nowiki}}
", "this is {{{nowiki}}}}}")
tc("this is nowiki}}}
", "this is {{{nowiki}}}}}}")
tc("this is nowiki}}}}
", "this is {{{nowiki}}}}}}}")
end
def test_html_escaping
# Special HTML chars should be escaped
tc("<b>not bold</b>
", "not bold")
# Image tags should be escape
tc("", "{{image.jpg|\"tag\"}}")
# Malicious links should not be converted.
tc("Click
", "[[javascript:alert(\"Boo!\")|Click]]")
end
def test_escape
tc "** Not Bold **
", "~** Not Bold ~**"
tc "// Not Italic //
", "~// Not Italic ~//"
tc "* Not Bullet
", "~* Not Bullet"
# Following char is not a blank (space or line feed)
tc "Hello ~ world
", "Hello ~ world\n"
tc "Hello ~ world
", "Hello ~\nworld\n"
# Not escaping inside URLs (Creole1.0 not clear on this)
tc "http://example.org/~user/
", "http://example.org/~user/"
# Escaping links
tc "http://www.wikicreole.org/
", "~http://www.wikicreole.org/"
end
def test_horizontal_rule
# Creole: Four hyphens make a horizontal rule
tc "
", "----"
# Creole1.0: Whitespace around them is allowed
tc "
", " ----"
tc "
", "---- "
tc "
", " ---- "
tc "
", " \t ---- \t "
# Creole1.0: Nothing else than hyphens and whitespace is "allowed"
tc "foo ----
", "foo ----\n"
tc "---- foo
", "---- foo\n"
# Creole1.0: [...] no whitespace is allowed between them
tc " -- --
", " -- -- "
tc " -- --
", " --\t-- "
end
def test_table
tc "", "|Hello, World!|"
# Multiple columns
tc "", "|c1|c2|c3|"
# Multiple rows
tc "", "|c11|c12|\n|c21|c22|\n"
# End pipe is optional
tc "", "|c1|c2|c3"
# Empty cells
tc "", "|c1||c3"
# Escaping cell separator
tc "", "|c1~|c2|c3"
# Escape in last cell + empty cell
tc "", "|c1|c2~|"
tc "", "|c1|c2~||"
tc "", "|c1|c2~|||"
# Equal sign after pipe make a header
tc "", "|=Header|"
tc "", "|c1|[[Link|Link text]]|{{Image|Image text}}|"
end
def test_following_table
# table followed by heading
tc("heading
", "|table|\n=heading=\n")
tc("heading
", "|table|\n\n=heading=\n")
# table followed by paragraph
tc("par
", "|table|\npar\n")
tc("par
", "|table|\n\npar\n")
# table followed by unordered list
tc("", "|table|\n*item\n")
tc("", "|table|\n\n*item\n")
# table followed by ordered list
tc("- item
", "|table|\n#item\n")
tc("- item
", "|table|\n\n#item\n")
# table followed by horizontal rule
tc("
", "|table|\n----\n")
tc("
", "|table|\n\n----\n")
# table followed by nowiki block
tc("pre
", "|table|\n{{{\npre\n}}}\n")
tc("pre
", "|table|\n\n{{{\npre\n}}}\n")
# table followed by table
tc("", "|table|\n|table|\n")
tc("", "|table|\n\n|table|\n")
end
def test_following_heading
# heading
tc("heading1
heading2
", "=heading1=\n=heading2\n")
tc("heading1
heading2
", "=heading1=\n\n=heading2\n")
# paragraph
tc("heading
par
", "=heading=\npar\n")
tc("heading
par
", "=heading=\n\npar\n")
# unordered list
tc("heading
", "=heading=\n*item\n")
tc("heading
", "=heading=\n\n*item\n")
# ordered list
tc("heading
- item
", "=heading=\n#item\n")
tc("heading
- item
", "=heading=\n\n#item\n")
# horizontal rule
tc("heading
", "=heading=\n----\n")
tc("heading
", "=heading=\n\n----\n")
# nowiki block
tc("heading
nowiki
", "=heading=\n{{{\nnowiki\n}}}\n")
tc("heading
nowiki
", "=heading=\n\n{{{\nnowiki\n}}}\n")
# table
tc("heading
", "=heading=\n|table|\n")
tc("heading
", "=heading=\n\n|table|\n")
end
def test_following_paragraph
# heading
tc("par
heading
", "par\n=heading=")
tc("par
heading
", "par\n\n=heading=")
# paragraph
tc("par par
", "par\npar\n")
tc("par
par
", "par\n\npar\n")
# unordered
tc("par
", "par\n*item")
tc("par
", "par\n\n*item")
# ordered
tc("par
- item
", "par\n#item\n")
tc("par
- item
", "par\n\n#item\n")
# horizontal
tc("par
", "par\n----\n")
tc("par
", "par\n\n----\n")
# nowiki
tc("par
nowiki
", "par\n{{{\nnowiki\n}}}\n")
tc("par
nowiki
", "par\n\n{{{\nnowiki\n}}}\n")
# table
tc("par
", "par\n|table|\n")
tc("par
", "par\n\n|table|\n")
end
def test_following_unordered_list
# heading
tc("heading
", "*item\n=heading=")
tc("heading
", "*item\n\n=heading=")
# paragraph
tc("", "*item\npar\n") # items may span multiple lines
tc("par
", "*item\n\npar\n")
# unordered
tc("", "*item\n*item\n")
tc("", "*item\n\n*item\n")
# ordered
tc("- item
", "*item\n#item\n")
tc("- item
", "*item\n\n#item\n")
# horizontal rule
tc("
", "*item\n----\n")
tc("
", "*item\n\n----\n")
# nowiki
tc("nowiki
", "*item\n{{{\nnowiki\n}}}\n")
tc("nowiki
", "*item\n\n{{{\nnowiki\n}}}\n")
# table
tc("", "*item\n|table|\n")
tc("", "*item\n\n|table|\n")
end
def test_following_ordered_list
# heading
tc("- item
heading
", "#item\n=heading=")
tc("- item
heading
", "#item\n\n=heading=")
# paragraph
tc("- item par
", "#item\npar\n") # items may span multiple lines
tc("- item
par
", "#item\n\npar\n")
# unordered
tc("- item
", "#item\n*item\n")
tc("- item
", "#item\n\n*item\n")
# ordered
tc("- item
- item
", "#item\n#item\n")
tc("- item
- item
", "#item\n\n#item\n")
# horizontal role
tc("- item
", "#item\n----\n")
tc("- item
", "#item\n\n----\n")
# nowiki
tc("- item
nowiki
", "#item\n{{{\nnowiki\n}}}\n")
tc("- item
nowiki
", "#item\n\n{{{\nnowiki\n}}}\n")
# table
tc("- item
", "#item\n|table|\n")
tc("- item
", "#item\n\n|table|\n")
end
def test_following_horizontal_rule
# heading
tc("
heading
", "----\n=heading=")
tc("
heading
", "----\n\n=heading=")
# paragraph
tc("
par
", "----\npar\n")
tc("
par
", "----\n\npar\n")
# unordered
tc("
", "----\n*item")
tc("
", "----\n*item")
# ordered
tc("
- item
", "----\n#item")
tc("
- item
", "----\n#item")
# horizontal
tc("
", "----\n----\n")
tc("
", "----\n\n----\n")
# nowiki
tc("
nowiki
", "----\n{{{\nnowiki\n}}}\n")
tc("
nowiki
", "----\n\n{{{\nnowiki\n}}}\n")
# table
tc("
", "----\n|table|\n")
tc("
", "----\n\n|table|\n")
end
def test_following_nowiki_block
# heading
tc("nowiki
heading
", "{{{\nnowiki\n}}}\n=heading=")
tc("nowiki
heading
", "{{{\nnowiki\n}}}\n\n=heading=")
# paragraph
tc("nowiki
par
", "{{{\nnowiki\n}}}\npar")
tc("nowiki
par
", "{{{\nnowiki\n}}}\n\npar")
# unordered
tc("nowiki
", "{{{\nnowiki\n}}}\n*item\n")
tc("nowiki
", "{{{\nnowiki\n}}}\n\n*item\n")
# ordered
tc("nowiki
- item
", "{{{\nnowiki\n}}}\n#item\n")
tc("nowiki
- item
", "{{{\nnowiki\n}}}\n\n#item\n")
# horizontal
tc("nowiki
", "{{{\nnowiki\n}}}\n----\n")
tc("nowiki
", "{{{\nnowiki\n}}}\n\n----\n")
# nowiki
tc("nowiki
nowiki
", "{{{\nnowiki\n}}}\n{{{\nnowiki\n}}}\n")
tc("nowiki
nowiki
", "{{{\nnowiki\n}}}\n\n{{{\nnowiki\n}}}\n")
# table
tc("nowiki
", "{{{\nnowiki\n}}}\n|table|\n")
tc("nowiki
", "{{{\nnowiki\n}}}\n\n|table|\n")
end
def test_image
tc("", "{{image.jpg}}")
tc("", "{{image.jpg|tag}}")
tc("", "{{http://example.org/image.jpg}}")
end
def test_bold_combo
tc("bold and
end
",
"**bold and\n|table|\nend**")
end
end