diff --git a/markdown/test_tools.py b/markdown/test_tools.py new file mode 100644 index 000000000..cebb2bb7b --- /dev/null +++ b/markdown/test_tools.py @@ -0,0 +1,44 @@ +import unittest +import textwrap +from markdown import markdown + + +class TestCase(unittest.TestCase): + """ + A unittest.TestCase subclass with helpers for testing Markdown output. + + Define `default_kwargs` as a dict of keywords to pass to Markdown for each + test. The defaults can be overridden on individual tests. + + The `assertMarkdownRenders` method accepts the source text, the expected + output, and any keywords to pass to Markdown. The `default_kwargs` are used + except where overridden by `kwargs`. The ouput and expected ouput are passed + to `TestCase.assertMultiLineEqual`. An AssertionError is raised with a diff + if the actual output does not equal the expected output. + + The `dedent` method is available to dedent triple-quoted strings if + necessary. + + In all other respects, behaves as unittest.TestCase. + """ + + default_kwargs = {} + + def assertMarkdownRenders(self, source, expected, **kwargs): + """ + Test that source Markdown text renders to expected output with given keywords. + """ + + kws = self.default_kwargs.copy() + kws.update(kwargs) + output = markdown(source, **kws) + self.assertMultiLineEqual(output, expected) + + def dedent(self, text): + """ + Dedent text. + """ + + # TODO: If/when actual output ends with a newline, then use: + # return textwrap.dedent(text.strip('/n')) + return textwrap.dedent(text).strip() diff --git a/tests/test_syntax/__init__.py b/tests/test_syntax/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_syntax/blocks/__init__.py b/tests/test_syntax/blocks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_syntax/blocks/test_code_blocks.py b/tests/test_syntax/blocks/test_code_blocks.py new file mode 100644 index 000000000..00e607099 --- /dev/null +++ b/tests/test_syntax/blocks/test_code_blocks.py @@ -0,0 +1,67 @@ +from markdown.test_tools import TestCase + + +class TestCodeBlocks(TestCase): + + def test_spaced_codeblock(self): + self.assertMarkdownRenders( + ' # A code block.', + + self.dedent( + """ +
# A code block.
+
+ """
+ )
+ )
+
+ def test_tabbed_codeblock(self):
+ self.assertMarkdownRenders(
+ '\t# A code block.',
+
+ self.dedent(
+ """
+ # A code block.
+
+ """
+ )
+ )
+
+ def test_multiline_codeblock(self):
+ self.assertMarkdownRenders(
+ ' # Line 1\n # Line 2\n',
+
+ self.dedent(
+ """
+ # Line 1
+ # Line 2
+
+ """
+ )
+ )
+
+ def test_codeblock_with_blankline(self):
+ self.assertMarkdownRenders(
+ ' # Line 1\n\n # Line 2\n',
+
+ self.dedent(
+ """
+ # Line 1
+
+ # Line 2
+
+ """
+ )
+ )
+
+ def test_codeblock_escape(self):
+ self.assertMarkdownRenders(
+ ' <foo & bar>
+
+ """
+ )
+ )
diff --git a/tests/test_syntax/blocks/test_headers.py b/tests/test_syntax/blocks/test_headers.py
new file mode 100644
index 000000000..63e3a7f18
--- /dev/null
+++ b/tests/test_syntax/blocks/test_headers.py
@@ -0,0 +1,688 @@
+import unittest
+from markdown.test_tools import TestCase
+
+
+class TestSetextHeaders(TestCase):
+
+ def test_setext_h1(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is an H1
+ =============
+ """
+ ),
+
+ 'Followed by a Paragraph with no blank line.
+ """ + ) + ) + + def test_setext_h2_followed_by_p(self): + self.assertMarkdownRenders( + self.dedent( + """ + This is an H2 + ------------- + Followed by a Paragraph with no blank line. + """ + ), + self.dedent( + """ +Followed by a Paragraph with no blank line.
+ """ + ) + ) + + # TODO: fix this + # see http://johnmacfarlane.net/babelmark2/?normalize=1&text=Paragraph%0AAn+H1%0A%3D%3D%3D%3D%3D + @unittest.skip('This is broken in Python-Markdown') + def test_p_followed_by_setext_h1(self): + self.assertMarkdownRenders( + self.dedent( + """ + This is a Paragraph. + Followed by an H1 with no blank line. + ===================================== + """ + ), + self.dedent( + """ +This is a Paragraph.
+This is a Paragraph.
+Followed by a Paragraph with no blank line.
+ """ + ) + ) + + def test_hash_h2_followed_by_p(self): + self.assertMarkdownRenders( + self.dedent( + """ + ## This is an H2 + Followed by a Paragraph with no blank line. + """ + ), + self.dedent( + """ +Followed by a Paragraph with no blank line.
+ """ + ) + ) + + def test_hash_h3_followed_by_p(self): + self.assertMarkdownRenders( + self.dedent( + """ + ### This is an H3 + Followed by a Paragraph with no blank line. + """ + ), + self.dedent( + """ +Followed by a Paragraph with no blank line.
+ """ + ) + ) + + def test_hash_h4_followed_by_p(self): + self.assertMarkdownRenders( + self.dedent( + """ + #### This is an H4 + Followed by a Paragraph with no blank line. + """ + ), + self.dedent( + """ +Followed by a Paragraph with no blank line.
+ """ + ) + ) + + def test_hash_h5_followed_by_p(self): + self.assertMarkdownRenders( + self.dedent( + """ + ##### This is an H5 + Followed by a Paragraph with no blank line. + """ + ), + self.dedent( + """ +Followed by a Paragraph with no blank line.
+ """ + ) + ) + + def test_hash_h6_followed_by_p(self): + self.assertMarkdownRenders( + self.dedent( + """ + ###### This is an H6 + Followed by a Paragraph with no blank line. + """ + ), + self.dedent( + """ +Followed by a Paragraph with no blank line.
+ """ + ) + ) + + def test_hash_h1_leading_space(self): + self.assertMarkdownRenders( + ' # This is an H1', + + '# This is an H1
' + ) + + def test_hash_h2_leading_space(self): + self.assertMarkdownRenders( + ' ## This is an H2', + + '## This is an H2
' + ) + + def test_hash_h3_leading_space(self): + self.assertMarkdownRenders( + ' ### This is an H3', + + '### This is an H3
' + ) + + def test_hash_h4_leading_space(self): + self.assertMarkdownRenders( + ' #### This is an H4', + + '#### This is an H4
' + ) + + def test_hash_h5_leading_space(self): + self.assertMarkdownRenders( + ' ##### This is an H5', + + '##### This is an H5
' + ) + + def test_hash_h6_leading_space(self): + self.assertMarkdownRenders( + ' ###### This is an H6', + + '###### This is an H6
' + ) + + def test_hash_h1_open_trailing_space(self): + self.assertMarkdownRenders( + '# This is an H1 ', + + 'Followed by a Paragraph with no blank line.
+ """ + ) + ) + + def test_p_followed_by_hash(self): + self.assertMarkdownRenders( + self.dedent( + """ + This is a Paragraph. + # Followed by an H1 with no blank line. + """ + ), + self.dedent( + """ +This is a Paragraph.
+An HR followed by a paragraph with no blank line.
+ """ + ) + ) + + def test_hr_after_paragraph(self): + self.assertMarkdownRenders( + self.dedent( + """ + A paragraph followed by an HR with no blank line. + *** + """ + ), + self.dedent( + """ +A paragraph followed by an HR with no blank line.
+**
' + ) + + def test_not_hr_2_asterisks_spaces(self): + self.assertMarkdownRenders( + '* *', + + self.dedent( + """ +--
' + ) + + def test_not_hr_2_hyphens_spaces(self): + self.assertMarkdownRenders( + '- -', + + self.dedent( + """ +__
' + ) + + def test_not_hr_2_underscores_spaces(self): + self.assertMarkdownRenders( + '_ _', + + '_ _
' + ) diff --git a/tests/test_syntax/blocks/test_paragraphs.py b/tests/test_syntax/blocks/test_paragraphs.py new file mode 100644 index 000000000..b458fc1fe --- /dev/null +++ b/tests/test_syntax/blocks/test_paragraphs.py @@ -0,0 +1,208 @@ +from markdown.test_tools import TestCase + + +class TestParagraphBlocks(TestCase): + + def test_simple_paragraph(self): + self.assertMarkdownRenders( + 'A simple paragraph.', + + 'A simple paragraph.
' + ) + + def test_blank_line_before_paragraph(self): + self.assertMarkdownRenders( + '\nA paragraph preceded by a blank line.', + + 'A paragraph preceded by a blank line.
' + ) + + def test_multiline_paragraph(self): + self.assertMarkdownRenders( + self.dedent( + """ + This is a paragraph + on multiple lines + with hard returns. + """ + ), + self.dedent( + """ +This is a paragraph + on multiple lines + with hard returns.
+ """ + ) + ) + + def test_paragraph_long_line(self): + self.assertMarkdownRenders( + 'A very long long long long long long long long long long long long long long long long long long long ' + 'long long long long long long long long long long long long long paragraph on 1 line.', + + 'A very long long long long long long long long long long long long long long long long long long ' + 'long long long long long long long long long long long long long long paragraph on 1 line.
' + ) + + def test_2_paragraphs_long_line(self): + self.assertMarkdownRenders( + 'A very long long long long long long long long long long long long long long long long long long long ' + 'long long long long long long long long long long long long long paragraph on 1 line.\n\n' + + 'A new long long long long long long long long long long long long long long long ' + 'long paragraph on 1 line.', + + 'A very long long long long long long long long long long long long long long long long long long ' + 'long long long long long long long long long long long long long long paragraph on 1 line.
\n' + 'A new long long long long long long long long long long long long long long long ' + 'long paragraph on 1 line.
' + ) + + def test_consecutive_paragraphs(self): + self.assertMarkdownRenders( + self.dedent( + """ + Paragraph 1. + + Paragraph 2. + """ + ), + self.dedent( + """ +Paragraph 1.
+Paragraph 2.
+ """ + ) + ) + + def test_consecutive_paragraphs_tab(self): + self.assertMarkdownRenders( + self.dedent( + """ + Paragraph followed by a line with a tab only. + \t + Paragraph after a line with a tab only. + """ + ), + self.dedent( + """ +Paragraph followed by a line with a tab only.
+Paragraph after a line with a tab only.
+ """ + ) + ) + + def test_consecutive_paragraphs_space(self): + self.assertMarkdownRenders( + self.dedent( + """ + Paragraph followed by a line with a space only. + + Paragraph after a line with a space only. + """ + ), + self.dedent( + """ +Paragraph followed by a line with a space only.
+Paragraph after a line with a space only.
+ """ + ) + ) + + def test_consecutive_multiline_paragraphs(self): + self.assertMarkdownRenders( + self.dedent( + """ + Paragraph 1, line 1. + Paragraph 1, line 2. + + Paragraph 2, line 1. + Paragraph 2, line 2. + """ + ), + self.dedent( + """ +Paragraph 1, line 1. + Paragraph 1, line 2.
+Paragraph 2, line 1. + Paragraph 2, line 2.
+ """ + ) + ) + + def test_paragraph_leading_space(self): + self.assertMarkdownRenders( + ' A paragraph with 1 leading space.', + + 'A paragraph with 1 leading space.
' + ) + + def test_paragraph_2_leading_spaces(self): + self.assertMarkdownRenders( + ' A paragraph with 2 leading spaces.', + + 'A paragraph with 2 leading spaces.
' + ) + + def test_paragraph_3_leading_spaces(self): + self.assertMarkdownRenders( + ' A paragraph with 3 leading spaces.', + + 'A paragraph with 3 leading spaces.
' + ) + + def test_paragraph_trailing_leading_space(self): + self.assertMarkdownRenders( + ' A paragraph with 1 trailing and 1 leading space. ', + + 'A paragraph with 1 trailing and 1 leading space.
' + ) + + def test_paragraph_trailing_tab(self): + self.assertMarkdownRenders( + 'A paragraph with 1 trailing tab.\t', + + 'A paragraph with 1 trailing tab.
' + ) + + def test_paragraphs_CR(self): + self.assertMarkdownRenders( + 'Paragraph 1, line 1.\rParagraph 1, line 2.\r\rParagraph 2, line 1.\rParagraph 2, line 2.\r', + + self.dedent( + """ +Paragraph 1, line 1. + Paragraph 1, line 2.
+Paragraph 2, line 1. + Paragraph 2, line 2.
+ """ + ) + ) + + def test_paragraphs_LF(self): + self.assertMarkdownRenders( + 'Paragraph 1, line 1.\nParagraph 1, line 2.\n\nParagraph 2, line 1.\nParagraph 2, line 2.\n', + + self.dedent( + """ +Paragraph 1, line 1. + Paragraph 1, line 2.
+Paragraph 2, line 1. + Paragraph 2, line 2.
+ """ + ) + ) + + def test_paragraphs_CR_LF(self): + self.assertMarkdownRenders( + 'Paragraph 1, line 1.\r\nParagraph 1, line 2.\r\n\r\nParagraph 2, line 1.\r\nParagraph 2, line 2.\r\n', + + self.dedent( + """ +Paragraph 1, line 1. + Paragraph 1, line 2.
+Paragraph 2, line 1. + Paragraph 2, line 2.
+ """ + ) + )