From 4e6f9a254910ce3524ea9a1c9bfb6feb54716f01 Mon Sep 17 00:00:00 2001
From: Armin Ronacher <armin.ronacher@active-4.com>
Date: Fri, 23 May 2008 23:57:38 +0200
Subject: [PATCH] added unittest for Markup and let the markup constructor
 accept `__html__` objects

--HG--
branch : trunk
---
 jinja2/filters.py      |  2 +-
 jinja2/utils.py        |  5 +++++
 tests/test_filters.py  | 31 +++++++++++++++++++------------
 tests/test_security.py | 31 +++++++++++++++++++++++++++++++
 4 files changed, 56 insertions(+), 13 deletions(-)

diff --git a/jinja2/filters.py b/jinja2/filters.py
index 94086a6..2cbb46c 100644
--- a/jinja2/filters.py
+++ b/jinja2/filters.py
@@ -74,7 +74,7 @@ def do_replace(environment, s, old, new, count=None):
         s = escape(s)
     else:
         s = soft_unicode(s)
-    return s.replace(old, new, count)
+    return s.replace(soft_unicode(old), soft_unicode(new), count)
 
 
 def do_upper(s):
diff --git a/jinja2/utils.py b/jinja2/utils.py
index 90eb737..2a671d0 100644
--- a/jinja2/utils.py
+++ b/jinja2/utils.py
@@ -234,6 +234,11 @@ class Markup(unicode):
     """
     __slots__ = ()
 
+    def __new__(cls, base=u''):
+        if hasattr(base, '__html__'):
+            base = base.__html__()
+        return unicode.__new__(cls, base)
+
     def __html__(self):
         return self
 
diff --git a/tests/test_filters.py b/tests/test_filters.py
index 6592027..559b0b1 100644
--- a/tests/test_filters.py
+++ b/tests/test_filters.py
@@ -3,17 +3,11 @@
     unit test for the filters
     ~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    Missing tests:
-
-    -   wordcount
-    -   rst
-    -   markdown
-    -   textile
-
-    :copyright: 2007 by Armin Ronacher.
+    :copyright: 2008 by Armin Ronacher.
     :license: BSD, see LICENSE for more details.
 """
-from jinja2 import Markup
+from jinja2 import Markup, Environment
+
 
 CAPITALIZE = '''{{ "foo bar"|capitalize }}'''
 CENTER = '''{{ "foo"|center(9) }}'''
@@ -164,6 +158,10 @@ def test_join(env):
     out = tmpl.render()
     assert out == '1|2|3'
 
+    env2 = Environment(autoescape=True)
+    tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}')
+    assert tmpl.render() == '&lt;foo&gt;<span>foo</span>'
+
 
 def test_last(env):
     tmpl = env.from_string(LAST)
@@ -293,9 +291,18 @@ def test_filtertag(env):
     assert tmpl.render() == 'fooBAR'
 
 
-def test_replace(env):
-    tmpl = env.from_string('{{ "foo"|replace("o", 42)}}')
-    assert tmpl.render() == 'f4242'
+def test_replace():
+    env = Environment()
+    tmpl = env.from_string('{{ string|replace("o", 42) }}')
+    assert tmpl.render(string='<foo>') == '<f4242>'
+
+    env = Environment(autoescape=True)
+    tmpl = env.from_string('{{ string|replace("o", 42) }}')
+    assert tmpl.render(string='<foo>') == '&lt;f4242&gt;'
+    tmpl = env.from_string('{{ string|replace("<", 42) }}')
+    assert tmpl.render(string='<foo>') == '42foo&gt;'
+    tmpl = env.from_string('{{ string|replace("o", ">x<") }}')
+    assert tmpl.render(string=Markup('foo')) == 'f&gt;x&lt;&gt;x&lt;'
 
 
 def test_forceescape(env):
diff --git a/tests/test_security.py b/tests/test_security.py
index 5974e1f..0cacf5f 100644
--- a/tests/test_security.py
+++ b/tests/test_security.py
@@ -8,6 +8,7 @@
 """
 from jinja2.sandbox import SandboxedEnvironment, \
      ImmutableSandboxedEnvironment, unsafe
+from jinja2 import Markup, escape
 
 
 class PrivateStuff(object):
@@ -82,3 +83,33 @@ Traceback (most recent call last):
     ...
 SecurityError: access to attribute 'clear' of 'dict' object is unsafe.
 '''
+
+def test_markup_operations():
+    # adding two strings should escape the unsafe one
+    unsafe = '<script type="application/x-some-script">alert("foo");</script>'
+    safe = Markup('<em>username</em>')
+    assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe)
+
+    # string interpolations are safe to use too
+    assert Markup('<em>%s</em>') % '<bad user>' == \
+           '<em>&lt;bad user&gt;</em>'
+    assert Markup('<em>%(username)s</em>') % {
+        'username': '<bad user>'
+    } == '<em>&lt;bad user&gt;</em>'
+
+    # an escaped object is markup too
+    assert type(Markup('foo') + 'bar') is Markup
+
+    # and it implements __html__ by returning itself
+    x = Markup("foo")
+    assert x.__html__() is x
+
+    # it also knows how to treat __html__ objects
+    class Foo(object):
+        def __html__(self):
+            return '<em>awesome</em>'
+        def __unicode__(self):
+            return 'awesome'
+    assert Markup(Foo()) == '<em>awesome</em>'
+    assert Markup('<strong>%s</strong>') % Foo() == \
+           '<strong><em>awesome</em></strong>'
-- 
2.26.2