aboutsummaryrefslogtreecommitdiff
path: root/tests/test_syntax/extensions
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:10:28 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:10:28 +0000
commit6ff587f109e9b9e63d722716d2491b943b78cc17 (patch)
tree1a110fa9fa982e746b1969458c5b9d373c70a38c /tests/test_syntax/extensions
parent785cf9483e09324449c2e32022872c0e55b177a8 (diff)
parentb8ef043a4bce3c0e0c3a83a5309c17d1962be972 (diff)
downloadmarkdown-android14-mainline-sdkext-release.tar.gz
Change-Id: I89d9bffdfa82f0d26aa4638c7d74276fc5a0910f
Diffstat (limited to 'tests/test_syntax/extensions')
-rw-r--r--tests/test_syntax/extensions/__init__.py20
-rw-r--r--tests/test_syntax/extensions/test_abbr.py242
-rw-r--r--tests/test_syntax/extensions/test_admonition.py245
-rw-r--r--tests/test_syntax/extensions/test_attr_list.py80
-rw-r--r--tests/test_syntax/extensions/test_code_hilite.py764
-rw-r--r--tests/test_syntax/extensions/test_def_list.py323
-rw-r--r--tests/test_syntax/extensions/test_fenced_code.py1020
-rw-r--r--tests/test_syntax/extensions/test_footnotes.py338
-rw-r--r--tests/test_syntax/extensions/test_legacy_attrs.py67
-rw-r--r--tests/test_syntax/extensions/test_legacy_em.py66
-rw-r--r--tests/test_syntax/extensions/test_md_in_html.py1216
-rw-r--r--tests/test_syntax/extensions/test_smarty.py36
-rw-r--r--tests/test_syntax/extensions/test_tables.py922
-rw-r--r--tests/test_syntax/extensions/test_toc.py614
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="&quot;Abbreviation&quot;">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">&lt;?php</span> '
+ '<span class="k">print</span><span class="p">(</span><span class="s2">&quot;Hello World&quot;</span>'
+ '<span class="p">);</span> <span class="cp">?&gt;</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code>&lt;?php print(&quot;Hello World&quot;); ?&gt;\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">&lt;?php</span> '
+ '<span class="k">print</span><span class="p">(</span><span class="s2">&quot;Hello World&quot;</span>'
+ '<span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-php">&lt;?php print(&quot;Hello World&quot;); ?&gt;\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">&lt;?php</span> '
+ '<span class="k">print</span><span class="p">(</span><span class="s2">'
+ '&quot;Hello World&quot;</span><span class="p">);</span> <span class="cp">?&gt;</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">'
+ '&lt;?php print(&quot;Hello World&quot;); ?&gt;\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">&lt;?php print(&quot;Hello World&quot;); ?&gt;\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">&lt;?php print(&quot;Hello World&quot;); ?&gt;\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">&lt;?php print(&quot;Hello World&quot;); ?&gt;\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">&quot;Hello World&quot;</span><span class="p">);</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-php">print(&quot;Hello World&quot;);\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">&lt;</span><span class="nt">span</span><span class="p">&gt;</span>'
+ 'This<span class="ni">&amp;amp;</span>That'
+ '<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>'
+ '\n</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-html">'
+ '&lt;span&gt;This&amp;amp;That&lt;/span&gt;\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ (
+ '\t:::html\n'
+ '\t<span>This&amp;That</span>'
+ ),
+ expected,
+ extensions=['codehilite']
+ )
+
+ def testEntitiesIntact(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre>'
+ '<span></span>'
+ '<code>&lt; &amp;lt; and &gt; &amp;gt;'
+ '\n</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text">'
+ '&lt; &amp;lt; and &gt; &amp;gt;\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ (
+ '\t:::text\n'
+ '\t< &lt; and > &gt;'
+ ),
+ expected,
+ extensions=['codehilite']
+ )
+
+ def testHighlightAmps(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code>&amp;\n'
+ '&amp;amp;\n'
+ '&amp;amp;amp;\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text">&amp;\n'
+ '&amp;amp;\n'
+ '&amp;amp;amp;\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ (
+ '\t:::text\n'
+ '\t&\n'
+ '\t&amp;\n'
+ '\t&amp;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">&lt;?php</span> '
+ '<span class="k">print</span><span class="p">(</span>'
+ '<span class="s2">&quot;Hello World&quot;</span>'
+ '<span class="p">);</span> <span class="cp">?&gt;</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code>&lt;?php print(&quot;Hello World&quot;); ?&gt;\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="&quot;foo"><code class="language-&quot;weird" bar="&quot;&gt;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">&lt;</span><span class="nt">span</span>'
+ '<span class="p">&gt;</span>This<span class="ni">&amp;amp;</span>'
+ 'That<span class="p">&lt;/</span><span class="nt">span</span>'
+ '<span class="p">&gt;</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-html">'
+ '&lt;span&gt;This&amp;amp;That&lt;/span&gt;\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ```html
+ <span>This&amp;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;
+ &amp;amp;amp;
+ </code></pre></div>
+ '''
+ )
+ else:
+ expected = self.dedent(
+ '''
+ <pre class="codehilite"><code class="language-text">&amp;
+ &amp;amp;
+ &amp;amp;amp;
+ </code></pre>
+ '''
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ```text
+ &
+ &amp;
+ &amp;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&#160;world</text>
+ <text x="0" y="33" xml:space="preserve">hello&#160;another&#160;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&#160;<a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">&#8617;</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&#160;<a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
+ '</li>\n'
+ '<li id="fn:2">\n'
+ '<p>Footnote 2&#160;<a class="footnote-backref" href="#fnref:2"'
+ ' title="Jump back to footnote 2 in the text">&#8617;</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&#160;<a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
+ '</li>\n'
+ '<li id="fn:2">\n'
+ '<p>Footnote 2&#160;<a class="footnote-backref" href="#fnref:2"'
+ ' title="Jump back to footnote 2 in the text">&#8617;</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&#160;<a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">&#8617;</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&#160;<a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">&#8617;</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">&#8617;</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">&#8617;</a></p>\n'
+ '</li>\n'
+ '<li id="fn:2">\n'
+ '<p>Second footnote</p>\n'
+ '<p>paragraph 2&#160;<a class="footnote-backref" href="#fnref:2"'
+ ' title="Jump back to footnote 2 in the text">&#8617;</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">&#8617;</a></p>\n'
+ '</li>\n'
+ '<li id="fn:2">\n'
+ '<p>Second footnote</p>\n'
+ '<p>paragraph 2&#160;<a class="footnote-backref" href="#fnref:2"'
+ ' title="Jump back to footnote 2 in the text">&#8617;</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&#160;<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&#160;<a class="footnote-backref" href="#fnref-1"'
+ ' title="Jump back to footnote 1 in the text">&#8617;</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&#160;<a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote">&#8617;</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&#160;<a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">&#8617;</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>&lt;h1&gt;code span&lt;/h1&gt;</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>&lt;h1&gt;code span&lt;/h1&gt;</code></p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_code_span_unclosed(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ `<p>`
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p><code>&lt;p&gt;</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>&lt;script&gt;</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.&#160;'
+ '<a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text">&#8617;</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&quot;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>[!\\"\\#$%&amp;'()*+,\\-./:;&lt;=&gt;?@\\[\\\\\\]^_`{|}~]</code></td>
+ <td><code>[!\\"\\#$%&amp;'()*+,\\-./:;&lt;=&gt;?@\\[\\\\\\]^`_`{|}~]</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">&para;</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">&para;</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">&para;</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">&para;</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">&para;</a></h2>\n' # noqa
+ '<h3 id="header-3">Header 3' # noqa
+ '<a class="headerlink" href="#header-3" title="Permanent link">&para;</a></h3>\n' # noqa
+ '<h4 id="header-4">Header 4' # noqa
+ '<a class="headerlink" href="#header-4" title="Permanent link">&para;</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">&para;</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">&lt;test&gt;</a></li>
+ </ul>
+ </div>
+ <h1 id="test"><code>&lt;test&gt;</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">&para;</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">&para;</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">&para;</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">&para;</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">&para;</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="パーマリンク">&para;</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">&para;</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")]
+ )