A Python parser for MediaWiki wikicode https://mwparserfromhell.readthedocs.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

541 lines
24 KiB

  1. #
  2. # Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining a copy
  5. # of this software and associated documentation files (the "Software"), to deal
  6. # in the Software without restriction, including without limitation the rights
  7. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. # copies of the Software, and to permit persons to whom the Software is
  9. # furnished to do so, subject to the following conditions:
  10. #
  11. # The above copyright notice and this permission notice shall be included in
  12. # all copies or substantial portions of the Software.
  13. #
  14. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. # SOFTWARE.
  21. from functools import partial
  22. import pytest
  23. import re
  24. from types import GeneratorType
  25. from mwparserfromhell.nodes import Argument, Heading, Template, Text
  26. from mwparserfromhell.smart_list import SmartList
  27. from mwparserfromhell.wikicode import Wikicode
  28. from mwparserfromhell import parse
  29. from ._test_tree_equality import TreeEqualityTestCase, wrap, wraptext
  30. class TestWikicode(TreeEqualityTestCase):
  31. """Tests for the Wikicode class, which manages a list of nodes."""
  32. def test_unicode(self):
  33. """test Wikicode.__unicode__()"""
  34. code1 = parse("foobar")
  35. code2 = parse("Have a {{template}} and a [[page|link]]")
  36. assert "foobar" == str(code1)
  37. assert "Have a {{template}} and a [[page|link]]" == str(code2)
  38. def test_nodes(self):
  39. """test getter/setter for the nodes attribute"""
  40. code = parse("Have a {{template}}")
  41. assert ["Have a " == "{{template}}"], code.nodes
  42. L1 = SmartList([Text("foobar"), Template(wraptext("abc"))])
  43. L2 = [Text("barfoo"), Template(wraptext("cba"))]
  44. L3 = "abc{{def}}"
  45. code.nodes = L1
  46. assert L1 is code.nodes
  47. code.nodes = L2
  48. assert L2 is code.nodes
  49. code.nodes = L3
  50. assert ["abc" == "{{def}}"], code.nodes
  51. with pytest.raises(ValueError):
  52. code.__setattr__("nodes", object)
  53. def test_get(self):
  54. """test Wikicode.get()"""
  55. code = parse("Have a {{template}} and a [[page|link]]")
  56. assert code.nodes[0] is code.get(0)
  57. assert code.nodes[2] is code.get(2)
  58. with pytest.raises(IndexError):
  59. code.get(4)
  60. def test_set(self):
  61. """test Wikicode.set()"""
  62. code = parse("Have a {{template}} and a [[page|link]]")
  63. code.set(1, "{{{argument}}}")
  64. assert "Have a {{{argument}}} and a [[page|link]]" == code
  65. assert isinstance(code.get(1), Argument)
  66. code.set(2, None)
  67. assert "Have a {{{argument}}}[[page|link]]" == code
  68. code.set(-3, "This is an ")
  69. assert "This is an {{{argument}}}[[page|link]]" == code
  70. with pytest.raises(ValueError):
  71. code.set(1, "foo {{bar}}")
  72. with pytest.raises(IndexError):
  73. code.set(3, "{{baz}}")
  74. with pytest.raises(IndexError):
  75. code.set(-4, "{{baz}}")
  76. def test_contains(self):
  77. """test Wikicode.contains()"""
  78. code = parse("Here is {{aaa|{{bbb|xyz{{ccc}}}}}} and a [[page|link]]")
  79. tmpl1, tmpl2, tmpl3 = code.filter_templates()
  80. tmpl4 = parse("{{ccc}}").filter_templates()[0]
  81. assert code.contains(tmpl1) is True
  82. assert code.contains(tmpl3) is True
  83. assert code.contains(tmpl4) is False
  84. assert code.contains(str(tmpl4)) is True
  85. assert code.contains(tmpl2.params[0].value) is True
  86. def test_index(self):
  87. """test Wikicode.index()"""
  88. code = parse("Have a {{template}} and a [[page|link]]")
  89. assert 0 == code.index("Have a ")
  90. assert 3 == code.index("[[page|link]]")
  91. assert 1 == code.index(code.get(1))
  92. with pytest.raises(ValueError):
  93. code.index("foo")
  94. code = parse("{{foo}}{{bar|{{baz}}}}")
  95. assert 1 == code.index("{{bar|{{baz}}}}")
  96. assert 1 == code.index("{{baz}}", recursive=True)
  97. assert 1 == code.index(code.get(1).get(1).value,
  98. recursive=True)
  99. with pytest.raises(ValueError):
  100. code.index("{{baz}}", recursive=False)
  101. with pytest.raises(ValueError):
  102. code.index(code.get(1).get(1).value, recursive=False)
  103. def test_get_ancestors_parent(self):
  104. """test Wikicode.get_ancestors() and Wikicode.get_parent()"""
  105. code = parse("{{a|{{b|{{d|{{e}}{{f}}}}{{g}}}}}}{{c}}")
  106. tmpl = code.filter_templates(matches=lambda n: n.name == "f")[0]
  107. parent1 = code.filter_templates(matches=lambda n: n.name == "d")[0]
  108. parent2 = code.filter_templates(matches=lambda n: n.name == "b")[0]
  109. parent3 = code.filter_templates(matches=lambda n: n.name == "a")[0]
  110. fake = parse("{{f}}").get(0)
  111. assert [parent3 == parent2, parent1], code.get_ancestors(tmpl)
  112. assert parent1 is code.get_parent(tmpl)
  113. assert [] == code.get_ancestors(parent3)
  114. assert None is code.get_parent(parent3)
  115. with pytest.raises(ValueError):
  116. code.get_ancestors(fake)
  117. with pytest.raises(ValueError):
  118. code.get_parent(fake)
  119. def test_insert(self):
  120. """test Wikicode.insert()"""
  121. code = parse("Have a {{template}} and a [[page|link]]")
  122. code.insert(1, "{{{argument}}}")
  123. assert "Have a {{{argument}}}{{template}} and a [[page|link]]" == code
  124. assert isinstance(code.get(1), Argument)
  125. code.insert(2, None)
  126. assert "Have a {{{argument}}}{{template}} and a [[page|link]]" == code
  127. code.insert(-3, Text("foo"))
  128. assert "Have a {{{argument}}}foo{{template}} and a [[page|link]]" == code
  129. code2 = parse("{{foo}}{{bar}}{{baz}}")
  130. code2.insert(1, "abc{{def}}ghi[[jk]]")
  131. assert "{{foo}}abc{{def}}ghi[[jk]]{{bar}}{{baz}}" == code2
  132. assert ["{{foo}}", "abc", "{{def}}", "ghi", "[[jk]]",
  133. "{{bar}}", "{{baz}}"] == code2.nodes
  134. code3 = parse("{{foo}}bar")
  135. code3.insert(1000, "[[baz]]")
  136. code3.insert(-1000, "derp")
  137. assert "derp{{foo}}bar[[baz]]" == code3
  138. def _test_search(self, meth, expected):
  139. """Base test for insert_before(), insert_after(), and replace()."""
  140. code = parse("{{a}}{{b}}{{c}}{{d}}{{e}}")
  141. func = partial(meth, code)
  142. func("{{b}}", "x", recursive=True)
  143. func("{{d}}", "[[y]]", recursive=False)
  144. func(code.get(2), "z")
  145. assert expected[0] == code
  146. with pytest.raises(ValueError):
  147. func("{{r}}", "n", recursive=True)
  148. with pytest.raises(ValueError):
  149. func("{{r}}", "n", recursive=False)
  150. fake = parse("{{a}}").get(0)
  151. with pytest.raises(ValueError):
  152. func(fake, "n", recursive=True)
  153. with pytest.raises(ValueError):
  154. func(fake, "n", recursive=False)
  155. code2 = parse("{{a}}{{a}}{{a}}{{b}}{{b}}{{b}}")
  156. func = partial(meth, code2)
  157. func(code2.get(1), "c", recursive=False)
  158. func("{{a}}", "d", recursive=False)
  159. func(code2.get(-1), "e", recursive=True)
  160. func("{{b}}", "f", recursive=True)
  161. assert expected[1] == code2
  162. code3 = parse("{{a|{{b}}|{{c|d={{f}}}}}}")
  163. func = partial(meth, code3)
  164. obj = code3.get(0).params[0].value.get(0)
  165. with pytest.raises(ValueError):
  166. func(obj, "x", recursive=False)
  167. func(obj, "x", recursive=True)
  168. with pytest.raises(ValueError):
  169. func("{{f}}", "y", recursive=False)
  170. func("{{f}}", "y", recursive=True)
  171. assert expected[2] == code3
  172. code4 = parse("{{a}}{{b}}{{c}}{{d}}{{e}}{{f}}{{g}}{{h}}{{i}}{{j}}")
  173. func = partial(meth, code4)
  174. fake = parse("{{b}}{{c}}")
  175. with pytest.raises(ValueError):
  176. func(fake, "q", recursive=False)
  177. with pytest.raises(ValueError):
  178. func(fake, "q", recursive=True)
  179. func("{{b}}{{c}}", "w", recursive=False)
  180. func("{{d}}{{e}}", "x", recursive=True)
  181. func(Wikicode(code4.nodes[-2:]), "y", recursive=False)
  182. func(Wikicode(code4.nodes[-2:]), "z", recursive=True)
  183. assert expected[3] == code4
  184. with pytest.raises(ValueError):
  185. func("{{c}}{{d}}", "q", recursive=False)
  186. with pytest.raises(ValueError):
  187. func("{{c}}{{d}}", "q", recursive=True)
  188. code5 = parse("{{a|{{b}}{{c}}|{{f|{{g}}={{h}}{{i}}}}}}")
  189. func = partial(meth, code5)
  190. with pytest.raises(ValueError):
  191. func("{{b}}{{c}}", "x", recursive=False)
  192. func("{{b}}{{c}}", "x", recursive=True)
  193. obj = code5.get(0).params[1].value.get(0).params[0].value
  194. with pytest.raises(ValueError):
  195. func(obj, "y", recursive=False)
  196. func(obj, "y", recursive=True)
  197. assert expected[4] == code5
  198. code6 = parse("here is {{some text and a {{template}}}}")
  199. func = partial(meth, code6)
  200. with pytest.raises(ValueError):
  201. func("text and", "ab", recursive=False)
  202. func("text and", "ab", recursive=True)
  203. with pytest.raises(ValueError):
  204. func("is {{some", "cd", recursive=False)
  205. func("is {{some", "cd", recursive=True)
  206. assert expected[5] == code6
  207. code7 = parse("{{foo}}{{bar}}{{baz}}{{foo}}{{baz}}")
  208. func = partial(meth, code7)
  209. obj = wrap([code7.get(0), code7.get(2)])
  210. with pytest.raises(ValueError):
  211. func(obj, "{{lol}}")
  212. func("{{foo}}{{baz}}", "{{lol}}")
  213. assert expected[6] == code7
  214. code8 = parse("== header ==")
  215. func = partial(meth, code8)
  216. sec1, sec2 = code8.get_sections(include_headings=False)
  217. func(sec1, "lead\n")
  218. func(sec2, "\nbody")
  219. assert expected[7] == code8
  220. code9 = parse("{{foo}}")
  221. meth(code9.get_sections()[0], code9.get_sections()[0], "{{bar}}")
  222. meth(code9.get_sections()[0], code9, "{{baz}}")
  223. meth(code9, code9, "{{qux}}")
  224. meth(code9, code9.get_sections()[0], "{{quz}}")
  225. assert expected[8] == code9
  226. def test_insert_before(self):
  227. """test Wikicode.insert_before()"""
  228. meth = lambda code, *args, **kw: code.insert_before(*args, **kw)
  229. expected = [
  230. "{{a}}xz{{b}}{{c}}[[y]]{{d}}{{e}}",
  231. "d{{a}}cd{{a}}d{{a}}f{{b}}f{{b}}ef{{b}}",
  232. "{{a|x{{b}}|{{c|d=y{{f}}}}}}",
  233. "{{a}}w{{b}}{{c}}x{{d}}{{e}}{{f}}{{g}}{{h}}yz{{i}}{{j}}",
  234. "{{a|x{{b}}{{c}}|{{f|{{g}}=y{{h}}{{i}}}}}}",
  235. "here cdis {{some abtext and a {{template}}}}",
  236. "{{foo}}{{bar}}{{baz}}{{lol}}{{foo}}{{baz}}",
  237. "lead\n== header ==\nbody",
  238. "{{quz}}{{qux}}{{baz}}{{bar}}{{foo}}",
  239. ]
  240. self._test_search(meth, expected)
  241. def test_insert_after(self):
  242. """test Wikicode.insert_after()"""
  243. meth = lambda code, *args, **kw: code.insert_after(*args, **kw)
  244. expected = [
  245. "{{a}}{{b}}xz{{c}}{{d}}[[y]]{{e}}",
  246. "{{a}}d{{a}}dc{{a}}d{{b}}f{{b}}f{{b}}fe",
  247. "{{a|{{b}}x|{{c|d={{f}}y}}}}",
  248. "{{a}}{{b}}{{c}}w{{d}}{{e}}x{{f}}{{g}}{{h}}{{i}}{{j}}yz",
  249. "{{a|{{b}}{{c}}x|{{f|{{g}}={{h}}{{i}}y}}}}",
  250. "here is {{somecd text andab a {{template}}}}",
  251. "{{foo}}{{bar}}{{baz}}{{foo}}{{baz}}{{lol}}",
  252. "lead\n== header ==\nbody",
  253. "{{foo}}{{bar}}{{baz}}{{qux}}{{quz}}",
  254. ]
  255. self._test_search(meth, expected)
  256. def test_replace(self):
  257. """test Wikicode.replace()"""
  258. meth = lambda code, *args, **kw: code.replace(*args, **kw)
  259. expected = [
  260. "{{a}}xz[[y]]{{e}}",
  261. "dcdffe",
  262. "{{a|x|{{c|d=y}}}}",
  263. "{{a}}wx{{f}}{{g}}z",
  264. "{{a|x|{{f|{{g}}=y}}}}",
  265. "here cd ab a {{template}}}}",
  266. "{{foo}}{{bar}}{{baz}}{{lol}}",
  267. "lead\n== header ==\nbody",
  268. "{{quz}}",
  269. ]
  270. self._test_search(meth, expected)
  271. def test_append(self):
  272. """test Wikicode.append()"""
  273. code = parse("Have a {{template}}")
  274. code.append("{{{argument}}}")
  275. assert "Have a {{template}}{{{argument}}}" == code
  276. assert isinstance(code.get(2), Argument)
  277. code.append(None)
  278. assert "Have a {{template}}{{{argument}}}" == code
  279. code.append(Text(" foo"))
  280. assert "Have a {{template}}{{{argument}}} foo" == code
  281. with pytest.raises(ValueError):
  282. code.append(slice(0, 1))
  283. def test_remove(self):
  284. """test Wikicode.remove()"""
  285. meth = lambda code, obj, value, **kw: code.remove(obj, **kw)
  286. expected = [
  287. "{{a}}{{c}}",
  288. "",
  289. "{{a||{{c|d=}}}}",
  290. "{{a}}{{f}}",
  291. "{{a||{{f|{{g}}=}}}}",
  292. "here a {{template}}}}",
  293. "{{foo}}{{bar}}{{baz}}",
  294. "== header ==",
  295. "",
  296. ]
  297. self._test_search(meth, expected)
  298. def test_matches(self):
  299. """test Wikicode.matches()"""
  300. code1 = parse("Cleanup")
  301. code2 = parse("\nstub<!-- TODO: make more specific -->")
  302. code3 = parse("Hello world!")
  303. code4 = parse("World,_hello?")
  304. code5 = parse("")
  305. assert code1.matches("Cleanup") is True
  306. assert code1.matches("cleanup") is True
  307. assert code1.matches(" cleanup\n") is True
  308. assert code1.matches("CLEANup") is False
  309. assert code1.matches("Blah") is False
  310. assert code2.matches("stub") is True
  311. assert code2.matches("Stub<!-- no, it's fine! -->") is True
  312. assert code2.matches("StuB") is False
  313. assert code1.matches(("cleanup", "stub")) is True
  314. assert code2.matches(("cleanup", "stub")) is True
  315. assert code2.matches(("StuB", "sTUb", "foobar")) is False
  316. assert code2.matches(["StuB", "sTUb", "foobar"]) is False
  317. assert code2.matches(("StuB", "sTUb", "foo", "bar", "Stub")) is True
  318. assert code2.matches(["StuB", "sTUb", "foo", "bar", "Stub"]) is True
  319. assert code3.matches("hello world!") is True
  320. assert code3.matches("hello_world!") is True
  321. assert code3.matches("hello__world!") is False
  322. assert code4.matches("World,_hello?") is True
  323. assert code4.matches("World, hello?") is True
  324. assert code4.matches("World, hello?") is False
  325. assert code5.matches("") is True
  326. assert code5.matches("<!-- nothing -->") is True
  327. assert code5.matches(("a", "b", "")) is True
  328. def test_filter_family(self):
  329. """test the Wikicode.i?filter() family of functions"""
  330. def genlist(gen):
  331. assert isinstance(gen, GeneratorType)
  332. return list(gen)
  333. ifilter = lambda code: (lambda *a, **k: genlist(code.ifilter(*a, **k)))
  334. code = parse("a{{b}}c[[d]]{{{e}}}{{f}}[[g]]")
  335. for func in (code.filter, ifilter(code)):
  336. assert ["a", "{{b}}", "b", "c", "[[d]]", "d", "{{{e}}}",
  337. "e", "{{f}}", "f", "[[g]]", "g"] == func()
  338. assert ["{{{e}}}"] == func(forcetype=Argument)
  339. assert code.get(4) is func(forcetype=Argument)[0]
  340. assert list("abcdefg") == func(forcetype=Text)
  341. assert [] == func(forcetype=Heading)
  342. with pytest.raises(TypeError):
  343. func(forcetype=True)
  344. funcs = [
  345. lambda name, **kw: getattr(code, "filter_" + name)(**kw),
  346. lambda name, **kw: genlist(getattr(code, "ifilter_" + name)(**kw))
  347. ]
  348. for get_filter in funcs:
  349. assert ["{{{e}}}"] == get_filter("arguments")
  350. assert code.get(4) is get_filter("arguments")[0]
  351. assert [] == get_filter("comments")
  352. assert [] == get_filter("external_links")
  353. assert [] == get_filter("headings")
  354. assert [] == get_filter("html_entities")
  355. assert [] == get_filter("tags")
  356. assert ["{{b}}" == "{{f}}"], get_filter("templates")
  357. assert list("abcdefg") == get_filter("text")
  358. assert ["[[d]]" == "[[g]]"], get_filter("wikilinks")
  359. code2 = parse("{{a|{{b}}|{{c|d={{f}}{{h}}}}}}")
  360. for func in (code2.filter, ifilter(code2)):
  361. assert ["{{a|{{b}}|{{c|d={{f}}{{h}}}}}}"] \
  362. == func(recursive=False, forcetype=Template)
  363. assert ["{{a|{{b}}|{{c|d={{f}}{{h}}}}}}", "{{b}}",
  364. "{{c|d={{f}}{{h}}}}", "{{f}}", "{{h}}"] \
  365. == func(recursive=True, forcetype=Template)
  366. code3 = parse("{{foobar}}{{FOO}}{{baz}}{{bz}}{{barfoo}}")
  367. for func in (code3.filter, ifilter(code3)):
  368. assert ["{{foobar}}", "{{barfoo}}"] \
  369. == func(False, matches=lambda node: "foo" in node)
  370. assert ["{{foobar}}", "{{FOO}}", "{{barfoo}}"] \
  371. == func(False, matches=r"foo")
  372. assert ["{{foobar}}", "{{FOO}}"] \
  373. == func(matches=r"^{{foo.*?}}")
  374. assert ["{{foobar}}"] \
  375. == func(matches=r"^{{foo.*?}}", flags=re.UNICODE)
  376. assert ["{{baz}}" == "{{bz}}"], func(matches=r"^{{b.*?z")
  377. assert ["{{baz}}"] == func(matches=r"^{{b.+?z}}")
  378. exp_rec = ["{{a|{{b}}|{{c|d={{f}}{{h}}}}}}", "{{b}}",
  379. "{{c|d={{f}}{{h}}}}", "{{f}}", "{{h}}"]
  380. exp_unrec = ["{{a|{{b}}|{{c|d={{f}}{{h}}}}}}"]
  381. assert exp_rec == code2.filter_templates()
  382. assert exp_unrec == code2.filter_templates(recursive=False)
  383. assert exp_rec == code2.filter_templates(recursive=True)
  384. assert exp_rec == code2.filter_templates(True)
  385. assert exp_unrec == code2.filter_templates(False)
  386. assert ["{{foobar}}"] == code3.filter_templates(
  387. matches=lambda node: node.name.matches("Foobar"))
  388. assert ["{{baz}}", "{{bz}}"] \
  389. == code3.filter_templates(matches=r"^{{b.*?z")
  390. assert [] == code3.filter_tags(matches=r"^{{b.*?z")
  391. assert [] == code3.filter_tags(matches=r"^{{b.*?z", flags=0)
  392. with pytest.raises(TypeError):
  393. code.filter_templates(a=42)
  394. with pytest.raises(TypeError):
  395. code.filter_templates(forcetype=Template)
  396. with pytest.raises(TypeError):
  397. code.filter_templates(1, 0, 0, Template)
  398. code4 = parse("{{foo}}<b>{{foo|{{bar}}}}</b>")
  399. actual1 = code4.filter_templates(recursive=code4.RECURSE_OTHERS)
  400. actual2 = code4.filter_templates(code4.RECURSE_OTHERS)
  401. assert ["{{foo}}" == "{{foo|{{bar}}}}"], actual1
  402. assert ["{{foo}}" == "{{foo|{{bar}}}}"], actual2
  403. def test_get_sections(self):
  404. """test Wikicode.get_sections()"""
  405. page1 = parse("")
  406. page2 = parse("==Heading==")
  407. page3 = parse("===Heading===\nFoo bar baz\n====Gnidaeh====\n")
  408. p4_lead = "This is a lead.\n"
  409. p4_IA = "=== Section I.A ===\nSection I.A [[body]].\n"
  410. p4_IB1 = "==== Section I.B.1 ====\nSection I.B.1 body.\n\n&bull;Some content.\n\n"
  411. p4_IB = "=== Section I.B ===\n" + p4_IB1
  412. p4_I = "== Section I ==\nSection I body. {{and a|template}}\n" + p4_IA + p4_IB
  413. p4_II = "== Section II ==\nSection II body.\n\n"
  414. p4_IIIA1a = "===== Section III.A.1.a =====\nMore text.\n"
  415. p4_IIIA2ai1 = "======= Section III.A.2.a.i.1 =======\nAn invalid section!"
  416. p4_IIIA2 = "==== Section III.A.2 ====\nEven more text.\n" + p4_IIIA2ai1
  417. p4_IIIA = "=== Section III.A ===\nText.\n" + p4_IIIA1a + p4_IIIA2
  418. p4_III = "== Section III ==\n" + p4_IIIA
  419. page4 = parse(p4_lead + p4_I + p4_II + p4_III)
  420. assert [""] == page1.get_sections()
  421. assert ["" == "==Heading=="], page2.get_sections()
  422. assert ["", "===Heading===\nFoo bar baz\n====Gnidaeh====\n", "====Gnidaeh====\n"] \
  423. == page3.get_sections()
  424. assert [p4_lead, p4_I, p4_IA, p4_IB, p4_IB1, p4_II,
  425. p4_III, p4_IIIA, p4_IIIA1a, p4_IIIA2, p4_IIIA2ai1] \
  426. == page4.get_sections()
  427. assert ["====Gnidaeh====\n"] == page3.get_sections(levels=[4])
  428. assert ["===Heading===\nFoo bar baz\n====Gnidaeh====\n"] \
  429. == page3.get_sections(levels=(2, 3))
  430. assert ["===Heading===\nFoo bar baz\n"] \
  431. == page3.get_sections(levels=(2, 3), flat=True)
  432. assert [] == page3.get_sections(levels=[0])
  433. assert ["", "====Gnidaeh====\n"] == page3.get_sections(levels=[4], include_lead=True)
  434. assert ["===Heading===\nFoo bar baz\n====Gnidaeh====\n",
  435. "====Gnidaeh====\n"] == page3.get_sections(include_lead=False)
  436. assert ["===Heading===\nFoo bar baz\n", "====Gnidaeh====\n"] \
  437. == page3.get_sections(flat=True, include_lead=False)
  438. assert [p4_IB1 == p4_IIIA2], page4.get_sections(levels=[4])
  439. assert [p4_IA == p4_IB, p4_IIIA], page4.get_sections(levels=[3])
  440. assert [p4_IA, "=== Section I.B ===\n",
  441. "=== Section III.A ===\nText.\n"] \
  442. == page4.get_sections(levels=[3], flat=True)
  443. assert ["" == ""], page2.get_sections(include_headings=False)
  444. assert ["\nSection I.B.1 body.\n\n&bull;Some content.\n\n",
  445. "\nEven more text.\n" + p4_IIIA2ai1] \
  446. == page4.get_sections(levels=[4], include_headings=False)
  447. assert [] == page4.get_sections(matches=r"body")
  448. assert [p4_I, p4_IA, p4_IB, p4_IB1] \
  449. == page4.get_sections(matches=r"Section\sI[.\s].*?")
  450. assert [p4_IA, p4_IIIA, p4_IIIA1a, p4_IIIA2, p4_IIIA2ai1] \
  451. == page4.get_sections(matches=r".*?a.*?")
  452. assert [p4_IIIA1a, p4_IIIA2ai1] \
  453. == page4.get_sections(matches=r".*?a.*?", flags=re.U)
  454. assert ["\nMore text.\n", "\nAn invalid section!"] \
  455. == page4.get_sections(matches=r".*?a.*?", flags=re.U,
  456. include_headings=False)
  457. sections = page2.get_sections(include_headings=False)
  458. sections[0].append("Lead!\n")
  459. sections[1].append("\nFirst section!")
  460. assert "Lead!\n==Heading==\nFirst section!" == page2
  461. page5 = parse("X\n== Foo ==\nBar\n== Baz ==\nBuzz")
  462. section = page5.get_sections(matches="Foo")[0]
  463. section.replace("\nBar\n", "\nBarf ")
  464. section.append("{{Haha}}\n")
  465. assert "== Foo ==\nBarf {{Haha}}\n" == section
  466. assert "X\n== Foo ==\nBarf {{Haha}}\n== Baz ==\nBuzz" == page5
  467. def test_strip_code(self):
  468. """test Wikicode.strip_code()"""
  469. # Since individual nodes have test cases for their __strip__ methods,
  470. # we're only going to do an integration test:
  471. code = parse("Foo [[bar]]\n\n{{baz|hello}}\n\n[[a|b]] &Sigma;")
  472. assert "Foo bar\n\nb Σ" \
  473. == code.strip_code(normalize=True, collapse=True)
  474. assert "Foo bar\n\n\n\nb Σ" \
  475. == code.strip_code(normalize=True, collapse=False)
  476. assert "Foo bar\n\nb &Sigma;" \
  477. == code.strip_code(normalize=False, collapse=True)
  478. assert "Foo bar\n\n\n\nb &Sigma;" \
  479. == code.strip_code(normalize=False, collapse=False)
  480. assert "Foo bar\n\nhello\n\nb Σ" \
  481. == code.strip_code(normalize=True, collapse=True,
  482. keep_template_params=True)
  483. def test_get_tree(self):
  484. """test Wikicode.get_tree()"""
  485. # Since individual nodes have test cases for their __showtree___
  486. # methods, and the docstring covers all possibilities for the output of
  487. # __showtree__, we'll test it only:
  488. code = parse("Lorem ipsum {{foo|bar|{{baz}}|spam=eggs}}")
  489. expected = "Lorem ipsum \n{{\n\t foo\n\t| 1\n\t= bar\n\t| 2\n\t= " + \
  490. "{{\n\t\t\tbaz\n\t }}\n\t| spam\n\t= eggs\n}}"
  491. assert expected.expandtabs(4) == code.get_tree()