A Python parser for MediaWiki wikicode https://mwparserfromhell.readthedocs.io/
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. # Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. #
  3. # Permission is hereby granted, free of charge, to any person obtaining a copy
  4. # of this software and associated documentation files (the "Software"), to deal
  5. # in the Software without restriction, including without limitation the rights
  6. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. # copies of the Software, and to permit persons to whom the Software is
  8. # furnished to do so, subject to the following conditions:
  9. #
  10. # The above copyright notice and this permission notice shall be included in
  11. # all copies or substantial portions of the Software.
  12. #
  13. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  19. # SOFTWARE.
  20. """
  21. Test cases for the Tag node.
  22. """
  23. import pytest
  24. from mwparserfromhell.nodes import Tag, Template, Text
  25. from mwparserfromhell.nodes.extras import Attribute
  26. from .conftest import assert_wikicode_equal, wrap, wraptext
  27. agen = lambda name, value: Attribute(wraptext(name), wraptext(value))
  28. agennv = lambda name: Attribute(wraptext(name))
  29. agennq = lambda name, value: Attribute(wraptext(name), wraptext(value), None)
  30. agenp = lambda name, v, a, b, c: Attribute(wraptext(name), v, '"', a, b, c)
  31. agenpnv = lambda name, a, b, c: Attribute(wraptext(name), None, '"', a, b, c)
  32. def test_str():
  33. """test Tag.__str__()"""
  34. node1 = Tag(wraptext("ref"))
  35. node2 = Tag(wraptext("span"), wraptext("foo"), [agen("style", "color: red;")])
  36. node3 = Tag(
  37. wraptext("ref"),
  38. attrs=[agennq("name", "foo"), agenpnv("some_attr", " ", "", "")],
  39. self_closing=True,
  40. )
  41. node4 = Tag(wraptext("br"), self_closing=True, padding=" ")
  42. node5 = Tag(wraptext("br"), self_closing=True, implicit=True)
  43. node6 = Tag(wraptext("br"), self_closing=True, invalid=True, implicit=True)
  44. node7 = Tag(wraptext("br"), self_closing=True, invalid=True, padding=" ")
  45. node8 = Tag(wraptext("hr"), wiki_markup="----", self_closing=True)
  46. node9 = Tag(wraptext("i"), wraptext("italics!"), wiki_markup="''")
  47. assert "<ref></ref>" == str(node1)
  48. assert '<span style="color: red;">foo</span>' == str(node2)
  49. assert "<ref name=foo some_attr/>" == str(node3)
  50. assert "<br />" == str(node4)
  51. assert "<br>" == str(node5)
  52. assert "</br>" == str(node6)
  53. assert "</br />" == str(node7)
  54. assert "----" == str(node8)
  55. assert "''italics!''" == str(node9)
  56. def test_children():
  57. """test Tag.__children__()"""
  58. # <ref>foobar</ref>
  59. node1 = Tag(wraptext("ref"), wraptext("foobar"))
  60. # '''bold text'''
  61. node2 = Tag(wraptext("b"), wraptext("bold text"), wiki_markup="'''")
  62. # <img id="foo" class="bar" selected />
  63. node3 = Tag(
  64. wraptext("img"),
  65. attrs=[agen("id", "foo"), agen("class", "bar"), agennv("selected")],
  66. self_closing=True,
  67. padding=" ",
  68. )
  69. gen1 = node1.__children__()
  70. gen2 = node2.__children__()
  71. gen3 = node3.__children__()
  72. assert node1.tag == next(gen1)
  73. assert node3.tag == next(gen3)
  74. assert node3.attributes[0].name == next(gen3)
  75. assert node3.attributes[0].value == next(gen3)
  76. assert node3.attributes[1].name == next(gen3)
  77. assert node3.attributes[1].value == next(gen3)
  78. assert node3.attributes[2].name == next(gen3)
  79. assert node1.contents == next(gen1)
  80. assert node2.contents == next(gen2)
  81. assert node1.closing_tag == next(gen1)
  82. with pytest.raises(StopIteration):
  83. next(gen1)
  84. with pytest.raises(StopIteration):
  85. next(gen2)
  86. with pytest.raises(StopIteration):
  87. next(gen3)
  88. def test_strip():
  89. """test Tag.__strip__()"""
  90. node1 = Tag(wraptext("i"), wraptext("foobar"))
  91. node2 = Tag(wraptext("math"), wraptext("foobar"))
  92. node3 = Tag(wraptext("br"), self_closing=True)
  93. assert "foobar" == node1.__strip__()
  94. assert node2.__strip__() is None
  95. assert node3.__strip__() is None
  96. def test_showtree():
  97. """test Tag.__showtree__()"""
  98. output = []
  99. getter, marker = object(), object()
  100. get = lambda code: output.append((getter, code))
  101. mark = lambda: output.append(marker)
  102. node1 = Tag(
  103. wraptext("ref"), wraptext("text"), [agen("name", "foo"), agennv("selected")]
  104. )
  105. node2 = Tag(wraptext("br"), self_closing=True, padding=" ")
  106. node3 = Tag(
  107. wraptext("br"), self_closing=True, invalid=True, implicit=True, padding=" "
  108. )
  109. node1.__showtree__(output.append, get, mark)
  110. node2.__showtree__(output.append, get, mark)
  111. node3.__showtree__(output.append, get, mark)
  112. valid = [
  113. "<",
  114. (getter, node1.tag),
  115. (getter, node1.attributes[0].name),
  116. " = ",
  117. marker,
  118. (getter, node1.attributes[0].value),
  119. (getter, node1.attributes[1].name),
  120. ">",
  121. (getter, node1.contents),
  122. "</",
  123. (getter, node1.closing_tag),
  124. ">",
  125. "<",
  126. (getter, node2.tag),
  127. "/>",
  128. "</",
  129. (getter, node3.tag),
  130. ">",
  131. ]
  132. assert valid == output
  133. def test_tag():
  134. """test getter/setter for the tag attribute"""
  135. tag = wraptext("ref")
  136. node = Tag(tag, wraptext("text"))
  137. assert tag is node.tag
  138. assert tag is node.closing_tag
  139. node.tag = "span"
  140. assert_wikicode_equal(wraptext("span"), node.tag)
  141. assert_wikicode_equal(wraptext("span"), node.closing_tag)
  142. assert "<span>text</span>" == node
  143. def test_contents():
  144. """test getter/setter for the contents attribute"""
  145. contents = wraptext("text")
  146. node = Tag(wraptext("ref"), contents)
  147. assert contents is node.contents
  148. node.contents = "text and a {{template}}"
  149. parsed = wrap([Text("text and a "), Template(wraptext("template"))])
  150. assert_wikicode_equal(parsed, node.contents)
  151. assert "<ref>text and a {{template}}</ref>" == node
  152. def test_attributes():
  153. """test getter for the attributes attribute"""
  154. attrs = [agen("name", "bar")]
  155. node1 = Tag(wraptext("ref"), wraptext("foo"))
  156. node2 = Tag(wraptext("ref"), wraptext("foo"), attrs)
  157. assert [] == node1.attributes
  158. assert attrs is node2.attributes
  159. def test_wiki_markup():
  160. """test getter/setter for the wiki_markup attribute"""
  161. node = Tag(wraptext("i"), wraptext("italic text"))
  162. assert None is node.wiki_markup
  163. node.wiki_markup = "''"
  164. assert "''" == node.wiki_markup
  165. assert "''italic text''" == node
  166. node.wiki_markup = False
  167. assert node.wiki_markup is None
  168. assert "<i>italic text</i>" == node
  169. def test_self_closing():
  170. """test getter/setter for the self_closing attribute"""
  171. node = Tag(wraptext("ref"), wraptext("foobar"))
  172. assert node.self_closing is False
  173. node.self_closing = True
  174. assert node.self_closing is True
  175. assert "<ref/>" == node
  176. node.self_closing = 0
  177. assert node.self_closing is False
  178. assert "<ref>foobar</ref>" == node
  179. def test_invalid():
  180. """test getter/setter for the invalid attribute"""
  181. node = Tag(wraptext("br"), self_closing=True, implicit=True)
  182. assert node.invalid is False
  183. node.invalid = True
  184. assert node.invalid is True
  185. assert "</br>" == node
  186. node.invalid = 0
  187. assert node.invalid is False
  188. assert "<br>" == node
  189. def test_implicit():
  190. """test getter/setter for the implicit attribute"""
  191. node = Tag(wraptext("br"), self_closing=True)
  192. assert node.implicit is False
  193. node.implicit = True
  194. assert node.implicit is True
  195. assert "<br>" == node
  196. node.implicit = 0
  197. assert node.implicit is False
  198. assert "<br/>" == node
  199. def test_padding():
  200. """test getter/setter for the padding attribute"""
  201. node = Tag(wraptext("ref"), wraptext("foobar"))
  202. assert "" == node.padding
  203. node.padding = " "
  204. assert " " == node.padding
  205. assert "<ref >foobar</ref>" == node
  206. node.padding = None
  207. assert "" == node.padding
  208. assert "<ref>foobar</ref>" == node
  209. with pytest.raises(ValueError):
  210. node.__setattr__("padding", True)
  211. def test_closing_tag():
  212. """test getter/setter for the closing_tag attribute"""
  213. tag = wraptext("ref")
  214. node = Tag(tag, wraptext("foobar"))
  215. assert tag is node.closing_tag
  216. node.closing_tag = "ref {{ignore me}}"
  217. parsed = wrap([Text("ref "), Template(wraptext("ignore me"))])
  218. assert_wikicode_equal(parsed, node.closing_tag)
  219. assert "<ref>foobar</ref {{ignore me}}>" == node
  220. def test_wiki_style_separator():
  221. """test getter/setter for wiki_style_separator attribute"""
  222. node = Tag(wraptext("table"), wraptext("\n"))
  223. assert None is node.wiki_style_separator
  224. node.wiki_style_separator = "|"
  225. assert "|" == node.wiki_style_separator
  226. node.wiki_markup = "{"
  227. assert "{|\n{" == node
  228. node2 = Tag(wraptext("table"), wraptext("\n"), wiki_style_separator="|")
  229. assert "|" == node2.wiki_style_separator
  230. def test_closing_wiki_markup():
  231. """test getter/setter for closing_wiki_markup attribute"""
  232. node = Tag(wraptext("table"), wraptext("\n"))
  233. assert None is node.closing_wiki_markup
  234. node.wiki_markup = "{|"
  235. assert "{|" == node.closing_wiki_markup
  236. node.closing_wiki_markup = "|}"
  237. assert "|}" == node.closing_wiki_markup
  238. assert "{|\n|}" == node
  239. node.wiki_markup = "!!"
  240. assert "|}" == node.closing_wiki_markup
  241. assert "!!\n|}" == node
  242. node.wiki_markup = False
  243. assert node.closing_wiki_markup is None
  244. assert "<table>\n</table>" == node
  245. node2 = Tag(
  246. wraptext("table"),
  247. wraptext("\n"),
  248. attrs=[agen("id", "foo")],
  249. wiki_markup="{|",
  250. closing_wiki_markup="|}",
  251. )
  252. assert "|}" == node2.closing_wiki_markup
  253. assert '{| id="foo"\n|}' == node2
  254. def test_has():
  255. """test Tag.has()"""
  256. node = Tag(wraptext("ref"), wraptext("cite"), [agen("name", "foo")])
  257. assert node.has("name") is True
  258. assert node.has(" name ") is True
  259. assert node.has(wraptext("name")) is True
  260. assert node.has("Name") is False
  261. assert node.has("foo") is False
  262. attrs = [
  263. agen("id", "foo"),
  264. agenp("class", "bar", " ", "\n", "\n"),
  265. agen("foo", "bar"),
  266. agenpnv("foo", " ", " \n ", " \t"),
  267. ]
  268. node2 = Tag(wraptext("div"), attrs=attrs, self_closing=True)
  269. assert node2.has("id") is True
  270. assert node2.has("class") is True
  271. assert (
  272. node2.has(attrs[1].pad_first + str(attrs[1].name) + attrs[1].pad_before_eq)
  273. is True
  274. )
  275. assert node2.has(attrs[3]) is True
  276. assert node2.has(str(attrs[3])) is True
  277. assert node2.has("idclass") is False
  278. assert node2.has("id class") is False
  279. assert node2.has("id=foo") is False
  280. def test_get():
  281. """test Tag.get()"""
  282. attrs = [agen("name", "foo")]
  283. node = Tag(wraptext("ref"), wraptext("cite"), attrs)
  284. assert attrs[0] is node.get("name")
  285. assert attrs[0] is node.get(" name ")
  286. assert attrs[0] is node.get(wraptext("name"))
  287. with pytest.raises(ValueError):
  288. node.get("Name")
  289. with pytest.raises(ValueError):
  290. node.get("foo")
  291. attrs = [
  292. agen("id", "foo"),
  293. agenp("class", "bar", " ", "\n", "\n"),
  294. agen("foo", "bar"),
  295. agenpnv("foo", " ", " \n ", " \t"),
  296. ]
  297. node2 = Tag(wraptext("div"), attrs=attrs, self_closing=True)
  298. assert attrs[0] is node2.get("id")
  299. assert attrs[1] is node2.get("class")
  300. assert attrs[1] is node2.get(
  301. attrs[1].pad_first + str(attrs[1].name) + attrs[1].pad_before_eq
  302. )
  303. assert attrs[3] is node2.get(attrs[3])
  304. assert attrs[3] is node2.get(str(attrs[3]))
  305. assert attrs[3] is node2.get(" foo")
  306. with pytest.raises(ValueError):
  307. node2.get("idclass")
  308. with pytest.raises(ValueError):
  309. node2.get("id class")
  310. with pytest.raises(ValueError):
  311. node2.get("id=foo")
  312. def test_add():
  313. """test Tag.add()"""
  314. node = Tag(wraptext("ref"), wraptext("cite"))
  315. node.add("name", "value")
  316. node.add("name", "value", quotes=None)
  317. node.add("name", "value", quotes="'")
  318. node.add("name")
  319. node.add(1, False)
  320. node.add("style", "{{foobar}}")
  321. node.add("name", "value", '"', "\n", " ", " ")
  322. attr1 = ' name="value"'
  323. attr2 = " name=value"
  324. attr3 = " name='value'"
  325. attr4 = " name"
  326. attr5 = ' 1="False"'
  327. attr6 = ' style="{{foobar}}"'
  328. attr7 = '\nname = "value"'
  329. assert attr1 == node.attributes[0]
  330. assert attr2 == node.attributes[1]
  331. assert attr3 == node.attributes[2]
  332. assert attr4 == node.attributes[3]
  333. assert attr5 == node.attributes[4]
  334. assert attr6 == node.attributes[5]
  335. assert attr7 == node.attributes[6]
  336. assert attr7 == node.get("name")
  337. assert_wikicode_equal(
  338. wrap([Template(wraptext("foobar"))]), node.attributes[5].value
  339. )
  340. assert (
  341. "".join(
  342. ("<ref", attr1, attr2, attr3, attr4, attr5, attr6, attr7, ">cite</ref>")
  343. )
  344. == node
  345. )
  346. with pytest.raises(ValueError):
  347. node.add("name", "foo", quotes="bar")
  348. with pytest.raises(ValueError):
  349. node.add("name", "a bc d", quotes=None)
  350. def test_remove():
  351. """test Tag.remove()"""
  352. attrs = [
  353. agen("id", "foo"),
  354. agenp("class", "bar", " ", "\n", "\n"),
  355. agen("foo", "bar"),
  356. agenpnv("foo", " ", " \n ", " \t"),
  357. ]
  358. node = Tag(wraptext("div"), attrs=attrs, self_closing=True)
  359. node.remove("class")
  360. assert '<div id="foo" foo="bar" foo \n />' == node
  361. node.remove("foo")
  362. assert '<div id="foo"/>' == node
  363. with pytest.raises(ValueError):
  364. node.remove("foo")
  365. node.remove("id")
  366. assert "<div/>" == node