1. from django.template import Context, Template
    
  2. from django.test import SimpleTestCase
    
  3. from django.utils import html, translation
    
  4. from django.utils.functional import Promise, lazy, lazystr
    
  5. from django.utils.safestring import SafeData, SafeString, mark_safe
    
  6. from django.utils.translation import gettext_lazy
    
  7. 
    
  8. 
    
  9. class customescape(str):
    
  10.     def __html__(self):
    
  11.         # Implement specific and wrong escaping in order to be able to detect
    
  12.         # when it runs.
    
  13.         return self.replace("<", "<<").replace(">", ">>")
    
  14. 
    
  15. 
    
  16. class SafeStringTest(SimpleTestCase):
    
  17.     def assertRenderEqual(self, tpl, expected, **context):
    
  18.         context = Context(context)
    
  19.         tpl = Template(tpl)
    
  20.         self.assertEqual(tpl.render(context), expected)
    
  21. 
    
  22.     def test_mark_safe(self):
    
  23.         s = mark_safe("a&b")
    
  24. 
    
  25.         self.assertRenderEqual("{{ s }}", "a&b", s=s)
    
  26.         self.assertRenderEqual("{{ s|force_escape }}", "a&amp;b", s=s)
    
  27. 
    
  28.     def test_mark_safe_str(self):
    
  29.         """
    
  30.         Calling str() on a SafeString instance doesn't lose the safe status.
    
  31.         """
    
  32.         s = mark_safe("a&b")
    
  33.         self.assertIsInstance(str(s), type(s))
    
  34. 
    
  35.     def test_mark_safe_object_implementing_dunder_html(self):
    
  36.         e = customescape("<a&b>")
    
  37.         s = mark_safe(e)
    
  38.         self.assertIs(s, e)
    
  39. 
    
  40.         self.assertRenderEqual("{{ s }}", "<<a&b>>", s=s)
    
  41.         self.assertRenderEqual("{{ s|force_escape }}", "&lt;a&amp;b&gt;", s=s)
    
  42. 
    
  43.     def test_mark_safe_lazy(self):
    
  44.         safe_s = mark_safe(lazystr("a&b"))
    
  45. 
    
  46.         self.assertIsInstance(safe_s, Promise)
    
  47.         self.assertRenderEqual("{{ s }}", "a&b", s=safe_s)
    
  48.         self.assertIsInstance(str(safe_s), SafeData)
    
  49. 
    
  50.     def test_mark_safe_lazy_i18n(self):
    
  51.         s = mark_safe(gettext_lazy("name"))
    
  52.         tpl = Template("{{ s }}")
    
  53.         with translation.override("fr"):
    
  54.             self.assertEqual(tpl.render(Context({"s": s})), "nom")
    
  55. 
    
  56.     def test_mark_safe_object_implementing_dunder_str(self):
    
  57.         class Obj:
    
  58.             def __str__(self):
    
  59.                 return "<obj>"
    
  60. 
    
  61.         s = mark_safe(Obj())
    
  62. 
    
  63.         self.assertRenderEqual("{{ s }}", "<obj>", s=s)
    
  64. 
    
  65.     def test_mark_safe_result_implements_dunder_html(self):
    
  66.         self.assertEqual(mark_safe("a&b").__html__(), "a&b")
    
  67. 
    
  68.     def test_mark_safe_lazy_result_implements_dunder_html(self):
    
  69.         self.assertEqual(mark_safe(lazystr("a&b")).__html__(), "a&b")
    
  70. 
    
  71.     def test_add_lazy_safe_text_and_safe_text(self):
    
  72.         s = html.escape(lazystr("a"))
    
  73.         s += mark_safe("&b")
    
  74.         self.assertRenderEqual("{{ s }}", "a&b", s=s)
    
  75. 
    
  76.         s = html.escapejs(lazystr("a"))
    
  77.         s += mark_safe("&b")
    
  78.         self.assertRenderEqual("{{ s }}", "a&b", s=s)
    
  79. 
    
  80.     def test_mark_safe_as_decorator(self):
    
  81.         """
    
  82.         mark_safe used as a decorator leaves the result of a function
    
  83.         unchanged.
    
  84.         """
    
  85. 
    
  86.         def clean_string_provider():
    
  87.             return "<html><body>dummy</body></html>"
    
  88. 
    
  89.         self.assertEqual(mark_safe(clean_string_provider)(), clean_string_provider())
    
  90. 
    
  91.     def test_mark_safe_decorator_does_not_affect_dunder_html(self):
    
  92.         """
    
  93.         mark_safe doesn't affect a callable that has an __html__() method.
    
  94.         """
    
  95. 
    
  96.         class SafeStringContainer:
    
  97.             def __html__(self):
    
  98.                 return "<html></html>"
    
  99. 
    
  100.         self.assertIs(mark_safe(SafeStringContainer), SafeStringContainer)
    
  101. 
    
  102.     def test_mark_safe_decorator_does_not_affect_promises(self):
    
  103.         """
    
  104.         mark_safe doesn't affect lazy strings (Promise objects).
    
  105.         """
    
  106. 
    
  107.         def html_str():
    
  108.             return "<html></html>"
    
  109. 
    
  110.         lazy_str = lazy(html_str, str)()
    
  111.         self.assertEqual(mark_safe(lazy_str), html_str())
    
  112. 
    
  113.     def test_default_additional_attrs(self):
    
  114.         s = SafeString("a&b")
    
  115.         msg = "object has no attribute 'dynamic_attr'"
    
  116.         with self.assertRaisesMessage(AttributeError, msg):
    
  117.             s.dynamic_attr = True
    
  118. 
    
  119.     def test_default_safe_data_additional_attrs(self):
    
  120.         s = SafeData()
    
  121.         msg = "object has no attribute 'dynamic_attr'"
    
  122.         with self.assertRaisesMessage(AttributeError, msg):
    
  123.             s.dynamic_attr = True