Skip to content

Commit 0c2e4bf

Browse files
committed
Rework new tests for footnote extension
- Move new tests to proper file (tests/test_syntax/extensions/test_footnotes.py) - Adapt new tests to use markdown.test_tools.TestCase and style convention in test_footnotes.py - Remove a few of the new tests which turned out to be redundant, given the existing ones in test_footnotes.py
1 parent 9547ade commit 0c2e4bf

File tree

2 files changed

+189
-201
lines changed

2 files changed

+189
-201
lines changed

tests/test_extensions.py

Lines changed: 0 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -302,204 +302,3 @@ def testCustomSubstitutions(self):
302302
is the ‚mdash‘: \u2014
303303
Must not be confused with &sbquo;ndash&lsquo; (\u2013) \u2026 ]</p>"""
304304
self.assertEqual(self.md.convert(text), correct)
305-
306-
307-
class TestFootnotes(unittest.TestCase):
308-
"""Test Footnotes Extension."""
309-
310-
def setUp(self):
311-
self.md = markdown.Markdown(extensions=["footnotes"])
312-
313-
def testBasicFootnote(self):
314-
""" Test basic footnote syntax. """
315-
text = "This is a footnote reference[^1].\n\n[^1]: This is the footnote."
316-
317-
expected = (
318-
'<p>This is a footnote reference<sup id="fnref:1">'
319-
'<a class="footnote-ref" href="#fn:1">1</a></sup>.</p>\n'
320-
'<div class="footnote">\n'
321-
"<hr />\n"
322-
"<ol>\n"
323-
'<li id="fn:1">\n'
324-
"<p>This is the footnote.&#160;"
325-
'<a class="footnote-backref" href="#fnref:1" title="Jump back to '
326-
'footnote 1 in the text">&#8617;</a></p>\n'
327-
"</li>\n"
328-
"</ol>\n"
329-
"</div>"
330-
)
331-
332-
self.assertEqual(self.md.convert(text), expected)
333-
334-
def testFootnoteOrder(self):
335-
""" Test that footnotes are ordered correctly. """
336-
text = (
337-
"First footnote reference[^first]. Second footnote reference[^last].\n\n"
338-
"[^last]: Second footnote.\n[^first]: First footnote."
339-
)
340-
341-
expected = (
342-
'<p>First footnote reference<sup id="fnref:first"><a class="footnote-ref" '
343-
'href="#fn:first">1</a></sup>. Second footnote reference<sup id="fnref:last">'
344-
'<a class="footnote-ref" href="#fn:last">2</a></sup>.</p>\n'
345-
'<div class="footnote">\n'
346-
"<hr />\n"
347-
"<ol>\n"
348-
'<li id="fn:first">\n'
349-
'<p>First footnote.&#160;<a class="footnote-backref" href="#fnref:first" '
350-
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
351-
"</li>\n"
352-
'<li id="fn:last">\n'
353-
'<p>Second footnote.&#160;<a class="footnote-backref" href="#fnref:last" '
354-
'title="Jump back to footnote 2 in the text">&#8617;</a></p>\n'
355-
"</li>\n"
356-
"</ol>\n"
357-
"</div>"
358-
)
359-
360-
self.assertEqual(self.md.convert(text), expected)
361-
362-
def testFootnoteReferenceWithinCodeSpan(self):
363-
""" Test footnote reference within a code span. """
364-
365-
text = "A `code span with a footnote[^1] reference`."
366-
expected = "<p>A <code>code span with a footnote[^1] reference</code>.</p>"
367-
368-
self.assertEqual(self.md.convert(text), expected)
369-
370-
def testFootnoteReferenceInLink(self):
371-
""" Test footnote reference within a link. """
372-
373-
text = "A [link with a footnote[^1] reference](http://example.com)."
374-
expected = '<p>A <a href="http://example.com">link with a footnote[^1] reference</a>.</p>'
375-
376-
self.assertEqual(self.md.convert(text), expected)
377-
378-
def testDuplicateFootnoteReferences(self):
379-
""" Test multiple references to the same footnote. """
380-
text = "First[^dup] and second[^dup] reference.\n\n[^dup]: Duplicate footnote."
381-
382-
expected = (
383-
'<p>First<sup id="fnref:dup">'
384-
'<a class="footnote-ref" href="#fn:dup">1</a></sup> and second<sup id="fnref2:dup">'
385-
'<a class="footnote-ref" href="#fn:dup">1</a></sup> reference.</p>\n'
386-
'<div class="footnote">\n'
387-
"<hr />\n"
388-
"<ol>\n"
389-
'<li id="fn:dup">\n'
390-
"<p>Duplicate footnote.&#160;"
391-
'<a class="footnote-backref" href="#fnref:dup" '
392-
'title="Jump back to footnote 1 in the text">&#8617;</a>'
393-
'<a class="footnote-backref" href="#fnref2:dup" '
394-
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
395-
"</li>\n"
396-
"</ol>\n"
397-
"</div>"
398-
)
399-
400-
self.assertEqual(self.md.convert(text), expected)
401-
402-
def testFootnoteReferenceWithoutDefinition(self):
403-
""" Test footnote reference without corresponding definition. """
404-
text = "This has a missing footnote[^missing]."
405-
expected = "<p>This has a missing footnote[^missing].</p>"
406-
407-
self.assertEqual(self.md.convert(text), expected)
408-
409-
def testFootnoteDefinitionWithoutReference(self):
410-
""" Test footnote definition without corresponding reference. """
411-
text = "No reference here.\n\n[^orphan]: Orphaned footnote."
412-
413-
self.assertIn("fn:orphan", self.md.convert(text))
414-
415-
# For the opposite behavior:
416-
# self.assertNotIn("fn:orphan", self.md.convert(text))
417-
418-
def testMultilineFootnote(self):
419-
""" Test footnote definition spanning multiple lines. """
420-
421-
text = (
422-
"Multi-line footnote[^multi].\n\n"
423-
"[^multi]: This is a footnote\n"
424-
" that spans multiple lines\n"
425-
" with proper indentation."
426-
)
427-
428-
expected = (
429-
'<p>Multi-line footnote<sup id="fnref:multi"><a class="footnote-ref" href="#fn:multi">1</a></sup>.</p>\n'
430-
'<div class="footnote">\n'
431-
'<hr />\n'
432-
'<ol>\n'
433-
'<li id="fn:multi">\n'
434-
'<p>This is a footnote\n'
435-
'that spans multiple lines\n'
436-
'with proper indentation.&#160;<a class="footnote-backref" href="#fnref:multi" '
437-
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
438-
'</li>\n'
439-
'</ol>\n'
440-
'</div>'
441-
)
442-
self.assertEqual(self.md.convert(text), expected)
443-
444-
def testFootnoteInBlockquote(self):
445-
""" Test footnote reference within a blockquote. """
446-
text = "> This is a quote with a footnote[^quote].\n\n[^quote]: Quote footnote."
447-
448-
result = self.md.convert(text)
449-
self.assertIn("<blockquote>", result)
450-
self.assertIn("fnref:quote", result)
451-
452-
def testFootnoteInList(self):
453-
""" Test footnote reference within a list item. """
454-
text = "1. First item with footnote[^note]\n1. Second item\n\n[^note]: List footnote."
455-
456-
result = self.md.convert(text)
457-
self.assertIn("<ol>", result)
458-
self.assertIn("fnref:note", result)
459-
460-
def testNestedFootnotes(self):
461-
""" Test footnote definition containing another footnote reference. """
462-
text = (
463-
"Main footnote[^main].\n\n"
464-
"[^main]: This footnote references another[^nested].\n"
465-
"[^nested]: Nested footnote."
466-
)
467-
result = self.md.convert(text)
468-
469-
self.assertIn("fnref:main", result)
470-
self.assertIn("fnref:nested", result)
471-
self.assertIn("fn:main", result)
472-
self.assertIn("fn:nested", result)
473-
474-
def testFootnoteReset(self):
475-
""" Test that footnotes are properly reset between documents. """
476-
text1 = "First doc[^1].\n\n[^1]: First footnote."
477-
text2 = "Second doc[^1].\n\n[^1]: Different footnote."
478-
479-
result1 = self.md.convert(text1)
480-
self.md.reset()
481-
result2 = self.md.convert(text2)
482-
483-
self.assertIn("First footnote", result1)
484-
self.assertIn("Different footnote", result2)
485-
self.assertNotIn("Different footnote", result1)
486-
487-
def testFootnoteIdWithSpecialChars(self):
488-
""" Test footnote id containing special and unicode characters. """
489-
text = "Unicode footnote id[^!#¤%/()=?+}{§øé].\n\n[^!#¤%/()=?+}{§øé]: Footnote with unicode id."
490-
491-
self.assertIn("fnref:!#¤%/()=?+}{§øé", self.md.convert(text))
492-
493-
def testFootnoteRefInHtml(self):
494-
""" Test footnote reference within HTML tags. """
495-
text = "A <span>footnote reference[^1] in an HTML</span>.\n\n[^1]: The footnote."
496-
497-
self.assertIn("fnref:1", self.md.convert(text))
498-
499-
def testFootnoteWithHtmlAndMarkdown(self):
500-
""" Test footnote containing HTML and markdown elements. """
501-
text = "A footnote with style[^html].\n\n[^html]: Has *emphasis* and <strong>bold</strong>."
502-
503-
result = self.md.convert(text)
504-
self.assertIn("<em>emphasis</em>", result)
505-
self.assertIn("<strong>bold</strong>", result)

tests/test_syntax/extensions/test_footnotes.py

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,192 @@ def test_superscript_text(self):
336336
'</div>',
337337
extension_configs={'footnotes': {'SUPERSCRIPT_TEXT': '[{}]'}}
338338
)
339+
340+
def test_footnote_order(self):
341+
"""Test that footnotes occur in order of reference appearance."""
342+
343+
self.assertMarkdownRenders(
344+
'First footnote reference[^first]. Second footnote reference[^last].\n\n'
345+
'[^last]: Second footnote.\n[^first]: First footnote.',
346+
'<p>First footnote reference<sup id="fnref:first"><a class="footnote-ref" '
347+
'href="#fn:first">1</a></sup>. Second footnote reference<sup id="fnref:last">'
348+
'<a class="footnote-ref" href="#fn:last">2</a></sup>.</p>\n'
349+
'<div class="footnote">\n'
350+
'<hr />\n'
351+
'<ol>\n'
352+
'<li id="fn:first">\n'
353+
'<p>First footnote.&#160;<a class="footnote-backref" href="#fnref:first" '
354+
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
355+
'</li>\n'
356+
'<li id="fn:last">\n'
357+
'<p>Second footnote.&#160;<a class="footnote-backref" href="#fnref:last" '
358+
'title="Jump back to footnote 2 in the text">&#8617;</a></p>\n'
359+
'</li>\n'
360+
'</ol>\n'
361+
'</div>'
362+
)
363+
364+
def test_footnote_reference_within_code_span(self):
365+
"""Test footnote reference within a code span."""
366+
367+
self.assertMarkdownRenders(
368+
'A `code span with a footnote[^1] reference`.',
369+
'<p>A <code>code span with a footnote[^1] reference</code>.</p>'
370+
)
371+
372+
def test_footnote_reference_within_link(self):
373+
"""Test footnote reference within a link."""
374+
375+
self.assertMarkdownRenders(
376+
'A [link with a footnote[^1] reference](http://example.com).',
377+
'<p>A <a href="http://example.com">link with a footnote[^1] reference</a>.</p>'
378+
)
379+
380+
def test_footnote_reference_within_footnote_definition(self):
381+
"""Test footnote definition containing another footnote reference."""
382+
383+
self.assertMarkdownRenders(
384+
'Main footnote[^main].\n\n'
385+
'[^main]: This footnote references another[^nested].\n'
386+
'[^nested]: Nested footnote.',
387+
'<p>Main footnote<sup id="fnref:main"><a class="footnote-ref" href="#fn:main">1</a></sup>.</p>\n'
388+
'<div class="footnote">\n'
389+
'<hr />\n'
390+
'<ol>\n'
391+
'<li id="fn:main">\n'
392+
'<p>This footnote references another<sup id="fnref:nested"><a class="footnote-ref" '
393+
'href="#fn:nested">2</a></sup>.&#160;<a class="footnote-backref" href="#fnref:main" '
394+
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
395+
'</li>\n'
396+
'<li id="fn:nested">\n'
397+
'<p>Nested footnote.&#160;<a class="footnote-backref" href="#fnref:nested" '
398+
'title="Jump back to footnote 2 in the text">&#8617;</a></p>\n'
399+
'</li>\n'
400+
'</ol>\n'
401+
'</div>'
402+
)
403+
404+
def test_footnote_reference_within_blockquote(self):
405+
"""Test footnote reference within a blockquote."""
406+
407+
self.assertMarkdownRenders(
408+
'> This is a quote with a footnote[^quote].\n\n[^quote]: Quote footnote.',
409+
'<blockquote>\n'
410+
'<p>This is a quote with a footnote<sup id="fnref:quote">'
411+
'<a class="footnote-ref" href="#fn:quote">1</a></sup>.</p>\n'
412+
'</blockquote>\n'
413+
'<div class="footnote">\n'
414+
'<hr />\n'
415+
'<ol>\n'
416+
'<li id="fn:quote">\n'
417+
'<p>Quote footnote.&#160;<a class="footnote-backref" href="#fnref:quote" '
418+
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
419+
'</li>\n'
420+
'</ol>\n'
421+
'</div>'
422+
)
423+
424+
def test_footnote_reference_within_list(self):
425+
"""Test footnote reference within a list item."""
426+
427+
self.assertMarkdownRenders(
428+
'1. First item with footnote[^note]\n1. Second item\n\n[^note]: List footnote.',
429+
'<ol>\n'
430+
'<li>First item with footnote<sup id="fnref:note">'
431+
'<a class="footnote-ref" href="#fn:note">1</a></sup></li>\n'
432+
'<li>Second item</li>\n'
433+
'</ol>\n'
434+
'<div class="footnote">\n'
435+
'<hr />\n'
436+
'<ol>\n'
437+
'<li id="fn:note">\n'
438+
'<p>List footnote.&#160;<a class="footnote-backref" href="#fnref:note" '
439+
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
440+
'</li>\n'
441+
'</ol>\n'
442+
'</div>'
443+
)
444+
445+
def test_footnote_reference_within_html(self):
446+
"""Test footnote reference within HTML tags."""
447+
448+
self.assertMarkdownRenders(
449+
'A <span>footnote reference[^1] within a span element</span>.\n\n[^1]: The footnote.',
450+
'<p>A <span>footnote reference<sup id="fnref:1">'
451+
'<a class="footnote-ref" href="#fn:1">1</a>'
452+
'</sup> within a span element</span>.</p>\n'
453+
'<div class="footnote">\n'
454+
'<hr />\n'
455+
'<ol>\n'
456+
'<li id="fn:1">\n'
457+
'<p>The footnote.&#160;<a class="footnote-backref" href="#fnref:1" '
458+
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
459+
'</li>\n'
460+
'</ol>\n'
461+
'</div>'
462+
)
463+
464+
def test_duplicate_footnote_references(self):
465+
"""Test multiple references to the same footnote."""
466+
467+
self.assertMarkdownRenders(
468+
'First[^dup] and second[^dup] reference.\n\n[^dup]: Duplicate footnote.',
469+
'<p>First<sup id="fnref:dup">'
470+
'<a class="footnote-ref" href="#fn:dup">1</a></sup> and second<sup id="fnref2:dup">'
471+
'<a class="footnote-ref" href="#fn:dup">1</a></sup> reference.</p>\n'
472+
'<div class="footnote">\n'
473+
'<hr />\n'
474+
'<ol>\n'
475+
'<li id="fn:dup">\n'
476+
'<p>Duplicate footnote.&#160;'
477+
'<a class="footnote-backref" href="#fnref:dup" '
478+
'title="Jump back to footnote 1 in the text">&#8617;</a>'
479+
'<a class="footnote-backref" href="#fnref2:dup" '
480+
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
481+
'</li>\n'
482+
'</ol>\n'
483+
'</div>'
484+
)
485+
486+
def test_footnote_reference_without_definition(self):
487+
"""Test footnote reference without corresponding definition."""
488+
489+
self.assertMarkdownRenders(
490+
'This has a missing footnote[^missing].',
491+
'<p>This has a missing footnote[^missing].</p>'
492+
)
493+
494+
def test_footnote_definition_without_reference(self):
495+
"""Test footnote definition without corresponding reference."""
496+
497+
self.assertMarkdownRenders(
498+
'No reference here.\n\n[^orphan]: Orphaned footnote.',
499+
'<p>No reference here.</p>\n'
500+
'<div class="footnote">\n'
501+
'<hr />\n'
502+
'<ol>\n'
503+
'<li id="fn:orphan">\n'
504+
'<p>Orphaned footnote.&#160;<a class="footnote-backref" href="#fnref:orphan" '
505+
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
506+
'</li>\n'
507+
'</ol>\n'
508+
'</div>'
509+
)
510+
511+
def test_footnote_id_with_special_chars(self):
512+
"""Test footnote id containing special and Unicode characters."""
513+
514+
self.assertMarkdownRenders(
515+
'Special footnote id[^!#¤%/()=?+}{§øé].\n\n[^!#¤%/()=?+}{§øé]: The footnote.',
516+
'<p>Special footnote id<sup id="fnref:!#¤%/()=?+}{§øé">'
517+
'<a class="footnote-ref" href="#fn:!#¤%/()=?+}{§øé">1</a></sup>.</p>\n'
518+
'<div class="footnote">\n'
519+
'<hr />\n'
520+
'<ol>\n'
521+
'<li id="fn:!#¤%/()=?+}{§øé">\n'
522+
'<p>The footnote.&#160;<a class="footnote-backref" href="#fnref:!#¤%/()=?+}{§øé" '
523+
'title="Jump back to footnote 1 in the text">&#8617;</a></p>\n'
524+
'</li>\n'
525+
'</ol>\n'
526+
'</div>'
527+
)

0 commit comments

Comments
 (0)