from django.template import Context, Template from django.test import SimpleTestCase from django.utils import html, translation from django.utils.functional import Promise, lazy, lazystr from django.utils.safestring import SafeData, SafeString, mark_safe from django.utils.translation import gettext_lazy class customescape(str): def __html__(self): # Implement specific and wrong escaping in order to be able to detect # when it runs. return self.replace("<", "<<").replace(">", ">>") class SafeStringTest(SimpleTestCase): def assertRenderEqual(self, tpl, expected, **context): context = Context(context) tpl = Template(tpl) self.assertEqual(tpl.render(context), expected) def test_mark_safe(self): s = mark_safe("a&b") self.assertRenderEqual("{{ s }}", "a&b", s=s) self.assertRenderEqual("{{ s|force_escape }}", "a&b", s=s) def test_mark_safe_str(self): """ Calling str() on a SafeString instance doesn't lose the safe status. """ s = mark_safe("a&b") self.assertIsInstance(str(s), type(s)) def test_mark_safe_object_implementing_dunder_html(self): e = customescape("") s = mark_safe(e) self.assertIs(s, e) self.assertRenderEqual("{{ s }}", "<>", s=s) self.assertRenderEqual("{{ s|force_escape }}", "<a&b>", s=s) def test_mark_safe_lazy(self): safe_s = mark_safe(lazystr("a&b")) self.assertIsInstance(safe_s, Promise) self.assertRenderEqual("{{ s }}", "a&b", s=safe_s) self.assertIsInstance(str(safe_s), SafeData) def test_mark_safe_lazy_i18n(self): s = mark_safe(gettext_lazy("name")) tpl = Template("{{ s }}") with translation.override("fr"): self.assertEqual(tpl.render(Context({"s": s})), "nom") def test_mark_safe_object_implementing_dunder_str(self): class Obj: def __str__(self): return "" s = mark_safe(Obj()) self.assertRenderEqual("{{ s }}", "", s=s) def test_mark_safe_result_implements_dunder_html(self): self.assertEqual(mark_safe("a&b").__html__(), "a&b") def test_mark_safe_lazy_result_implements_dunder_html(self): self.assertEqual(mark_safe(lazystr("a&b")).__html__(), "a&b") def test_add_lazy_safe_text_and_safe_text(self): s = html.escape(lazystr("a")) s += mark_safe("&b") self.assertRenderEqual("{{ s }}", "a&b", s=s) s = html.escapejs(lazystr("a")) s += mark_safe("&b") self.assertRenderEqual("{{ s }}", "a&b", s=s) def test_mark_safe_as_decorator(self): """ mark_safe used as a decorator leaves the result of a function unchanged. """ def clean_string_provider(): return "dummy" self.assertEqual(mark_safe(clean_string_provider)(), clean_string_provider()) def test_mark_safe_decorator_does_not_affect_dunder_html(self): """ mark_safe doesn't affect a callable that has an __html__() method. """ class SafeStringContainer: def __html__(self): return "" self.assertIs(mark_safe(SafeStringContainer), SafeStringContainer) def test_mark_safe_decorator_does_not_affect_promises(self): """ mark_safe doesn't affect lazy strings (Promise objects). """ def html_str(): return "" lazy_str = lazy(html_str, str)() self.assertEqual(mark_safe(lazy_str), html_str()) def test_default_additional_attrs(self): s = SafeString("a&b") msg = "object has no attribute 'dynamic_attr'" with self.assertRaisesMessage(AttributeError, msg): s.dynamic_attr = True def test_default_safe_data_additional_attrs(self): s = SafeData() msg = "object has no attribute 'dynamic_attr'" with self.assertRaisesMessage(AttributeError, msg): s.dynamic_attr = True