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.
 
 
 
 

841 regels
28 KiB

  1. # Copyright (C) 2012-2025 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. Tests for the builder, which turns tokens into Wikicode objects.
  22. """
  23. from __future__ import annotations
  24. import pytest
  25. from mwparserfromhell.nodes import (
  26. Argument,
  27. Comment,
  28. ExternalLink,
  29. Heading,
  30. HTMLEntity,
  31. Tag,
  32. Template,
  33. Text,
  34. Wikilink,
  35. )
  36. from mwparserfromhell.nodes.extras import Attribute, Parameter
  37. from mwparserfromhell.parser import ParserError, tokens
  38. from mwparserfromhell.parser.builder import Builder
  39. from .conftest import assert_wikicode_equal, wrap, wraptext
  40. @pytest.fixture()
  41. def builder():
  42. return Builder()
  43. @pytest.mark.parametrize(
  44. "test,valid",
  45. [
  46. ([tokens.Text(text="foobar")], wraptext("foobar")),
  47. ([tokens.Text(text="fóóbar")], wraptext("fóóbar")),
  48. (
  49. [tokens.Text(text="spam"), tokens.Text(text="eggs")],
  50. wraptext("spam", "eggs"),
  51. ),
  52. ],
  53. )
  54. def test_text(builder, test, valid):
  55. """tests for building Text nodes"""
  56. assert_wikicode_equal(valid, builder.build(test))
  57. @pytest.mark.parametrize(
  58. "test,valid",
  59. [
  60. (
  61. [tokens.TemplateOpen(), tokens.Text(text="foobar"), tokens.TemplateClose()],
  62. wrap([Template(wraptext("foobar"))]),
  63. ),
  64. (
  65. [
  66. tokens.TemplateOpen(),
  67. tokens.Text(text="spam"),
  68. tokens.Text(text="eggs"),
  69. tokens.TemplateClose(),
  70. ],
  71. wrap([Template(wraptext("spam", "eggs"))]),
  72. ),
  73. (
  74. [
  75. tokens.TemplateOpen(),
  76. tokens.Text(text="foo"),
  77. tokens.TemplateParamSeparator(),
  78. tokens.Text(text="bar"),
  79. tokens.TemplateClose(),
  80. ],
  81. wrap(
  82. [
  83. Template(
  84. wraptext("foo"),
  85. params=[
  86. Parameter(wraptext("1"), wraptext("bar"), showkey=False)
  87. ],
  88. )
  89. ]
  90. ),
  91. ),
  92. (
  93. [
  94. tokens.TemplateOpen(),
  95. tokens.Text(text="foo"),
  96. tokens.TemplateParamSeparator(),
  97. tokens.Text(text="bar"),
  98. tokens.TemplateParamEquals(),
  99. tokens.Text(text="baz"),
  100. tokens.TemplateClose(),
  101. ],
  102. wrap(
  103. [
  104. Template(
  105. wraptext("foo"),
  106. params=[Parameter(wraptext("bar"), wraptext("baz"))],
  107. )
  108. ]
  109. ),
  110. ),
  111. (
  112. [
  113. tokens.TemplateOpen(),
  114. tokens.TemplateParamSeparator(),
  115. tokens.TemplateParamSeparator(),
  116. tokens.TemplateParamEquals(),
  117. tokens.TemplateParamSeparator(),
  118. tokens.TemplateClose(),
  119. ],
  120. wrap(
  121. [
  122. Template(
  123. wrap([]),
  124. params=[
  125. Parameter(wraptext("1"), wrap([]), showkey=False),
  126. Parameter(wrap([]), wrap([]), showkey=True),
  127. Parameter(wraptext("2"), wrap([]), showkey=False),
  128. ],
  129. )
  130. ]
  131. ),
  132. ),
  133. (
  134. [
  135. tokens.TemplateOpen(),
  136. tokens.Text(text="foo"),
  137. tokens.TemplateParamSeparator(),
  138. tokens.Text(text="bar"),
  139. tokens.TemplateParamEquals(),
  140. tokens.Text(text="baz"),
  141. tokens.TemplateParamSeparator(),
  142. tokens.Text(text="biz"),
  143. tokens.TemplateParamSeparator(),
  144. tokens.Text(text="buzz"),
  145. tokens.TemplateParamSeparator(),
  146. tokens.Text(text="3"),
  147. tokens.TemplateParamEquals(),
  148. tokens.Text(text="buff"),
  149. tokens.TemplateParamSeparator(),
  150. tokens.Text(text="baff"),
  151. tokens.TemplateClose(),
  152. ],
  153. wrap(
  154. [
  155. Template(
  156. wraptext("foo"),
  157. params=[
  158. Parameter(wraptext("bar"), wraptext("baz")),
  159. Parameter(wraptext("1"), wraptext("biz"), showkey=False),
  160. Parameter(wraptext("2"), wraptext("buzz"), showkey=False),
  161. Parameter(wraptext("3"), wraptext("buff")),
  162. Parameter(wraptext("3"), wraptext("baff"), showkey=False),
  163. ],
  164. )
  165. ]
  166. ),
  167. ),
  168. ],
  169. )
  170. def test_template(builder, test, valid):
  171. """tests for building Template nodes"""
  172. assert_wikicode_equal(valid, builder.build(test))
  173. @pytest.mark.parametrize(
  174. "test,valid",
  175. [
  176. (
  177. [tokens.ArgumentOpen(), tokens.Text(text="foobar"), tokens.ArgumentClose()],
  178. wrap([Argument(wraptext("foobar"))]),
  179. ),
  180. (
  181. [
  182. tokens.ArgumentOpen(),
  183. tokens.Text(text="spam"),
  184. tokens.Text(text="eggs"),
  185. tokens.ArgumentClose(),
  186. ],
  187. wrap([Argument(wraptext("spam", "eggs"))]),
  188. ),
  189. (
  190. [
  191. tokens.ArgumentOpen(),
  192. tokens.Text(text="foo"),
  193. tokens.ArgumentSeparator(),
  194. tokens.Text(text="bar"),
  195. tokens.ArgumentClose(),
  196. ],
  197. wrap([Argument(wraptext("foo"), wraptext("bar"))]),
  198. ),
  199. (
  200. [
  201. tokens.ArgumentOpen(),
  202. tokens.Text(text="foo"),
  203. tokens.Text(text="bar"),
  204. tokens.ArgumentSeparator(),
  205. tokens.Text(text="baz"),
  206. tokens.Text(text="biz"),
  207. tokens.ArgumentClose(),
  208. ],
  209. wrap([Argument(wraptext("foo", "bar"), wraptext("baz", "biz"))]),
  210. ),
  211. ],
  212. )
  213. def test_argument(builder, test, valid):
  214. """tests for building Argument nodes"""
  215. assert_wikicode_equal(valid, builder.build(test))
  216. @pytest.mark.parametrize(
  217. "test,valid",
  218. [
  219. (
  220. [tokens.WikilinkOpen(), tokens.Text(text="foobar"), tokens.WikilinkClose()],
  221. wrap([Wikilink(wraptext("foobar"))]),
  222. ),
  223. (
  224. [
  225. tokens.WikilinkOpen(),
  226. tokens.Text(text="spam"),
  227. tokens.Text(text="eggs"),
  228. tokens.WikilinkClose(),
  229. ],
  230. wrap([Wikilink(wraptext("spam", "eggs"))]),
  231. ),
  232. (
  233. [
  234. tokens.WikilinkOpen(),
  235. tokens.Text(text="foo"),
  236. tokens.WikilinkSeparator(),
  237. tokens.Text(text="bar"),
  238. tokens.WikilinkClose(),
  239. ],
  240. wrap([Wikilink(wraptext("foo"), wraptext("bar"))]),
  241. ),
  242. (
  243. [
  244. tokens.WikilinkOpen(),
  245. tokens.Text(text="foo"),
  246. tokens.Text(text="bar"),
  247. tokens.WikilinkSeparator(),
  248. tokens.Text(text="baz"),
  249. tokens.Text(text="biz"),
  250. tokens.WikilinkClose(),
  251. ],
  252. wrap([Wikilink(wraptext("foo", "bar"), wraptext("baz", "biz"))]),
  253. ),
  254. ],
  255. )
  256. def test_wikilink(builder, test, valid):
  257. """tests for building Wikilink nodes"""
  258. assert_wikicode_equal(valid, builder.build(test))
  259. @pytest.mark.parametrize(
  260. "test,valid",
  261. [
  262. (
  263. [
  264. tokens.ExternalLinkOpen(brackets=False),
  265. tokens.Text(text="http://example.com/"),
  266. tokens.ExternalLinkClose(),
  267. ],
  268. wrap([ExternalLink(wraptext("http://example.com/"), brackets=False)]),
  269. ),
  270. (
  271. [
  272. tokens.ExternalLinkOpen(brackets=True),
  273. tokens.Text(text="http://example.com/"),
  274. tokens.ExternalLinkClose(),
  275. ],
  276. wrap([ExternalLink(wraptext("http://example.com/"))]),
  277. ),
  278. (
  279. [
  280. tokens.ExternalLinkOpen(brackets=True),
  281. tokens.Text(text="http://example.com/"),
  282. tokens.ExternalLinkSeparator(),
  283. tokens.ExternalLinkClose(),
  284. ],
  285. wrap([ExternalLink(wraptext("http://example.com/"), wrap([]))]),
  286. ),
  287. (
  288. [
  289. tokens.ExternalLinkOpen(brackets=True),
  290. tokens.Text(text="http://example.com/"),
  291. tokens.ExternalLinkSeparator(),
  292. tokens.Text(text="Example"),
  293. tokens.ExternalLinkClose(),
  294. ],
  295. wrap([ExternalLink(wraptext("http://example.com/"), wraptext("Example"))]),
  296. ),
  297. (
  298. [
  299. tokens.ExternalLinkOpen(brackets=False),
  300. tokens.Text(text="http://example"),
  301. tokens.Text(text=".com/foo"),
  302. tokens.ExternalLinkClose(),
  303. ],
  304. wrap(
  305. [ExternalLink(wraptext("http://example", ".com/foo"), brackets=False)]
  306. ),
  307. ),
  308. (
  309. [
  310. tokens.ExternalLinkOpen(brackets=True),
  311. tokens.Text(text="http://example"),
  312. tokens.Text(text=".com/foo"),
  313. tokens.ExternalLinkSeparator(),
  314. tokens.Text(text="Example"),
  315. tokens.Text(text=" Web Page"),
  316. tokens.ExternalLinkClose(),
  317. ],
  318. wrap(
  319. [
  320. ExternalLink(
  321. wraptext("http://example", ".com/foo"),
  322. wraptext("Example", " Web Page"),
  323. )
  324. ]
  325. ),
  326. ),
  327. ],
  328. )
  329. def test_external_link(builder, test, valid):
  330. """tests for building ExternalLink nodes"""
  331. assert_wikicode_equal(valid, builder.build(test))
  332. @pytest.mark.parametrize(
  333. "test,valid",
  334. [
  335. (
  336. [
  337. tokens.HTMLEntityStart(),
  338. tokens.Text(text="nbsp"),
  339. tokens.HTMLEntityEnd(),
  340. ],
  341. wrap([HTMLEntity("nbsp", named=True, hexadecimal=False)]),
  342. ),
  343. (
  344. [
  345. tokens.HTMLEntityStart(),
  346. tokens.HTMLEntityNumeric(),
  347. tokens.Text(text="107"),
  348. tokens.HTMLEntityEnd(),
  349. ],
  350. wrap([HTMLEntity("107", named=False, hexadecimal=False)]),
  351. ),
  352. (
  353. [
  354. tokens.HTMLEntityStart(),
  355. tokens.HTMLEntityNumeric(),
  356. tokens.HTMLEntityHex(char="X"),
  357. tokens.Text(text="6B"),
  358. tokens.HTMLEntityEnd(),
  359. ],
  360. wrap([HTMLEntity("6B", named=False, hexadecimal=True, hex_char="X")]),
  361. ),
  362. ],
  363. )
  364. def test_html_entity(builder, test, valid):
  365. """tests for building HTMLEntity nodes"""
  366. assert_wikicode_equal(valid, builder.build(test))
  367. @pytest.mark.parametrize(
  368. "test,valid",
  369. [
  370. (
  371. [
  372. tokens.HeadingStart(level=2),
  373. tokens.Text(text="foobar"),
  374. tokens.HeadingEnd(),
  375. ],
  376. wrap([Heading(wraptext("foobar"), 2)]),
  377. ),
  378. (
  379. [
  380. tokens.HeadingStart(level=4),
  381. tokens.Text(text="spam"),
  382. tokens.Text(text="eggs"),
  383. tokens.HeadingEnd(),
  384. ],
  385. wrap([Heading(wraptext("spam", "eggs"), 4)]),
  386. ),
  387. ],
  388. )
  389. def test_heading(builder, test, valid):
  390. """tests for building Heading nodes"""
  391. assert_wikicode_equal(valid, builder.build(test))
  392. @pytest.mark.parametrize(
  393. "test,valid",
  394. [
  395. (
  396. [tokens.CommentStart(), tokens.Text(text="foobar"), tokens.CommentEnd()],
  397. wrap([Comment("foobar")]),
  398. ),
  399. (
  400. [
  401. tokens.CommentStart(),
  402. tokens.Text(text="spam"),
  403. tokens.Text(text="eggs"),
  404. tokens.CommentEnd(),
  405. ],
  406. wrap([Comment("spameggs")]),
  407. ),
  408. ],
  409. )
  410. def test_comment(builder, test, valid):
  411. """tests for building Comment nodes"""
  412. assert_wikicode_equal(valid, builder.build(test))
  413. @pytest.mark.parametrize(
  414. "test,valid",
  415. [
  416. # <ref></ref>
  417. (
  418. [
  419. tokens.TagOpenOpen(),
  420. tokens.Text(text="ref"),
  421. tokens.TagCloseOpen(padding=""),
  422. tokens.TagOpenClose(),
  423. tokens.Text(text="ref"),
  424. tokens.TagCloseClose(),
  425. ],
  426. wrap([Tag(wraptext("ref"), wrap([]), closing_tag=wraptext("ref"))]),
  427. ),
  428. # <ref name></ref>
  429. (
  430. [
  431. tokens.TagOpenOpen(),
  432. tokens.Text(text="ref"),
  433. tokens.TagAttrStart(pad_first=" ", pad_before_eq="", pad_after_eq=""),
  434. tokens.Text(text="name"),
  435. tokens.TagCloseOpen(padding=""),
  436. tokens.TagOpenClose(),
  437. tokens.Text(text="ref"),
  438. tokens.TagCloseClose(),
  439. ],
  440. wrap([Tag(wraptext("ref"), wrap([]), attrs=[Attribute(wraptext("name"))])]),
  441. ),
  442. # <ref name="abc" />
  443. (
  444. [
  445. tokens.TagOpenOpen(),
  446. tokens.Text(text="ref"),
  447. tokens.TagAttrStart(pad_first=" ", pad_before_eq="", pad_after_eq=""),
  448. tokens.Text(text="name"),
  449. tokens.TagAttrEquals(),
  450. tokens.TagAttrQuote(char='"'),
  451. tokens.Text(text="abc"),
  452. tokens.TagCloseSelfclose(padding=" "),
  453. ],
  454. wrap(
  455. [
  456. Tag(
  457. wraptext("ref"),
  458. attrs=[Attribute(wraptext("name"), wraptext("abc"))],
  459. self_closing=True,
  460. padding=" ",
  461. )
  462. ]
  463. ),
  464. ),
  465. # <br/>
  466. (
  467. [
  468. tokens.TagOpenOpen(),
  469. tokens.Text(text="br"),
  470. tokens.TagCloseSelfclose(padding=""),
  471. ],
  472. wrap([Tag(wraptext("br"), self_closing=True)]),
  473. ),
  474. # <li>
  475. (
  476. [
  477. tokens.TagOpenOpen(),
  478. tokens.Text(text="li"),
  479. tokens.TagCloseSelfclose(padding="", implicit=True),
  480. ],
  481. wrap([Tag(wraptext("li"), self_closing=True, implicit=True)]),
  482. ),
  483. # </br>
  484. (
  485. [
  486. tokens.TagOpenOpen(invalid=True),
  487. tokens.Text(text="br"),
  488. tokens.TagCloseSelfclose(padding="", implicit=True),
  489. ],
  490. wrap([Tag(wraptext("br"), self_closing=True, invalid=True, implicit=True)]),
  491. ),
  492. # </br/>
  493. (
  494. [
  495. tokens.TagOpenOpen(invalid=True),
  496. tokens.Text(text="br"),
  497. tokens.TagCloseSelfclose(padding=""),
  498. ],
  499. wrap([Tag(wraptext("br"), self_closing=True, invalid=True)]),
  500. ),
  501. # <ref name={{abc}} foo="bar {{baz}}" abc={{de}}f ghi=j{{k}}{{l}}
  502. # mno = '{{p}} [[q]] {{r}}'>[[Source]]</ref>
  503. (
  504. [
  505. tokens.TagOpenOpen(),
  506. tokens.Text(text="ref"),
  507. tokens.TagAttrStart(pad_first=" ", pad_before_eq="", pad_after_eq=""),
  508. tokens.Text(text="name"),
  509. tokens.TagAttrEquals(),
  510. tokens.TemplateOpen(),
  511. tokens.Text(text="abc"),
  512. tokens.TemplateClose(),
  513. tokens.TagAttrStart(pad_first=" ", pad_before_eq="", pad_after_eq=""),
  514. tokens.Text(text="foo"),
  515. tokens.TagAttrEquals(),
  516. tokens.TagAttrQuote(char='"'),
  517. tokens.Text(text="bar "),
  518. tokens.TemplateOpen(),
  519. tokens.Text(text="baz"),
  520. tokens.TemplateClose(),
  521. tokens.TagAttrStart(pad_first=" ", pad_before_eq="", pad_after_eq=""),
  522. tokens.Text(text="abc"),
  523. tokens.TagAttrEquals(),
  524. tokens.TemplateOpen(),
  525. tokens.Text(text="de"),
  526. tokens.TemplateClose(),
  527. tokens.Text(text="f"),
  528. tokens.TagAttrStart(pad_first=" ", pad_before_eq="", pad_after_eq=""),
  529. tokens.Text(text="ghi"),
  530. tokens.TagAttrEquals(),
  531. tokens.Text(text="j"),
  532. tokens.TemplateOpen(),
  533. tokens.Text(text="k"),
  534. tokens.TemplateClose(),
  535. tokens.TemplateOpen(),
  536. tokens.Text(text="l"),
  537. tokens.TemplateClose(),
  538. tokens.TagAttrStart(
  539. pad_first=" \n ", pad_before_eq=" ", pad_after_eq=" "
  540. ),
  541. tokens.Text(text="mno"),
  542. tokens.TagAttrEquals(),
  543. tokens.TagAttrQuote(char="'"),
  544. tokens.TemplateOpen(),
  545. tokens.Text(text="p"),
  546. tokens.TemplateClose(),
  547. tokens.Text(text=" "),
  548. tokens.WikilinkOpen(),
  549. tokens.Text(text="q"),
  550. tokens.WikilinkClose(),
  551. tokens.Text(text=" "),
  552. tokens.TemplateOpen(),
  553. tokens.Text(text="r"),
  554. tokens.TemplateClose(),
  555. tokens.TagCloseOpen(padding=""),
  556. tokens.WikilinkOpen(),
  557. tokens.Text(text="Source"),
  558. tokens.WikilinkClose(),
  559. tokens.TagOpenClose(),
  560. tokens.Text(text="ref"),
  561. tokens.TagCloseClose(),
  562. ],
  563. wrap(
  564. [
  565. Tag(
  566. wraptext("ref"),
  567. wrap([Wikilink(wraptext("Source"))]),
  568. [
  569. Attribute(
  570. wraptext("name"),
  571. wrap([Template(wraptext("abc"))]),
  572. None,
  573. ),
  574. Attribute(
  575. wraptext("foo"),
  576. wrap([Text("bar "), Template(wraptext("baz"))]),
  577. pad_first=" ",
  578. ),
  579. Attribute(
  580. wraptext("abc"),
  581. wrap([Template(wraptext("de")), Text("f")]),
  582. None,
  583. ),
  584. Attribute(
  585. wraptext("ghi"),
  586. wrap(
  587. [
  588. Text("j"),
  589. Template(wraptext("k")),
  590. Template(wraptext("l")),
  591. ]
  592. ),
  593. None,
  594. ),
  595. Attribute(
  596. wraptext("mno"),
  597. wrap(
  598. [
  599. Template(wraptext("p")),
  600. Text(" "),
  601. Wikilink(wraptext("q")),
  602. Text(" "),
  603. Template(wraptext("r")),
  604. ]
  605. ),
  606. "'",
  607. " \n ",
  608. " ",
  609. " ",
  610. ),
  611. ],
  612. )
  613. ]
  614. ),
  615. ),
  616. # "''italic text''"
  617. (
  618. [
  619. tokens.TagOpenOpen(wiki_markup="''"),
  620. tokens.Text(text="i"),
  621. tokens.TagCloseOpen(),
  622. tokens.Text(text="italic text"),
  623. tokens.TagOpenClose(),
  624. tokens.Text(text="i"),
  625. tokens.TagCloseClose(),
  626. ],
  627. wrap([Tag(wraptext("i"), wraptext("italic text"), wiki_markup="''")]),
  628. ),
  629. # * bullet
  630. (
  631. [
  632. tokens.TagOpenOpen(wiki_markup="*"),
  633. tokens.Text(text="li"),
  634. tokens.TagCloseSelfclose(),
  635. tokens.Text(text=" bullet"),
  636. ],
  637. wrap(
  638. [
  639. Tag(wraptext("li"), wiki_markup="*", self_closing=True),
  640. Text(" bullet"),
  641. ]
  642. ),
  643. ),
  644. ],
  645. )
  646. def test_tag(builder, test, valid):
  647. """tests for building Tag nodes"""
  648. assert_wikicode_equal(valid, builder.build(test))
  649. def test_integration(builder):
  650. """a test for building a combination of templates together"""
  651. # {{{{{{{{foo}}bar|baz=biz}}buzz}}usr|{{bin}}}}
  652. test = [
  653. tokens.TemplateOpen(),
  654. tokens.TemplateOpen(),
  655. tokens.TemplateOpen(),
  656. tokens.TemplateOpen(),
  657. tokens.Text(text="foo"),
  658. tokens.TemplateClose(),
  659. tokens.Text(text="bar"),
  660. tokens.TemplateParamSeparator(),
  661. tokens.Text(text="baz"),
  662. tokens.TemplateParamEquals(),
  663. tokens.Text(text="biz"),
  664. tokens.TemplateClose(),
  665. tokens.Text(text="buzz"),
  666. tokens.TemplateClose(),
  667. tokens.Text(text="usr"),
  668. tokens.TemplateParamSeparator(),
  669. tokens.TemplateOpen(),
  670. tokens.Text(text="bin"),
  671. tokens.TemplateClose(),
  672. tokens.TemplateClose(),
  673. ]
  674. valid = wrap(
  675. [
  676. Template(
  677. wrap(
  678. [
  679. Template(
  680. wrap(
  681. [
  682. Template(
  683. wrap([Template(wraptext("foo")), Text("bar")]),
  684. params=[
  685. Parameter(wraptext("baz"), wraptext("biz"))
  686. ],
  687. ),
  688. Text("buzz"),
  689. ]
  690. )
  691. ),
  692. Text("usr"),
  693. ]
  694. ),
  695. params=[
  696. Parameter(
  697. wraptext("1"), wrap([Template(wraptext("bin"))]), showkey=False
  698. )
  699. ],
  700. )
  701. ]
  702. )
  703. assert_wikicode_equal(valid, builder.build(test))
  704. def test_integration2(builder):
  705. """an even more audacious test for building a horrible wikicode mess"""
  706. # {{a|b|{{c|[[d]]{{{e}}}}}}}[[f|{{{g}}}<!--h-->]]{{i|j=&nbsp;}}
  707. test = [
  708. tokens.TemplateOpen(),
  709. tokens.Text(text="a"),
  710. tokens.TemplateParamSeparator(),
  711. tokens.Text(text="b"),
  712. tokens.TemplateParamSeparator(),
  713. tokens.TemplateOpen(),
  714. tokens.Text(text="c"),
  715. tokens.TemplateParamSeparator(),
  716. tokens.WikilinkOpen(),
  717. tokens.Text(text="d"),
  718. tokens.WikilinkClose(),
  719. tokens.ArgumentOpen(),
  720. tokens.Text(text="e"),
  721. tokens.ArgumentClose(),
  722. tokens.TemplateClose(),
  723. tokens.TemplateClose(),
  724. tokens.WikilinkOpen(),
  725. tokens.Text(text="f"),
  726. tokens.WikilinkSeparator(),
  727. tokens.ArgumentOpen(),
  728. tokens.Text(text="g"),
  729. tokens.ArgumentClose(),
  730. tokens.CommentStart(),
  731. tokens.Text(text="h"),
  732. tokens.CommentEnd(),
  733. tokens.WikilinkClose(),
  734. tokens.TemplateOpen(),
  735. tokens.Text(text="i"),
  736. tokens.TemplateParamSeparator(),
  737. tokens.Text(text="j"),
  738. tokens.TemplateParamEquals(),
  739. tokens.HTMLEntityStart(),
  740. tokens.Text(text="nbsp"),
  741. tokens.HTMLEntityEnd(),
  742. tokens.TemplateClose(),
  743. ]
  744. valid = wrap(
  745. [
  746. Template(
  747. wraptext("a"),
  748. params=[
  749. Parameter(wraptext("1"), wraptext("b"), showkey=False),
  750. Parameter(
  751. wraptext("2"),
  752. wrap(
  753. [
  754. Template(
  755. wraptext("c"),
  756. params=[
  757. Parameter(
  758. wraptext("1"),
  759. wrap(
  760. [
  761. Wikilink(wraptext("d")),
  762. Argument(wraptext("e")),
  763. ]
  764. ),
  765. showkey=False,
  766. )
  767. ],
  768. )
  769. ]
  770. ),
  771. showkey=False,
  772. ),
  773. ],
  774. ),
  775. Wikilink(wraptext("f"), wrap([Argument(wraptext("g")), Comment("h")])),
  776. Template(
  777. wraptext("i"),
  778. params=[
  779. Parameter(wraptext("j"), wrap([HTMLEntity("nbsp", named=True)]))
  780. ],
  781. ),
  782. ]
  783. )
  784. assert_wikicode_equal(valid, builder.build(test))
  785. @pytest.mark.parametrize(
  786. "tokens",
  787. [
  788. [tokens.TemplateOpen(), tokens.TemplateParamSeparator()],
  789. [tokens.TemplateOpen()],
  790. [tokens.ArgumentOpen()],
  791. [tokens.WikilinkOpen()],
  792. [tokens.ExternalLinkOpen()],
  793. [tokens.HeadingStart()],
  794. [tokens.CommentStart()],
  795. [tokens.TagOpenOpen(), tokens.TagAttrStart()],
  796. [tokens.TagOpenOpen()],
  797. ],
  798. )
  799. def test_parser_errors(builder, tokens):
  800. """test whether ParserError gets thrown for bad input"""
  801. with pytest.raises(ParserError):
  802. builder.build(tokens)
  803. def test_parser_errors_templateclose(builder):
  804. with pytest.raises(
  805. ParserError, match=r"_handle_token\(\) got unexpected TemplateClose"
  806. ):
  807. builder.build([tokens.TemplateClose()])