# -*- 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). Python-Markdown Extension Regression Tests ========================================== A collection of regression tests to confirm that the included extensions continue to work as advertised. This used to be accomplished by doctests. """ from __future__ import unicode_literals import unittest import markdown class TestCaseWithAssertStartsWith(unittest.TestCase): def assertStartsWith(self, expectedPrefix, text, msg=None): if not text.startswith(expectedPrefix): if len(expectedPrefix) + 5 < len(text): text = text[:len(expectedPrefix) + 5] + '...' standardMsg = '%s not found at the start of %s' % (repr(expectedPrefix), repr(text)) self.fail(self._formatMessage(msg, standardMsg)) class TestExtensionClass(unittest.TestCase): """ Test markdown.extensions.Extension. """ def setUp(self): class TestExtension(markdown.extensions.Extension): config = { 'foo': ['bar', 'Description of foo'], 'bar': ['baz', 'Description of bar'] } self.ext = TestExtension() self.ExtKlass = TestExtension def testGetConfig(self): self.assertEqual(self.ext.getConfig('foo'), 'bar') def testGetConfigDefault(self): self.assertEqual(self.ext.getConfig('baz'), '') self.assertEqual(self.ext.getConfig('baz', default='missing'), 'missing') def testGetConfigs(self): self.assertEqual(self.ext.getConfigs(), {'foo': 'bar', 'bar': 'baz'}) def testGetConfigInfo(self): self.assertEqual( dict(self.ext.getConfigInfo()), dict([ ('foo', 'Description of foo'), ('bar', 'Description of bar') ]) ) def testSetConfig(self): self.ext.setConfig('foo', 'baz') self.assertEqual(self.ext.getConfigs(), {'foo': 'baz', 'bar': 'baz'}) def testSetConfigWithBadKey(self): # self.ext.setConfig('bad', 'baz) ==> KeyError self.assertRaises(KeyError, self.ext.setConfig, 'bad', 'baz') def testConfigAsKwargsOnInit(self): ext = self.ExtKlass(foo='baz', bar='blah') self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'}) class TestAbbr(unittest.TestCase): """ Test abbr extension. """ def setUp(self): self.md = markdown.Markdown(extensions=['abbr']) def testSimpleAbbr(self): """ Test Abbreviations. """ text = 'Some text with an ABBR and a REF. Ignore REFERENCE and ref.' + \ '\n\n*[ABBR]: Abbreviation\n' + \ '*[REF]: Abbreviation Reference' self.assertEqual( self.md.convert(text), '

Some text with an ABBR ' 'and a REF. Ignore ' 'REFERENCE and ref.

' ) def testNestedAbbr(self): """ Test Nested Abbreviations. """ text = '[ABBR](/foo) and _ABBR_\n\n' + \ '*[ABBR]: Abreviation' self.assertEqual( self.md.convert(text), '

ABBR ' 'and ABBR

' ) class TestCodeHilite(TestCaseWithAssertStartsWith): """ Test codehilite extension. """ def setUp(self): self.has_pygments = True try: import pygments # noqa except ImportError: self.has_pygments = False def testBasicCodeHilite(self): text = '\t# A Code Comment' md = markdown.Markdown(extensions=['codehilite']) if self.has_pygments: # Pygments can use random lexer here as we did not specify the language self.assertStartsWith('
', md.convert(text))
        else:
            self.assertEqual(
                md.convert(text),
                '
# A Code Comment'
                '
' ) def testLinenumsTrue(self): text = '\t# A Code Comment' md = markdown.Markdown( extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=True)]) if self.has_pygments: # Different versions of pygments output slightly different markup. # So we use 'startwith' and test just enough to confirm that # pygments received and processed linenums. self.assertStartsWith( '
', md.convert(text) ) else: self.assertEqual( md.convert(text), '
# A Code Comment'
                '
' ) def testLinenumsFalse(self): text = '\t#!Python\n\t# A Code Comment' md = markdown.Markdown( extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=False)]) if self.has_pygments: self.assertStartsWith('
# A Code Comment'
                '
' ) def testLinenumsNone(self): text = '\t# A Code Comment' md = markdown.Markdown( extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)]) if self.has_pygments: # Pygments can use random lexer here as we did not specify the language self.assertStartsWith('
', md.convert(text))
        else:
            self.assertEqual(
                md.convert(text),
                '
# A Code Comment'
                '
' ) def testLinenumsNoneWithShebang(self): text = '\t#!Python\n\t# A Code Comment' md = markdown.Markdown( extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)]) if self.has_pygments: # Differant versions of pygments output slightly different markup. # So we use 'startwith' and test just enough to confirm that # pygments received and processed linenums. self.assertStartsWith( '
', md.convert(text) ) else: self.assertEqual( md.convert(text), '
# A Code Comment'
                '
' ) def testLinenumsNoneWithColon(self): text = '\t:::Python\n\t# A Code Comment' md = markdown.Markdown( extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)] ) if self.has_pygments: self.assertStartsWith('
# A Code Comment'
                '
' ) def testHighlightLinesWithColon(self): # Test with hl_lines delimited by single or double quotes. text0 = '\t:::Python hl_lines="1"\n\t#line 1\n\t#line 2\n\t#line 3' text1 = "\t:::Python hl_lines='1'\n\t#line 1\n\t#line 2\n\t#line 3" for text in (text0, text1): md = markdown.Markdown(extensions=['codehilite']) if self.has_pygments: self.assertStartsWith( '
', '')
                )
            else:
                self.assertEqual(
                    md.convert(text),
                    '
'
                    '#line 1\n'
                    '#line 2\n'
                    '#line 3
' ) def testUsePygmentsFalse(self): text = '\t:::Python\n\t# A Code Comment' md = markdown.Markdown( extensions=[markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False)] ) self.assertEqual( md.convert(text), '
# A Code Comment'
            '
' ) class TestFencedCode(TestCaseWithAssertStartsWith): """ Test fenced_code extension. """ def setUp(self): self.md = markdown.Markdown(extensions=['fenced_code']) self.has_pygments = True try: import pygments # noqa except ImportError: self.has_pygments = False def testBasicFence(self): """ Test Fenced Code Blocks. """ text = ''' A paragraph before a fenced code block: ~~~ Fenced code block ~~~''' self.assertEqual( self.md.convert(text), '

A paragraph before a fenced code block:

\n' '
Fenced code block\n'
            '
' ) def testSafeFence(self): """ Test Fenced Code with safe_mode. """ text = '~~~\nCode\n~~~' self.md.safeMode = 'replace' self.assertEqual( self.md.convert(text), '
Code\n'
            '
' ) def testNestedFence(self): """ Test nested fence. """ text = ''' ~~~~~~~~ ~~~~ ~~~~~~~~''' self.assertEqual( self.md.convert(text), '
\n'
            '~~~~\n'
            '
' ) def testFencedLanguage(self): """ Test Language Tags. """ text = ''' ~~~~{.python} # Some python code ~~~~''' self.assertEqual( self.md.convert(text), '
# Some python code\n'
            '
' ) def testFencedBackticks(self): """ Test Code Fenced with Backticks. """ text = ''' ````` # Arbitrary code ~~~~~ # these tildes will not close the block `````''' self.assertEqual( self.md.convert(text), '
# Arbitrary code\n'
            '~~~~~ # these tildes will not close the block\n'
            '
' ) def testFencedCodeWithHighlightLines(self): """ Test Fenced Code with Highlighted Lines. """ text = ''' ```hl_lines="1 3" line 1 line 2 line 3 ```''' md = markdown.Markdown( extensions=[ markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False), 'fenced_code' ] ) if self.has_pygments: self.assertStartsWith( '
', '')
            )
        else:
            self.assertEqual(
                md.convert(text),
                '
line 1\n'
                'line 2\n'
                'line 3
' ) def testFencedLanguageAndHighlightLines(self): """ Test Fenced Code with Highlighted Lines. """ text0 = ''' ```.python hl_lines="1 3" #line 1 #line 2 #line 3 ```''' text1 = ''' ~~~{.python hl_lines='1 3'} #line 1 #line 2 #line 3 ~~~''' for text in (text0, text1): md = markdown.Markdown( extensions=[ markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False), 'fenced_code' ] ) if self.has_pygments: self.assertStartsWith( '
', '')
                )
            else:
                self.assertEqual(
                    md.convert(text),
                    '
#line 1\n'
                    '#line 2\n'
                    '#line 3
' ) def testFencedLanguageAndPygmentsDisabled(self): """ Test if fenced_code honors CodeHilite option use_pygments=False. """ text = '```python\nfrom __future__ import braces\n```' md = markdown.Markdown( extensions=[ markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False), 'fenced_code' ] ) self.assertTrue('' in md.convert(text)) class TestMetaData(unittest.TestCase): """ Test MetaData extension. """ def setUp(self): self.md = markdown.Markdown(extensions=['meta']) def testBasicMetaData(self): """ Test basic metadata. """ text = '''Title: A Test Doc. Author: Waylan Limberg John Doe Blank_Data: The body. This is paragraph one.''' self.assertEqual( self.md.convert(text), '

The body. This is paragraph one.

' ) self.assertEqual( self.md.Meta, { 'author': ['Waylan Limberg', 'John Doe'], 'blank_data': [''], 'title': ['A Test Doc.'] } ) def testYamlMetaData(self): """ Test metadata specified as simple YAML. """ text = '''--- Title: A Test Doc. Author: [Waylan Limberg, John Doe] Blank_Data: --- The body. This is paragraph one.''' self.assertEqual( self.md.convert(text), '

The body. This is paragraph one.

' ) self.assertEqual( self.md.Meta, { 'author': ['[Waylan Limberg, John Doe]'], 'blank_data': [''], 'title': ['A Test Doc.'] } ) def testMissingMetaData(self): """ Test document without Meta Data. """ text = ' Some Code - not extra lines of meta data.' self.assertEqual( self.md.convert(text), '
Some Code - not extra lines of meta data.\n'
            '
' ) self.assertEqual(self.md.Meta, {}) def testMetaDataWithoutNewline(self): """ Test doocument with only metadata and no newline at end.""" text = 'title: No newline' self.assertEqual(self.md.convert(text), '') self.assertEqual(self.md.Meta, {'title': ['No newline']}) def testMetaDataReset(self): """ Test that reset call remove Meta entirely """ text = '''Title: A Test Doc. Author: Waylan Limberg John Doe Blank_Data: The body. This is paragraph one.''' self.md.convert(text) self.md.reset() self.assertEqual(self.md.Meta, {}) class TestWikiLinks(unittest.TestCase): """ Test Wikilinks Extension. """ def setUp(self): self.md = markdown.Markdown(extensions=['wikilinks']) self.text = "Some text with a [[WikiLink]]." def testBasicWikilinks(self): """ Test [[wikilinks]]. """ self.assertEqual( self.md.convert(self.text), '

Some text with a ' 'WikiLink.

' ) def testWikilinkWhitespace(self): """ Test whitespace in wikilinks. """ self.assertEqual( self.md.convert('[[ foo bar_baz ]]'), '

foo bar_baz

' ) self.assertEqual( self.md.convert('foo [[ ]] bar'), '

foo bar

' ) def testSimpleSettings(self): """ Test Simple Settings. """ self.assertEqual(markdown.markdown( self.text, extensions=[ markdown.extensions.wikilinks.WikiLinkExtension( base_url='/wiki/', end_url='.html', html_class='foo') ] ), '

Some text with a ' 'WikiLink.

') def testComplexSettings(self): """ Test Complex Settings. """ md = markdown.Markdown( extensions=['wikilinks'], extension_configs={ 'wikilinks': [ ('base_url', 'http://example.com/'), ('end_url', '.html'), ('html_class', '') ] }, safe_mode=True ) self.assertEqual( md.convert(self.text), '

Some text with a ' 'WikiLink.

' ) def testWikilinksMetaData(self): """ test MetaData with Wikilinks Extension. """ text = """wiki_base_url: http://example.com/ wiki_end_url: .html wiki_html_class: Some text with a [[WikiLink]].""" md = markdown.Markdown(extensions=['meta', 'wikilinks']) self.assertEqual( md.convert(text), '

Some text with a ' 'WikiLink.

' ) # MetaData should not carry over to next document: self.assertEqual( md.convert("No [[MetaData]] here."), '

No MetaData ' 'here.

' ) def testURLCallback(self): """ Test used of a custom URL builder. """ from markdown.extensions.wikilinks import WikiLinkExtension def my_url_builder(label, base, end): return '/bar/' md = markdown.Markdown(extensions=[WikiLinkExtension(build_url=my_url_builder)]) self.assertEqual( md.convert('[[foo]]'), '

foo

' ) class TestAdmonition(unittest.TestCase): """ Test Admonition Extension. """ def setUp(self): self.md = markdown.Markdown(extensions=['admonition']) def testRE(self): RE = self.md.parser.blockprocessors['admonition'].RE tests = [ ('!!! note', ('note', None)), ('!!! note "Please Note"', ('note', 'Please Note')), ('!!! note ""', ('note', '')), ] for test, expected in tests: self.assertEqual(RE.match(test).groups(), expected) class TestTOC(TestCaseWithAssertStartsWith): """ Test TOC Extension. """ def setUp(self): self.md = markdown.Markdown(extensions=['toc']) def testMarker(self): """ Test TOC with a Marker. """ text = '[TOC]\n\n# Header 1\n\n## Header 2' self.assertEqual( self.md.convert(text), '
\n' '
    \n' # noqa '
  • Header 1' # noqa '\n' # noqa '
  • \n' # noqa '
\n' # noqa '
\n' '

Header 1

\n' '

Header 2

' ) def testNoMarker(self): """ Test TOC without a Marker. """ text = '# Header 1\n\n## Header 2' self.assertEqual( self.md.convert(text), '

Header 1

\n' '

Header 2

' ) self.assertEqual( self.md.toc, '
\n' '
    \n' # noqa '
  • Header 1' # noqa '\n' # noqa '
  • \n' # noqa '
\n' # noqa '
\n' ) def testAlternateMarker(self): """ Test TOC with user defined marker. """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(marker='{{marker}}')] ) text = '{{marker}}\n\n# Header 1\n\n## Header 2' self.assertEqual( md.convert(text), '
\n' '
    \n' # noqa '
  • Header 1' # noqa '\n' # noqa '
  • \n' # noqa '
\n' # noqa '
\n' '

Header 1

\n' '

Header 2

' ) def testDisabledMarker(self): """ Test TOC with disabled marker. """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(marker='')] ) text = '[TOC]\n\n# Header 1\n\n## Header 2' self.assertEqual( md.convert(text), '

[TOC]

\n' '

Header 1

\n' '

Header 2

' ) self.assertStartsWith('
', md.toc) def testReset(self): """ Test TOC Reset. """ self.assertEqual(self.md.toc, '') self.md.convert('# Header 1\n\n## Header 2') self.assertStartsWith('
', self.md.toc) self.md.reset() self.assertEqual(self.md.toc, '') def testUniqueIds(self): """ Test Unique IDs. """ text = '#Header\n#Header\n#Header' self.assertEqual( self.md.convert(text), '

Header

\n' '

Header

\n' '

Header

' ) def testHtmlEntities(self): """ Test Headers with HTML Entities. """ text = '# Foo & bar' self.assertEqual( self.md.convert(text), '

Foo & bar

' ) def testRawHtml(self): """ Test Headers with raw HTML. """ text = '# Foo Bar Baz.' self.assertEqual( self.md.convert(text), '

Foo Bar Baz.

' ) def testBaseLevel(self): """ Test Header Base Level. """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(baselevel=5)] ) text = '# Some Header\n\n## Next Level\n\n### Too High' self.assertEqual( md.convert(text), '
Some Header
\n' '
Next Level
\n' '
Too High
' ) self.assertEqual( md.toc, '
\n' '\n' # noqa '
\n' ) def testHeaderInlineMarkup(self): """ Test Headers with inline markup. """ text = '#Some *Header* with [markup](http://example.com).' self.assertEqual( self.md.convert(text), '

Some Header with ' 'markup.

' ) def testAnchorLink(self): """ Test TOC Anchorlink. """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(anchorlink=True)] ) text = '# Header 1\n\n## Header *2*' self.assertEqual( md.convert(text), '

Header 1

\n' '

Header 2

' ) def testAnchorLinkWithSingleInlineCode(self): """ Test TOC Anchorlink with single inline code. """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(anchorlink=True)] ) text = '# This is `code`.' self.assertEqual( md.convert(text), '

' # noqa '' # noqa 'This is code.' # noqa '' # noqa '

' # noqa ) def testAnchorLinkWithDoubleInlineCode(self): """ Test TOC Anchorlink with double inline code. """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(anchorlink=True)] ) text = '# This is `code` and `this` too.' self.assertEqual( md.convert(text), '

' # noqa '' # noqa 'This is code and this too.' # noqa '' # noqa '

' # noqa ) def testPermalink(self): """ Test TOC Permalink. """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(permalink=True)] ) text = '# Header' self.assertEqual( md.convert(text), '

' # noqa 'Header' # noqa '' # noqa '

' # noqa ) def testPermalinkWithSingleInlineCode(self): """ Test TOC Permalink with single inline code. """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(permalink=True)] ) text = '# This is `code`.' self.assertEqual( md.convert(text), '

' # noqa 'This is code.' # noqa '' # noqa '

' # noqa ) def testPermalinkWithDoubleInlineCode(self): """ Test TOC Permalink with double inline code. """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(permalink=True)] ) text = '# This is `code` and `this` too.' self.assertEqual( md.convert(text), '

' # noqa 'This is code and this too.' # noqa '' # noqa '

' # noqa ) def testTitle(self): """ Test TOC Title. """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(title='Table of Contents')] ) md.convert('# Header 1\n\n## Header 2') self.assertStartsWith( '
Table of Contents
    ', md.toc ) def testWithAttrList(self): """ Test TOC with attr_list Extension. """ md = markdown.Markdown(extensions=['toc', 'attr_list']) text = '# Header 1\n\n## Header 2 { #foo }\n\n## Header 3 { data-toc-label="Foo Bar"}' self.assertEqual( md.convert(text), '

    Header 1

    \n' '

    Header 2

    \n' '

    Header 3

    ' ) self.assertEqual( md.toc, '
    \n' '\n' # noqa '
    \n' ) self.assertEqual( md.toc_tokens, [ { 'level': 1, 'id': 'header-1', 'name': 'Header 1', 'children': [ {'level': 2, 'id': 'foo', 'name': 'Header 2', 'children': []}, {'level': 2, 'id': 'header-3', 'name': 'Foo Bar', 'children': []} ] } ] ) def testUniqueFunc(self): """ Test 'unique' function. """ from markdown.extensions.toc import unique ids = set(['foo']) self.assertEqual(unique('foo', ids), 'foo_1') self.assertEqual(ids, set(['foo', 'foo_1'])) def testTocInHeaders(self): text = '[TOC]\n#[TOC]' self.assertEqual( self.md.convert(text), '
    \n' # noqa '
      \n' # noqa '
    • [TOC]
    • \n' # noqa '
    \n' # noqa '
    \n' # noqa '

    [TOC]

    ' # noqa ) text = '#[TOC]\n[TOC]' self.assertEqual( self.md.convert(text), '

    [TOC]

    \n' # noqa '
    \n' # noqa '
      \n' # noqa '
    • [TOC]
    • \n' # noqa '
    \n' # noqa '
    ' # noqa ) text = '[TOC]\n# *[TOC]*' self.assertEqual( self.md.convert(text), '
    \n' # noqa '
      \n' # noqa '
    • [TOC]
    • \n' # noqa '
    \n' # noqa '
    \n' # noqa '

    [TOC]

    ' # noqa ) def testMaxLevel(self): """ Test toc_depth setting """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(toc_depth=2)] ) text = '# Header 1\n\n## Header 2\n\n###Header 3 not in TOC' self.assertEqual( md.convert(text), '

    Header 1

    \n' '

    Header 2

    \n' '

    Header 3 not in TOC

    ' ) self.assertEqual( md.toc, '
    \n' '
      \n' # noqa '
    • Header 1' # noqa '\n' # noqa '
    • \n' # noqa '
    \n' # noqa '
    \n' ) self.assertNotIn("Header 3", md.toc) def testMaxLevelwithBaseLevel(self): """ Test toc_depth setting together with baselevel """ md = markdown.Markdown( extensions=[markdown.extensions.toc.TocExtension(toc_depth=3, baselevel=2)] ) text = '# Some Header\n\n## Next Level\n\n### Too High' self.assertEqual( md.convert(text), '

    Some Header

    \n' '

    Next Level

    \n' '

    Too High

    ' ) self.assertEqual( md.toc, '
    \n' '\n' # noqa '
    \n' ) self.assertNotIn("Too High", md.toc) class TestSmarty(unittest.TestCase): def setUp(self): config = { 'smarty': [ ('smart_angled_quotes', True), ('substitutions', { 'ndash': '\u2013', 'mdash': '\u2014', 'ellipsis': '\u2026', 'left-single-quote': '‚', # sb is not a typo! 'right-single-quote': '‘', 'left-double-quote': '„', 'right-double-quote': '“', 'left-angle-quote': '[', 'right-angle-quote': ']', }), ] } self.md = markdown.Markdown( extensions=['smarty'], extension_configs=config ) def testCustomSubstitutions(self): text = """<< The "Unicode char of the year 2014" is the 'mdash': --- Must not be confused with 'ndash' (--) ... >> """ correct = """

    [ The „Unicode char of the year 2014“ is the ‚mdash‘: \u2014 Must not be confused with ‚ndash‘ (\u2013) \u2026 ]

    """ self.assertEqual(self.md.convert(text), correct) class TestFootnotes(unittest.TestCase): """ Test Footnotes extension. """ def testBacklinkText(self): md = markdown.Markdown( extensions=['footnotes'], extension_configs={'footnotes': {'BACKLINK_TEXT': 'back'}} ) text = 'paragraph[^1]\n\n[^1]: A Footnote' self.assertEqual( md.convert(text), '

    paragraph1

    \n' '
    \n' '
    \n' '
      \n' '
    1. \n' '

      A Footnote back

      \n' '
    2. \n' '
    \n' '
    ' )