A fork of hyde, the static site generation. Some patches will be pushed upstream.
 
 
 

600 lines
14 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_markdown():
  111. source = """
  112. {%markdown%}
  113. ### Heading 3
  114. {%endmarkdown%}
  115. """
  116. t = Jinja2Template(JINJA2.path)
  117. t.configure(None)
  118. html = t.render(source, {}).strip()
  119. assert html == u'<h3>Heading 3</h3>'
  120. def test_markdown_with_extensions():
  121. source = """
  122. {%markdown%}
  123. ### Heading 3
  124. {%endmarkdown%}
  125. """
  126. t = Jinja2Template(JINJA2.path)
  127. s = Site(JINJA2.path)
  128. c = Config(JINJA2.path, config_dict=dict(markdown=dict(extensions=['headerid'])))
  129. s.config = c
  130. t.configure(s)
  131. t.env.filters['dateformat'] = dateformat
  132. html = t.render(source, {}).strip()
  133. assert html == u'<h3 id="heading_3">Heading 3</h3>'
  134. def test_line_statements():
  135. source = """
  136. $$$ markdown
  137. ### Heading 3
  138. $$$ endmarkdown
  139. """
  140. t = Jinja2Template(JINJA2.path)
  141. s = Site(JINJA2.path)
  142. c = Config(JINJA2.path, config_dict=dict(markdown=dict(extensions=['headerid'])))
  143. s.config = c
  144. t.configure(s)
  145. t.env.filters['dateformat'] = dateformat
  146. html = t.render(source, {}).strip()
  147. assert html == u'<h3 id="heading_3">Heading 3</h3>'
  148. def test_line_statements_with_config():
  149. source = """
  150. %% markdown
  151. ### Heading 3
  152. %% endmarkdown
  153. """
  154. config = """
  155. markdown:
  156. extensions:
  157. - headerid
  158. jinja2:
  159. line_statement_prefix: '%%'
  160. """
  161. t = Jinja2Template(JINJA2.path)
  162. s = Site(JINJA2.path)
  163. s.config = Config(JINJA2.path, config_dict=yaml.load(config))
  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. TEST_SITE = File(__file__).parent.child_folder('_test')
  169. @nottest
  170. def assert_markdown_typogrify_processed_well(include_text, includer_text):
  171. site = Site(TEST_SITE)
  172. site.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin']
  173. inc = File(TEST_SITE.child('content/inc.md'))
  174. inc.write(include_text)
  175. site.load()
  176. gen = Generator(site)
  177. gen.load_template_if_needed()
  178. template = gen.template
  179. html = template.render(includer_text, {}).strip()
  180. assert html
  181. q = PyQuery(html)
  182. assert "is_processable" not in html
  183. assert "This is a" in q("h1").text()
  184. assert "heading" in q("h1").text()
  185. assert q(".amp").length == 1
  186. return html
  187. class TestJinjaTemplate(object):
  188. def setUp(self):
  189. TEST_SITE.make()
  190. TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE)
  191. def tearDown(self):
  192. TEST_SITE.delete()
  193. def test_depends(self):
  194. t = Jinja2Template(JINJA2.path)
  195. t.configure(None)
  196. t.env.filters['dateformat'] = dateformat
  197. deps = list(t.get_dependencies('index.html'))
  198. assert len(deps) == 2
  199. assert 'helpers.html' in deps
  200. assert 'layout.html' in deps
  201. def test_depends_multi_level(self):
  202. site = Site(TEST_SITE)
  203. JINJA2.copy_contents_to(site.content.source)
  204. inc = File(TEST_SITE.child('content/inc.md'))
  205. inc.write("{% extends 'index.html' %}")
  206. site.load()
  207. gen = Generator(site)
  208. gen.load_template_if_needed()
  209. t = gen.template
  210. deps = list(t.get_dependencies('inc.md'))
  211. assert len(deps) == 3
  212. assert 'helpers.html' in deps
  213. assert 'layout.html' in deps
  214. assert 'index.html' in deps
  215. def test_line_statements_with_blocks(self):
  216. site = Site(TEST_SITE)
  217. JINJA2.copy_contents_to(site.content.source)
  218. inc = File(TEST_SITE.child('content/inc.md'))
  219. text = """
  220. {% extends 'index.html' %}
  221. $$$ block body
  222. <div id="article">Heya</div>
  223. $$$ endblock
  224. """
  225. site.load()
  226. gen = Generator(site)
  227. gen.load_template_if_needed()
  228. template = gen.template
  229. template.env.filters['dateformat'] = dateformat
  230. html = template.render(text, {}).strip()
  231. assert html
  232. q = PyQuery(html)
  233. article = q("#article")
  234. assert article.length == 1
  235. assert article.text() == "Heya"
  236. def test_depends_with_reference_tag(self):
  237. site = Site(TEST_SITE)
  238. JINJA2.copy_contents_to(site.content.source)
  239. inc = File(TEST_SITE.child('content/inc.md'))
  240. inc.write("{% refer to 'index.html' as index%}")
  241. site.load()
  242. gen = Generator(site)
  243. gen.load_template_if_needed()
  244. t = gen.template
  245. deps = list(t.get_dependencies('inc.md'))
  246. assert len(deps) == 3
  247. assert 'helpers.html' in deps
  248. assert 'layout.html' in deps
  249. assert 'index.html' in deps
  250. def test_cant_find_depends_with_reference_tag_var(self):
  251. site = Site(TEST_SITE)
  252. JINJA2.copy_contents_to(site.content.source)
  253. inc = File(TEST_SITE.child('content/inc.md'))
  254. inc.write("{% set ind = 'index.html' %}{% refer to ind as index %}")
  255. site.load()
  256. gen = Generator(site)
  257. gen.load_template_if_needed()
  258. t = gen.template
  259. deps = list(t.get_dependencies('inc.md'))
  260. assert len(deps) == 1
  261. assert not deps[0]
  262. def test_can_include_templates_with_processing(self):
  263. text = """
  264. ===
  265. is_processable: False
  266. ===
  267. {% filter typogrify %}{% markdown %}
  268. This is a heading
  269. =================
  270. Hyde & Jinja.
  271. {% endmarkdown %}{% endfilter %}
  272. """
  273. text2 = """{% include "inc.md" %}"""
  274. assert_markdown_typogrify_processed_well(text, text2)
  275. def test_includetext(self):
  276. text = """
  277. ===
  278. is_processable: False
  279. ===
  280. This is a heading
  281. =================
  282. Hyde & Jinja.
  283. """
  284. text2 = """{% includetext "inc.md" %}"""
  285. assert_markdown_typogrify_processed_well(text, text2)
  286. def test_reference_is_noop(self):
  287. text = """
  288. ===
  289. is_processable: False
  290. ===
  291. {% mark heading %}
  292. This is a heading
  293. =================
  294. {% endmark %}
  295. {% reference content %}
  296. Hyde & Jinja.
  297. {% endreference %}
  298. """
  299. text2 = """{% includetext "inc.md" %}"""
  300. html = assert_markdown_typogrify_processed_well(text, text2)
  301. assert "mark" not in html
  302. assert "reference" not in html
  303. def test_reference_is_not_callable(self):
  304. text = """
  305. ===
  306. is_processable: False
  307. ===
  308. {% mark heading %}
  309. This is a heading
  310. =================
  311. {% endmark %}
  312. {% reference content %}
  313. Hyde & Jinja.
  314. {% endreference %}
  315. {% mark repeated %}
  316. <span class="junk">Junk</span>
  317. {% endmark %}
  318. {{ self.repeated() }}
  319. {{ self.repeated }}
  320. """
  321. text2 = """{% includetext "inc.md" %}"""
  322. html = assert_markdown_typogrify_processed_well(text, text2)
  323. assert "mark" not in html
  324. assert "reference" not in html
  325. q = PyQuery(html)
  326. assert q("span.junk").length == 1
  327. def test_refer(self):
  328. text = """
  329. ===
  330. is_processable: False
  331. ===
  332. {% filter markdown|typogrify %}
  333. {% mark heading %}
  334. This is a heading
  335. =================
  336. {% endmark %}
  337. {% reference content %}
  338. Hyde & Jinja.
  339. {% endreference %}
  340. {% endfilter %}
  341. """
  342. text2 = """
  343. {% refer to "inc.md" as inc %}
  344. {% filter markdown|typogrify %}
  345. {{ inc.heading }}
  346. {{ inc.content }}
  347. {% endfilter %}
  348. """
  349. html = assert_markdown_typogrify_processed_well(text, text2)
  350. assert "mark" not in html
  351. assert "reference" not in html
  352. def test_refer_with_full_html(self):
  353. text = """
  354. ===
  355. is_processable: False
  356. ===
  357. <div class="fulltext">
  358. {% filter markdown|typogrify %}
  359. {% mark heading %}
  360. This is a heading
  361. =================
  362. {% endmark %}
  363. {% reference content %}
  364. Hyde & Jinja.
  365. {% endreference %}
  366. {% endfilter %}
  367. </div>
  368. """
  369. text2 = """
  370. {% refer to "inc.md" as inc %}
  371. {{ inc.html('.fulltext') }}
  372. """
  373. html = assert_markdown_typogrify_processed_well(text, text2)
  374. assert "mark" not in html
  375. assert "reference" not in html
  376. def test_two_level_refer_with_var(self):
  377. text = """
  378. ===
  379. is_processable: False
  380. ===
  381. <div class="fulltext">
  382. {% filter markdown|typogrify %}
  383. {% mark heading %}
  384. This is a heading
  385. =================
  386. {% endmark %}
  387. {% reference content %}
  388. Hyde & Jinja.
  389. {% endreference %}
  390. {% endfilter %}
  391. </div>
  392. """
  393. text2 = """
  394. {% set super = 'super.md' %}
  395. {% refer to super as sup %}
  396. <div class="justhead">
  397. {% mark child %}
  398. {{ sup.heading }}
  399. {% endmark %}
  400. {% mark cont %}
  401. {{ sup.content }}
  402. {% endmark %}
  403. </div>
  404. """
  405. text3 = """
  406. {% set incu = 'inc.md' %}
  407. {% refer to incu as inc %}
  408. {% filter markdown|typogrify %}
  409. {{ inc.child }}
  410. {{ inc.cont }}
  411. {% endfilter %}
  412. """
  413. superinc = File(TEST_SITE.child('content/super.md'))
  414. superinc.write(text)
  415. html = assert_markdown_typogrify_processed_well(text2, text3)
  416. assert "mark" not in html
  417. assert "reference" not in html
  418. def test_refer_with_var(self):
  419. text = """
  420. ===
  421. is_processable: False
  422. ===
  423. <div class="fulltext">
  424. {% filter markdown|typogrify %}
  425. {% mark heading %}
  426. This is a heading
  427. =================
  428. {% endmark %}
  429. {% reference content %}
  430. Hyde & Jinja.
  431. {% endreference %}
  432. {% endfilter %}
  433. </div>
  434. """
  435. text2 = """
  436. {% set incu = 'inc.md' %}
  437. {% refer to incu as inc %}
  438. {{ inc.html('.fulltext') }}
  439. """
  440. html = assert_markdown_typogrify_processed_well(text, text2)
  441. assert "mark" not in html
  442. assert "reference" not in html
  443. def test_yaml_tag(salf):
  444. text = """
  445. {% yaml test %}
  446. one:
  447. - A
  448. - B
  449. - C
  450. two:
  451. - D
  452. - E
  453. - F
  454. {% endyaml %}
  455. {% for section, values in test.items() %}
  456. <ul class="{{ section }}">
  457. {% for value in values %}
  458. <li>{{ value }}</li>
  459. {% endfor %}
  460. </ul>
  461. {% endfor %}
  462. """
  463. t = Jinja2Template(JINJA2.path)
  464. t.configure(None)
  465. t.env.filters['dateformat'] = dateformat
  466. html = t.render(text, {}).strip()
  467. actual = PyQuery(html)
  468. assert actual("ul").length == 2
  469. assert actual("ul.one").length == 1
  470. assert actual("ul.two").length == 1
  471. assert actual("li").length == 6
  472. assert actual("ul.one li").length == 3
  473. assert actual("ul.two li").length == 3
  474. ones = [item.text for item in actual("ul.one li")]
  475. assert ones == ["A", "B", "C"]
  476. twos = [item.text for item in actual("ul.two li")]
  477. assert twos == ["D", "E", "F"]
  478. def test_top_filter(self):
  479. text = """
  480. {% yaml test %}
  481. item_list:
  482. - A
  483. - B
  484. - C
  485. - D
  486. - E
  487. - F
  488. {% endyaml %}
  489. <ul class="top">
  490. {% for value in test.item_list|top(3) %}
  491. <li>{{ value }}</li>
  492. {% endfor %}
  493. </ul>
  494. <ul class="mid">
  495. {% for value in test.item_list|islice(3, 6) %}
  496. <li>{{ value }}</li>
  497. {% endfor %}
  498. </ul>
  499. """
  500. t = Jinja2Template(JINJA2.path)
  501. t.configure(None)
  502. t.env.filters['dateformat'] = dateformat
  503. html = t.render(text, {}).strip()
  504. print html
  505. actual = PyQuery(html)
  506. assert actual("ul").length == 2
  507. assert actual("li").length == 6
  508. items = [item.text for item in actual("ul.top li")]
  509. assert items == ["A", "B", "C"]
  510. items = [item.text for item in actual("ul.mid li")]
  511. assert items == ["D", "E", "F"]