tests.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import sys
  2. from django.template import Context, Engine, TemplateDoesNotExist, TemplateSyntaxError
  3. from django.template.base import UNKNOWN_SOURCE
  4. from django.test import SimpleTestCase, override_settings
  5. from django.urls import NoReverseMatch
  6. from django.utils import translation
  7. from django.utils.html import escape
  8. class TemplateTestMixin:
  9. def _engine(self, **kwargs):
  10. return Engine(debug=self.debug_engine, **kwargs)
  11. def test_string_origin(self):
  12. template = self._engine().from_string("string template")
  13. self.assertEqual(template.origin.name, UNKNOWN_SOURCE)
  14. self.assertIsNone(template.origin.loader_name)
  15. self.assertEqual(template.source, "string template")
  16. @override_settings(SETTINGS_MODULE=None)
  17. def test_url_reverse_no_settings_module(self):
  18. """
  19. #9005 -- url tag shouldn't require settings.SETTINGS_MODULE to
  20. be set.
  21. """
  22. t = self._engine().from_string("{% url will_not_match %}")
  23. c = Context()
  24. with self.assertRaises(NoReverseMatch):
  25. t.render(c)
  26. def test_url_reverse_view_name(self):
  27. """
  28. #19827 -- url tag should keep original stack trace when reraising
  29. exception.
  30. """
  31. t = self._engine().from_string("{% url will_not_match %}")
  32. c = Context()
  33. try:
  34. t.render(c)
  35. except NoReverseMatch:
  36. tb = sys.exc_info()[2]
  37. depth = 0
  38. while tb.tb_next is not None:
  39. tb = tb.tb_next
  40. depth += 1
  41. self.assertGreater(
  42. depth, 5, "The traceback context was lost when reraising the traceback."
  43. )
  44. def test_no_wrapped_exception(self):
  45. """
  46. # 16770 -- The template system doesn't wrap exceptions, but annotates
  47. them.
  48. """
  49. engine = self._engine()
  50. c = Context({"coconuts": lambda: 42 / 0})
  51. t = engine.from_string("{{ coconuts }}")
  52. with self.assertRaises(ZeroDivisionError) as e:
  53. t.render(c)
  54. if self.debug_engine:
  55. debug = e.exception.template_debug
  56. self.assertEqual(debug["start"], 0)
  57. self.assertEqual(debug["end"], 14)
  58. def test_invalid_block_suggestion(self):
  59. """
  60. Error messages should include the unexpected block name and be in all
  61. English.
  62. """
  63. engine = self._engine()
  64. msg = (
  65. "Invalid block tag on line 1: 'endblock', expected 'elif', 'else' "
  66. "or 'endif'. Did you forget to register or load this tag?"
  67. )
  68. with self.settings(USE_I18N=True), translation.override("de"):
  69. with self.assertRaisesMessage(TemplateSyntaxError, msg):
  70. engine.from_string("{% if 1 %}lala{% endblock %}{% endif %}")
  71. def test_unknown_block_tag(self):
  72. engine = self._engine()
  73. msg = (
  74. "Invalid block tag on line 1: 'foobar'. Did you forget to "
  75. "register or load this tag?"
  76. )
  77. with self.assertRaisesMessage(TemplateSyntaxError, msg):
  78. engine.from_string("lala{% foobar %}")
  79. def test_compile_filter_expression_error(self):
  80. """
  81. 19819 -- Make sure the correct token is highlighted for
  82. FilterExpression errors.
  83. """
  84. engine = self._engine()
  85. msg = "Could not parse the remainder: '@bar' from 'foo@bar'"
  86. with self.assertRaisesMessage(TemplateSyntaxError, msg) as e:
  87. engine.from_string("{% if 1 %}{{ foo@bar }}{% endif %}")
  88. if self.debug_engine:
  89. debug = e.exception.template_debug
  90. self.assertEqual((debug["start"], debug["end"]), (10, 23))
  91. self.assertEqual((debug["during"]), "{{ foo@bar }}")
  92. def test_compile_tag_error(self):
  93. """
  94. Errors raised while compiling nodes should include the token
  95. information.
  96. """
  97. engine = self._engine(
  98. libraries={"bad_tag": "template_tests.templatetags.bad_tag"},
  99. )
  100. with self.assertRaises(RuntimeError) as e:
  101. engine.from_string("{% load bad_tag %}{% badtag %}")
  102. if self.debug_engine:
  103. self.assertEqual(e.exception.template_debug["during"], "{% badtag %}")
  104. def test_compile_tag_error_27584(self):
  105. engine = self._engine(
  106. app_dirs=True,
  107. libraries={"tag_27584": "template_tests.templatetags.tag_27584"},
  108. )
  109. t = engine.get_template("27584_parent.html")
  110. with self.assertRaises(TemplateSyntaxError) as e:
  111. t.render(Context())
  112. if self.debug_engine:
  113. self.assertEqual(e.exception.template_debug["during"], "{% badtag %}")
  114. def test_compile_tag_error_27956(self):
  115. """Errors in a child of {% extends %} are displayed correctly."""
  116. engine = self._engine(
  117. app_dirs=True,
  118. libraries={"tag_27584": "template_tests.templatetags.tag_27584"},
  119. )
  120. t = engine.get_template("27956_child.html")
  121. with self.assertRaises(TemplateSyntaxError) as e:
  122. t.render(Context())
  123. if self.debug_engine:
  124. self.assertEqual(e.exception.template_debug["during"], "{% badtag %}")
  125. def test_render_tag_error_in_extended_block(self):
  126. """Errors in extended block are displayed correctly."""
  127. e = self._engine(app_dirs=True)
  128. template = e.get_template("test_extends_block_error.html")
  129. context = Context()
  130. with self.assertRaises(TemplateDoesNotExist) as cm:
  131. template.render(context)
  132. if self.debug_engine:
  133. self.assertEqual(
  134. cm.exception.template_debug["during"],
  135. escape('{% include "missing.html" %}'),
  136. )
  137. def test_super_errors(self):
  138. """
  139. #18169 -- NoReverseMatch should not be silence in block.super.
  140. """
  141. engine = self._engine(app_dirs=True)
  142. t = engine.get_template("included_content.html")
  143. with self.assertRaises(NoReverseMatch):
  144. t.render(Context())
  145. def test_extends_generic_template(self):
  146. """
  147. #24338 -- Allow extending django.template.backends.django.Template
  148. objects.
  149. """
  150. engine = self._engine()
  151. parent = engine.from_string("{% block content %}parent{% endblock %}")
  152. child = engine.from_string(
  153. "{% extends parent %}{% block content %}child{% endblock %}"
  154. )
  155. self.assertEqual(child.render(Context({"parent": parent})), "child")
  156. def test_node_origin(self):
  157. """
  158. #25848 -- Set origin on Node so debugging tools can determine which
  159. template the node came from even if extending or including templates.
  160. """
  161. template = self._engine().from_string("content")
  162. for node in template.nodelist:
  163. self.assertEqual(node.origin, template.origin)
  164. def test_render_built_in_type_method(self):
  165. """
  166. Templates should not crash when rendering methods for built-in types
  167. without required arguments.
  168. """
  169. template = self._engine().from_string("{{ description.count }}")
  170. self.assertEqual(template.render(Context({"description": "test"})), "")
  171. class TemplateTests(TemplateTestMixin, SimpleTestCase):
  172. debug_engine = False
  173. class DebugTemplateTests(TemplateTestMixin, SimpleTestCase):
  174. debug_engine = True