A fork of hyde, the static site generation. Some patches will be pushed upstream.
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.
 
 
 

673 lines
16 KiB

  1. # -*- coding: utf-8 -*-
  2. """
  3. Use nose
  4. `$ pip install nose`
  5. `$ nosetests`
  6. Code borrowed from rwbench.py from the jinja2 examples
  7. """
  8. from datetime import datetime
  9. from hyde.ext.templates.jinja import Jinja2Template
  10. from hyde.fs import File, Folder
  11. from hyde.site import Site
  12. from hyde.generator import Generator
  13. from hyde.model import Config
  14. import jinja2
  15. from jinja2.utils import generate_lorem_ipsum
  16. from random import choice, randrange
  17. from util import assert_html_equals
  18. import yaml
  19. from pyquery import PyQuery
  20. from nose.tools import raises, nottest, with_setup
  21. ROOT = File(__file__).parent
  22. JINJA2 = ROOT.child_folder('templates/jinja2')
  23. class Article(object):
  24. def __init__(self, id):
  25. self.id = id
  26. self.href = '/article/%d' % self.id
  27. self.title = generate_lorem_ipsum(1, False, 5, 10)
  28. self.user = choice(users)
  29. self.body = generate_lorem_ipsum()
  30. self.pub_date = datetime.utcfromtimestamp(randrange(10 ** 9, 2 * 10 ** 9))
  31. self.published = True
  32. def dateformat(x):
  33. return x.strftime('%Y-%m-%d')
  34. class User(object):
  35. def __init__(self, username):
  36. self.href = '/user/%s' % username
  37. self.username = username
  38. users = map(User, [u'John Doe', u'Jane Doe', u'Peter Somewhat'])
  39. articles = map(Article, range(20))
  40. navigation = [
  41. ('index', 'Index'),
  42. ('about', 'About'),
  43. ('foo?bar=1', 'Foo with Bar'),
  44. ('foo?bar=2&s=x', 'Foo with X'),
  45. ('blah', 'Blub Blah'),
  46. ('hehe', 'Haha'),
  47. ] * 5
  48. context = dict(users=users, articles=articles, page_navigation=navigation)
  49. def test_render():
  50. """
  51. Uses pyquery to test the html structure for validity
  52. """
  53. t = Jinja2Template(JINJA2.path)
  54. t.configure(None)
  55. t.env.filters['dateformat'] = dateformat
  56. source = File(JINJA2.child('index.html')).read_all()
  57. html = t.render(source, context)
  58. actual = PyQuery(html)
  59. assert actual(".navigation li").length == 30
  60. assert actual("div.article").length == 20
  61. assert actual("div.article h2").length == 20
  62. assert actual("div.article h2 a").length == 20
  63. assert actual("div.article p.meta").length == 20
  64. assert actual("div.article div.text").length == 20
  65. def test_typogrify():
  66. source = """
  67. {%filter typogrify%}
  68. One & two
  69. {%endfilter%}
  70. """
  71. t = Jinja2Template(JINJA2.path)
  72. t.configure(None)
  73. t.env.filters['dateformat'] = dateformat
  74. html = t.render(source, {}).strip()
  75. assert html == u'One <span class="amp">&amp;</span>&nbsp;two'
  76. def test_spaceless():
  77. source = """
  78. {%spaceless%}
  79. <html>
  80. <body>
  81. <ul>
  82. <li>
  83. One
  84. </li>
  85. <li>
  86. Two
  87. </li>
  88. <li>
  89. Three
  90. </li>
  91. </ul>
  92. </body>
  93. </html>
  94. {%endspaceless%}
  95. """
  96. t = Jinja2Template(JINJA2.path)
  97. t.configure(None)
  98. t.env.filters['dateformat'] = dateformat
  99. html = t.render(source, {}).strip()
  100. expected = u"""
  101. <html><body><ul><li>
  102. One
  103. </li><li>
  104. Two
  105. </li><li>
  106. Three
  107. </li></ul></body></html>
  108. """
  109. assert html.strip() == expected.strip()
  110. def test_asciidoc():
  111. source = """
  112. {%asciidoc%}
  113. == Heading 2 ==
  114. * test1
  115. * test2
  116. * test3
  117. {%endasciidoc%}
  118. """
  119. t = Jinja2Template(JINJA2.path)
  120. t.configure(None)
  121. html = t.render(source, {}).strip()
  122. expected_output="""
  123. <hr>
  124. <h2><a name="_heading_2"></a>Heading 2</h2>
  125. <ul>
  126. <li>
  127. <p>
  128. test1
  129. </p>
  130. </li>
  131. <li>
  132. <p>
  133. test2
  134. </p>
  135. </li>
  136. <li>
  137. <p>
  138. test3
  139. </p>
  140. </li>
  141. </ul>
  142. """
  143. assert html == expected_output
  144. def test_markdown():
  145. source = """
  146. {%markdown%}
  147. ### Heading 3
  148. {%endmarkdown%}
  149. """
  150. t = Jinja2Template(JINJA2.path)
  151. t.configure(None)
  152. html = t.render(source, {}).strip()
  153. assert html == u'<h3>Heading 3</h3>'
  154. def test_markdown_with_extensions():
  155. source = """
  156. {%markdown%}
  157. ### Heading 3
  158. {%endmarkdown%}
  159. """
  160. t = Jinja2Template(JINJA2.path)
  161. s = Site(JINJA2.path)
  162. c = Config(JINJA2.path, config_dict=dict(markdown=dict(extensions=['headerid'])))
  163. s.config = c
  164. t.configure(s)
  165. t.env.filters['dateformat'] = dateformat
  166. html = t.render(source, {}).strip()
  167. assert html == u'<h3 id="heading_3">Heading 3</h3>'
  168. def test_line_statements():
  169. source = """
  170. $$$ markdown
  171. ### Heading 3
  172. $$$ endmarkdown
  173. """
  174. t = Jinja2Template(JINJA2.path)
  175. s = Site(JINJA2.path)
  176. c = Config(JINJA2.path, config_dict=dict(markdown=dict(extensions=['headerid'])))
  177. s.config = c
  178. t.configure(s)
  179. t.env.filters['dateformat'] = dateformat
  180. html = t.render(source, {}).strip()
  181. assert html == u'<h3 id="heading_3">Heading 3</h3>'
  182. def test_line_statements_with_config():
  183. source = """
  184. %% markdown
  185. ### Heading 3
  186. %% endmarkdown
  187. """
  188. config = """
  189. markdown:
  190. extensions:
  191. - headerid
  192. jinja2:
  193. line_statement_prefix: '%%'
  194. """
  195. t = Jinja2Template(JINJA2.path)
  196. s = Site(JINJA2.path)
  197. s.config = Config(JINJA2.path, config_dict=yaml.load(config))
  198. t.configure(s)
  199. t.env.filters['dateformat'] = dateformat
  200. html = t.render(source, {}).strip()
  201. assert html == u'<h3 id="heading_3">Heading 3</h3>'
  202. TEST_SITE = File(__file__).parent.child_folder('_test')
  203. @nottest
  204. def assert_markdown_typogrify_processed_well(include_text, includer_text):
  205. site = Site(TEST_SITE)
  206. site.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin']
  207. inc = File(TEST_SITE.child('content/inc.md'))
  208. inc.write(include_text)
  209. site.load()
  210. gen = Generator(site)
  211. gen.load_template_if_needed()
  212. template = gen.template
  213. html = template.render(includer_text, {}).strip()
  214. assert html
  215. q = PyQuery(html)
  216. assert "is_processable" not in html
  217. assert "This is a" in q("h1").text()
  218. assert "heading" in q("h1").text()
  219. assert q(".amp").length == 1
  220. return html
  221. class TestJinjaTemplate(object):
  222. def setUp(self):
  223. TEST_SITE.make()
  224. TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE)
  225. def tearDown(self):
  226. TEST_SITE.delete()
  227. def test_depends(self):
  228. t = Jinja2Template(JINJA2.path)
  229. t.configure(None)
  230. t.env.filters['dateformat'] = dateformat
  231. deps = list(t.get_dependencies('index.html'))
  232. assert len(deps) == 2
  233. assert 'helpers.html' in deps
  234. assert 'layout.html' in deps
  235. def test_depends_multi_level(self):
  236. site = Site(TEST_SITE)
  237. JINJA2.copy_contents_to(site.content.source)
  238. inc = File(TEST_SITE.child('content/inc.md'))
  239. inc.write("{% extends 'index.html' %}")
  240. site.load()
  241. gen = Generator(site)
  242. gen.load_template_if_needed()
  243. t = gen.template
  244. deps = list(t.get_dependencies('inc.md'))
  245. assert len(deps) == 3
  246. assert 'helpers.html' in deps
  247. assert 'layout.html' in deps
  248. assert 'index.html' in deps
  249. def test_line_statements_with_blocks(self):
  250. site = Site(TEST_SITE)
  251. JINJA2.copy_contents_to(site.content.source)
  252. inc = File(TEST_SITE.child('content/inc.md'))
  253. text = """
  254. {% extends 'index.html' %}
  255. $$$ block body
  256. <div id="article">Heya</div>
  257. $$$ endblock
  258. """
  259. site.load()
  260. gen = Generator(site)
  261. gen.load_template_if_needed()
  262. template = gen.template
  263. template.env.filters['dateformat'] = dateformat
  264. html = template.render(text, {}).strip()
  265. assert html
  266. q = PyQuery(html)
  267. article = q("#article")
  268. assert article.length == 1
  269. assert article.text() == "Heya"
  270. def test_depends_with_reference_tag(self):
  271. site = Site(TEST_SITE)
  272. JINJA2.copy_contents_to(site.content.source)
  273. inc = File(TEST_SITE.child('content/inc.md'))
  274. inc.write("{% refer to 'index.html' as index%}")
  275. site.load()
  276. gen = Generator(site)
  277. gen.load_template_if_needed()
  278. t = gen.template
  279. deps = list(t.get_dependencies('inc.md'))
  280. assert len(deps) == 3
  281. assert 'helpers.html' in deps
  282. assert 'layout.html' in deps
  283. assert 'index.html' in deps
  284. def test_cant_find_depends_with_reference_tag_var(self):
  285. site = Site(TEST_SITE)
  286. JINJA2.copy_contents_to(site.content.source)
  287. inc = File(TEST_SITE.child('content/inc.md'))
  288. inc.write("{% set ind = 'index.html' %}{% refer to ind as index %}")
  289. site.load()
  290. gen = Generator(site)
  291. gen.load_template_if_needed()
  292. t = gen.template
  293. deps = list(t.get_dependencies('inc.md'))
  294. assert len(deps) == 1
  295. assert not deps[0]
  296. def test_can_include_templates_with_processing(self):
  297. text = """
  298. ===
  299. is_processable: False
  300. ===
  301. {% filter typogrify %}{% markdown %}
  302. This is a heading
  303. =================
  304. Hyde & Jinja.
  305. {% endmarkdown %}{% endfilter %}
  306. """
  307. text2 = """{% include "inc.md" %}"""
  308. assert_markdown_typogrify_processed_well(text, text2)
  309. def test_includetext(self):
  310. text = """
  311. ===
  312. is_processable: False
  313. ===
  314. This is a heading
  315. =================
  316. Hyde & Jinja.
  317. """
  318. text2 = """{% includetext "inc.md" %}"""
  319. assert_markdown_typogrify_processed_well(text, text2)
  320. def test_reference_is_noop(self):
  321. text = """
  322. ===
  323. is_processable: False
  324. ===
  325. {% mark heading %}
  326. This is a heading
  327. =================
  328. {% endmark %}
  329. {% reference content %}
  330. Hyde & Jinja.
  331. {% endreference %}
  332. """
  333. text2 = """{% includetext "inc.md" %}"""
  334. html = assert_markdown_typogrify_processed_well(text, text2)
  335. assert "mark" not in html
  336. assert "reference" not in html
  337. def test_reference_is_not_callable(self):
  338. text = """
  339. ===
  340. is_processable: False
  341. ===
  342. {% mark heading %}
  343. This is a heading
  344. =================
  345. {% endmark %}
  346. {% reference content %}
  347. Hyde & Jinja.
  348. {% endreference %}
  349. {% mark repeated %}
  350. <span class="junk">Junk</span>
  351. {% endmark %}
  352. {{ self.repeated() }}
  353. {{ self.repeated }}
  354. """
  355. text2 = """{% includetext "inc.md" %}"""
  356. html = assert_markdown_typogrify_processed_well(text, text2)
  357. assert "mark" not in html
  358. assert "reference" not in html
  359. q = PyQuery(html)
  360. assert q("span.junk").length == 1
  361. def test_refer(self):
  362. text = """
  363. ===
  364. is_processable: False
  365. ===
  366. {% filter markdown|typogrify %}
  367. {% mark heading %}
  368. This is a heading
  369. =================
  370. {% endmark %}
  371. {% reference content %}
  372. Hyde & Jinja.
  373. {% endreference %}
  374. {% endfilter %}
  375. """
  376. text2 = """
  377. {% refer to "inc.md" as inc %}
  378. {% filter markdown|typogrify %}
  379. {{ inc.heading }}
  380. {{ inc.content }}
  381. {% endfilter %}
  382. """
  383. html = assert_markdown_typogrify_processed_well(text, text2)
  384. assert "mark" not in html
  385. assert "reference" not in html
  386. def test_refer_with_full_html(self):
  387. text = """
  388. ===
  389. is_processable: False
  390. ===
  391. <div class="fulltext">
  392. {% filter markdown|typogrify %}
  393. {% mark heading %}
  394. This is a heading
  395. =================
  396. {% endmark %}
  397. {% reference content %}
  398. Hyde & Jinja.
  399. {% endreference %}
  400. {% endfilter %}
  401. </div>
  402. """
  403. text2 = """
  404. {% refer to "inc.md" as inc %}
  405. {{ inc.html('.fulltext') }}
  406. """
  407. html = assert_markdown_typogrify_processed_well(text, text2)
  408. assert "mark" not in html
  409. assert "reference" not in html
  410. def test_two_level_refer_with_var(self):
  411. text = """
  412. ===
  413. is_processable: False
  414. ===
  415. <div class="fulltext">
  416. {% filter markdown|typogrify %}
  417. {% mark heading %}
  418. This is a heading
  419. =================
  420. {% endmark %}
  421. {% reference content %}
  422. Hyde & Jinja.
  423. {% endreference %}
  424. {% endfilter %}
  425. </div>
  426. """
  427. text2 = """
  428. {% set super = 'super.md' %}
  429. {% refer to super as sup %}
  430. <div class="justhead">
  431. {% mark child %}
  432. {{ sup.heading }}
  433. {% endmark %}
  434. {% mark cont %}
  435. {{ sup.content }}
  436. {% endmark %}
  437. </div>
  438. """
  439. text3 = """
  440. {% set incu = 'inc.md' %}
  441. {% refer to incu as inc %}
  442. {% filter markdown|typogrify %}
  443. {{ inc.child }}
  444. {{ inc.cont }}
  445. {% endfilter %}
  446. """
  447. superinc = File(TEST_SITE.child('content/super.md'))
  448. superinc.write(text)
  449. html = assert_markdown_typogrify_processed_well(text2, text3)
  450. assert "mark" not in html
  451. assert "reference" not in html
  452. def test_refer_with_var(self):
  453. text = """
  454. ===
  455. is_processable: False
  456. ===
  457. <div class="fulltext">
  458. {% filter markdown|typogrify %}
  459. {% mark heading %}
  460. This is a heading
  461. =================
  462. {% endmark %}
  463. {% reference content %}
  464. Hyde & Jinja.
  465. {% endreference %}
  466. {% endfilter %}
  467. </div>
  468. """
  469. text2 = """
  470. {% set incu = 'inc.md' %}
  471. {% refer to incu as inc %}
  472. {{ inc.html('.fulltext') }}
  473. """
  474. html = assert_markdown_typogrify_processed_well(text, text2)
  475. assert "mark" not in html
  476. assert "reference" not in html
  477. def test_yaml_tag(salf):
  478. text = """
  479. {% yaml test %}
  480. one:
  481. - A
  482. - B
  483. - C
  484. two:
  485. - D
  486. - E
  487. - F
  488. {% endyaml %}
  489. {% for section, values in test.items() %}
  490. <ul class="{{ section }}">
  491. {% for value in values %}
  492. <li>{{ value }}</li>
  493. {% endfor %}
  494. </ul>
  495. {% endfor %}
  496. """
  497. t = Jinja2Template(JINJA2.path)
  498. t.configure(None)
  499. t.env.filters['dateformat'] = dateformat
  500. html = t.render(text, {}).strip()
  501. actual = PyQuery(html)
  502. assert actual("ul").length == 2
  503. assert actual("ul.one").length == 1
  504. assert actual("ul.two").length == 1
  505. assert actual("li").length == 6
  506. assert actual("ul.one li").length == 3
  507. assert actual("ul.two li").length == 3
  508. ones = [item.text for item in actual("ul.one li")]
  509. assert ones == ["A", "B", "C"]
  510. twos = [item.text for item in actual("ul.two li")]
  511. assert twos == ["D", "E", "F"]
  512. def test_top_filter(self):
  513. text = """
  514. {% yaml test %}
  515. item_list:
  516. - A
  517. - B
  518. - C
  519. - D
  520. - E
  521. - F
  522. {% endyaml %}
  523. <ul class="top">
  524. {% for value in test.item_list|top(3) %}
  525. <li>{{ value }}</li>
  526. {% endfor %}
  527. </ul>
  528. <ul class="mid">
  529. {% for value in test.item_list|islice(3, 6) %}
  530. <li>{{ value }}</li>
  531. {% endfor %}
  532. </ul>
  533. """
  534. t = Jinja2Template(JINJA2.path)
  535. t.configure(None)
  536. t.env.filters['dateformat'] = dateformat
  537. html = t.render(text, {}).strip()
  538. actual = PyQuery(html)
  539. assert actual("ul").length == 2
  540. assert actual("li").length == 6
  541. items = [item.text for item in actual("ul.top li")]
  542. assert items == ["A", "B", "C"]
  543. items = [item.text for item in actual("ul.mid li")]
  544. assert items == ["D", "E", "F"]
  545. def test_raw_tag(self):
  546. expected = """
  547. <div class="template secondary">
  548. <aside class="secondary">
  549. <ul>
  550. {{#sections}}
  551. <li><a href="#{{id}}">{{textContent}}</a></li>
  552. {{/sections}}
  553. </ul>
  554. </aside>
  555. """
  556. text = "{%% raw %%}%s{%% endraw %%}" % expected
  557. t = Jinja2Template(JINJA2.path)
  558. t.configure(None)
  559. html = t.render(text, {}).strip()
  560. assert html.strip() == expected.strip()
  561. def test_raw_tag_with_markdown_typogrify(self):
  562. expected = """
  563. <div class="template secondary">
  564. <aside class="secondary">
  565. <ul>
  566. {{#sections}}
  567. <li><a href="#{{id}}">{{textContent}}</a></li>
  568. {{/sections}}
  569. </ul>
  570. </aside>
  571. """
  572. text = "{%% filter markdown|typogrify %%}{%% raw %%}%s{%% endraw %%}{%% endfilter %%}" % expected
  573. t = Jinja2Template(JINJA2.path)
  574. t.configure(None)
  575. html = t.render(text, {}).strip()
  576. assert html.strip() == expected.strip()