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.
 
 
 
 

838 lines
27 KiB

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