diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 05:10:28 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 05:10:28 +0000 |
commit | 6ff587f109e9b9e63d722716d2491b943b78cc17 (patch) | |
tree | 1a110fa9fa982e746b1969458c5b9d373c70a38c /tests/test_syntax/extensions | |
parent | 785cf9483e09324449c2e32022872c0e55b177a8 (diff) | |
parent | b8ef043a4bce3c0e0c3a83a5309c17d1962be972 (diff) | |
download | markdown-android14-mainline-sdkext-release.tar.gz |
Snap for 10453563 from b8ef043a4bce3c0e0c3a83a5309c17d1962be972 to mainline-sdkext-releaseaml_sdk_341710000aml_sdk_341510000aml_sdk_341410000aml_sdk_341110080aml_sdk_341110000aml_sdk_341010000aml_sdk_340912010android14-mainline-sdkext-release
Change-Id: I89d9bffdfa82f0d26aa4638c7d74276fc5a0910f
Diffstat (limited to 'tests/test_syntax/extensions')
-rw-r--r-- | tests/test_syntax/extensions/__init__.py | 20 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_abbr.py | 242 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_admonition.py | 245 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_attr_list.py | 80 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_code_hilite.py | 764 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_def_list.py | 323 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_fenced_code.py | 1020 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_footnotes.py | 338 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_legacy_attrs.py | 67 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_legacy_em.py | 66 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_md_in_html.py | 1216 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_smarty.py | 36 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_tables.py | 922 | ||||
-rw-r--r-- | tests/test_syntax/extensions/test_toc.py | 614 |
14 files changed, 5953 insertions, 0 deletions
diff --git a/tests/test_syntax/extensions/__init__.py b/tests/test_syntax/extensions/__init__.py new file mode 100644 index 0000000..564ba3b --- /dev/null +++ b/tests/test_syntax/extensions/__init__.py @@ -0,0 +1,20 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" diff --git a/tests/test_syntax/extensions/test_abbr.py b/tests/test_syntax/extensions/test_abbr.py new file mode 100644 index 0000000..64388c2 --- /dev/null +++ b/tests/test_syntax/extensions/test_abbr.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase + + +class TestAbbr(TestCase): + + default_kwargs = {'extensions': ['abbr']} + + def test_abbr_upper(self): + self.assertMarkdownRenders( + self.dedent( + """ + ABBR + + *[ABBR]: Abbreviation + """ + ), + self.dedent( + """ + <p><abbr title="Abbreviation">ABBR</abbr></p> + """ + ) + ) + + def test_abbr_lower(self): + self.assertMarkdownRenders( + self.dedent( + """ + abbr + + *[abbr]: Abbreviation + """ + ), + self.dedent( + """ + <p><abbr title="Abbreviation">abbr</abbr></p> + """ + ) + ) + + def test_abbr_multiple(self): + self.assertMarkdownRenders( + self.dedent( + """ + The HTML specification + is maintained by the W3C. + + *[HTML]: Hyper Text Markup Language + *[W3C]: World Wide Web Consortium + """ + ), + self.dedent( + """ + <p>The <abbr title="Hyper Text Markup Language">HTML</abbr> specification + is maintained by the <abbr title="World Wide Web Consortium">W3C</abbr>.</p> + """ + ) + ) + + def test_abbr_override(self): + self.assertMarkdownRenders( + self.dedent( + """ + ABBR + + *[ABBR]: Ignored + *[ABBR]: The override + """ + ), + self.dedent( + """ + <p><abbr title="The override">ABBR</abbr></p> + """ + ) + ) + + def test_abbr_no_blank_Lines(self): + self.assertMarkdownRenders( + self.dedent( + """ + ABBR + *[ABBR]: Abbreviation + ABBR + """ + ), + self.dedent( + """ + <p><abbr title="Abbreviation">ABBR</abbr></p> + <p><abbr title="Abbreviation">ABBR</abbr></p> + """ + ) + ) + + def test_abbr_no_space(self): + self.assertMarkdownRenders( + self.dedent( + """ + ABBR + + *[ABBR]:Abbreviation + """ + ), + self.dedent( + """ + <p><abbr title="Abbreviation">ABBR</abbr></p> + """ + ) + ) + + def test_abbr_extra_space(self): + self.assertMarkdownRenders( + self.dedent( + """ + ABBR + + *[ABBR] : Abbreviation + """ + ), + self.dedent( + """ + <p><abbr title="Abbreviation">ABBR</abbr></p> + """ + ) + ) + + def test_abbr_line_break(self): + self.assertMarkdownRenders( + self.dedent( + """ + ABBR + + *[ABBR]: + Abbreviation + """ + ), + self.dedent( + """ + <p><abbr title="Abbreviation">ABBR</abbr></p> + """ + ) + ) + + def test_abbr_ignore_unmatched_case(self): + self.assertMarkdownRenders( + self.dedent( + """ + ABBR abbr + + *[ABBR]: Abbreviation + """ + ), + self.dedent( + """ + <p><abbr title="Abbreviation">ABBR</abbr> abbr</p> + """ + ) + ) + + def test_abbr_partial_word(self): + self.assertMarkdownRenders( + self.dedent( + """ + ABBR ABBREVIATION + + *[ABBR]: Abbreviation + """ + ), + self.dedent( + """ + <p><abbr title="Abbreviation">ABBR</abbr> ABBREVIATION</p> + """ + ) + ) + + def test_abbr_unused(self): + self.assertMarkdownRenders( + self.dedent( + """ + foo bar + + *[ABBR]: Abbreviation + """ + ), + self.dedent( + """ + <p>foo bar</p> + """ + ) + ) + + def test_abbr_double_quoted(self): + self.assertMarkdownRenders( + self.dedent( + """ + ABBR + + *[ABBR]: "Abbreviation" + """ + ), + self.dedent( + """ + <p><abbr title=""Abbreviation"">ABBR</abbr></p> + """ + ) + ) + + def test_abbr_single_quoted(self): + self.assertMarkdownRenders( + self.dedent( + """ + ABBR + + *[ABBR]: 'Abbreviation' + """ + ), + self.dedent( + """ + <p><abbr title="'Abbreviation'">ABBR</abbr></p> + """ + ) + ) diff --git a/tests/test_syntax/extensions/test_admonition.py b/tests/test_syntax/extensions/test_admonition.py new file mode 100644 index 0000000..44c70d1 --- /dev/null +++ b/tests/test_syntax/extensions/test_admonition.py @@ -0,0 +1,245 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2019 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase + + +class TestAdmonition(TestCase): + + def test_with_lists(self): + self.assertMarkdownRenders( + self.dedent( + ''' + - List + + !!! note "Admontion" + + - Paragraph + + Paragraph + ''' + ), + self.dedent( + ''' + <ul> + <li> + <p>List</p> + <div class="admonition note"> + <p class="admonition-title">Admontion</p> + <ul> + <li> + <p>Paragraph</p> + <p>Paragraph</p> + </li> + </ul> + </div> + </li> + </ul> + ''' + ), + extensions=['admonition'] + ) + + def test_with_big_lists(self): + self.assertMarkdownRenders( + self.dedent( + ''' + - List + + !!! note "Admontion" + + - Paragraph + + Paragraph + + - Paragraph + + paragraph + ''' + ), + self.dedent( + ''' + <ul> + <li> + <p>List</p> + <div class="admonition note"> + <p class="admonition-title">Admontion</p> + <ul> + <li> + <p>Paragraph</p> + <p>Paragraph</p> + </li> + <li> + <p>Paragraph</p> + <p>paragraph</p> + </li> + </ul> + </div> + </li> + </ul> + ''' + ), + extensions=['admonition'] + ) + + def test_with_complex_lists(self): + self.assertMarkdownRenders( + self.dedent( + ''' + - List + + !!! note "Admontion" + + - Paragraph + + !!! note "Admontion" + + 1. Paragraph + + Paragraph + ''' + ), + self.dedent( + ''' + <ul> + <li> + <p>List</p> + <div class="admonition note"> + <p class="admonition-title">Admontion</p> + <ul> + <li> + <p>Paragraph</p> + <div class="admonition note"> + <p class="admonition-title">Admontion</p> + <ol> + <li> + <p>Paragraph</p> + <p>Paragraph</p> + </li> + </ol> + </div> + </li> + </ul> + </div> + </li> + </ul> + ''' + ), + extensions=['admonition'] + ) + + def test_definition_list(self): + self.assertMarkdownRenders( + self.dedent( + ''' + - List + + !!! note "Admontion" + + Term + + : Definition + + More text + + : Another + definition + + Even more text + ''' + ), + self.dedent( + ''' + <ul> + <li> + <p>List</p> + <div class="admonition note"> + <p class="admonition-title">Admontion</p> + <dl> + <dt>Term</dt> + <dd> + <p>Definition</p> + <p>More text</p> + </dd> + <dd> + <p>Another + definition</p> + <p>Even more text</p> + </dd> + </dl> + </div> + </li> + </ul> + ''' + ), + extensions=['admonition', 'def_list'] + ) + + def test_with_preceding_text(self): + self.assertMarkdownRenders( + self.dedent( + ''' + foo + **foo** + !!! note "Admonition" + ''' + ), + self.dedent( + ''' + <p>foo + <strong>foo</strong></p> + <div class="admonition note"> + <p class="admonition-title">Admonition</p> + </div> + ''' + ), + extensions=['admonition'] + ) + + def test_admontion_detabbing(self): + self.assertMarkdownRenders( + self.dedent( + ''' + !!! note "Admonition" + - Parent 1 + + - Child 1 + - Child 2 + ''' + ), + self.dedent( + ''' + <div class="admonition note"> + <p class="admonition-title">Admonition</p> + <ul> + <li> + <p>Parent 1</p> + <ul> + <li>Child 1</li> + <li>Child 2</li> + </ul> + </li> + </ul> + </div> + ''' + ), + extensions=['admonition'] + ) diff --git a/tests/test_syntax/extensions/test_attr_list.py b/tests/test_syntax/extensions/test_attr_list.py new file mode 100644 index 0000000..6baaafb --- /dev/null +++ b/tests/test_syntax/extensions/test_attr_list.py @@ -0,0 +1,80 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2020 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase + + +class TestAttrList(TestCase): + + maxDiff = None + + # TODO: Move the rest of the attr_list tests here. + + def test_empty_list(self): + self.assertMarkdownRenders( + '*foo*{ }', + '<p><em>foo</em>{ }</p>', + extensions=['attr_list'] + ) + + def test_table_td(self): + self.assertMarkdownRenders( + self.dedent( + """ + | A { .foo } | *B*{ .foo } | C { } | D{ .foo } | E { .foo } F | + |-------------|-------------|-------|---------------|--------------| + | a { .foo } | *b*{ .foo } | c { } | d{ .foo } | e { .foo } f | + | valid on td | inline | empty | missing space | not at end | + """ + ), + self.dedent( + """ + <table> + <thead> + <tr> + <th class="foo">A</th> + <th><em class="foo">B</em></th> + <th>C { }</th> + <th>D{ .foo }</th> + <th>E { .foo } F</th> + </tr> + </thead> + <tbody> + <tr> + <td class="foo">a</td> + <td><em class="foo">b</em></td> + <td>c { }</td> + <td>d{ .foo }</td> + <td>e { .foo } f</td> + </tr> + <tr> + <td>valid on td</td> + <td>inline</td> + <td>empty</td> + <td>missing space</td> + <td>not at end</td> + </tr> + </tbody> + </table> + """ + ), + extensions=['attr_list', 'tables'] + ) diff --git a/tests/test_syntax/extensions/test_code_hilite.py b/tests/test_syntax/extensions/test_code_hilite.py new file mode 100644 index 0000000..09dd523 --- /dev/null +++ b/tests/test_syntax/extensions/test_code_hilite.py @@ -0,0 +1,764 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2019 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase +from markdown.extensions.codehilite import CodeHiliteExtension, CodeHilite +import os + +try: + import pygments # noqa + has_pygments = True +except ImportError: + has_pygments = False + +# The version required by the tests is the version specified and installed in the 'pygments' tox env. +# In any environment where the PYGMENTS_VERSION environment variable is either not defined or doesn't +# match the version of Pygments installed, all tests which rely in pygments will be skipped. +required_pygments_version = os.environ.get('PYGMENTS_VERSION', '') + + +class TestCodeHiliteClass(TestCase): + """ Test the markdown.extensions.codehilite.CodeHilite class. """ + + def setUp(self): + if has_pygments and pygments.__version__ != required_pygments_version: + self.skipTest(f'Pygments=={required_pygments_version} is required') + + maxDiff = None + + def assertOutputEquals(self, source, expected, **options): + """ + Test that source code block results in the expected output with given options. + """ + + output = CodeHilite(source, **options).hilite() + self.assertMultiLineEqual(output.strip(), expected) + + def test_codehilite_defaults(self): + if has_pygments: + # Odd result as no lang given and a single comment is not enough for guessing. + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="err"># A Code Comment</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code># A Code Comment\n' + '</code></pre>' + ) + self.assertOutputEquals('# A Code Comment', expected) + + def test_codehilite_guess_lang(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="cp"><?php</span> ' + '<span class="k">print</span><span class="p">(</span><span class="s2">"Hello World"</span>' + '<span class="p">);</span> <span class="cp">?></span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code><?php print("Hello World"); ?>\n' + '</code></pre>' + ) + # Use PHP as the the starting `<?php` tag ensures an accurate guess. + self.assertOutputEquals('<?php print("Hello World"); ?>', expected, guess_lang=True) + + def test_codehilite_guess_lang_plain_text(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="err">plain text</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code>plain text\n' + '</code></pre>' + ) + # This will be difficult to guess. + self.assertOutputEquals('plain text', expected, guess_lang=True) + + def test_codehilite_set_lang(self): + if has_pygments: + # Note an extra `<span class="x">` is added to end of code block when lang explicitly set. + # Compare with expected output for `test_guess_lang`. Not sure why this happens. + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="cp"><?php</span> ' + '<span class="k">print</span><span class="p">(</span><span class="s2">"Hello World"</span>' + '<span class="p">);</span> <span class="cp">?></span><span class="x"></span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-php"><?php print("Hello World"); ?>\n' + '</code></pre>' + ) + self.assertOutputEquals('<?php print("Hello World"); ?>', expected, lang='php') + + def test_codehilite_bad_lang(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="cp"><?php</span> ' + '<span class="k">print</span><span class="p">(</span><span class="s2">' + '"Hello World"</span><span class="p">);</span> <span class="cp">?></span>\n' + '</code></pre></div>' + ) + else: + # Note that without pygments there is no way to check that the language name is bad. + expected = ( + '<pre class="codehilite"><code class="language-unkown">' + '<?php print("Hello World"); ?>\n' + '</code></pre>' + ) + # The starting `<?php` tag ensures an accurate guess. + self.assertOutputEquals('<?php print("Hello World"); ?>', expected, lang='unkown') + + def test_codehilite_use_pygments_false(self): + expected = ( + '<pre class="codehilite"><code class="language-php"><?php print("Hello World"); ?>\n' + '</code></pre>' + ) + self.assertOutputEquals('<?php print("Hello World"); ?>', expected, lang='php', use_pygments=False) + + def test_codehilite_lang_prefix_empty(self): + expected = ( + '<pre class="codehilite"><code class="php"><?php print("Hello World"); ?>\n' + '</code></pre>' + ) + self.assertOutputEquals( + '<?php print("Hello World"); ?>', expected, lang='php', use_pygments=False, lang_prefix='' + ) + + def test_codehilite_lang_prefix(self): + expected = ( + '<pre class="codehilite"><code class="lang-php"><?php print("Hello World"); ?>\n' + '</code></pre>' + ) + self.assertOutputEquals( + '<?php print("Hello World"); ?>', expected, lang='php', use_pygments=False, lang_prefix='lang-' + ) + + def test_codehilite_linenos_true(self): + if has_pygments: + expected = ( + '<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div>' + '</td><td class="code"><div class="codehilite"><pre><span></span><code>plain text\n' + '</code></pre></div>\n' + '</td></tr></table>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text linenums">plain text\n' + '</code></pre>' + ) + self.assertOutputEquals('plain text', expected, lang='text', linenos=True) + + def test_codehilite_linenos_false(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code>plain text\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text">plain text\n' + '</code></pre>' + ) + self.assertOutputEquals('plain text', expected, lang='text', linenos=False) + + def test_codehilite_linenos_none(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code>plain text\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text">plain text\n' + '</code></pre>' + ) + self.assertOutputEquals('plain text', expected, lang='text', linenos=None) + + def test_codehilite_linenos_table(self): + if has_pygments: + expected = ( + '<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div>' + '</td><td class="code"><div class="codehilite"><pre><span></span><code>plain text\n' + '</code></pre></div>\n' + '</td></tr></table>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text linenums">plain text\n' + '</code></pre>' + ) + self.assertOutputEquals('plain text', expected, lang='text', linenos='table') + + def test_codehilite_linenos_inline(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="linenos">1</span>plain text\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text linenums">plain text\n' + '</code></pre>' + ) + self.assertOutputEquals('plain text', expected, lang='text', linenos='inline') + + def test_codehilite_linenums_true(self): + if has_pygments: + expected = ( + '<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div>' + '</td><td class="code"><div class="codehilite"><pre><span></span><code>plain text\n' + '</code></pre></div>\n' + '</td></tr></table>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text linenums">plain text\n' + '</code></pre>' + ) + self.assertOutputEquals('plain text', expected, lang='text', linenums=True) + + def test_codehilite_set_cssclass(self): + if has_pygments: + expected = ( + '<div class="override"><pre><span></span><code>plain text\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="override"><code class="language-text">plain text\n' + '</code></pre>' + ) + self.assertOutputEquals('plain text', expected, lang='text', cssclass='override') + + def test_codehilite_set_css_class(self): + if has_pygments: + expected = ( + '<div class="override"><pre><span></span><code>plain text\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="override"><code class="language-text">plain text\n' + '</code></pre>' + ) + self.assertOutputEquals('plain text', expected, lang='text', css_class='override') + + def test_codehilite_linenostart(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="linenos">42</span>plain text\n' + '</code></pre></div>' + ) + else: + # TODO: Implement linenostart for no-pygments. Will need to check what JS libs look for. + expected = ( + '<pre class="codehilite"><code class="language-text linenums">plain text\n' + '</code></pre>' + ) + self.assertOutputEquals('plain text', expected, lang='text', linenos='inline', linenostart=42) + + def test_codehilite_linenos_hl_lines(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code>' + '<span class="linenos">1</span><span class="hll">line 1\n' + '</span><span class="linenos">2</span>line 2\n' + '<span class="linenos">3</span><span class="hll">line 3\n' + '</span></code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text linenums">line 1\n' + 'line 2\n' + 'line 3\n' + '</code></pre>' + ) + self.assertOutputEquals('line 1\nline 2\nline 3', expected, lang='text', linenos='inline', hl_lines=[1, 3]) + + def test_codehilite_linenos_linenostep(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="linenos"> </span>line 1\n' + '<span class="linenos">2</span>line 2\n' + '<span class="linenos"> </span>line 3\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text linenums">line 1\n' + 'line 2\n' + 'line 3\n' + '</code></pre>' + ) + self.assertOutputEquals('line 1\nline 2\nline 3', expected, lang='text', linenos='inline', linenostep=2) + + def test_codehilite_linenos_linenospecial(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="linenos">1</span>line 1\n' + '<span class="linenos special">2</span>line 2\n' + '<span class="linenos">3</span>line 3\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text linenums">line 1\n' + 'line 2\n' + 'line 3\n' + '</code></pre>' + ) + self.assertOutputEquals('line 1\nline 2\nline 3', expected, lang='text', linenos='inline', linenospecial=2) + + def test_codehilite_startinline(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="k">print</span><span class="p">(</span>' + '<span class="s2">"Hello World"</span><span class="p">);</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-php">print("Hello World");\n' + '</code></pre>' + ) + self.assertOutputEquals('print("Hello World");', expected, lang='php', startinline=True) + + +class TestCodeHiliteExtension(TestCase): + """ Test codehilite extension. """ + + def setUp(self): + if has_pygments and pygments.__version__ != required_pygments_version: + self.skipTest(f'Pygments=={required_pygments_version} is required') + + # Define a custom Pygments formatter (same example in the documentation) + if has_pygments: + class CustomAddLangHtmlFormatter(pygments.formatters.HtmlFormatter): + def __init__(self, lang_str='', **options): + super().__init__(**options) + self.lang_str = lang_str + + def _wrap_code(self, source): + yield 0, f'<code class="{self.lang_str}">' + yield from source + yield 0, '</code>' + else: + CustomAddLangHtmlFormatter = None + + self.custom_pygments_formatter = CustomAddLangHtmlFormatter + + maxDiff = None + + def testBasicCodeHilite(self): + if has_pygments: + # Odd result as no lang given and a single comment is not enough for guessing. + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="err"># A Code Comment</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code># A Code Comment\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + '\t# A Code Comment', + expected, + extensions=['codehilite'] + ) + + def testLinenumsTrue(self): + if has_pygments: + expected = ( + '<table class="codehilitetable"><tr>' + '<td class="linenos"><div class="linenodiv"><pre>1</pre></div></td>' + '<td class="code"><div class="codehilite"><pre><span></span>' + '<code><span class="err"># A Code Comment</span>\n' + '</code></pre></div>\n' + '</td></tr></table>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="linenums"># A Code Comment\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + '\t# A Code Comment', + expected, + extensions=[CodeHiliteExtension(linenums=True)] + ) + + def testLinenumsFalse(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="c1"># A Code Comment</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-python"># A Code Comment\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + ( + '\t#!Python\n' + '\t# A Code Comment' + ), + expected, + extensions=[CodeHiliteExtension(linenums=False)] + ) + + def testLinenumsNone(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="err"># A Code Comment</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code># A Code Comment\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + '\t# A Code Comment', + expected, + extensions=[CodeHiliteExtension(linenums=None)] + ) + + def testLinenumsNoneWithShebang(self): + if has_pygments: + expected = ( + '<table class="codehilitetable"><tr>' + '<td class="linenos"><div class="linenodiv"><pre>1</pre></div></td>' + '<td class="code"><div class="codehilite"><pre><span></span>' + '<code><span class="c1"># A Code Comment</span>\n' + '</code></pre></div>\n' + '</td></tr></table>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-python linenums"># A Code Comment\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + ( + '\t#!Python\n' + '\t# A Code Comment' + ), + expected, + extensions=[CodeHiliteExtension(linenums=None)] + ) + + def testLinenumsNoneWithColon(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="c1"># A Code Comment</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-python"># A Code Comment\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + ( + '\t:::Python\n' + '\t# A Code Comment' + ), + expected, + extensions=[CodeHiliteExtension(linenums=None)] + ) + + def testHighlightLinesWithColon(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="hll"><span class="c1">#line 1</span>\n' + '</span><span class="c1">#line 2</span>\n' + '<span class="c1">#line 3</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-python">#line 1\n' + '#line 2\n' + '#line 3\n' + '</code></pre>' + ) + # Double quotes + self.assertMarkdownRenders( + ( + '\t:::Python hl_lines="1"\n' + '\t#line 1\n' + '\t#line 2\n' + '\t#line 3' + ), + expected, + extensions=['codehilite'] + ) + # Single quotes + self.assertMarkdownRenders( + ( + "\t:::Python hl_lines='1'\n" + '\t#line 1\n' + '\t#line 2\n' + '\t#line 3' + ), + expected, + extensions=['codehilite'] + ) + + def testUsePygmentsFalse(self): + self.assertMarkdownRenders( + ( + '\t:::Python\n' + '\t# A Code Comment' + ), + ( + '<pre class="codehilite"><code class="language-python"># A Code Comment\n' + '</code></pre>' + ), + extensions=[CodeHiliteExtension(use_pygments=False)] + ) + + def testLangPrefixEmpty(self): + self.assertMarkdownRenders( + ( + '\t:::Python\n' + '\t# A Code Comment' + ), + ( + '<pre class="codehilite"><code class="python"># A Code Comment\n' + '</code></pre>' + ), + extensions=[CodeHiliteExtension(use_pygments=False, lang_prefix='')] + ) + + def testLangPrefix(self): + self.assertMarkdownRenders( + ( + '\t:::Python\n' + '\t# A Code Comment' + ), + ( + '<pre class="codehilite"><code class="lang-python"># A Code Comment\n' + '</code></pre>' + ), + extensions=[CodeHiliteExtension(use_pygments=False, lang_prefix='lang-')] + ) + + def testDoubleEscape(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre>' + '<span></span>' + '<code><span class="p"><</span><span class="nt">span</span><span class="p">></span>' + 'This<span class="ni">&amp;</span>That' + '<span class="p"></</span><span class="nt">span</span><span class="p">></span>' + '\n</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-html">' + '<span>This&amp;That</span>\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + ( + '\t:::html\n' + '\t<span>This&That</span>' + ), + expected, + extensions=['codehilite'] + ) + + def testEntitiesIntact(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre>' + '<span></span>' + '<code>< &lt; and > &gt;' + '\n</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text">' + '< &lt; and > &gt;\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + ( + '\t:::text\n' + '\t< < and > >' + ), + expected, + extensions=['codehilite'] + ) + + def testHighlightAmps(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code>&\n' + '&amp;\n' + '&amp;amp;\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-text">&\n' + '&amp;\n' + '&amp;amp;\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + ( + '\t:::text\n' + '\t&\n' + '\t&\n' + '\t&amp;' + ), + expected, + extensions=['codehilite'] + ) + + def testUnknownOption(self): + if has_pygments: + # Odd result as no lang given and a single comment is not enough for guessing. + expected = ( + '<div class="codehilite"><pre><span></span><code><span class="err"># A Code Comment</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code># A Code Comment\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + '\t# A Code Comment', + expected, + extensions=[CodeHiliteExtension(unknown='some value')], + ) + + def testMultipleBlocksSameStyle(self): + if has_pygments: + # See also: https://github.com/Python-Markdown/markdown/issues/1240 + expected = ( + '<div class="codehilite" style="background: #202020"><pre style="line-height: 125%; margin: 0;">' + '<span></span><code><span style="color: #999999; font-style: italic"># First Code Block</span>\n' + '</code></pre></div>\n\n' + '<p>Normal paragraph</p>\n' + '<div class="codehilite" style="background: #202020"><pre style="line-height: 125%; margin: 0;">' + '<span></span><code><span style="color: #999999; font-style: italic"># Second Code Block</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-python"># First Code Block\n' + '</code></pre>\n\n' + '<p>Normal paragraph</p>\n' + '<pre class="codehilite"><code class="language-python"># Second Code Block\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + ( + '\t:::Python\n' + '\t# First Code Block\n\n' + 'Normal paragraph\n\n' + '\t:::Python\n' + '\t# Second Code Block' + ), + expected, + extensions=[CodeHiliteExtension(pygments_style="native", noclasses=True)] + ) + + def testFormatterLangStr(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code class="language-python">' + '<span class="c1"># A Code Comment</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-python"># A Code Comment\n' + '</code></pre>' + ) + + self.assertMarkdownRenders( + '\t:::Python\n' + '\t# A Code Comment', + expected, + extensions=[ + CodeHiliteExtension( + guess_lang=False, + pygments_formatter=self.custom_pygments_formatter + ) + ] + ) + + def testFormatterLangStrGuessLang(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span>' + '<code class="language-js+php"><span class="cp"><?php</span> ' + '<span class="k">print</span><span class="p">(</span>' + '<span class="s2">"Hello World"</span>' + '<span class="p">);</span> <span class="cp">?></span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code><?php print("Hello World"); ?>\n' + '</code></pre>' + ) + # Use PHP as the the starting `<?php` tag ensures an accurate guess. + self.assertMarkdownRenders( + '\t<?php print("Hello World"); ?>', + expected, + extensions=[CodeHiliteExtension(pygments_formatter=self.custom_pygments_formatter)] + ) + + def testFormatterLangStrEmptyLang(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span>' + '<code class="language-text"># A Code Comment\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code># A Code Comment\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + '\t# A Code Comment', + expected, + extensions=[ + CodeHiliteExtension( + guess_lang=False, + pygments_formatter=self.custom_pygments_formatter, + ) + ] + ) diff --git a/tests/test_syntax/extensions/test_def_list.py b/tests/test_syntax/extensions/test_def_list.py new file mode 100644 index 0000000..8273410 --- /dev/null +++ b/tests/test_syntax/extensions/test_def_list.py @@ -0,0 +1,323 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2019 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase + + +class TestDefList(TestCase): + + def test_def_list_with_ol(self): + self.assertMarkdownRenders( + self.dedent( + ''' + + term + + : this is a definition for term. it has + multiple lines in the first paragraph. + + 1. first thing + + first thing details in a second paragraph. + + 1. second thing + + second thing details in a second paragraph. + + 1. third thing + + third thing details in a second paragraph. + ''' + ), + self.dedent( + ''' + <dl> + <dt>term</dt> + <dd> + <p>this is a definition for term. it has + multiple lines in the first paragraph.</p> + <ol> + <li> + <p>first thing</p> + <p>first thing details in a second paragraph.</p> + </li> + <li> + <p>second thing</p> + <p>second thing details in a second paragraph.</p> + </li> + <li> + <p>third thing</p> + <p>third thing details in a second paragraph.</p> + </li> + </ol> + </dd> + </dl> + ''' + ), + extensions=['def_list'] + ) + + def test_def_list_with_ul(self): + self.assertMarkdownRenders( + self.dedent( + ''' + + term + + : this is a definition for term. it has + multiple lines in the first paragraph. + + - first thing + + first thing details in a second paragraph. + + - second thing + + second thing details in a second paragraph. + + - third thing + + third thing details in a second paragraph. + ''' + ), + self.dedent( + ''' + <dl> + <dt>term</dt> + <dd> + <p>this is a definition for term. it has + multiple lines in the first paragraph.</p> + <ul> + <li> + <p>first thing</p> + <p>first thing details in a second paragraph.</p> + </li> + <li> + <p>second thing</p> + <p>second thing details in a second paragraph.</p> + </li> + <li> + <p>third thing</p> + <p>third thing details in a second paragraph.</p> + </li> + </ul> + </dd> + </dl> + ''' + ), + extensions=['def_list'] + ) + + def test_def_list_with_nesting(self): + self.assertMarkdownRenders( + self.dedent( + ''' + + term + + : this is a definition for term. it has + multiple lines in the first paragraph. + + 1. first thing + + first thing details in a second paragraph. + + - first nested thing + + second nested thing details + ''' + ), + self.dedent( + ''' + <dl> + <dt>term</dt> + <dd> + <p>this is a definition for term. it has + multiple lines in the first paragraph.</p> + <ol> + <li> + <p>first thing</p> + <p>first thing details in a second paragraph.</p> + <ul> + <li> + <p>first nested thing</p> + <p>second nested thing details</p> + </li> + </ul> + </li> + </ol> + </dd> + </dl> + ''' + ), + extensions=['def_list'] + ) + + def test_def_list_with_nesting_self(self): + self.assertMarkdownRenders( + self.dedent( + ''' + + term + + : this is a definition for term. it has + multiple lines in the first paragraph. + + inception + + : this is a definition for term. it has + multiple lines in the first paragraph. + + - bullet point + + another paragraph + ''' + ), + self.dedent( + ''' + <dl> + <dt>term</dt> + <dd> + <p>this is a definition for term. it has + multiple lines in the first paragraph.</p> + <dl> + <dt>inception</dt> + <dd> + <p>this is a definition for term. it has + multiple lines in the first paragraph.</p> + <ul> + <li>bullet point</li> + </ul> + <p>another paragraph</p> + </dd> + </dl> + </dd> + </dl> + ''' + ), + extensions=['def_list'] + ) + + def test_def_list_unreasonable_nesting(self): + self.assertMarkdownRenders( + self.dedent( + ''' + + turducken + + : this is a definition for term. it has + multiple lines in the first paragraph. + + 1. ordered list + + - nested list + + term + + : definition + + - item 1 paragraph 1 + + item 1 paragraph 2 + ''' + ), + self.dedent( + ''' + <dl> + <dt>turducken</dt> + <dd> + <p>this is a definition for term. it has + multiple lines in the first paragraph.</p> + <ol> + <li> + <p>ordered list</p> + <ul> + <li> + <p>nested list</p> + <dl> + <dt>term</dt> + <dd> + <p>definition</p> + <ul> + <li> + <p>item 1 paragraph 1</p> + <p>item 1 paragraph 2</p> + </li> + </ul> + </dd> + </dl> + </li> + </ul> + </li> + </ol> + </dd> + </dl> + ''' + ), + extensions=['def_list'] + ) + + def test_def_list_nested_admontions(self): + self.assertMarkdownRenders( + self.dedent( + ''' + term + + : definition + + !!! note "Admontion" + + term + + : definition + + 1. list + + continue + ''' + ), + self.dedent( + ''' + <dl> + <dt>term</dt> + <dd> + <p>definition</p> + <div class="admonition note"> + <p class="admonition-title">Admontion</p> + <dl> + <dt>term</dt> + <dd> + <p>definition</p> + <ol> + <li> + <p>list</p> + <p>continue</p> + </li> + </ol> + </dd> + </dl> + </div> + </dd> + </dl> + ''' + ), + extensions=['def_list', 'admonition'] + ) diff --git a/tests/test_syntax/extensions/test_fenced_code.py b/tests/test_syntax/extensions/test_fenced_code.py new file mode 100644 index 0000000..be3c215 --- /dev/null +++ b/tests/test_syntax/extensions/test_fenced_code.py @@ -0,0 +1,1020 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2019 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase +import markdown +import markdown.extensions.codehilite +import os + +try: + import pygments # noqa + import pygments.formatters # noqa + has_pygments = True +except ImportError: + has_pygments = False + +# The version required by the tests is the version specified and installed in the 'pygments' tox env. +# In any environment where the PYGMENTS_VERSION environment variable is either not defined or doesn't +# match the version of Pygments installed, all tests which rely in pygments will be skipped. +required_pygments_version = os.environ.get('PYGMENTS_VERSION', '') + + +class TestFencedCode(TestCase): + + def testBasicFence(self): + self.assertMarkdownRenders( + self.dedent( + ''' + A paragraph before a fenced code block: + + ``` + Fenced code block + ``` + ''' + ), + self.dedent( + ''' + <p>A paragraph before a fenced code block:</p> + <pre><code>Fenced code block + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testNestedFence(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ```` + + ``` + ```` + ''' + ), + self.dedent( + ''' + <pre><code> + ``` + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testFencedTildes(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ~~~ + # Arbitrary code + ``` # these backticks will not close the block + ~~~ + ''' + ), + self.dedent( + ''' + <pre><code># Arbitrary code + ``` # these backticks will not close the block + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testFencedLanguageNoDot(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` python + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testFencedLanguageWithDot(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` .python + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def test_fenced_code_in_raw_html(self): + self.assertMarkdownRenders( + self.dedent( + """ + <details> + ``` + Begone placeholders! + ``` + </details> + """ + ), + self.dedent( + """ + <details> + + <pre><code>Begone placeholders! + </code></pre> + + </details> + """ + ), + extensions=['fenced_code'] + ) + + def testFencedLanguageInAttr(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` {.python} + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testFencedMultipleClassesInAttr(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` {.python .foo .bar} + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre class="foo bar"><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testFencedIdInAttr(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { #foo } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre id="foo"><code># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testFencedIdAndLangInAttr(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python #foo } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre id="foo"><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testFencedIdAndLangAndClassInAttr(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python #foo .bar } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre id="foo" class="bar"><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testFencedLanguageIdAndPygmentsDisabledInAttrNoCodehilite(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python #foo use_pygments=False } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre id="foo"><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testFencedLanguageIdAndPygmentsEnabledInAttrNoCodehilite(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python #foo use_pygments=True } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre id="foo"><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code'] + ) + + def testFencedLanguageNoCodehiliteWithAttrList(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python foo=bar } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre><code class="language-python" foo="bar"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code', 'attr_list'] + ) + + def testFencedLanguagePygmentsDisabledInAttrNoCodehiliteWithAttrList(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python foo=bar use_pygments=False } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre><code class="language-python" foo="bar"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code', 'attr_list'] + ) + + def testFencedLanguagePygmentsEnabledInAttrNoCodehiliteWithAttrList(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python foo=bar use_pygments=True } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code', 'attr_list'] + ) + + def testFencedLanguageNoPrefix(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` python + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre><code class="python"># Some python code + </code></pre> + ''' + ), + extensions=[markdown.extensions.fenced_code.FencedCodeExtension(lang_prefix='')] + ) + + def testFencedLanguageAltPrefix(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` python + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre><code class="lang-python"># Some python code + </code></pre> + ''' + ), + extensions=[markdown.extensions.fenced_code.FencedCodeExtension(lang_prefix='lang-')] + ) + + def testFencedCodeEscapedAttrs(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { ."weird #"foo bar=">baz } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre id=""foo"><code class="language-"weird" bar="">baz"># Some python code + </code></pre> + ''' + ), + extensions=['fenced_code', 'attr_list'] + ) + + +class TestFencedCodeWithCodehilite(TestCase): + + def setUp(self): + if has_pygments and pygments.__version__ != required_pygments_version: + self.skipTest(f'Pygments=={required_pygments_version} is required') + + def test_shebang(self): + + if has_pygments: + expected = ''' + <div class="codehilite"><pre><span></span><code>#!test + </code></pre></div> + ''' + else: + expected = ''' + <pre class="codehilite"><code>#!test + </code></pre> + ''' + + self.assertMarkdownRenders( + self.dedent( + ''' + ``` + #!test + ``` + ''' + ), + self.dedent( + expected + ), + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False), + 'fenced_code' + ] + ) + + def testFencedCodeWithHighlightLines(self): + if has_pygments: + expected = self.dedent( + ''' + <div class="codehilite"><pre><span></span><code><span class="hll">line 1 + </span>line 2 + <span class="hll">line 3 + </span></code></pre></div> + ''' + ) + else: + expected = self.dedent( + ''' + <pre class="codehilite"><code>line 1 + line 2 + line 3 + </code></pre> + ''' + ) + self.assertMarkdownRenders( + self.dedent( + ''' + ```hl_lines="1 3" + line 1 + line 2 + line 3 + ``` + ''' + ), + expected, + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False), + 'fenced_code' + ] + ) + + def testFencedLanguageAndHighlightLines(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code>' + '<span class="hll"><span class="n">line</span> <span class="mi">1</span>\n' + '</span><span class="n">line</span> <span class="mi">2</span>\n' + '<span class="hll"><span class="n">line</span> <span class="mi">3</span>\n' + '</span></code></pre></div>' + ) + else: + expected = self.dedent( + ''' + <pre class="codehilite"><code class="language-python">line 1 + line 2 + line 3 + </code></pre> + ''' + ) + self.assertMarkdownRenders( + self.dedent( + ''' + ``` .python hl_lines="1 3" + line 1 + line 2 + line 3 + ``` + ''' + ), + expected, + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False), + 'fenced_code' + ] + ) + + def testFencedLanguageAndPygmentsDisabled(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` .python + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False), + 'fenced_code' + ] + ) + + def testFencedLanguageDoubleEscape(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code>' + '<span class="p"><</span><span class="nt">span</span>' + '<span class="p">></span>This<span class="ni">&amp;</span>' + 'That<span class="p"></</span><span class="nt">span</span>' + '<span class="p">></span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-html">' + '<span>This&amp;That</span>\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + self.dedent( + ''' + ```html + <span>This&That</span> + ``` + ''' + ), + expected, + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension(), + 'fenced_code' + ] + ) + + def testFencedAmps(self): + if has_pygments: + expected = self.dedent( + ''' + <div class="codehilite"><pre><span></span><code>& + &amp; + &amp;amp; + </code></pre></div> + ''' + ) + else: + expected = self.dedent( + ''' + <pre class="codehilite"><code class="language-text">& + &amp; + &amp;amp; + </code></pre> + ''' + ) + self.assertMarkdownRenders( + self.dedent( + ''' + ```text + & + & + &amp; + ``` + ''' + ), + expected, + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension(), + 'fenced_code' + ] + ) + + def testFencedCodeWithHighlightLinesInAttr(self): + if has_pygments: + expected = self.dedent( + ''' + <div class="codehilite"><pre><span></span><code><span class="hll">line 1 + </span>line 2 + <span class="hll">line 3 + </span></code></pre></div> + ''' + ) + else: + expected = self.dedent( + ''' + <pre class="codehilite"><code>line 1 + line 2 + line 3 + </code></pre> + ''' + ) + self.assertMarkdownRenders( + self.dedent( + ''' + ```{ hl_lines="1 3" } + line 1 + line 2 + line 3 + ``` + ''' + ), + expected, + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False), + 'fenced_code' + ] + ) + + def testFencedLanguageAndHighlightLinesInAttr(self): + if has_pygments: + expected = ( + '<div class="codehilite"><pre><span></span><code>' + '<span class="hll"><span class="n">line</span> <span class="mi">1</span>\n' + '</span><span class="n">line</span> <span class="mi">2</span>\n' + '<span class="hll"><span class="n">line</span> <span class="mi">3</span>\n' + '</span></code></pre></div>' + ) + else: + expected = self.dedent( + ''' + <pre class="codehilite"><code class="language-python">line 1 + line 2 + line 3 + </code></pre> + ''' + ) + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python hl_lines="1 3" } + line 1 + line 2 + line 3 + ``` + ''' + ), + expected, + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False), + 'fenced_code' + ] + ) + + def testFencedLanguageIdInAttrAndPygmentsDisabled(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python #foo } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre id="foo"><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False), + 'fenced_code' + ] + ) + + def testFencedLanguageIdAndPygmentsDisabledInAttr(self): + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python #foo use_pygments=False } + # Some python code + ``` + ''' + ), + self.dedent( + ''' + <pre id="foo"><code class="language-python"># Some python code + </code></pre> + ''' + ), + extensions=['codehilite', 'fenced_code'] + ) + + def testFencedLanguageAttrCssclass(self): + if has_pygments: + expected = self.dedent( + ''' + <div class="pygments"><pre><span></span><code><span class="c1"># Some python code</span> + </code></pre></div> + ''' + ) + else: + expected = ( + '<pre class="pygments"><code class="language-python"># Some python code\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python css_class='pygments' } + # Some python code + ``` + ''' + ), + expected, + extensions=['codehilite', 'fenced_code'] + ) + + def testFencedLanguageAttrLinenums(self): + if has_pygments: + expected = ( + '<table class="codehilitetable"><tr>' + '<td class="linenos"><div class="linenodiv"><pre>1</pre></div></td>' + '<td class="code"><div class="codehilite"><pre><span></span>' + '<code><span class="c1"># Some python code</span>\n' + '</code></pre></div>\n' + '</td></tr></table>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-python linenums"># Some python code\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python linenums=True } + # Some python code + ``` + ''' + ), + expected, + extensions=['codehilite', 'fenced_code'] + ) + + def testFencedLanguageAttrGuesslang(self): + if has_pygments: + expected = self.dedent( + ''' + <div class="codehilite"><pre><span></span><code># Some python code + </code></pre></div> + ''' + ) + else: + expected = ( + '<pre class="codehilite"><code># Some python code\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { guess_lang=False } + # Some python code + ``` + ''' + ), + expected, + extensions=['codehilite', 'fenced_code'] + ) + + def testFencedLanguageAttrNoclasses(self): + if has_pygments: + expected = ( + '<div class="codehilite" style="background: #f8f8f8">' + '<pre style="line-height: 125%; margin: 0;"><span></span><code>' + '<span style="color: #408080; font-style: italic"># Some python code</span>\n' + '</code></pre></div>' + ) + else: + expected = ( + '<pre class="codehilite"><code class="language-python"># Some python code\n' + '</code></pre>' + ) + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python noclasses=True } + # Some python code + ``` + ''' + ), + expected, + extensions=['codehilite', 'fenced_code'] + ) + + def testFencedMultipleBlocksSameStyle(self): + if has_pygments: + # See also: https://github.com/Python-Markdown/markdown/issues/1240 + expected = ( + '<div class="codehilite" style="background: #202020"><pre style="line-height: 125%; margin: 0;">' + '<span></span><code><span style="color: #999999; font-style: italic"># First Code Block</span>\n' + '</code></pre></div>\n\n' + '<p>Normal paragraph</p>\n' + '<div class="codehilite" style="background: #202020"><pre style="line-height: 125%; margin: 0;">' + '<span></span><code><span style="color: #999999; font-style: italic"># Second Code Block</span>\n' + '</code></pre></div>' + ) + else: + expected = ''' + <pre class="codehilite"><code class="language-python"># First Code Block + </code></pre> + + <p>Normal paragraph</p> + <pre class="codehilite"><code class="language-python"># Second Code Block + </code></pre> + ''' + + self.assertMarkdownRenders( + self.dedent( + ''' + ``` { .python } + # First Code Block + ``` + + Normal paragraph + + ``` { .python } + # Second Code Block + ``` + ''' + ), + self.dedent( + expected + ), + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension(pygments_style="native", noclasses=True), + 'fenced_code' + ] + ) + + def testCustomPygmentsFormatter(self): + if has_pygments: + class CustomFormatter(pygments.formatters.HtmlFormatter): + def wrap(self, source, outfile): + return self._wrap_div(self._wrap_code(source)) + + def _wrap_code(self, source): + yield 0, '<code>' + for i, t in source: + if i == 1: + t += '<br>' + yield i, t + yield 0, '</code>' + + expected = ''' + <div class="codehilite"><code>hello world + <br>hello another world + <br></code></div> + ''' + + else: + CustomFormatter = None + expected = ''' + <pre class="codehilite"><code>hello world + hello another world + </code></pre> + ''' + + self.assertMarkdownRenders( + self.dedent( + ''' + ``` + hello world + hello another world + ``` + ''' + ), + self.dedent( + expected + ), + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension( + pygments_formatter=CustomFormatter, + guess_lang=False, + ), + 'fenced_code' + ] + ) + + def testPygmentsAddLangClassFormatter(self): + if has_pygments: + class CustomAddLangHtmlFormatter(pygments.formatters.HtmlFormatter): + def __init__(self, lang_str='', **options): + super().__init__(**options) + self.lang_str = lang_str + + def _wrap_code(self, source): + yield 0, f'<code class="{self.lang_str}">' + yield from source + yield 0, '</code>' + + expected = ''' + <div class="codehilite"><pre><span></span><code class="language-text">hello world + hello another world + </code></pre></div> + ''' + else: + CustomAddLangHtmlFormatter = None + expected = ''' + <pre class="codehilite"><code class="language-text">hello world + hello another world + </code></pre> + ''' + + self.assertMarkdownRenders( + self.dedent( + ''' + ```text + hello world + hello another world + ``` + ''' + ), + self.dedent( + expected + ), + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension( + guess_lang=False, + pygments_formatter=CustomAddLangHtmlFormatter, + ), + 'fenced_code' + ] + ) + + def testSvgCustomPygmentsFormatter(self): + if has_pygments: + expected = ''' + <?xml version="1.0"?> + <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> + <svg xmlns="http://www.w3.org/2000/svg"> + <g font-family="monospace" font-size="14px"> + <text x="0" y="14" xml:space="preserve">hello world</text> + <text x="0" y="33" xml:space="preserve">hello another world</text> + <text x="0" y="52" xml:space="preserve"></text></g></svg> + ''' + + else: + expected = ''' + <pre class="codehilite"><code>hello world + hello another world + </code></pre> + ''' + + self.assertMarkdownRenders( + self.dedent( + ''' + ``` + hello world + hello another world + ``` + ''' + ), + self.dedent( + expected + ), + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension( + pygments_formatter='svg', + linenos=False, + guess_lang=False, + ), + 'fenced_code' + ] + ) + + def testInvalidCustomPygmentsFormatter(self): + if has_pygments: + expected = ''' + <div class="codehilite"><pre><span></span><code>hello world + hello another world + </code></pre></div> + ''' + + else: + expected = ''' + <pre class="codehilite"><code>hello world + hello another world + </code></pre> + ''' + + self.assertMarkdownRenders( + self.dedent( + ''' + ``` + hello world + hello another world + ``` + ''' + ), + self.dedent( + expected + ), + extensions=[ + markdown.extensions.codehilite.CodeHiliteExtension( + pygments_formatter='invalid', + guess_lang=False, + ), + 'fenced_code' + ] + ) diff --git a/tests/test_syntax/extensions/test_footnotes.py b/tests/test_syntax/extensions/test_footnotes.py new file mode 100644 index 0000000..9a6b32a --- /dev/null +++ b/tests/test_syntax/extensions/test_footnotes.py @@ -0,0 +1,338 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase + + +class TestFootnotes(TestCase): + + default_kwargs = {'extensions': ['footnotes']} + maxDiff = None + + def test_basic_footnote(self): + self.assertMarkdownRenders( + self.dedent( + """ + paragraph[^1] + + [^1]: A Footnote + """ + ), + '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>A Footnote <a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote 1 in the text">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>' + ) + + def test_multiple_footnotes(self): + self.assertMarkdownRenders( + self.dedent( + """ + foo[^1] + + bar[^2] + + [^1]: Footnote 1 + [^2]: Footnote 2 + """ + ), + '<p>foo<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '<p>bar<sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>Footnote 1 <a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote 1 in the text">↩</a></p>\n' + '</li>\n' + '<li id="fn:2">\n' + '<p>Footnote 2 <a class="footnote-backref" href="#fnref:2"' + ' title="Jump back to footnote 2 in the text">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>' + ) + + def test_multiple_footnotes_multiline(self): + self.assertMarkdownRenders( + self.dedent( + """ + foo[^1] + + bar[^2] + + [^1]: Footnote 1 + line 2 + [^2]: Footnote 2 + """ + ), + '<p>foo<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '<p>bar<sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>Footnote 1\nline 2 <a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote 1 in the text">↩</a></p>\n' + '</li>\n' + '<li id="fn:2">\n' + '<p>Footnote 2 <a class="footnote-backref" href="#fnref:2"' + ' title="Jump back to footnote 2 in the text">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>' + ) + + def test_footnote_multi_line(self): + self.assertMarkdownRenders( + self.dedent( + """ + paragraph[^1] + [^1]: A Footnote + line 2 + """ + ), + '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>A Footnote\nline 2 <a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote 1 in the text">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>' + ) + + def test_footnote_multi_line_lazy_indent(self): + self.assertMarkdownRenders( + self.dedent( + """ + paragraph[^1] + [^1]: A Footnote + line 2 + """ + ), + '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>A Footnote\nline 2 <a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote 1 in the text">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>' + ) + + def test_footnote_multi_line_complex(self): + self.assertMarkdownRenders( + self.dedent( + """ + paragraph[^1] + + [^1]: + + A Footnote + line 2 + + * list item + + > blockquote + """ + ), + '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>A Footnote\nline 2</p>\n' + '<ul>\n<li>list item</li>\n</ul>\n' + '<blockquote>\n<p>blockquote</p>\n</blockquote>\n' + '<p><a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote 1 in the text">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>' + ) + + def test_footnote_multple_complex(self): + self.assertMarkdownRenders( + self.dedent( + """ + foo[^1] + + bar[^2] + + [^1]: + + A Footnote + line 2 + + * list item + + > blockquote + + [^2]: Second footnote + + paragraph 2 + """ + ), + '<p>foo<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '<p>bar<sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>A Footnote\nline 2</p>\n' + '<ul>\n<li>list item</li>\n</ul>\n' + '<blockquote>\n<p>blockquote</p>\n</blockquote>\n' + '<p><a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote 1 in the text">↩</a></p>\n' + '</li>\n' + '<li id="fn:2">\n' + '<p>Second footnote</p>\n' + '<p>paragraph 2 <a class="footnote-backref" href="#fnref:2"' + ' title="Jump back to footnote 2 in the text">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>' + ) + + def test_footnote_multple_complex_no_blank_line_between(self): + self.assertMarkdownRenders( + self.dedent( + """ + foo[^1] + + bar[^2] + + [^1]: + + A Footnote + line 2 + + * list item + + > blockquote + [^2]: Second footnote + + paragraph 2 + """ + ), + '<p>foo<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '<p>bar<sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>A Footnote\nline 2</p>\n' + '<ul>\n<li>list item</li>\n</ul>\n' + '<blockquote>\n<p>blockquote</p>\n</blockquote>\n' + '<p><a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote 1 in the text">↩</a></p>\n' + '</li>\n' + '<li id="fn:2">\n' + '<p>Second footnote</p>\n' + '<p>paragraph 2 <a class="footnote-backref" href="#fnref:2"' + ' title="Jump back to footnote 2 in the text">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>' + ) + + def test_backlink_text(self): + """Test backlink configuration.""" + + self.assertMarkdownRenders( + 'paragraph[^1]\n\n[^1]: A Footnote', + '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>A Footnote <a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote 1 in the text">back</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>', + extension_configs={'footnotes': {'BACKLINK_TEXT': 'back'}} + ) + + def test_footnote_separator(self): + """Test separator configuration.""" + + self.assertMarkdownRenders( + 'paragraph[^1]\n\n[^1]: A Footnote', + '<p>paragraph<sup id="fnref-1"><a class="footnote-ref" href="#fn-1">1</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn-1">\n' + '<p>A Footnote <a class="footnote-backref" href="#fnref-1"' + ' title="Jump back to footnote 1 in the text">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>', + extension_configs={'footnotes': {'SEPARATOR': '-'}} + ) + + def test_backlink_title(self): + """Test backlink title configuration without placeholder.""" + + self.assertMarkdownRenders( + 'paragraph[^1]\n\n[^1]: A Footnote', + '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>A Footnote <a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>', + extension_configs={'footnotes': {'BACKLINK_TITLE': 'Jump back to footnote'}} + ) + + def test_superscript_text(self): + """Test superscript text configuration.""" + + self.assertMarkdownRenders( + 'paragraph[^1]\n\n[^1]: A Footnote', + '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">[1]</a></sup></p>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>A Footnote <a class="footnote-backref" href="#fnref:1"' + ' title="Jump back to footnote 1 in the text">↩</a></p>\n' + '</li>\n' + '</ol>\n' + '</div>', + extension_configs={'footnotes': {'SUPERSCRIPT_TEXT': '[{}]'}} + ) diff --git a/tests/test_syntax/extensions/test_legacy_attrs.py b/tests/test_syntax/extensions/test_legacy_attrs.py new file mode 100644 index 0000000..b4ba5e7 --- /dev/null +++ b/tests/test_syntax/extensions/test_legacy_attrs.py @@ -0,0 +1,67 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase + + +class TestLegacyAtrributes(TestCase): + + maxDiff = None + + def testLegacyAttrs(self): + self.assertMarkdownRenders( + self.dedent(""" + # Header {@id=inthebeginning} + + Now, let's try something *inline{@class=special}*, to see if it works + + @id=TABLE.OF.CONTENTS} + + + * {@id=TABLEOFCONTENTS} + + + Or in the middle of the text {@id=TABLEOFCONTENTS} + + {@id=tableofcontents} + + [![{@style=float: left; margin: 10px; border: + none;}](http://fourthought.com/images/ftlogo.png "Fourthought + logo")](http://fourthought.com/) + + ![img{@id=foo}][img] + + [img]: http://example.com/i.jpg + """), + self.dedent(""" + <h1 id="inthebeginning">Header </h1> + <p>Now, let's try something <em class="special">inline</em>, to see if it works</p> + <p>@id=TABLE.OF.CONTENTS}</p> + <ul> + <li id="TABLEOFCONTENTS"></li> + </ul> + <p id="TABLEOFCONTENTS">Or in the middle of the text </p> + <p id="tableofcontents"></p> + <p><a href="http://fourthought.com/"><img alt="" src="http://fourthought.com/images/ftlogo.png" style="float: left; margin: 10px; border: none;" title="Fourthought logo" /></a></p> + <p><img alt="img" id="foo" src="http://example.com/i.jpg" /></p> + """), # noqa: E501 + extensions=['legacy_attrs'] + ) diff --git a/tests/test_syntax/extensions/test_legacy_em.py b/tests/test_syntax/extensions/test_legacy_em.py new file mode 100644 index 0000000..c5daf1c --- /dev/null +++ b/tests/test_syntax/extensions/test_legacy_em.py @@ -0,0 +1,66 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase + + +class TestLegacyEm(TestCase): + def test_legacy_emphasis(self): + self.assertMarkdownRenders( + '_connected_words_', + '<p><em>connected</em>words_</p>', + extensions=['legacy_em'] + ) + + def test_legacy_strong(self): + self.assertMarkdownRenders( + '__connected__words__', + '<p><strong>connected</strong>words__</p>', + extensions=['legacy_em'] + ) + + def test_complex_emphasis_underscore(self): + self.assertMarkdownRenders( + 'This is text __bold _italic bold___ with more text', + '<p>This is text <strong>bold <em>italic bold</em></strong> with more text</p>', + extensions=['legacy_em'] + ) + + def test_complex_emphasis_underscore_mid_word(self): + self.assertMarkdownRenders( + 'This is text __bold_italic bold___ with more text', + '<p>This is text <strong>bold<em>italic bold</em></strong> with more text</p>', + extensions=['legacy_em'] + ) + + def test_complex_multple_underscore_type(self): + + self.assertMarkdownRenders( + 'traced ___along___ bla __blocked__ if other ___or___', + '<p>traced <strong><em>along</em></strong> bla <strong>blocked</strong> if other <strong><em>or</em></strong></p>' # noqa: E501 + ) + + def test_complex_multple_underscore_type_variant2(self): + + self.assertMarkdownRenders( + 'on the __1-4 row__ of the AP Combat Table ___and___ receive', + '<p>on the <strong>1-4 row</strong> of the AP Combat Table <strong><em>and</em></strong> receive</p>' + ) diff --git a/tests/test_syntax/extensions/test_md_in_html.py b/tests/test_syntax/extensions/test_md_in_html.py new file mode 100644 index 0000000..6c13f11 --- /dev/null +++ b/tests/test_syntax/extensions/test_md_in_html.py @@ -0,0 +1,1216 @@ +# -*- coding: utf-8 -*- +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from unittest import TestSuite +from markdown.test_tools import TestCase +from ..blocks.test_html_blocks import TestHTMLBlocks +from markdown import Markdown +from xml.etree.ElementTree import Element + + +class TestMarkdownInHTMLPostProcessor(TestCase): + """ Ensure any remaining elements in HTML stash are properly serialized. """ + + def test_stash_to_string(self): + # There should be no known cases where this actually happens so we need to + # forcefully pass an etree Element to the method to ensure proper behavior. + element = Element('div') + element.text = 'Foo bar.' + md = Markdown(extensions=['md_in_html']) + result = md.postprocessors['raw_html'].stash_to_string(element) + self.assertEqual(result, '<div>Foo bar.</div>') + + +class TestDefaultwMdInHTML(TestHTMLBlocks): + """ Ensure the md_in_html extension does not break the default behavior. """ + + default_kwargs = {'extensions': ['md_in_html']} + + +class TestMdInHTML(TestCase): + + default_kwargs = {'extensions': ['md_in_html']} + + def test_md1_paragraph(self): + self.assertMarkdownRenders( + '<p markdown="1">*foo*</p>', + '<p><em>foo</em></p>' + ) + + def test_md1_p_linebreaks(self): + self.assertMarkdownRenders( + self.dedent( + """ + <p markdown="1"> + *foo* + </p> + """ + ), + self.dedent( + """ + <p> + <em>foo</em> + </p> + """ + ) + ) + + def test_md1_p_blank_lines(self): + self.assertMarkdownRenders( + self.dedent( + """ + <p markdown="1"> + + *foo* + + </p> + """ + ), + self.dedent( + """ + <p> + + <em>foo</em> + + </p> + """ + ) + ) + + def test_md1_div(self): + self.assertMarkdownRenders( + '<div markdown="1">*foo*</div>', + self.dedent( + """ + <div> + <p><em>foo</em></p> + </div> + """ + ) + ) + + def test_md1_div_linebreaks(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + *foo* + </div> + """ + ), + self.dedent( + """ + <div> + <p><em>foo</em></p> + </div> + """ + ) + ) + + def test_md1_code_span(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + `<h1>code span</h1>` + </div> + """ + ), + self.dedent( + """ + <div> + <p><code><h1>code span</h1></code></p> + </div> + """ + ) + ) + + def test_md1_code_span_oneline(self): + self.assertMarkdownRenders( + '<div markdown="1">`<h1>code span</h1>`</div>', + self.dedent( + """ + <div> + <p><code><h1>code span</h1></code></p> + </div> + """ + ) + ) + + def test_md1_code_span_unclosed(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + `<p>` + </div> + """ + ), + self.dedent( + """ + <div> + <p><code><p></code></p> + </div> + """ + ) + ) + + def test_md1_code_span_script_tag(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + `<script>` + </div> + """ + ), + self.dedent( + """ + <div> + <p><code><script></code></p> + </div> + """ + ) + ) + + def test_md1_div_blank_lines(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + + *foo* + + </div> + """ + ), + self.dedent( + """ + <div> + <p><em>foo</em></p> + </div> + """ + ) + ) + + def test_md1_div_multi(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + + *foo* + + __bar__ + + </div> + """ + ), + self.dedent( + """ + <div> + <p><em>foo</em></p> + <p><strong>bar</strong></p> + </div> + """ + ) + ) + + def test_md1_div_nested(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + + <div markdown="1"> + *foo* + </div> + + </div> + """ + ), + self.dedent( + """ + <div> + <div> + <p><em>foo</em></p> + </div> + </div> + """ + ) + ) + + def test_md1_div_multi_nest(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + + <div markdown="1"> + <p markdown="1">*foo*</p> + </div> + + </div> + """ + ), + self.dedent( + """ + <div> + <div> + <p><em>foo</em></p> + </div> + </div> + """ + ) + ) + + def text_md1_details(self): + self.assertMarkdownRenders( + self.dedent( + """ + <details markdown="1"> + <summary>Click to expand</summary> + *foo* + </details> + """ + ), + self.dedent( + """ + <details> + <summary>Click to expand</summary> + <p><em>foo</em></p> + </details> + """ + ) + ) + + def test_md1_mix(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + A _Markdown_ paragraph before a raw child. + + <p markdown="1">A *raw* child.</p> + + A _Markdown_ tail to the raw child. + </div> + """ + ), + self.dedent( + """ + <div> + <p>A <em>Markdown</em> paragraph before a raw child.</p> + <p>A <em>raw</em> child.</p> + <p>A <em>Markdown</em> tail to the raw child.</p> + </div> + """ + ) + ) + + def test_md1_deep_mix(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + + A _Markdown_ paragraph before a raw child. + + A second Markdown paragraph + with two lines. + + <div markdown="1"> + + A *raw* child. + + <p markdown="1">*foo*</p> + + Raw child tail. + + </div> + + A _Markdown_ tail to the raw child. + + A second tail item + with two lines. + + <p markdown="1">More raw.</p> + + </div> + """ + ), + self.dedent( + """ + <div> + <p>A <em>Markdown</em> paragraph before a raw child.</p> + <p>A second Markdown paragraph + with two lines.</p> + <div> + <p>A <em>raw</em> child.</p> + <p><em>foo</em></p> + <p>Raw child tail.</p> + </div> + <p>A <em>Markdown</em> tail to the raw child.</p> + <p>A second tail item + with two lines.</p> + <p>More raw.</p> + </div> + """ + ) + ) + + def test_md1_div_raw_inline(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + + <em>foo</em> + + </div> + """ + ), + self.dedent( + """ + <div> + <p><em>foo</em></p> + </div> + """ + ) + ) + + def test_no_md1_paragraph(self): + self.assertMarkdownRenders( + '<p>*foo*</p>', + '<p>*foo*</p>' + ) + + def test_no_md1_nest(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + A _Markdown_ paragraph before a raw child. + + <p>A *raw* child.</p> + + A _Markdown_ tail to the raw child. + </div> + """ + ), + self.dedent( + """ + <div> + <p>A <em>Markdown</em> paragraph before a raw child.</p> + <p>A *raw* child.</p> + <p>A <em>Markdown</em> tail to the raw child.</p> + </div> + """ + ) + ) + + def test_md1_nested_empty(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + A _Markdown_ paragraph before a raw empty tag. + + <img src="image.png" alt="An image" /> + + A _Markdown_ tail to the raw empty tag. + </div> + """ + ), + self.dedent( + """ + <div> + <p>A <em>Markdown</em> paragraph before a raw empty tag.</p> + <p><img src="image.png" alt="An image" /></p> + <p>A <em>Markdown</em> tail to the raw empty tag.</p> + </div> + """ + ) + ) + + def test_md1_nested_empty_block(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + A _Markdown_ paragraph before a raw empty tag. + + <hr /> + + A _Markdown_ tail to the raw empty tag. + </div> + """ + ), + self.dedent( + """ + <div> + <p>A <em>Markdown</em> paragraph before a raw empty tag.</p> + <hr /> + <p>A <em>Markdown</em> tail to the raw empty tag.</p> + </div> + """ + ) + ) + + def test_empty_tags(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + <div></div> + </div> + """ + ), + self.dedent( + """ + <div> + <div></div> + </div> + """ + ) + ) + + def test_orphan_end_tag_in_raw_html(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + <div> + Test + + </pre> + + Test + </div> + </div> + """ + ), + self.dedent( + """ + <div> + <div> + Test + + </pre> + + Test + </div> + </div> + """ + ) + ) + + def test_complex_nested_case(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + **test** + <div> + **test** + <img src=""/> + <code>Test</code> + <span>**test**</span> + <p>Test 2</p> + </div> + </div> + """ + ), + self.dedent( + """ + <div> + <p><strong>test</strong></p> + <div> + **test** + <img src=""/> + <code>Test</code> + <span>**test**</span> + <p>Test 2</p> + </div> + </div> + """ + ) + ) + + def test_complex_nested_case_whitespace(self): + self.assertMarkdownRenders( + self.dedent( + """ + Text with space\t + <div markdown="1">\t + \t + <div> + **test** + <img src=""/> + <code>Test</code> + <span>**test**</span> + <div>With whitespace</div> + <p>Test 2</p> + </div> + **test** + </div> + """ + ), + self.dedent( + """ + <p>Text with space </p> + <div> + <div> + **test** + <img src=""/> + <code>Test</code> + <span>**test**</span> + <div>With whitespace</div> + <p>Test 2</p> + </div> + <p><strong>test</strong></p> + </div> + """ + ) + ) + + def test_md1_intail_md1(self): + self.assertMarkdownRenders( + '<div markdown="1">*foo*</div><div markdown="1">*bar*</div>', + self.dedent( + """ + <div> + <p><em>foo</em></p> + </div> + <div> + <p><em>bar</em></p> + </div> + """ + ) + ) + + def test_md1_no_blank_line_before(self): + self.assertMarkdownRenders( + self.dedent( + """ + A _Markdown_ paragraph with no blank line after. + <div markdown="1"> + A _Markdown_ paragraph in an HTML block with no blank line before. + </div> + """ + ), + self.dedent( + """ + <p>A <em>Markdown</em> paragraph with no blank line after.</p> + <div> + <p>A <em>Markdown</em> paragraph in an HTML block with no blank line before.</p> + </div> + """ + ) + ) + + def test_md1_no_line_break(self): + # The div here is parsed as a span-level element. Bad input equals bad output! + self.assertMarkdownRenders( + 'A _Markdown_ paragraph with <div markdown="1">no _line break_.</div>', + '<p>A <em>Markdown</em> paragraph with <div markdown="1">no <em>line break</em>.</div></p>' + ) + + def test_md1_in_tail(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div></div><div markdown="1"> + A _Markdown_ paragraph in an HTML block in tail of previous element. + </div> + """ + ), + self.dedent( + """ + <div></div> + <div> + <p>A <em>Markdown</em> paragraph in an HTML block in tail of previous element.</p> + </div> + """ + ) + ) + + def test_md1_PI_oneliner(self): + self.assertMarkdownRenders( + '<div markdown="1"><?php print("foo"); ?></div>', + self.dedent( + """ + <div> + <?php print("foo"); ?> + </div> + """ + ) + ) + + def test_md1_PI_multiline(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + <?php print("foo"); ?> + </div> + """ + ), + self.dedent( + """ + <div> + <?php print("foo"); ?> + </div> + """ + ) + ) + + def test_md1_PI_blank_lines(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + + <?php print("foo"); ?> + + </div> + """ + ), + self.dedent( + """ + <div> + <?php print("foo"); ?> + </div> + """ + ) + ) + + def test_md_span_paragraph(self): + self.assertMarkdownRenders( + '<p markdown="span">*foo*</p>', + '<p><em>foo</em></p>' + ) + + def test_md_block_paragraph(self): + self.assertMarkdownRenders( + '<p markdown="block">*foo*</p>', + self.dedent( + """ + <p> + <p><em>foo</em></p> + </p> + """ + ) + ) + + def test_md_span_div(self): + self.assertMarkdownRenders( + '<div markdown="span">*foo*</div>', + '<div><em>foo</em></div>' + ) + + def test_md_block_div(self): + self.assertMarkdownRenders( + '<div markdown="block">*foo*</div>', + self.dedent( + """ + <div> + <p><em>foo</em></p> + </div> + """ + ) + ) + + def test_md_span_nested_in_block(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="block"> + <div markdown="span">*foo*</div> + </div> + """ + ), + self.dedent( + """ + <div> + <div><em>foo</em></div> + </div> + """ + ) + ) + + def test_md_block_nested_in_span(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="span"> + <div markdown="block">*foo*</div> + </div> + """ + ), + self.dedent( + """ + <div> + <div><em>foo</em></div> + </div> + """ + ) + ) + + def test_md_block_after_span_nested_in_block(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="block"> + <div markdown="span">*foo*</div> + <div markdown="block">*bar*</div> + </div> + """ + ), + self.dedent( + """ + <div> + <div><em>foo</em></div> + <div> + <p><em>bar</em></p> + </div> + </div> + """ + ) + ) + + def test_nomd_nested_in_md1(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + *foo* + <div> + *foo* + <p>*bar*</p> + *baz* + </div> + *bar* + </div> + """ + ), + self.dedent( + """ + <div> + <p><em>foo</em></p> + <div> + *foo* + <p>*bar*</p> + *baz* + </div> + <p><em>bar</em></p> + </div> + """ + ) + ) + + def test_md1_nested_in_nomd(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div> + <div markdown="1">*foo*</div> + </div> + """ + ), + self.dedent( + """ + <div> + <div markdown="1">*foo*</div> + </div> + """ + ) + ) + + def test_md1_single_quotes(self): + self.assertMarkdownRenders( + "<p markdown='1'>*foo*</p>", + '<p><em>foo</em></p>' + ) + + def test_md1_no_quotes(self): + self.assertMarkdownRenders( + '<p markdown=1>*foo*</p>', + '<p><em>foo</em></p>' + ) + + def test_md_no_value(self): + self.assertMarkdownRenders( + '<p markdown>*foo*</p>', + '<p><em>foo</em></p>' + ) + + def test_md1_preserve_attrs(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1" id="parent"> + + <div markdown="1" class="foo"> + <p markdown="1" class="bar baz">*foo*</p> + </div> + + </div> + """ + ), + self.dedent( + """ + <div id="parent"> + <div class="foo"> + <p class="bar baz"><em>foo</em></p> + </div> + </div> + """ + ) + ) + + def test_md1_unclosed_div(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + + _foo_ + + <div class="unclosed"> + + _bar_ + + </div> + """ + ), + self.dedent( + """ + <div> + <p><em>foo</em></p> + <div class="unclosed"> + + _bar_ + + </div> + </div> + """ + ) + ) + + def test_md1_orphan_endtag(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + + _foo_ + + </p> + + _bar_ + + </div> + """ + ), + self.dedent( + """ + <div> + <p><em>foo</em></p> + </p> + <p><em>bar</em></p> + </div> + """ + ) + ) + + def test_md1_unclosed_p(self): + self.assertMarkdownRenders( + self.dedent( + """ + <p markdown="1">_foo_ + <p markdown="1">_bar_ + """ + ), + self.dedent( + """ + <p><em>foo</em> + </p> + <p><em>bar</em> + + </p> + """ + ) + ) + + def test_md1_nested_unclosed_p(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + <p markdown="1">_foo_ + <p markdown="1">_bar_ + </div> + """ + ), + self.dedent( + """ + <div> + <p><em>foo</em> + </p> + <p><em>bar</em> + </p> + </div> + """ + ) + ) + + def test_md1_nested_comment(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + A *Markdown* paragraph. + <!-- foobar --> + A *Markdown* paragraph. + </div> + """ + ), + self.dedent( + """ + <div> + <p>A <em>Markdown</em> paragraph.</p> + <!-- foobar --> + <p>A <em>Markdown</em> paragraph.</p> + </div> + """ + ) + ) + + def test_md1_nested_link_ref(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + [link]: http://example.com + <div markdown="1"> + [link][link] + </div> + </div> + """ + ), + self.dedent( + """ + <div> + <div> + <p><a href="http://example.com">link</a></p> + </div> + </div> + """ + ) + ) + + def test_md1_hr_only_start(self): + self.assertMarkdownRenders( + self.dedent( + """ + *emphasis1* + <hr markdown="1"> + *emphasis2* + """ + ), + self.dedent( + """ + <p><em>emphasis1</em></p> + <hr> + <p><em>emphasis2</em></p> + """ + ) + ) + + def test_md1_hr_self_close(self): + self.assertMarkdownRenders( + self.dedent( + """ + *emphasis1* + <hr markdown="1" /> + *emphasis2* + """ + ), + self.dedent( + """ + <p><em>emphasis1</em></p> + <hr> + <p><em>emphasis2</em></p> + """ + ) + ) + + def test_md1_hr_start_and_end(self): + # Browsers ignore ending hr tags, so we don't try to do anything to handle them special. + self.assertMarkdownRenders( + self.dedent( + """ + *emphasis1* + <hr markdown="1"></hr> + *emphasis2* + """ + ), + self.dedent( + """ + <p><em>emphasis1</em></p> + <hr> + <p></hr> + <em>emphasis2</em></p> + """ + ) + ) + + def test_md1_hr_only_end(self): + # Browsers ignore ending hr tags, so we don't try to do anything to handle them special. + self.assertMarkdownRenders( + self.dedent( + """ + *emphasis1* + </hr> + *emphasis2* + """ + ), + self.dedent( + """ + <p><em>emphasis1</em> + </hr> + <em>emphasis2</em></p> + """ + ) + ) + + def test_md1_hr_with_content(self): + # Browsers ignore ending hr tags, so we don't try to do anything to handle them special. + # Content is not allowed and will be treated as normal content between two hr tags + self.assertMarkdownRenders( + self.dedent( + """ + *emphasis1* + <hr markdown="1"> + **content** + </hr> + *emphasis2* + """ + ), + self.dedent( + """ + <p><em>emphasis1</em></p> + <hr> + <p><strong>content</strong> + </hr> + <em>emphasis2</em></p> + """ + ) + ) + + def test_no_md1_hr_with_content(self): + # Browsers ignore ending hr tags, so we don't try to do anything to handle them special. + # Content is not allowed and will be treated as normal content between two hr tags + self.assertMarkdownRenders( + self.dedent( + """ + *emphasis1* + <hr> + **content** + </hr> + *emphasis2* + """ + ), + self.dedent( + """ + <p><em>emphasis1</em></p> + <hr> + <p><strong>content</strong> + </hr> + <em>emphasis2</em></p> + """ + ) + ) + + def test_md1_nested_abbr_ref(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + *[abbr]: Abbreviation + <div markdown="1"> + abbr + </div> + </div> + """ + ), + self.dedent( + """ + <div> + <div> + <p><abbr title="Abbreviation">abbr</abbr></p> + </div> + </div> + """ + ), + extensions=['md_in_html', 'abbr'] + ) + + def test_md1_nested_footnote_ref(self): + self.assertMarkdownRenders( + self.dedent( + """ + <div markdown="1"> + [^1]: The footnote. + <div markdown="1"> + Paragraph with a footnote.[^1] + </div> + </div> + """ + ), + '<div>\n' + '<div>\n' + '<p>Paragraph with a footnote.<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n' + '</div>\n' + '</div>\n' + '<div class="footnote">\n' + '<hr />\n' + '<ol>\n' + '<li id="fn:1">\n' + '<p>The footnote. ' + '<a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text">↩</a>' + '</p>\n' + '</li>\n' + '</ol>\n' + '</div>', + extensions=['md_in_html', 'footnotes'] + ) + + +def load_tests(loader, tests, pattern): + ''' Ensure TestHTMLBlocks doesn't get run twice by excluding it here. ''' + suite = TestSuite() + for test_class in [TestDefaultwMdInHTML, TestMdInHTML, TestMarkdownInHTMLPostProcessor]: + tests = loader.loadTestsFromTestCase(test_class) + suite.addTests(tests) + return suite diff --git a/tests/test_syntax/extensions/test_smarty.py b/tests/test_syntax/extensions/test_smarty.py new file mode 100644 index 0000000..fc635ad --- /dev/null +++ b/tests/test_syntax/extensions/test_smarty.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2022 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase + + +class TestSmarty(TestCase): + + default_kwargs = {'extensions': ['smarty']} + + def test_escaped_attr(self): + self.assertMarkdownRenders( + '![x\"x](x)', + '<p><img alt="x"x" src="x" /></p>' + ) + + # TODO: Move rest of smarty tests here. diff --git a/tests/test_syntax/extensions/test_tables.py b/tests/test_syntax/extensions/test_tables.py new file mode 100644 index 0000000..cd3fbe4 --- /dev/null +++ b/tests/test_syntax/extensions/test_tables.py @@ -0,0 +1,922 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase +from markdown.extensions.tables import TableExtension + + +class TestTableBlocks(TestCase): + + def test_empty_cells(self): + """Empty cells (nbsp).""" + + text = """ + | Second Header +------------- | ------------- + | Content Cell +Content Cell | +""" + + self.assertMarkdownRenders( + text, + self.dedent( + """ + <table> + <thead> + <tr> + <th> </th> + <th>Second Header</th> + </tr> + </thead> + <tbody> + <tr> + <td> </td> + <td>Content Cell</td> + </tr> + <tr> + <td>Content Cell</td> + <td> </td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_no_sides(self): + self.assertMarkdownRenders( + self.dedent( + """ + First Header | Second Header + ------------- | ------------- + Content Cell | Content Cell + Content Cell | Content Cell + """ + ), + self.dedent( + """ + <table> + <thead> + <tr> + <th>First Header</th> + <th>Second Header</th> + </tr> + </thead> + <tbody> + <tr> + <td>Content Cell</td> + <td>Content Cell</td> + </tr> + <tr> + <td>Content Cell</td> + <td>Content Cell</td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_both_sides(self): + self.assertMarkdownRenders( + self.dedent( + """ + | First Header | Second Header | + | ------------- | ------------- | + | Content Cell | Content Cell | + | Content Cell | Content Cell | + """ + ), + self.dedent( + """ + <table> + <thead> + <tr> + <th>First Header</th> + <th>Second Header</th> + </tr> + </thead> + <tbody> + <tr> + <td>Content Cell</td> + <td>Content Cell</td> + </tr> + <tr> + <td>Content Cell</td> + <td>Content Cell</td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_align_columns(self): + self.assertMarkdownRenders( + self.dedent( + """ + | Item | Value | + | :-------- | -----:| + | Computer | $1600 | + | Phone | $12 | + | Pipe | $1 | + """ + ), + self.dedent( + """ + <table> + <thead> + <tr> + <th style="text-align: left;">Item</th> + <th style="text-align: right;">Value</th> + </tr> + </thead> + <tbody> + <tr> + <td style="text-align: left;">Computer</td> + <td style="text-align: right;">$1600</td> + </tr> + <tr> + <td style="text-align: left;">Phone</td> + <td style="text-align: right;">$12</td> + </tr> + <tr> + <td style="text-align: left;">Pipe</td> + <td style="text-align: right;">$1</td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_styles_in_tables(self): + self.assertMarkdownRenders( + self.dedent( + """ + | Function name | Description | + | ------------- | ------------------------------ | + | `help()` | Display the help window. | + | `destroy()` | **Destroy your computer!** | + """ + ), + self.dedent( + """ + <table> + <thead> + <tr> + <th>Function name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>help()</code></td> + <td>Display the help window.</td> + </tr> + <tr> + <td><code>destroy()</code></td> + <td><strong>Destroy your computer!</strong></td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_align_three(self): + self.assertMarkdownRenders( + self.dedent( + """ + |foo|bar|baz| + |:--|:-:|--:| + | | Q | | + |W | | W| + """ + ), + self.dedent( + """ + <table> + <thead> + <tr> + <th style="text-align: left;">foo</th> + <th style="text-align: center;">bar</th> + <th style="text-align: right;">baz</th> + </tr> + </thead> + <tbody> + <tr> + <td style="text-align: left;"></td> + <td style="text-align: center;">Q</td> + <td style="text-align: right;"></td> + </tr> + <tr> + <td style="text-align: left;">W</td> + <td style="text-align: center;"></td> + <td style="text-align: right;">W</td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_three_columns(self): + self.assertMarkdownRenders( + self.dedent( + """ + foo|bar|baz + ---|---|--- + | Q | + W | | W + """ + ), + self.dedent( + """ + <table> + <thead> + <tr> + <th>foo</th> + <th>bar</th> + <th>baz</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td>Q</td> + <td></td> + </tr> + <tr> + <td>W</td> + <td></td> + <td>W</td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_three_spaces_prefix(self): + self.assertMarkdownRenders( + self.dedent( + """ + Three spaces in front of a table: + + First Header | Second Header + ------------ | ------------- + Content Cell | Content Cell + Content Cell | Content Cell + + | First Header | Second Header | + | ------------ | ------------- | + | Content Cell | Content Cell | + | Content Cell | Content Cell | + """), + self.dedent( + """ + <p>Three spaces in front of a table:</p> + <table> + <thead> + <tr> + <th>First Header</th> + <th>Second Header</th> + </tr> + </thead> + <tbody> + <tr> + <td>Content Cell</td> + <td>Content Cell</td> + </tr> + <tr> + <td>Content Cell</td> + <td>Content Cell</td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <th>First Header</th> + <th>Second Header</th> + </tr> + </thead> + <tbody> + <tr> + <td>Content Cell</td> + <td>Content Cell</td> + </tr> + <tr> + <td>Content Cell</td> + <td>Content Cell</td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_code_block_table(self): + self.assertMarkdownRenders( + self.dedent( + """ + Four spaces is a code block: + + First Header | Second Header + ------------ | ------------- + Content Cell | Content Cell + Content Cell | Content Cell + + | First Header | Second Header | + | ------------ | ------------- | + """), + self.dedent( + """ + <p>Four spaces is a code block:</p> + <pre><code>First Header | Second Header + ------------ | ------------- + Content Cell | Content Cell + Content Cell | Content Cell + </code></pre> + <table> + <thead> + <tr> + <th>First Header</th> + <th>Second Header</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td></td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_inline_code_blocks(self): + self.assertMarkdownRenders( + self.dedent( + """ + More inline code block tests + + Column 1 | Column 2 | Column 3 + ---------|----------|--------- + word 1 | word 2 | word 3 + word 1 | `word 2` | word 3 + word 1 | \\`word 2 | word 3 + word 1 | `word 2 | word 3 + word 1 | `word |2` | word 3 + words |`` some | code `` | more words + words |``` some | code ``` | more words + words |```` some | code ```` | more words + words |`` some ` | ` code `` | more words + words |``` some ` | ` code ``` | more words + words |```` some ` | ` code ```` | more words + """), + self.dedent( + """ + <p>More inline code block tests</p> + <table> + <thead> + <tr> + <th>Column 1</th> + <th>Column 2</th> + <th>Column 3</th> + </tr> + </thead> + <tbody> + <tr> + <td>word 1</td> + <td>word 2</td> + <td>word 3</td> + </tr> + <tr> + <td>word 1</td> + <td><code>word 2</code></td> + <td>word 3</td> + </tr> + <tr> + <td>word 1</td> + <td>`word 2</td> + <td>word 3</td> + </tr> + <tr> + <td>word 1</td> + <td>`word 2</td> + <td>word 3</td> + </tr> + <tr> + <td>word 1</td> + <td><code>word |2</code></td> + <td>word 3</td> + </tr> + <tr> + <td>words</td> + <td><code>some | code</code></td> + <td>more words</td> + </tr> + <tr> + <td>words</td> + <td><code>some | code</code></td> + <td>more words</td> + </tr> + <tr> + <td>words</td> + <td><code>some | code</code></td> + <td>more words</td> + </tr> + <tr> + <td>words</td> + <td><code>some ` | ` code</code></td> + <td>more words</td> + </tr> + <tr> + <td>words</td> + <td><code>some ` | ` code</code></td> + <td>more words</td> + </tr> + <tr> + <td>words</td> + <td><code>some ` | ` code</code></td> + <td>more words</td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_issue_440(self): + self.assertMarkdownRenders( + self.dedent( + """ + A test for issue #440: + + foo | bar + --- | --- + foo | (`bar`) and `baz`. + """), + self.dedent( + """ + <p>A test for issue #440:</p> + <table> + <thead> + <tr> + <th>foo</th> + <th>bar</th> + </tr> + </thead> + <tbody> + <tr> + <td>foo</td> + <td>(<code>bar</code>) and <code>baz</code>.</td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_lists_not_tables(self): + self.assertMarkdownRenders( + self.dedent( + """ + Lists are not tables + + - this | should | not + - be | a | table + """), + self.dedent( + """ + <p>Lists are not tables</p> + <ul> + <li>this | should | not</li> + <li>be | a | table</li> + </ul> + """ + ), + extensions=['tables'] + ) + + def test_issue_449(self): + self.assertMarkdownRenders( + self.dedent( + r""" + Add tests for issue #449 + + Odd backticks | Even backticks + ------------ | ------------- + ``[!\"\#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~]`` | ``[!\"\#$%&'()*+,\-./:;<=>?@\[\\\]^`_`{|}~]`` + + Escapes | More Escapes + ------- | ------ + `` `\`` | `\` + + Only the first backtick can be escaped + + Escaped | Bacticks + ------- | ------ + \`` \` | \`\` + + Test escaped pipes + + Column 1 | Column 2 + -------- | -------- + `|` \| | Pipes are okay in code and escaped. \| + + | Column 1 | Column 2 | + | -------- | -------- | + | row1 | row1 \| + | row2 | row2 | + + Test header escapes + + | `` `\`` \| | `\` \| + | ---------- | ---- | + | row1 | row1 | + | row2 | row2 | + + Escaped pipes in format row should not be a table + + | Column1 | Column2 | + | ------- \|| ------- | + | row1 | row1 | + | row2 | row2 | + + Test escaped code in Table + + Should not be code | Should be code + ------------------ | -------------- + \`Not code\` | \\`code` + \\\`Not code\\\` | \\\\`code` + """), + self.dedent( + """ + <p>Add tests for issue #449</p> + <table> + <thead> + <tr> + <th>Odd backticks</th> + <th>Even backticks</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>[!\\"\\#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_`{|}~]</code></td> + <td><code>[!\\"\\#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^`_`{|}~]</code></td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <th>Escapes</th> + <th>More Escapes</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>`\\</code></td> + <td><code>\\</code></td> + </tr> + </tbody> + </table> + <p>Only the first backtick can be escaped</p> + <table> + <thead> + <tr> + <th>Escaped</th> + <th>Bacticks</th> + </tr> + </thead> + <tbody> + <tr> + <td>`<code>\\</code></td> + <td>``</td> + </tr> + </tbody> + </table> + <p>Test escaped pipes</p> + <table> + <thead> + <tr> + <th>Column 1</th> + <th>Column 2</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>|</code> |</td> + <td>Pipes are okay in code and escaped. |</td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <th>Column 1</th> + <th>Column 2</th> + </tr> + </thead> + <tbody> + <tr> + <td>row1</td> + <td>row1 |</td> + </tr> + <tr> + <td>row2</td> + <td>row2</td> + </tr> + </tbody> + </table> + <p>Test header escapes</p> + <table> + <thead> + <tr> + <th><code>`\\</code> |</th> + <th><code>\\</code> |</th> + </tr> + </thead> + <tbody> + <tr> + <td>row1</td> + <td>row1</td> + </tr> + <tr> + <td>row2</td> + <td>row2</td> + </tr> + </tbody> + </table> + <p>Escaped pipes in format row should not be a table</p> + <p>| Column1 | Column2 | + | ------- || ------- | + | row1 | row1 | + | row2 | row2 |</p> + <p>Test escaped code in Table</p> + <table> + <thead> + <tr> + <th>Should not be code</th> + <th>Should be code</th> + </tr> + </thead> + <tbody> + <tr> + <td>`Not code`</td> + <td>\\<code>code</code></td> + </tr> + <tr> + <td>\\`Not code\\`</td> + <td>\\\\<code>code</code></td> + </tr> + </tbody> + </table> + """ + ), + extensions=['tables'] + ) + + def test_single_column_tables(self): + self.assertMarkdownRenders( + self.dedent( + """ + Single column tables + + | Is a Table | + | ---------- | + + | Is a Table + | ---------- + + Is a Table | + ---------- | + + | Is a Table | + | ---------- | + | row | + + | Is a Table + | ---------- + | row + + Is a Table | + ---------- | + row | + + | Is not a Table + -------------- + | row + + Is not a Table | + -------------- + row | + + | Is not a Table + | -------------- + row + + Is not a Table | + -------------- | + row + """), + self.dedent( + """ + <p>Single column tables</p> + <table> + <thead> + <tr> + <th>Is a Table</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <th>Is a Table</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <th>Is a Table</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <th>Is a Table</th> + </tr> + </thead> + <tbody> + <tr> + <td>row</td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <th>Is a Table</th> + </tr> + </thead> + <tbody> + <tr> + <td>row</td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <th>Is a Table</th> + </tr> + </thead> + <tbody> + <tr> + <td>row</td> + </tr> + </tbody> + </table> + <h2>| Is not a Table</h2> + <p>| row</p> + <h2>Is not a Table |</h2> + <p>row |</p> + <p>| Is not a Table + | -------------- + row</p> + <p>Is not a Table | + -------------- | + row</p> + """ + ), + extensions=['tables'] + ) + + def test_align_columns_legacy(self): + self.assertMarkdownRenders( + self.dedent( + """ + | Item | Value | + | :-------- | -----:| + | Computer | $1600 | + | Phone | $12 | + | Pipe | $1 | + """ + ), + self.dedent( + """ + <table> + <thead> + <tr> + <th align="left">Item</th> + <th align="right">Value</th> + </tr> + </thead> + <tbody> + <tr> + <td align="left">Computer</td> + <td align="right">$1600</td> + </tr> + <tr> + <td align="left">Phone</td> + <td align="right">$12</td> + </tr> + <tr> + <td align="left">Pipe</td> + <td align="right">$1</td> + </tr> + </tbody> + </table> + """ + ), + extensions=[TableExtension(use_align_attribute=True)] + ) + + def test_align_three_legacy(self): + self.assertMarkdownRenders( + self.dedent( + """ + |foo|bar|baz| + |:--|:-:|--:| + | | Q | | + |W | | W| + """ + ), + self.dedent( + """ + <table> + <thead> + <tr> + <th align="left">foo</th> + <th align="center">bar</th> + <th align="right">baz</th> + </tr> + </thead> + <tbody> + <tr> + <td align="left"></td> + <td align="center">Q</td> + <td align="right"></td> + </tr> + <tr> + <td align="left">W</td> + <td align="center"></td> + <td align="right">W</td> + </tr> + </tbody> + </table> + """ + ), + extensions=[TableExtension(use_align_attribute=True)] + ) diff --git a/tests/test_syntax/extensions/test_toc.py b/tests/test_syntax/extensions/test_toc.py new file mode 100644 index 0000000..d879f6e --- /dev/null +++ b/tests/test_syntax/extensions/test_toc.py @@ -0,0 +1,614 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2019 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +from markdown.test_tools import TestCase +from markdown.extensions.toc import TocExtension +from markdown.extensions.nl2br import Nl2BrExtension + + +class TestTOC(TestCase): + maxDiff = None + + # TODO: Move the rest of the TOC tests here. + + def testAnchorLink(self): + self.assertMarkdownRenders( + self.dedent( + ''' + # Header 1 + + ## Header *2* + ''' + ), + self.dedent( + ''' + <h1 id="header-1"><a class="toclink" href="#header-1">Header 1</a></h1> + <h2 id="header-2"><a class="toclink" href="#header-2">Header <em>2</em></a></h2> + ''' + ), + extensions=[TocExtension(anchorlink=True)] + ) + + def testAnchorLinkWithSingleInlineCode(self): + self.assertMarkdownRenders( + '# This is `code`.', + '<h1 id="this-is-code">' # noqa + '<a class="toclink" href="#this-is-code">' # noqa + 'This is <code>code</code>.' # noqa + '</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(anchorlink=True)] + ) + + def testAnchorLinkWithDoubleInlineCode(self): + self.assertMarkdownRenders( + '# This is `code` and `this` too.', + '<h1 id="this-is-code-and-this-too">' # noqa + '<a class="toclink" href="#this-is-code-and-this-too">' # noqa + 'This is <code>code</code> and <code>this</code> too.' # noqa + '</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(anchorlink=True)] + ) + + def testPermalink(self): + self.assertMarkdownRenders( + '# Header', + '<h1 id="header">' # noqa + 'Header' # noqa + '<a class="headerlink" href="#header" title="Permanent link">¶</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink=True)] + ) + + def testPermalinkWithSingleInlineCode(self): + self.assertMarkdownRenders( + '# This is `code`.', + '<h1 id="this-is-code">' # noqa + 'This is <code>code</code>.' # noqa + '<a class="headerlink" href="#this-is-code" title="Permanent link">¶</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink=True)] + ) + + def testPermalinkWithDoubleInlineCode(self): + self.assertMarkdownRenders( + '# This is `code` and `this` too.', + '<h1 id="this-is-code-and-this-too">' # noqa + 'This is <code>code</code> and <code>this</code> too.' # noqa + '<a class="headerlink" href="#this-is-code-and-this-too" title="Permanent link">¶</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink=True)] + ) + + def testMinMaxLevel(self): + self.assertMarkdownRenders( + self.dedent( + ''' + # Header 1 not in TOC + + ## Header 2 not in TOC + + ### Header 3 + + #### Header 4 + + ##### Header 5 not in TOC + ''' + ), + self.dedent( + ''' + <h1 id="header-1-not-in-toc">Header 1 not in TOC</h1> + <h2 id="header-2-not-in-toc">Header 2 not in TOC</h2> + <h3 id="header-3">Header 3</h3> + <h4 id="header-4">Header 4</h4> + <h5 id="header-5-not-in-toc">Header 5 not in TOC</h5> + ''' + ), + expected_attrs={ + 'toc': ( + '<div class="toc">\n' + '<ul>\n' # noqa + '<li><a href="#header-3">Header 3</a>' # noqa + '<ul>\n' # noqa + '<li><a href="#header-4">Header 4</a></li>\n' # noqa + '</ul>\n' # noqa + '</li>\n' # noqa + '</ul>\n' # noqa + '</div>\n' # noqa + ), + 'toc_tokens': [ + { + 'level': 3, + 'id': 'header-3', + 'name': 'Header 3', + 'children': [ + { + 'level': 4, + 'id': 'header-4', + 'name': 'Header 4', + 'children': [] + } + ] + } + ] + }, + extensions=[TocExtension(toc_depth='3-4')] + ) + + def testMaxLevel(self): + self.assertMarkdownRenders( + self.dedent( + ''' + # Header 1 + + ## Header 2 + + ### Header 3 not in TOC + ''' + ), + self.dedent( + ''' + <h1 id="header-1">Header 1</h1> + <h2 id="header-2">Header 2</h2> + <h3 id="header-3-not-in-toc">Header 3 not in TOC</h3> + ''' + ), + expected_attrs={ + 'toc': ( + '<div class="toc">\n' + '<ul>\n' # noqa + '<li><a href="#header-1">Header 1</a>' # noqa + '<ul>\n' # noqa + '<li><a href="#header-2">Header 2</a></li>\n' # noqa + '</ul>\n' # noqa + '</li>\n' # noqa + '</ul>\n' # noqa + '</div>\n' # noqa + ), + 'toc_tokens': [ + { + 'level': 1, + 'id': 'header-1', + 'name': 'Header 1', + 'children': [ + { + 'level': 2, + 'id': 'header-2', + 'name': 'Header 2', + 'children': [] + } + ] + } + ] + }, + extensions=[TocExtension(toc_depth=2)] + ) + + def testMinMaxLevelwithAnchorLink(self): + self.assertMarkdownRenders( + self.dedent( + ''' + # Header 1 not in TOC + + ## Header 2 not in TOC + + ### Header 3 + + #### Header 4 + + ##### Header 5 not in TOC + ''' + ), + '<h1 id="header-1-not-in-toc">' # noqa + '<a class="toclink" href="#header-1-not-in-toc">Header 1 not in TOC</a></h1>\n' # noqa + '<h2 id="header-2-not-in-toc">' # noqa + '<a class="toclink" href="#header-2-not-in-toc">Header 2 not in TOC</a></h2>\n' # noqa + '<h3 id="header-3">' # noqa + '<a class="toclink" href="#header-3">Header 3</a></h3>\n' # noqa + '<h4 id="header-4">' # noqa + '<a class="toclink" href="#header-4">Header 4</a></h4>\n' # noqa + '<h5 id="header-5-not-in-toc">' # noqa + '<a class="toclink" href="#header-5-not-in-toc">Header 5 not in TOC</a></h5>', # noqa + expected_attrs={ + 'toc': ( + '<div class="toc">\n' + '<ul>\n' # noqa + '<li><a href="#header-3">Header 3</a>' # noqa + '<ul>\n' # noqa + '<li><a href="#header-4">Header 4</a></li>\n' # noqa + '</ul>\n' # noqa + '</li>\n' # noqa + '</ul>\n' # noqa + '</div>\n' # noqa + ), + 'toc_tokens': [ + { + 'level': 3, + 'id': 'header-3', + 'name': 'Header 3', + 'children': [ + { + 'level': 4, + 'id': 'header-4', + 'name': 'Header 4', + 'children': [] + } + ] + } + ] + }, + extensions=[TocExtension(toc_depth='3-4', anchorlink=True)] + ) + + def testMinMaxLevelwithPermalink(self): + self.assertMarkdownRenders( + self.dedent( + ''' + # Header 1 not in TOC + + ## Header 2 not in TOC + + ### Header 3 + + #### Header 4 + + ##### Header 5 not in TOC + ''' + ), + '<h1 id="header-1-not-in-toc">Header 1 not in TOC' # noqa + '<a class="headerlink" href="#header-1-not-in-toc" title="Permanent link">¶</a></h1>\n' # noqa + '<h2 id="header-2-not-in-toc">Header 2 not in TOC' # noqa + '<a class="headerlink" href="#header-2-not-in-toc" title="Permanent link">¶</a></h2>\n' # noqa + '<h3 id="header-3">Header 3' # noqa + '<a class="headerlink" href="#header-3" title="Permanent link">¶</a></h3>\n' # noqa + '<h4 id="header-4">Header 4' # noqa + '<a class="headerlink" href="#header-4" title="Permanent link">¶</a></h4>\n' # noqa + '<h5 id="header-5-not-in-toc">Header 5 not in TOC' # noqa + '<a class="headerlink" href="#header-5-not-in-toc" title="Permanent link">¶</a></h5>', # noqa + expected_attrs={ + 'toc': ( + '<div class="toc">\n' + '<ul>\n' # noqa + '<li><a href="#header-3">Header 3</a>' # noqa + '<ul>\n' # noqa + '<li><a href="#header-4">Header 4</a></li>\n' # noqa + '</ul>\n' # noqa + '</li>\n' # noqa + '</ul>\n' # noqa + '</div>\n' # noqa + ), + 'toc_tokens': [ + { + 'level': 3, + 'id': 'header-3', + 'name': 'Header 3', + 'children': [ + { + 'level': 4, + 'id': 'header-4', + 'name': 'Header 4', + 'children': [] + } + ] + } + ] + }, + extensions=[TocExtension(toc_depth='3-4', permalink=True)] + ) + + def testMinMaxLevelwithBaseLevel(self): + self.assertMarkdownRenders( + self.dedent( + ''' + # First Header + + ## Second Level + + ### Third Level + + #### Forth Level + ''' + ), + self.dedent( + ''' + <h3 id="first-header">First Header</h3> + <h4 id="second-level">Second Level</h4> + <h5 id="third-level">Third Level</h5> + <h6 id="forth-level">Forth Level</h6> + ''' + ), + expected_attrs={ + 'toc': ( + '<div class="toc">\n' + '<ul>\n' # noqa + '<li><a href="#second-level">Second Level</a>' # noqa + '<ul>\n' # noqa + '<li><a href="#third-level">Third Level</a></li>\n' # noqa + '</ul>\n' # noqa + '</li>\n' # noqa + '</ul>\n' # noqa + '</div>\n' # noqa + ), + 'toc_tokens': [ + { + 'level': 4, + 'id': 'second-level', + 'name': 'Second Level', + 'children': [ + { + 'level': 5, + 'id': 'third-level', + 'name': 'Third Level', + 'children': [] + } + ] + } + ] + }, + extensions=[TocExtension(toc_depth='4-5', baselevel=3)] + ) + + def testMaxLevelwithBaseLevel(self): + self.assertMarkdownRenders( + self.dedent( + ''' + # Some Header + + ## Next Level + + ### Too High + ''' + ), + self.dedent( + ''' + <h2 id="some-header">Some Header</h2> + <h3 id="next-level">Next Level</h3> + <h4 id="too-high">Too High</h4> + ''' + ), + expected_attrs={ + 'toc': ( + '<div class="toc">\n' + '<ul>\n' # noqa + '<li><a href="#some-header">Some Header</a>' # noqa + '<ul>\n' # noqa + '<li><a href="#next-level">Next Level</a></li>\n' # noqa + '</ul>\n' # noqa + '</li>\n' # noqa + '</ul>\n' # noqa + '</div>\n' # noqa + ), + 'toc_tokens': [ + { + 'level': 2, + 'id': 'some-header', + 'name': 'Some Header', + 'children': [ + { + 'level': 3, + 'id': 'next-level', + 'name': 'Next Level', + 'children': [] + } + ] + } + ] + }, + extensions=[TocExtension(toc_depth=3, baselevel=2)] + ) + + def test_escaped_code(self): + self.assertMarkdownRenders( + self.dedent( + ''' + [TOC] + + # `<test>` + ''' + ), + self.dedent( + ''' + <div class="toc"> + <ul> + <li><a href="#test"><test></a></li> + </ul> + </div> + <h1 id="test"><code><test></code></h1> + ''' + ), + extensions=['toc'] + ) + + def test_escaped_char_in_id(self): + self.assertMarkdownRenders( + r'# escaped\_character', + '<h1 id="escaped_character">escaped_character</h1>', + extensions=['toc'] + ) + + def testAnchorLinkWithCustomClass(self): + self.assertMarkdownRenders( + self.dedent( + ''' + # Header 1 + + ## Header *2* + ''' + ), + self.dedent( + ''' + <h1 id="header-1"><a class="custom" href="#header-1">Header 1</a></h1> + <h2 id="header-2"><a class="custom" href="#header-2">Header <em>2</em></a></h2> + ''' + ), + extensions=[TocExtension(anchorlink=True, anchorlink_class="custom")] + ) + + def testAnchorLinkWithCustomClasses(self): + self.assertMarkdownRenders( + self.dedent( + ''' + # Header 1 + + ## Header *2* + ''' + ), + self.dedent( + ''' + <h1 id="header-1"><a class="custom1 custom2" href="#header-1">Header 1</a></h1> + <h2 id="header-2"><a class="custom1 custom2" href="#header-2">Header <em>2</em></a></h2> + ''' + ), + extensions=[TocExtension(anchorlink=True, anchorlink_class="custom1 custom2")] + ) + + def testPermalinkWithEmptyText(self): + self.assertMarkdownRenders( + '# Header', + '<h1 id="header">' # noqa + 'Header' # noqa + '<a class="headerlink" href="#header" title="Permanent link"></a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink="")] + ) + + def testPermalinkWithCustomClass(self): + self.assertMarkdownRenders( + '# Header', + '<h1 id="header">' # noqa + 'Header' # noqa + '<a class="custom" href="#header" title="Permanent link">¶</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink=True, permalink_class="custom")] + ) + + def testPermalinkWithCustomClasses(self): + self.assertMarkdownRenders( + '# Header', + '<h1 id="header">' # noqa + 'Header' # noqa + '<a class="custom1 custom2" href="#header" title="Permanent link">¶</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink=True, permalink_class="custom1 custom2")] + ) + + def testPermalinkWithCustomTitle(self): + self.assertMarkdownRenders( + '# Header', + '<h1 id="header">' # noqa + 'Header' # noqa + '<a class="headerlink" href="#header" title="custom">¶</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink=True, permalink_title="custom")] + ) + + def testPermalinkWithEmptyTitle(self): + self.assertMarkdownRenders( + '# Header', + '<h1 id="header">' # noqa + 'Header' # noqa + '<a class="headerlink" href="#header">¶</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink=True, permalink_title="")] + ) + + def testPermalinkWithUnicodeInID(self): + from markdown.extensions.toc import slugify_unicode + self.assertMarkdownRenders( + '# Unicode ヘッダー', + '<h1 id="unicode-ヘッダー">' # noqa + 'Unicode ヘッダー' # noqa + '<a class="headerlink" href="#unicode-ヘッダー" title="Permanent link">¶</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink=True, slugify=slugify_unicode)] + ) + + def testPermalinkWithUnicodeTitle(self): + from markdown.extensions.toc import slugify_unicode + self.assertMarkdownRenders( + '# Unicode ヘッダー', + '<h1 id="unicode-ヘッダー">' # noqa + 'Unicode ヘッダー' # noqa + '<a class="headerlink" href="#unicode-ヘッダー" title="パーマリンク">¶</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink=True, permalink_title="パーマリンク", slugify=slugify_unicode)] + ) + + def testPermalinkWithExtendedLatinInID(self): + self.assertMarkdownRenders( + '# Théâtre', + '<h1 id="theatre">' # noqa + 'Théâtre' # noqa + '<a class="headerlink" href="#theatre" title="Permanent link">¶</a>' # noqa + '</h1>', # noqa + extensions=[TocExtension(permalink=True)] + ) + + def testNl2brCompatibility(self): + self.assertMarkdownRenders( + '[TOC]\ntext', + '<p>[TOC]<br />\ntext</p>', + extensions=[TocExtension(), Nl2BrExtension()] + ) + + def testTOCWithCustomClass(self): + + self.assertMarkdownRenders( + self.dedent( + ''' + [TOC] + # Header + ''' + ), + self.dedent( + ''' + <div class="custom"> + <ul> + <li><a href="#header">Header</a></li> + </ul> + </div> + <h1 id="header">Header</h1> + ''' + ), + extensions=[TocExtension(toc_class="custom")] + ) + + def testTOCWithCustomClasses(self): + self.assertMarkdownRenders( + self.dedent( + ''' + [TOC] + # Header + ''' + ), + self.dedent( + ''' + <div class="custom1 custom2"> + <ul> + <li><a href="#header">Header</a></li> + </ul> + </div> + <h1 id="header">Header</h1> + ''' + ), + extensions=[TocExtension(toc_class="custom1 custom2")] + ) |