Coverage for tests/test_extension.py: 100.00%

78 statements  

« prev     ^ index     » next       coverage.py v7.6.2, created at 2024-10-12 18:59 +0200

1"""Tests for the extension module.""" 

2 

3from __future__ import annotations 

4 

5import re 

6import sys 

7from textwrap import dedent 

8from typing import TYPE_CHECKING 

9 

10import pytest 

11 

12if TYPE_CHECKING: 

13 from markdown import Markdown 

14 

15 from mkdocstrings.plugin import MkdocstringsPlugin 

16 

17 

18@pytest.mark.parametrize("ext_markdown", [{"markdown_extensions": [{"footnotes": {}}]}], indirect=["ext_markdown"]) 

19def test_multiple_footnotes(ext_markdown: Markdown) -> None: 

20 """Assert footnotes don't get added to subsequent docstrings.""" 

21 output = ext_markdown.convert( 

22 dedent( 

23 """ 

24 Top.[^aaa] 

25 

26 ::: tests.fixtures.footnotes.func_a 

27 

28 ::: tests.fixtures.footnotes.func_b 

29 

30 ::: tests.fixtures.footnotes.func_c 

31 

32 [^aaa]: Top footnote 

33 """, 

34 ), 

35 ) 

36 assert output.count("Footnote A") == 1 

37 assert output.count("Footnote B") == 1 

38 assert output.count("Top footnote") == 1 

39 

40 

41def test_markdown_heading_level(ext_markdown: Markdown) -> None: 

42 """Assert that Markdown headings' level doesn't exceed heading_level.""" 

43 output = ext_markdown.convert("::: tests.fixtures.headings\n options:\n show_root_heading: true") 

44 assert ">Foo</h3>" in output 

45 assert ">Bar</h5>" in output 

46 assert ">Baz</h6>" in output 

47 

48 

49def test_keeps_preceding_text(ext_markdown: Markdown) -> None: 

50 """Assert that autodoc is recognized in the middle of a block and preceding text is kept.""" 

51 output = ext_markdown.convert("**preceding**\n::: tests.fixtures.headings") 

52 assert "<strong>preceding</strong>" in output 

53 assert ">Foo</h2>" in output 

54 assert ":::" not in output 

55 

56 

57def test_reference_inside_autodoc(ext_markdown: Markdown) -> None: 

58 """Assert cross-reference Markdown extension works correctly.""" 

59 output = ext_markdown.convert("::: tests.fixtures.cross_reference") 

60 assert re.search(r"Link to <.*something\.Else.*>something\.Else<.*>\.", output) 

61 

62 

63@pytest.mark.skipif(sys.version_info < (3, 8), reason="typing.Literal requires Python 3.8") 

64def test_quote_inside_annotation(ext_markdown: Markdown) -> None: 

65 """Assert that inline highlighting doesn't double-escape HTML.""" 

66 output = ext_markdown.convert("::: tests.fixtures.string_annotation.Foo") 

67 assert ";hi&" in output 

68 assert "&amp;" not in output 

69 

70 

71def test_html_inside_heading(ext_markdown: Markdown) -> None: 

72 """Assert that headings don't double-escape HTML.""" 

73 output = ext_markdown.convert("::: tests.fixtures.html_tokens") 

74 assert "&#39;&lt;" in output 

75 assert "&amp;" not in output 

76 

77 

78@pytest.mark.parametrize( 

79 ("ext_markdown", "expect_permalink"), 

80 [ 

81 ({"markdown_extensions": [{"toc": {"permalink": "@@@"}}]}, "@@@"), 

82 ({"markdown_extensions": [{"toc": {"permalink": "TeSt"}}]}, "TeSt"), 

83 ({"markdown_extensions": [{"toc": {"permalink": True}}]}, "&para;"), 

84 ], 

85 indirect=["ext_markdown"], 

86) 

87def test_no_double_toc(ext_markdown: Markdown, expect_permalink: str) -> None: 

88 """Assert that the 'toc' extension doesn't apply its modification twice.""" 

89 output = ext_markdown.convert( 

90 dedent( 

91 """ 

92 # aa 

93 

94 ::: tests.fixtures.headings 

95 options: 

96 show_root_toc_entry: false 

97 

98 # bb 

99 """, 

100 ), 

101 ) 

102 assert output.count(expect_permalink) == 5 

103 assert 'id="tests.fixtures.headings--foo"' in output 

104 assert ext_markdown.toc_tokens == [ # type: ignore[attr-defined] # the member gets populated only with 'toc' extension 

105 { 

106 "level": 1, 

107 "id": "aa", 

108 "html": "aa", 

109 "name": "aa", 

110 "data-toc-label": "", 

111 "children": [ 

112 { 

113 "level": 2, 

114 "id": "tests.fixtures.headings--foo", 

115 "html": "Foo", 

116 "name": "Foo", 

117 "data-toc-label": "", 

118 "children": [ 

119 { 

120 "level": 4, 

121 "id": "tests.fixtures.headings--bar", 

122 "html": "Bar", 

123 "name": "Bar", 

124 "data-toc-label": "", 

125 "children": [ 

126 { 

127 "level": 6, 

128 "id": "tests.fixtures.headings--baz", 

129 "html": "Baz", 

130 "name": "Baz", 

131 "data-toc-label": "", 

132 "children": [], 

133 }, 

134 ], 

135 }, 

136 ], 

137 }, 

138 ], 

139 }, 

140 { 

141 "level": 1, 

142 "id": "bb", 

143 "html": "bb", 

144 "name": "bb", 

145 "data-toc-label": "", 

146 "children": [], 

147 }, 

148 ] 

149 

150 

151def test_use_custom_handler(ext_markdown: Markdown) -> None: 

152 """Assert that we use the custom handler declared in an individual autodoc instruction.""" 

153 with pytest.raises(ModuleNotFoundError): 

154 ext_markdown.convert("::: tests.fixtures.headings\n handler: not_here") 

155 

156 

157def test_dont_register_every_identifier_as_anchor(plugin: MkdocstringsPlugin, ext_markdown: Markdown) -> None: 

158 """Assert that we don't preemptively register all identifiers of a rendered object.""" 

159 handler = plugin._handlers.get_handler("python") # type: ignore[union-attr] 

160 ids = ("id1", "id2", "id3") 

161 handler.get_anchors = lambda _: ids # type: ignore[method-assign] 

162 ext_markdown.convert("::: tests.fixtures.headings") 

163 autorefs = ext_markdown.parser.blockprocessors["mkdocstrings"]._autorefs # type: ignore[attr-defined] 

164 for identifier in ids: 

165 assert identifier not in autorefs._url_map 

166 assert identifier not in autorefs._abs_url_map 

167 

168 

169def test_use_options_yaml_key(ext_markdown: Markdown) -> None: 

170 """Check that using the 'options' YAML key works as expected.""" 

171 assert "h1" in ext_markdown.convert("::: tests.fixtures.headings\n options:\n heading_level: 1") 

172 assert "h1" not in ext_markdown.convert("::: tests.fixtures.headings\n options:\n heading_level: 2") 

173 

174 

175def test_use_yaml_options_after_blank_line(ext_markdown: Markdown) -> None: 

176 """Check that YAML options are detected even after a blank line.""" 

177 assert "h1" not in ext_markdown.convert("::: tests.fixtures.headings\n\n options:\n heading_level: 2") 

178 

179 

180@pytest.mark.parametrize("ext_markdown", [{"markdown_extensions": [{"admonition": {}}]}], indirect=["ext_markdown"]) 

181def test_removing_duplicated_headings(ext_markdown: Markdown) -> None: 

182 """Assert duplicated headings are removed from the output.""" 

183 output = ext_markdown.convert( 

184 dedent( 

185 """ 

186 ::: tests.fixtures.headings_many.heading_1 

187 

188 !!! note 

189 

190 ::: tests.fixtures.headings_many.heading_2 

191 

192 ::: tests.fixtures.headings_many.heading_3 

193 """, 

194 ), 

195 ) 

196 assert output.count(">Heading one<") == 1 

197 assert output.count(">Heading two<") == 1 

198 assert output.count(">Heading three<") == 1 

199 assert output.count('class="mkdocstrings') == 0 

200 

201 

202def _assert_contains_in_order(items: list[str], string: str) -> None: 

203 index = 0 

204 for item in items: 

205 assert item in string[index:] 

206 index = string.index(item, index) + len(item) 

207 

208 

209@pytest.mark.parametrize("ext_markdown", [{"markdown_extensions": [{"attr_list": {}}]}], indirect=["ext_markdown"]) 

210def test_backup_of_anchors(ext_markdown: Markdown) -> None: 

211 """Anchors with empty `href` are backed up.""" 

212 output = ext_markdown.convert("::: tests.fixtures.markdown_anchors") 

213 

214 # Anchors with id and no href have been backed up and updated. 

215 _assert_contains_in_order( 

216 [ 

217 'id="anchor"', 

218 'id="tests.fixtures.markdown_anchors--anchor"', 

219 'id="heading-anchor-1"', 

220 'id="tests.fixtures.markdown_anchors--heading-anchor-1"', 

221 'id="heading-anchor-2"', 

222 'id="tests.fixtures.markdown_anchors--heading-anchor-2"', 

223 'id="heading-anchor-3"', 

224 'id="tests.fixtures.markdown_anchors--heading-anchor-3"', 

225 ], 

226 output, 

227 ) 

228 

229 # Anchors with href and with or without id have been updated but not backed up. 

230 _assert_contains_in_order( 

231 [ 

232 'id="tests.fixtures.markdown_anchors--with-id"', 

233 ], 

234 output, 

235 ) 

236 assert 'id="with-id"' not in output 

237 

238 _assert_contains_in_order( 

239 [ 

240 'href="#tests.fixtures.markdown_anchors--has-href1"', 

241 'href="#tests.fixtures.markdown_anchors--has-href2"', 

242 ], 

243 output, 

244 ) 

245 assert 'href="#has-href1"' not in output 

246 assert 'href="#has-href2"' not in output