1. from django.template import (
    
  2.     Context,
    
  3.     Engine,
    
  4.     TemplateDoesNotExist,
    
  5.     TemplateSyntaxError,
    
  6.     loader,
    
  7. )
    
  8. from django.template.loader_tags import IncludeNode
    
  9. from django.test import SimpleTestCase
    
  10. 
    
  11. from ..utils import setup
    
  12. from .test_basic import basic_templates
    
  13. 
    
  14. include_fail_templates = {
    
  15.     "include-fail1": "{% load bad_tag %}{% badtag %}",
    
  16.     "include-fail2": "{% load broken_tag %}",
    
  17. }
    
  18. 
    
  19. 
    
  20. class IncludeTagTests(SimpleTestCase):
    
  21.     libraries = {"bad_tag": "template_tests.templatetags.bad_tag"}
    
  22. 
    
  23.     @setup({"include01": '{% include "basic-syntax01" %}'}, basic_templates)
    
  24.     def test_include01(self):
    
  25.         output = self.engine.render_to_string("include01")
    
  26.         self.assertEqual(output, "something cool")
    
  27. 
    
  28.     @setup({"include02": '{% include "basic-syntax02" %}'}, basic_templates)
    
  29.     def test_include02(self):
    
  30.         output = self.engine.render_to_string("include02", {"headline": "Included"})
    
  31.         self.assertEqual(output, "Included")
    
  32. 
    
  33.     @setup({"include03": "{% include template_name %}"}, basic_templates)
    
  34.     def test_include03(self):
    
  35.         output = self.engine.render_to_string(
    
  36.             "include03",
    
  37.             {"template_name": "basic-syntax02", "headline": "Included"},
    
  38.         )
    
  39.         self.assertEqual(output, "Included")
    
  40. 
    
  41.     @setup({"include04": 'a{% include "nonexistent" %}b'})
    
  42.     def test_include04(self):
    
  43.         template = self.engine.get_template("include04")
    
  44.         with self.assertRaises(TemplateDoesNotExist):
    
  45.             template.render(Context({}))
    
  46. 
    
  47.     @setup(
    
  48.         {
    
  49.             "include 05": "template with a space",
    
  50.             "include06": '{% include "include 05"%}',
    
  51.         }
    
  52.     )
    
  53.     def test_include06(self):
    
  54.         output = self.engine.render_to_string("include06")
    
  55.         self.assertEqual(output, "template with a space")
    
  56. 
    
  57.     @setup(
    
  58.         {"include07": '{% include "basic-syntax02" with headline="Inline" %}'},
    
  59.         basic_templates,
    
  60.     )
    
  61.     def test_include07(self):
    
  62.         output = self.engine.render_to_string("include07", {"headline": "Included"})
    
  63.         self.assertEqual(output, "Inline")
    
  64. 
    
  65.     @setup(
    
  66.         {"include08": '{% include headline with headline="Dynamic" %}'}, basic_templates
    
  67.     )
    
  68.     def test_include08(self):
    
  69.         output = self.engine.render_to_string(
    
  70.             "include08", {"headline": "basic-syntax02"}
    
  71.         )
    
  72.         self.assertEqual(output, "Dynamic")
    
  73. 
    
  74.     @setup(
    
  75.         {
    
  76.             "include09": (
    
  77.                 "{{ first }}--"
    
  78.                 '{% include "basic-syntax03" with '
    
  79.                 "first=second|lower|upper second=first|upper %}"
    
  80.                 "--{{ second }}"
    
  81.             )
    
  82.         },
    
  83.         basic_templates,
    
  84.     )
    
  85.     def test_include09(self):
    
  86.         output = self.engine.render_to_string(
    
  87.             "include09", {"first": "Ul", "second": "lU"}
    
  88.         )
    
  89.         self.assertEqual(output, "Ul--LU --- UL--lU")
    
  90. 
    
  91.     @setup({"include10": '{% include "basic-syntax03" only %}'}, basic_templates)
    
  92.     def test_include10(self):
    
  93.         output = self.engine.render_to_string("include10", {"first": "1"})
    
  94.         if self.engine.string_if_invalid:
    
  95.             self.assertEqual(output, "INVALID --- INVALID")
    
  96.         else:
    
  97.             self.assertEqual(output, " --- ")
    
  98. 
    
  99.     @setup(
    
  100.         {"include11": '{% include "basic-syntax03" only with second=2 %}'},
    
  101.         basic_templates,
    
  102.     )
    
  103.     def test_include11(self):
    
  104.         output = self.engine.render_to_string("include11", {"first": "1"})
    
  105.         if self.engine.string_if_invalid:
    
  106.             self.assertEqual(output, "INVALID --- 2")
    
  107.         else:
    
  108.             self.assertEqual(output, " --- 2")
    
  109. 
    
  110.     @setup(
    
  111.         {"include12": '{% include "basic-syntax03" with first=1 only %}'},
    
  112.         basic_templates,
    
  113.     )
    
  114.     def test_include12(self):
    
  115.         output = self.engine.render_to_string("include12", {"second": "2"})
    
  116.         if self.engine.string_if_invalid:
    
  117.             self.assertEqual(output, "1 --- INVALID")
    
  118.         else:
    
  119.             self.assertEqual(output, "1 --- ")
    
  120. 
    
  121.     @setup(
    
  122.         {
    
  123.             "include13": (
    
  124.                 '{% autoescape off %}{% include "basic-syntax03" %}{% endautoescape %}'
    
  125.             )
    
  126.         },
    
  127.         basic_templates,
    
  128.     )
    
  129.     def test_include13(self):
    
  130.         output = self.engine.render_to_string("include13", {"first": "&"})
    
  131.         if self.engine.string_if_invalid:
    
  132.             self.assertEqual(output, "& --- INVALID")
    
  133.         else:
    
  134.             self.assertEqual(output, "& --- ")
    
  135. 
    
  136.     @setup(
    
  137.         {
    
  138.             "include14": "{% autoescape off %}"
    
  139.             '{% include "basic-syntax03" with first=var1 only %}'
    
  140.             "{% endautoescape %}"
    
  141.         },
    
  142.         basic_templates,
    
  143.     )
    
  144.     def test_include14(self):
    
  145.         output = self.engine.render_to_string("include14", {"var1": "&"})
    
  146.         if self.engine.string_if_invalid:
    
  147.             self.assertEqual(output, "& --- INVALID")
    
  148.         else:
    
  149.             self.assertEqual(output, "& --- ")
    
  150. 
    
  151.     # Include syntax errors
    
  152.     @setup({"include-error01": '{% include "basic-syntax01" with %}'})
    
  153.     def test_include_error01(self):
    
  154.         with self.assertRaises(TemplateSyntaxError):
    
  155.             self.engine.get_template("include-error01")
    
  156. 
    
  157.     @setup({"include-error02": '{% include "basic-syntax01" with "no key" %}'})
    
  158.     def test_include_error02(self):
    
  159.         with self.assertRaises(TemplateSyntaxError):
    
  160.             self.engine.get_template("include-error02")
    
  161. 
    
  162.     @setup(
    
  163.         {"include-error03": '{% include "basic-syntax01" with dotted.arg="error" %}'}
    
  164.     )
    
  165.     def test_include_error03(self):
    
  166.         with self.assertRaises(TemplateSyntaxError):
    
  167.             self.engine.get_template("include-error03")
    
  168. 
    
  169.     @setup({"include-error04": '{% include "basic-syntax01" something_random %}'})
    
  170.     def test_include_error04(self):
    
  171.         with self.assertRaises(TemplateSyntaxError):
    
  172.             self.engine.get_template("include-error04")
    
  173. 
    
  174.     @setup(
    
  175.         {"include-error05": '{% include "basic-syntax01" foo="duplicate" foo="key" %}'}
    
  176.     )
    
  177.     def test_include_error05(self):
    
  178.         with self.assertRaises(TemplateSyntaxError):
    
  179.             self.engine.get_template("include-error05")
    
  180. 
    
  181.     @setup({"include-error06": '{% include "basic-syntax01" only only %}'})
    
  182.     def test_include_error06(self):
    
  183.         with self.assertRaises(TemplateSyntaxError):
    
  184.             self.engine.get_template("include-error06")
    
  185. 
    
  186.     @setup(include_fail_templates)
    
  187.     def test_include_fail1(self):
    
  188.         with self.assertRaises(RuntimeError):
    
  189.             self.engine.get_template("include-fail1")
    
  190. 
    
  191.     @setup(include_fail_templates)
    
  192.     def test_include_fail2(self):
    
  193.         with self.assertRaises(TemplateSyntaxError):
    
  194.             self.engine.get_template("include-fail2")
    
  195. 
    
  196.     @setup({"include-error07": '{% include "include-fail1" %}'}, include_fail_templates)
    
  197.     def test_include_error07(self):
    
  198.         template = self.engine.get_template("include-error07")
    
  199.         with self.assertRaises(RuntimeError):
    
  200.             template.render(Context())
    
  201. 
    
  202.     @setup({"include-error08": '{% include "include-fail2" %}'}, include_fail_templates)
    
  203.     def test_include_error08(self):
    
  204.         template = self.engine.get_template("include-error08")
    
  205.         with self.assertRaises(TemplateSyntaxError):
    
  206.             template.render(Context())
    
  207. 
    
  208.     @setup({"include-error09": "{% include failed_include %}"}, include_fail_templates)
    
  209.     def test_include_error09(self):
    
  210.         context = Context({"failed_include": "include-fail1"})
    
  211.         template = self.engine.get_template("include-error09")
    
  212.         with self.assertRaises(RuntimeError):
    
  213.             template.render(context)
    
  214. 
    
  215.     @setup({"include-error10": "{% include failed_include %}"}, include_fail_templates)
    
  216.     def test_include_error10(self):
    
  217.         context = Context({"failed_include": "include-fail2"})
    
  218.         template = self.engine.get_template("include-error10")
    
  219.         with self.assertRaises(TemplateSyntaxError):
    
  220.             template.render(context)
    
  221. 
    
  222.     @setup({"include_empty": "{% include %}"})
    
  223.     def test_include_empty(self):
    
  224.         msg = (
    
  225.             "'include' tag takes at least one argument: the name of the "
    
  226.             "template to be included."
    
  227.         )
    
  228.         with self.assertRaisesMessage(TemplateSyntaxError, msg):
    
  229.             self.engine.get_template("include_empty")
    
  230. 
    
  231. 
    
  232. class IncludeTests(SimpleTestCase):
    
  233.     def test_include_missing_template(self):
    
  234.         """
    
  235.         The correct template is identified as not existing
    
  236.         when {% include %} specifies a template that does not exist.
    
  237.         """
    
  238.         engine = Engine(app_dirs=True, debug=True)
    
  239.         template = engine.get_template("test_include_error.html")
    
  240.         with self.assertRaisesMessage(TemplateDoesNotExist, "missing.html"):
    
  241.             template.render(Context())
    
  242. 
    
  243.     def test_extends_include_missing_baseloader(self):
    
  244.         """
    
  245.         #12787 -- The correct template is identified as not existing
    
  246.         when {% extends %} specifies a template that does exist, but that
    
  247.         template has an {% include %} of something that does not exist.
    
  248.         """
    
  249.         engine = Engine(app_dirs=True, debug=True)
    
  250.         template = engine.get_template("test_extends_error.html")
    
  251.         with self.assertRaisesMessage(TemplateDoesNotExist, "missing.html"):
    
  252.             template.render(Context())
    
  253. 
    
  254.     def test_extends_include_missing_cachedloader(self):
    
  255.         engine = Engine(
    
  256.             debug=True,
    
  257.             loaders=[
    
  258.                 (
    
  259.                     "django.template.loaders.cached.Loader",
    
  260.                     [
    
  261.                         "django.template.loaders.app_directories.Loader",
    
  262.                     ],
    
  263.                 ),
    
  264.             ],
    
  265.         )
    
  266. 
    
  267.         template = engine.get_template("test_extends_error.html")
    
  268.         with self.assertRaisesMessage(TemplateDoesNotExist, "missing.html"):
    
  269.             template.render(Context())
    
  270. 
    
  271.         # Repeat to ensure it still works when loading from the cache
    
  272.         template = engine.get_template("test_extends_error.html")
    
  273.         with self.assertRaisesMessage(TemplateDoesNotExist, "missing.html"):
    
  274.             template.render(Context())
    
  275. 
    
  276.     def test_include_template_argument(self):
    
  277.         """
    
  278.         Support any render() supporting object
    
  279.         """
    
  280.         engine = Engine()
    
  281.         ctx = Context(
    
  282.             {
    
  283.                 "tmpl": engine.from_string("This worked!"),
    
  284.             }
    
  285.         )
    
  286.         outer_tmpl = engine.from_string("{% include tmpl %}")
    
  287.         output = outer_tmpl.render(ctx)
    
  288.         self.assertEqual(output, "This worked!")
    
  289. 
    
  290.     def test_include_template_iterable(self):
    
  291.         engine = Engine.get_default()
    
  292.         outer_temp = engine.from_string("{% include var %}")
    
  293.         tests = [
    
  294.             ("admin/fail.html", "index.html"),
    
  295.             ["admin/fail.html", "index.html"],
    
  296.         ]
    
  297.         for template_names in tests:
    
  298.             with self.subTest(template_names):
    
  299.                 output = outer_temp.render(Context({"var": template_names}))
    
  300.                 self.assertEqual(output, "index\n")
    
  301. 
    
  302.     def test_include_template_none(self):
    
  303.         engine = Engine.get_default()
    
  304.         outer_temp = engine.from_string("{% include var %}")
    
  305.         ctx = Context({"var": None})
    
  306.         msg = "No template names provided"
    
  307.         with self.assertRaisesMessage(TemplateDoesNotExist, msg):
    
  308.             outer_temp.render(ctx)
    
  309. 
    
  310.     def test_include_from_loader_get_template(self):
    
  311.         tmpl = loader.get_template("include_tpl.html")  # {% include tmpl %}
    
  312.         output = tmpl.render({"tmpl": loader.get_template("index.html")})
    
  313.         self.assertEqual(output, "index\n\n")
    
  314. 
    
  315.     def test_include_immediate_missing(self):
    
  316.         """
    
  317.         #16417 -- Include tags pointing to missing templates should not raise
    
  318.         an error at parsing time.
    
  319.         """
    
  320.         Engine(debug=True).from_string('{% include "this_does_not_exist.html" %}')
    
  321. 
    
  322.     def test_include_recursive(self):
    
  323.         comments = [
    
  324.             {
    
  325.                 "comment": "A1",
    
  326.                 "children": [
    
  327.                     {"comment": "B1", "children": []},
    
  328.                     {"comment": "B2", "children": []},
    
  329.                     {"comment": "B3", "children": [{"comment": "C1", "children": []}]},
    
  330.                 ],
    
  331.             }
    
  332.         ]
    
  333.         engine = Engine(app_dirs=True)
    
  334.         t = engine.get_template("recursive_include.html")
    
  335.         self.assertEqual(
    
  336.             "Recursion!  A1  Recursion!  B1   B2   B3  Recursion!  C1",
    
  337.             t.render(Context({"comments": comments}))
    
  338.             .replace(" ", "")
    
  339.             .replace("\n", " ")
    
  340.             .strip(),
    
  341.         )
    
  342. 
    
  343.     def test_include_cache(self):
    
  344.         """
    
  345.         {% include %} keeps resolved templates constant (#27974). The
    
  346.         CounterNode object in the {% counter %} template tag is created once
    
  347.         if caching works properly. Each iteration increases the counter instead
    
  348.         of restarting it.
    
  349. 
    
  350.         This works as a regression test only if the cached loader
    
  351.         isn't used, so the @setup decorator isn't used.
    
  352.         """
    
  353.         engine = Engine(
    
  354.             loaders=[
    
  355.                 (
    
  356.                     "django.template.loaders.locmem.Loader",
    
  357.                     {
    
  358.                         "template": (
    
  359.                             '{% for x in vars %}{% include "include" %}{% endfor %}'
    
  360.                         ),
    
  361.                         "include": '{% include "next" %}',
    
  362.                         "next": "{% load custom %}{% counter %}",
    
  363.                     },
    
  364.                 ),
    
  365.             ],
    
  366.             libraries={"custom": "template_tests.templatetags.custom"},
    
  367.         )
    
  368.         output = engine.render_to_string("template", {"vars": range(9)})
    
  369.         self.assertEqual(output, "012345678")
    
  370. 
    
  371. 
    
  372. class IncludeNodeTests(SimpleTestCase):
    
  373.     def test_repr(self):
    
  374.         include_node = IncludeNode("app/template.html")
    
  375.         self.assertEqual(
    
  376.             repr(include_node),
    
  377.             "<IncludeNode: template='app/template.html'>",
    
  378.         )