1. import os
    
  2. 
    
  3. from django.template import Context, Engine, TemplateDoesNotExist
    
  4. from django.test import SimpleTestCase
    
  5. 
    
  6. from .utils import ROOT
    
  7. 
    
  8. RECURSIVE = os.path.join(ROOT, "recursive_templates")
    
  9. 
    
  10. 
    
  11. class ExtendsBehaviorTests(SimpleTestCase):
    
  12.     def test_normal_extend(self):
    
  13.         engine = Engine(dirs=[os.path.join(RECURSIVE, "fs")])
    
  14.         template = engine.get_template("one.html")
    
  15.         output = template.render(Context({}))
    
  16.         self.assertEqual(output.strip(), "three two one")
    
  17. 
    
  18.     def test_extend_recursive(self):
    
  19.         engine = Engine(
    
  20.             dirs=[
    
  21.                 os.path.join(RECURSIVE, "fs"),
    
  22.                 os.path.join(RECURSIVE, "fs2"),
    
  23.                 os.path.join(RECURSIVE, "fs3"),
    
  24.             ]
    
  25.         )
    
  26.         template = engine.get_template("recursive.html")
    
  27.         output = template.render(Context({}))
    
  28.         self.assertEqual(output.strip(), "fs3/recursive fs2/recursive fs/recursive")
    
  29. 
    
  30.     def test_extend_missing(self):
    
  31.         engine = Engine(dirs=[os.path.join(RECURSIVE, "fs")])
    
  32.         template = engine.get_template("extend-missing.html")
    
  33.         with self.assertRaises(TemplateDoesNotExist) as e:
    
  34.             template.render(Context({}))
    
  35. 
    
  36.         tried = e.exception.tried
    
  37.         self.assertEqual(len(tried), 1)
    
  38.         self.assertEqual(tried[0][0].template_name, "missing.html")
    
  39. 
    
  40.     def test_recursive_multiple_loaders(self):
    
  41.         engine = Engine(
    
  42.             dirs=[os.path.join(RECURSIVE, "fs")],
    
  43.             loaders=[
    
  44.                 (
    
  45.                     "django.template.loaders.locmem.Loader",
    
  46.                     {
    
  47.                         "one.html": (
    
  48.                             '{% extends "one.html" %}{% block content %}'
    
  49.                             "{{ block.super }} locmem-one{% endblock %}"
    
  50.                         ),
    
  51.                         "two.html": (
    
  52.                             '{% extends "two.html" %}{% block content %}'
    
  53.                             "{{ block.super }} locmem-two{% endblock %}"
    
  54.                         ),
    
  55.                         "three.html": (
    
  56.                             '{% extends "three.html" %}{% block content %}'
    
  57.                             "{{ block.super }} locmem-three{% endblock %}"
    
  58.                         ),
    
  59.                     },
    
  60.                 ),
    
  61.                 "django.template.loaders.filesystem.Loader",
    
  62.             ],
    
  63.         )
    
  64.         template = engine.get_template("one.html")
    
  65.         output = template.render(Context({}))
    
  66.         self.assertEqual(
    
  67.             output.strip(), "three locmem-three two locmem-two one locmem-one"
    
  68.         )
    
  69. 
    
  70.     def test_extend_self_error(self):
    
  71.         """
    
  72.         Catch if a template extends itself and no other matching
    
  73.         templates are found.
    
  74.         """
    
  75.         engine = Engine(dirs=[os.path.join(RECURSIVE, "fs")])
    
  76.         template = engine.get_template("self.html")
    
  77.         with self.assertRaises(TemplateDoesNotExist) as e:
    
  78.             template.render(Context({}))
    
  79.         tried = e.exception.tried
    
  80.         self.assertEqual(len(tried), 1)
    
  81.         origin, message = tried[0]
    
  82.         self.assertEqual(origin.template_name, "self.html")
    
  83.         self.assertEqual(message, "Skipped to avoid recursion")
    
  84. 
    
  85.     def test_extend_cached(self):
    
  86.         engine = Engine(
    
  87.             dirs=[
    
  88.                 os.path.join(RECURSIVE, "fs"),
    
  89.                 os.path.join(RECURSIVE, "fs2"),
    
  90.                 os.path.join(RECURSIVE, "fs3"),
    
  91.             ],
    
  92.             loaders=[
    
  93.                 (
    
  94.                     "django.template.loaders.cached.Loader",
    
  95.                     [
    
  96.                         "django.template.loaders.filesystem.Loader",
    
  97.                     ],
    
  98.                 ),
    
  99.             ],
    
  100.         )
    
  101.         template = engine.get_template("recursive.html")
    
  102.         output = template.render(Context({}))
    
  103.         self.assertEqual(output.strip(), "fs3/recursive fs2/recursive fs/recursive")
    
  104. 
    
  105.         cache = engine.template_loaders[0].get_template_cache
    
  106.         self.assertEqual(len(cache), 3)
    
  107.         expected_path = os.path.join("fs", "recursive.html")
    
  108.         self.assertTrue(cache["recursive.html"].origin.name.endswith(expected_path))
    
  109. 
    
  110.         # Render another path that uses the same templates from the cache
    
  111.         template = engine.get_template("other-recursive.html")
    
  112.         output = template.render(Context({}))
    
  113.         self.assertEqual(output.strip(), "fs3/recursive fs2/recursive fs/recursive")
    
  114. 
    
  115.         # Template objects should not be duplicated.
    
  116.         self.assertEqual(len(cache), 4)
    
  117.         expected_path = os.path.join("fs", "other-recursive.html")
    
  118.         self.assertTrue(
    
  119.             cache["other-recursive.html"].origin.name.endswith(expected_path)
    
  120.         )
    
  121. 
    
  122.     def test_unique_history_per_loader(self):
    
  123.         """
    
  124.         Extending should continue even if two loaders return the same
    
  125.         name for a template.
    
  126.         """
    
  127.         engine = Engine(
    
  128.             loaders=[
    
  129.                 [
    
  130.                     "django.template.loaders.locmem.Loader",
    
  131.                     {
    
  132.                         "base.html": (
    
  133.                             '{% extends "base.html" %}{% block content %}'
    
  134.                             "{{ block.super }} loader1{% endblock %}"
    
  135.                         ),
    
  136.                     },
    
  137.                 ],
    
  138.                 [
    
  139.                     "django.template.loaders.locmem.Loader",
    
  140.                     {
    
  141.                         "base.html": "{% block content %}loader2{% endblock %}",
    
  142.                     },
    
  143.                 ],
    
  144.             ]
    
  145.         )
    
  146.         template = engine.get_template("base.html")
    
  147.         output = template.render(Context({}))
    
  148.         self.assertEqual(output.strip(), "loader2 loader1")
    
  149. 
    
  150.     def test_block_override_in_extended_included_template(self):
    
  151.         """
    
  152.         ExtendsNode.find_template() initializes history with self.origin
    
  153.         (#28071).
    
  154.         """
    
  155.         engine = Engine(
    
  156.             loaders=[
    
  157.                 [
    
  158.                     "django.template.loaders.locmem.Loader",
    
  159.                     {
    
  160.                         "base.html": (
    
  161.                             "{% extends 'base.html' %}{% block base %}{{ block.super }}"
    
  162.                             "2{% endblock %}"
    
  163.                         ),
    
  164.                         "included.html": (
    
  165.                             "{% extends 'included.html' %}{% block included %}"
    
  166.                             "{{ block.super }}B{% endblock %}"
    
  167.                         ),
    
  168.                     },
    
  169.                 ],
    
  170.                 [
    
  171.                     "django.template.loaders.locmem.Loader",
    
  172.                     {
    
  173.                         "base.html": (
    
  174.                             "{% block base %}1{% endblock %}"
    
  175.                             "{% include 'included.html' %}"
    
  176.                         ),
    
  177.                         "included.html": "{% block included %}A{% endblock %}",
    
  178.                     },
    
  179.                 ],
    
  180.             ],
    
  181.         )
    
  182.         template = engine.get_template("base.html")
    
  183.         self.assertEqual(template.render(Context({})), "12AB")