diff --git a/tests/template_tests/filter_tests/__init__.py b/tests/template_tests/filter_tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/template_tests/filter_tests/test_add.py b/tests/template_tests/filter_tests/test_add.py
new file mode 100644
index 0000000000..9faf81dfa5
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_add.py
@@ -0,0 +1,46 @@
+from datetime import date, timedelta
+
+from django.test import SimpleTestCase
+
+from ..utils import render, setup
+
+
+class AddTests(SimpleTestCase):
+ """
+ Tests for #11687 and #16676
+ """
+
+ @setup({'add01': '{{ i|add:"5" }}'})
+ def test_add01(self):
+ output = render('add01', {'i': 2000})
+ self.assertEqual(output, '2005')
+
+ @setup({'add02': '{{ i|add:"napis" }}'})
+ def test_add02(self):
+ output = render('add02', {'i': 2000})
+ self.assertEqual(output, '')
+
+ @setup({'add03': '{{ i|add:16 }}'})
+ def test_add03(self):
+ output = render('add03', {'i': 'not_an_int'})
+ self.assertEqual(output, '')
+
+ @setup({'add04': '{{ i|add:"16" }}'})
+ def test_add04(self):
+ output = render('add04', {'i': 'not_an_int'})
+ self.assertEqual(output, 'not_an_int16')
+
+ @setup({'add05': '{{ l1|add:l2 }}'})
+ def test_add05(self):
+ output = render('add05', {'l1': [1, 2], 'l2': [3, 4]})
+ self.assertEqual(output, '[1, 2, 3, 4]')
+
+ @setup({'add06': '{{ t1|add:t2 }}'})
+ def test_add06(self):
+ output = render('add06', {'t1': (3, 4), 't2': (1, 2)})
+ self.assertEqual(output, '(3, 4, 1, 2)')
+
+ @setup({'add07': '{{ d|add:t }}'})
+ def test_add07(self):
+ output = render('add07', {'d': date(2000, 1, 1), 't': timedelta(10)})
+ self.assertEqual(output, 'Jan. 11, 2000')
diff --git a/tests/template_tests/filter_tests/test_addslashes.py b/tests/template_tests/filter_tests/test_addslashes.py
new file mode 100644
index 0000000000..4d712b4bd4
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_addslashes.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class AddslashesTests(SimpleTestCase):
+
+ @setup({'addslashes01': '{% autoescape off %}{{ a|addslashes }} {{ b|addslashes }}{% endautoescape %}'})
+ def test_addslashes01(self):
+ output = render('addslashes01', {"a": "'", "b": mark_safe("'")})
+ self.assertEqual(output, r"\' \'")
+
+ @setup({'addslashes02': '{{ a|addslashes }} {{ b|addslashes }}'})
+ def test_addslashes02(self):
+ output = render('addslashes02', {"a": "'", "b": mark_safe("'")})
+ self.assertEqual(output, r"<a>\' \'")
diff --git a/tests/template_tests/filter_tests/test_autoescape.py b/tests/template_tests/filter_tests/test_autoescape.py
new file mode 100644
index 0000000000..159e1fad3e
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_autoescape.py
@@ -0,0 +1,29 @@
+from django.test import SimpleTestCase
+
+from ..utils import render, setup, SafeClass, UnsafeClass
+
+
+class AutoescapeStringfilterTests(SimpleTestCase):
+ """
+ Filters decorated with stringfilter still respect is_safe.
+ """
+
+ @setup({'autoescape-stringfilter01': '{{ unsafe|capfirst }}'})
+ def test_autoescape_stringfilter01(self):
+ output = render('autoescape-stringfilter01', {'unsafe': UnsafeClass()})
+ self.assertEqual(output, 'You & me')
+
+ @setup({'autoescape-stringfilter02': '{% autoescape off %}{{ unsafe|capfirst }}{% endautoescape %}'})
+ def test_autoescape_stringfilter02(self):
+ output = render('autoescape-stringfilter02', {'unsafe': UnsafeClass()})
+ self.assertEqual(output, 'You & me')
+
+ @setup({'autoescape-stringfilter03': '{{ safe|capfirst }}'})
+ def test_autoescape_stringfilter03(self):
+ output = render('autoescape-stringfilter03', {'safe': SafeClass()})
+ self.assertEqual(output, 'You > me')
+
+ @setup({'autoescape-stringfilter04': '{% autoescape off %}{{ safe|capfirst }}{% endautoescape %}'})
+ def test_autoescape_stringfilter04(self):
+ output = render('autoescape-stringfilter04', {'safe': SafeClass()})
+ self.assertEqual(output, 'You > me')
diff --git a/tests/template_tests/filter_tests/test_capfirst.py b/tests/template_tests/filter_tests/test_capfirst.py
new file mode 100644
index 0000000000..07546ad6c7
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_capfirst.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class CapfirstTests(SimpleTestCase):
+
+ @setup({'capfirst01': '{% autoescape off %}{{ a|capfirst }} {{ b|capfirst }}{% endautoescape %}'})
+ def test_capfirst01(self):
+ output = render('capfirst01', {'a': 'fred>', 'b': mark_safe('fred>')})
+ self.assertEqual(output, 'Fred> Fred>')
+
+ @setup({'capfirst02': '{{ a|capfirst }} {{ b|capfirst }}'})
+ def test_capfirst02(self):
+ output = render('capfirst02', {'a': 'fred>', 'b': mark_safe('fred>')})
+ self.assertEqual(output, 'Fred> Fred>')
diff --git a/tests/template_tests/filter_tests/test_center.py b/tests/template_tests/filter_tests/test_center.py
new file mode 100644
index 0000000000..6f10f99a85
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_center.py
@@ -0,0 +1,18 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class CenterTests(SimpleTestCase):
+
+ @setup({'center01':
+ '{% autoescape off %}.{{ a|center:"5" }}. .{{ b|center:"5" }}.{% endautoescape %}'})
+ def test_center01(self):
+ output = render('center01', {"a": "a&b", "b": mark_safe("a&b")})
+ self.assertEqual(output, ". a&b . . a&b .")
+
+ @setup({'center02': '.{{ a|center:"5" }}. .{{ b|center:"5" }}.'})
+ def test_center02(self):
+ output = render('center02', {"a": "a&b", "b": mark_safe("a&b")})
+ self.assertEqual(output, ". a&b . . a&b .")
diff --git a/tests/template_tests/filter_tests/test_chaining.py b/tests/template_tests/filter_tests/test_chaining.py
new file mode 100644
index 0000000000..964bc4b873
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_chaining.py
@@ -0,0 +1,86 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class ChainingTests(SimpleTestCase):
+ """
+ Chaining safeness-preserving filters should not alter the safe status.
+ """
+
+ @setup({'chaining01': '{{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }}'})
+ def test_chaining01(self):
+ output = render('chaining01', {'a': 'a < b', 'b': mark_safe('a < b')})
+ self.assertEqual(output, ' A < b . A < b ')
+
+ @setup({'chaining02':
+ '{% autoescape off %}{{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }}{% endautoescape %}'})
+ def test_chaining02(self):
+ output = render('chaining02', {'a': 'a < b', 'b': mark_safe('a < b')})
+ self.assertEqual(output, ' A < b . A < b ')
+
+ # Using a filter that forces a string back to unsafe:
+ @setup({'chaining03': '{{ a|cut:"b"|capfirst }}.{{ b|cut:"b"|capfirst }}'})
+ def test_chaining03(self):
+ output = render('chaining03', {'a': 'a < b', 'b': mark_safe('a < b')})
+ self.assertEqual(output, 'A < .A < ')
+
+ @setup({'chaining04':
+ '{% autoescape off %}{{ a|cut:"b"|capfirst }}.{{ b|cut:"b"|capfirst }}{% endautoescape %}'})
+ def test_chaining04(self):
+ output = render('chaining04', {'a': 'a < b', 'b': mark_safe('a < b')})
+ self.assertEqual(output, 'A < .A < ')
+
+ # Using a filter that forces safeness does not lead to double-escaping
+ @setup({'chaining05': '{{ a|escape|capfirst }}'})
+ def test_chaining05(self):
+ output = render('chaining05', {'a': 'a < b'})
+ self.assertEqual(output, 'A < b')
+
+ @setup({'chaining06': '{% autoescape off %}{{ a|escape|capfirst }}{% endautoescape %}'})
+ def test_chaining06(self):
+ output = render('chaining06', {'a': 'a < b'})
+ self.assertEqual(output, 'A < b')
+
+ # Force to safe, then back (also showing why using force_escape too
+ # early in a chain can lead to unexpected results).
+ @setup({'chaining07': '{{ a|force_escape|cut:";" }}'})
+ def test_chaining07(self):
+ output = render('chaining07', {'a': 'a < b'})
+ self.assertEqual(output, 'a < b')
+
+ @setup({'chaining08': '{% autoescape off %}{{ a|force_escape|cut:";" }}{% endautoescape %}'})
+ def test_chaining08(self):
+ output = render('chaining08', {'a': 'a < b'})
+ self.assertEqual(output, 'a < b')
+
+ @setup({'chaining09': '{{ a|cut:";"|force_escape }}'})
+ def test_chaining09(self):
+ output = render('chaining09', {'a': 'a < b'})
+ self.assertEqual(output, 'a < b')
+
+ @setup({'chaining10': '{% autoescape off %}{{ a|cut:";"|force_escape }}{% endautoescape %}'})
+ def test_chaining10(self):
+ output = render('chaining10', {'a': 'a < b'})
+ self.assertEqual(output, 'a < b')
+
+ @setup({'chaining11': '{{ a|cut:"b"|safe }}'})
+ def test_chaining11(self):
+ output = render('chaining11', {'a': 'a < b'})
+ self.assertEqual(output, 'a < ')
+
+ @setup({'chaining12': '{% autoescape off %}{{ a|cut:"b"|safe }}{% endautoescape %}'})
+ def test_chaining12(self):
+ output = render('chaining12', {'a': 'a < b'})
+ self.assertEqual(output, 'a < ')
+
+ @setup({'chaining13': '{{ a|safe|force_escape }}'})
+ def test_chaining13(self):
+ output = render('chaining13', {"a": "a < b"})
+ self.assertEqual(output, 'a < b')
+
+ @setup({'chaining14': '{% autoescape off %}{{ a|safe|force_escape }}{% endautoescape %}'})
+ def test_chaining14(self):
+ output = render('chaining14', {"a": "a < b"})
+ self.assertEqual(output, 'a < b')
diff --git a/tests/template_tests/filter_tests/test_cut.py b/tests/template_tests/filter_tests/test_cut.py
new file mode 100644
index 0000000000..68568df30c
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_cut.py
@@ -0,0 +1,39 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class CutTests(SimpleTestCase):
+
+ @setup({'cut01': '{% autoescape off %}{{ a|cut:"x" }} {{ b|cut:"x" }}{% endautoescape %}'})
+ def test_cut01(self):
+ output = render('cut01', {"a": "x&y", "b": mark_safe("x&y")})
+ self.assertEqual(output, "&y &y")
+
+ @setup({'cut02': '{{ a|cut:"x" }} {{ b|cut:"x" }}'})
+ def test_cut02(self):
+ output = render('cut02', {"a": "x&y", "b": mark_safe("x&y")})
+ self.assertEqual(output, "&y &y")
+
+ @setup({'cut03': '{% autoescape off %}{{ a|cut:"&" }} {{ b|cut:"&" }}{% endautoescape %}'})
+ def test_cut03(self):
+ output = render('cut03', {"a": "x&y", "b": mark_safe("x&y")})
+ self.assertEqual(output, "xy xamp;y")
+
+ @setup({'cut04': '{{ a|cut:"&" }} {{ b|cut:"&" }}'})
+ def test_cut04(self):
+ output = render('cut04', {"a": "x&y", "b": mark_safe("x&y")})
+ self.assertEqual(output, "xy xamp;y")
+
+ # Passing ';' to cut can break existing HTML entities, so those strings
+ # are auto-escaped.
+ @setup({'cut05': '{% autoescape off %}{{ a|cut:";" }} {{ b|cut:";" }}{% endautoescape %}'})
+ def test_cut05(self):
+ output = render('cut05', {"a": "x&y", "b": mark_safe("x&y")})
+ self.assertEqual(output, "x&y x&y")
+
+ @setup({'cut06': '{{ a|cut:";" }} {{ b|cut:";" }}'})
+ def test_cut06(self):
+ output = render('cut06', {"a": "x&y", "b": mark_safe("x&y")})
+ self.assertEqual(output, "x&y x&y")
diff --git a/tests/template_tests/filter_tests/test_date.py b/tests/template_tests/filter_tests/test_date.py
new file mode 100644
index 0000000000..cbec356908
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_date.py
@@ -0,0 +1,60 @@
+from datetime import datetime, time
+
+from django.utils import timezone
+
+from .timezone_utils import TimezoneTestCase
+from ..utils import render, setup
+
+
+class DateTests(TimezoneTestCase):
+
+ @setup({'date01': '{{ d|date:"m" }}'})
+ def test_date01(self):
+ output = render('date01', {'d': datetime(2008, 1, 1)})
+ self.assertEqual(output, '01')
+
+ @setup({'date02': '{{ d|date }}'})
+ def test_date02(self):
+ output = render('date02', {'d': datetime(2008, 1, 1)})
+ self.assertEqual(output, 'Jan. 1, 2008')
+
+ @setup({'date03': '{{ d|date:"m" }}'})
+ def test_date03(self):
+ """
+ #9520: Make sure |date doesn't blow up on non-dates
+ """
+ output = render('date03', {'d': 'fail_string'})
+ self.assertEqual(output, '')
+
+ # ISO date formats
+ @setup({'date04': '{{ d|date:"o" }}'})
+ def test_date04(self):
+ output = render('date04', {'d': datetime(2008, 12, 29)})
+ self.assertEqual(output, '2009')
+
+ @setup({'date05': '{{ d|date:"o" }}'})
+ def test_date05(self):
+ output = render('date05', {'d': datetime(2010, 1, 3)})
+ self.assertEqual(output, '2009')
+
+ # Timezone name
+ @setup({'date06': '{{ d|date:"e" }}'})
+ def test_date06(self):
+ output = render('date06', {'d': datetime(2009, 3, 12, tzinfo=timezone.get_fixed_timezone(30))})
+ self.assertEqual(output, '+0030')
+
+ @setup({'date07': '{{ d|date:"e" }}'})
+ def test_date07(self):
+ output = render('date07', {'d': datetime(2009, 3, 12)})
+ self.assertEqual(output, '')
+
+ # #19370: Make sure |date doesn't blow up on a midnight time object
+ @setup({'date08': '{{ t|date:"H:i" }}'})
+ def test_date08(self):
+ output = render('date08', {'t': time(0, 1)})
+ self.assertEqual(output, '00:01')
+
+ @setup({'date09': '{{ t|date:"H:i" }}'})
+ def test_date09(self):
+ output = render('date09', {'t': time(0, 0)})
+ self.assertEqual(output, '00:00')
diff --git a/tests/template_tests/filter_tests/test_default.py b/tests/template_tests/filter_tests/test_default.py
new file mode 100644
index 0000000000..790f60cd3b
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_default.py
@@ -0,0 +1,47 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class DefaultTests(SimpleTestCase):
+ """
+ Literal string arguments to the default filter are always treated as
+ safe strings, regardless of the auto-escaping state.
+
+ Note: we have to use {"a": ""} here, otherwise the invalid template
+ variable string interferes with the test result.
+ """
+
+ @setup({'default01': '{{ a|default:"x<" }}'})
+ def test_default01(self):
+ output = render('default01', {"a": ""})
+ self.assertEqual(output, "x<")
+
+ @setup({'default02': '{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}'})
+ def test_default02(self):
+ output = render('default02', {"a": ""})
+ self.assertEqual(output, "x<")
+
+ @setup({'default03': '{{ a|default:"x<" }}'})
+ def test_default03(self):
+ output = render('default03', {"a": mark_safe("x>")})
+ self.assertEqual(output, "x>")
+
+ @setup({'default04': '{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}'})
+ def test_default04(self):
+ output = render('default04', {"a": mark_safe("x>")})
+ self.assertEqual(output, "x>")
+
+
+class DefaultIfNoneTests(SimpleTestCase):
+
+ @setup({'default_if_none01': '{{ a|default:"x<" }}'})
+ def test_default_if_none01(self):
+ output = render('default_if_none01', {"a": None})
+ self.assertEqual(output, "x<")
+
+ @setup({'default_if_none02': '{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}'})
+ def test_default_if_none02(self):
+ output = render('default_if_none02', {"a": None})
+ self.assertEqual(output, "x<")
diff --git a/tests/template_tests/filter_tests/test_escape.py b/tests/template_tests/filter_tests/test_escape.py
new file mode 100644
index 0000000000..81f0f4037a
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_escape.py
@@ -0,0 +1,33 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class EscapeTests(SimpleTestCase):
+ """
+ The "escape" filter works the same whether autoescape is on or off,
+ but it has no effect on strings already marked as safe.
+ """
+
+ @setup({'escape01': '{{ a|escape }} {{ b|escape }}'})
+ def test_escape01(self):
+ output = render('escape01', {"a": "x&y", "b": mark_safe("x&y")})
+ self.assertEqual(output, "x&y x&y")
+
+ @setup({'escape02': '{% autoescape off %}{{ a|escape }} {{ b|escape }}{% endautoescape %}'})
+ def test_escape02(self):
+ output = render('escape02', {"a": "x&y", "b": mark_safe("x&y")})
+ self.assertEqual(output, "x&y x&y")
+
+ # It is only applied once, regardless of the number of times it
+ # appears in a chain.
+ @setup({'escape03': '{% autoescape off %}{{ a|escape|escape }}{% endautoescape %}'})
+ def test_escape03(self):
+ output = render('escape03', {"a": "x&y"})
+ self.assertEqual(output, "x&y")
+
+ @setup({'escape04': '{{ a|escape|escape }}'})
+ def test_escape04(self):
+ output = render('escape04', {"a": "x&y"})
+ self.assertEqual(output, "x&y")
diff --git a/tests/template_tests/filter_tests/test_escapejs.py b/tests/template_tests/filter_tests/test_escapejs.py
new file mode 100644
index 0000000000..82019022ec
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_escapejs.py
@@ -0,0 +1,20 @@
+from django.test import SimpleTestCase
+
+from ..utils import render, setup
+
+
+class EscapejsTests(SimpleTestCase):
+
+ @setup({'escapejs01': '{{ a|escapejs }}'})
+ def test_escapejs01(self):
+ output = render('escapejs01', {'a': 'testing\r\njavascript \'string" escaping'})
+ self.assertEqual(output, 'testing\\u000D\\u000Ajavascript '
+ '\\u0027string\\u0022 \\u003Cb\\u003E'
+ 'escaping\\u003C/b\\u003E')
+
+ @setup({'escapejs02': '{% autoescape off %}{{ a|escapejs }}{% endautoescape %}'})
+ def test_escapejs02(self):
+ output = render('escapejs02', {'a': 'testing\r\njavascript \'string" escaping'})
+ self.assertEqual(output, 'testing\\u000D\\u000Ajavascript '
+ '\\u0027string\\u0022 \\u003Cb\\u003E'
+ 'escaping\\u003C/b\\u003E')
diff --git a/tests/template_tests/filter_tests/test_first.py b/tests/template_tests/filter_tests/test_first.py
new file mode 100644
index 0000000000..566a70c4ac
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_first.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class FirstTests(SimpleTestCase):
+
+ @setup({'first01': '{{ a|first }} {{ b|first }}'})
+ def test_first01(self):
+ output = render('first01', {"a": ["a&b", "x"], "b": [mark_safe("a&b"), "x"]})
+ self.assertEqual(output, "a&b a&b")
+
+ @setup({'first02': '{% autoescape off %}{{ a|first }} {{ b|first }}{% endautoescape %}'})
+ def test_first02(self):
+ output = render('first02', {"a": ["a&b", "x"], "b": [mark_safe("a&b"), "x"]})
+ self.assertEqual(output, "a&b a&b")
diff --git a/tests/template_tests/filter_tests/test_floatformat.py b/tests/template_tests/filter_tests/test_floatformat.py
new file mode 100644
index 0000000000..a69278f958
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_floatformat.py
@@ -0,0 +1,18 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class FloatformatTests(SimpleTestCase):
+
+ @setup({'floatformat01':
+ '{% autoescape off %}{{ a|floatformat }} {{ b|floatformat }}{% endautoescape %}'})
+ def test_floatformat01(self):
+ output = render('floatformat01', {"a": "1.42", "b": mark_safe("1.42")})
+ self.assertEqual(output, "1.4 1.4")
+
+ @setup({'floatformat02': '{{ a|floatformat }} {{ b|floatformat }}'})
+ def test_floatformat02(self):
+ output = render('floatformat02', {"a": "1.42", "b": mark_safe("1.42")})
+ self.assertEqual(output, "1.4 1.4")
diff --git a/tests/template_tests/filter_tests/test_force_escape.py b/tests/template_tests/filter_tests/test_force_escape.py
new file mode 100644
index 0000000000..1217e07793
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_force_escape.py
@@ -0,0 +1,52 @@
+from django.test import SimpleTestCase
+
+from ..utils import render, setup
+
+
+class ForceEscapeTests(SimpleTestCase):
+ """
+ Force_escape is applied immediately. It can be used to provide
+ double-escaping, for example.
+ """
+
+ @setup({'force-escape01': '{% autoescape off %}{{ a|force_escape }}{% endautoescape %}'})
+ def test_force_escape01(self):
+ output = render('force-escape01', {"a": "x&y"})
+ self.assertEqual(output, "x&y")
+
+ @setup({'force-escape02': '{{ a|force_escape }}'})
+ def test_force_escape02(self):
+ output = render('force-escape02', {"a": "x&y"})
+ self.assertEqual(output, "x&y")
+
+ @setup({'force-escape03': '{% autoescape off %}{{ a|force_escape|force_escape }}{% endautoescape %}'})
+ def test_force_escape03(self):
+ output = render('force-escape03', {"a": "x&y"})
+ self.assertEqual(output, "x&y")
+
+ @setup({'force-escape04': '{{ a|force_escape|force_escape }}'})
+ def test_force_escape04(self):
+ output = render('force-escape04', {"a": "x&y"})
+ self.assertEqual(output, "x&y")
+
+ # Because the result of force_escape is "safe", an additional
+ # escape filter has no effect.
+ @setup({'force-escape05': '{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}'})
+ def test_force_escape05(self):
+ output = render('force-escape05', {"a": "x&y"})
+ self.assertEqual(output, "x&y")
+
+ @setup({'force-escape06': '{{ a|force_escape|escape }}'})
+ def test_force_escape06(self):
+ output = render('force-escape06', {"a": "x&y"})
+ self.assertEqual(output, "x&y")
+
+ @setup({'force-escape07': '{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}'})
+ def test_force_escape07(self):
+ output = render('force-escape07', {"a": "x&y"})
+ self.assertEqual(output, "x&y")
+
+ @setup({'force-escape08': '{{ a|escape|force_escape }}'})
+ def test_force_escape08(self):
+ output = render('force-escape08', {"a": "x&y"})
+ self.assertEqual(output, "x&y")
diff --git a/tests/template_tests/filter_tests/test_iriencode.py b/tests/template_tests/filter_tests/test_iriencode.py
new file mode 100644
index 0000000000..b06f05b909
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_iriencode.py
@@ -0,0 +1,30 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class IriencodeTests(SimpleTestCase):
+ """
+ Ensure iriencode keeps safe strings.
+ """
+
+ @setup({'iriencode01': '{{ url|iriencode }}'})
+ def test_iriencode01(self):
+ output = render('iriencode01', {'url': '?test=1&me=2'})
+ self.assertEqual(output, '?test=1&me=2')
+
+ @setup({'iriencode02': '{% autoescape off %}{{ url|iriencode }}{% endautoescape %}'})
+ def test_iriencode02(self):
+ output = render('iriencode02', {'url': '?test=1&me=2'})
+ self.assertEqual(output, '?test=1&me=2')
+
+ @setup({'iriencode03': '{{ url|iriencode }}'})
+ def test_iriencode03(self):
+ output = render('iriencode03', {'url': mark_safe('?test=1&me=2')})
+ self.assertEqual(output, '?test=1&me=2')
+
+ @setup({'iriencode04': '{% autoescape off %}{{ url|iriencode }}{% endautoescape %}'})
+ def test_iriencode04(self):
+ output = render('iriencode04', {'url': mark_safe('?test=1&me=2')})
+ self.assertEqual(output, '?test=1&me=2')
diff --git a/tests/template_tests/filter_tests/test_join.py b/tests/template_tests/filter_tests/test_join.py
new file mode 100644
index 0000000000..a6c93e7332
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_join.py
@@ -0,0 +1,49 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class JoinTests(SimpleTestCase):
+
+ @setup({'join01': '{{ a|join:", " }}'})
+ def test_join01(self):
+ output = render('join01', {'a': ['alpha', 'beta & me']})
+ self.assertEqual(output, 'alpha, beta & me')
+
+ @setup({'join02': '{% autoescape off %}{{ a|join:", " }}{% endautoescape %}'})
+ def test_join02(self):
+ output = render('join02', {'a': ['alpha', 'beta & me']})
+ self.assertEqual(output, 'alpha, beta & me')
+
+ @setup({'join03': '{{ a|join:" & " }}'})
+ def test_join03(self):
+ output = render('join03', {'a': ['alpha', 'beta & me']})
+ self.assertEqual(output, 'alpha & beta & me')
+
+ @setup({'join04': '{% autoescape off %}{{ a|join:" & " }}{% endautoescape %}'})
+ def test_join04(self):
+ output = render('join04', {'a': ['alpha', 'beta & me']})
+ self.assertEqual(output, 'alpha & beta & me')
+
+ # #11377 Test that joining with unsafe joiners doesn't result in
+ # unsafe strings
+ @setup({'join05': '{{ a|join:var }}'})
+ def test_join05(self):
+ output = render('join05', {'a': ['alpha', 'beta & me'], 'var': ' & '})
+ self.assertEqual(output, 'alpha & beta & me')
+
+ @setup({'join06': '{{ a|join:var }}'})
+ def test_join06(self):
+ output = render('join06', {'a': ['alpha', 'beta & me'], 'var': mark_safe(' & ')})
+ self.assertEqual(output, 'alpha & beta & me')
+
+ @setup({'join07': '{{ a|join:var|lower }}'})
+ def test_join07(self):
+ output = render('join07', {'a': ['Alpha', 'Beta & me'], 'var': ' & '})
+ self.assertEqual(output, 'alpha & beta & me')
+
+ @setup({'join08': '{{ a|join:var|lower }}'})
+ def test_join08(self):
+ output = render('join08', {'a': ['Alpha', 'Beta & me'], 'var': mark_safe(' & ')})
+ self.assertEqual(output, 'alpha & beta & me')
diff --git a/tests/template_tests/filter_tests/test_last.py b/tests/template_tests/filter_tests/test_last.py
new file mode 100644
index 0000000000..1dcee663e9
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_last.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class LastTests(SimpleTestCase):
+
+ @setup({'last01': '{{ a|last }} {{ b|last }}'})
+ def test_last01(self):
+ output = render('last01', {"a": ["x", "a&b"], "b": ["x", mark_safe("a&b")]})
+ self.assertEqual(output, "a&b a&b")
+
+ @setup({'last02': '{% autoescape off %}{{ a|last }} {{ b|last }}{% endautoescape %}'})
+ def test_last02(self):
+ output = render('last02', {"a": ["x", "a&b"], "b": ["x", mark_safe("a&b")]})
+ self.assertEqual(output, "a&b a&b")
diff --git a/tests/template_tests/filter_tests/test_length.py b/tests/template_tests/filter_tests/test_length.py
new file mode 100644
index 0000000000..f81912ce03
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_length.py
@@ -0,0 +1,43 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class LengthTests(SimpleTestCase):
+
+ @setup({'length01': '{{ list|length }}'})
+ def test_length01(self):
+ output = render('length01', {'list': ['4', None, True, {}]})
+ self.assertEqual(output, '4')
+
+ @setup({'length02': '{{ list|length }}'})
+ def test_length02(self):
+ output = render('length02', {'list': []})
+ self.assertEqual(output, '0')
+
+ @setup({'length03': '{{ string|length }}'})
+ def test_length03(self):
+ output = render('length03', {'string': ''})
+ self.assertEqual(output, '0')
+
+ @setup({'length04': '{{ string|length }}'})
+ def test_length04(self):
+ output = render('length04', {'string': 'django'})
+ self.assertEqual(output, '6')
+
+ @setup({'length05': '{% if string|length == 6 %}Pass{% endif %}'})
+ def test_length05(self):
+ output = render('length05', {'string': mark_safe('django')})
+ self.assertEqual(output, 'Pass')
+
+ # Invalid uses that should fail silently.
+ @setup({'length06': '{{ int|length }}'})
+ def test_length06(self):
+ output = render('length06', {'int': 7})
+ self.assertEqual(output, '0')
+
+ @setup({'length07': '{{ None|length }}'})
+ def test_length07(self):
+ output = render('length07', {'None': None})
+ self.assertEqual(output, '0')
diff --git a/tests/template_tests/filter_tests/test_length_is.py b/tests/template_tests/filter_tests/test_length_is.py
new file mode 100644
index 0000000000..5d67d453d7
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_length_is.py
@@ -0,0 +1,63 @@
+from django.test import SimpleTestCase
+
+from ..utils import render, setup
+
+
+class LengthIsTests(SimpleTestCase):
+
+ @setup({'length_is01': '{% if some_list|length_is:"4" %}Four{% endif %}'})
+ def test_length_is01(self):
+ output = render('length_is01', {'some_list': ['4', None, True, {}]})
+ self.assertEqual(output, 'Four')
+
+ @setup({'length_is02': '{% if some_list|length_is:"4" %}Four{% else %}Not Four{% endif %}'})
+ def test_length_is02(self):
+ output = render('length_is02', {'some_list': ['4', None, True, {}, 17]})
+ self.assertEqual(output, 'Not Four')
+
+ @setup({'length_is03': '{% if mystring|length_is:"4" %}Four{% endif %}'})
+ def test_length_is03(self):
+ output = render('length_is03', {'mystring': 'word'})
+ self.assertEqual(output, 'Four')
+
+ @setup({'length_is04': '{% if mystring|length_is:"4" %}Four{% else %}Not Four{% endif %}'})
+ def test_length_is04(self):
+ output = render('length_is04', {'mystring': 'Python'})
+ self.assertEqual(output, 'Not Four')
+
+ @setup({'length_is05': '{% if mystring|length_is:"4" %}Four{% else %}Not Four{% endif %}'})
+ def test_length_is05(self):
+ output = render('length_is05', {'mystring': ''})
+ self.assertEqual(output, 'Not Four')
+
+ @setup({'length_is06': '{% with var|length as my_length %}{{ my_length }}{% endwith %}'})
+ def test_length_is06(self):
+ output = render('length_is06', {'var': 'django'})
+ self.assertEqual(output, '6')
+
+ # Boolean return value from length_is should not be coerced to a string
+ @setup({'length_is07': '{% if "X"|length_is:0 %}Length is 0{% else %}Length not 0{% endif %}'})
+ def test_length_is07(self):
+ output = render('length_is07', {})
+ self.assertEqual(output, 'Length not 0')
+
+ @setup({'length_is08': '{% if "X"|length_is:1 %}Length is 1{% else %}Length not 1{% endif %}'})
+ def test_length_is08(self):
+ output = render('length_is08', {})
+ self.assertEqual(output, 'Length is 1')
+
+ # Invalid uses that should fail silently.
+ @setup({'length_is09': '{{ var|length_is:"fish" }}'})
+ def test_length_is09(self):
+ output = render('length_is09', {'var': 'django'})
+ self.assertEqual(output, '')
+
+ @setup({'length_is10': '{{ int|length_is:"1" }}'})
+ def test_length_is10(self):
+ output = render('length_is10', {'int': 7})
+ self.assertEqual(output, '')
+
+ @setup({'length_is11': '{{ none|length_is:"1" }}'})
+ def test_length_is11(self):
+ output = render('length_is11', {'none': None})
+ self.assertEqual(output, '')
diff --git a/tests/template_tests/filter_tests/test_linebreaks.py b/tests/template_tests/filter_tests/test_linebreaks.py
new file mode 100644
index 0000000000..924fc0aefa
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_linebreaks.py
@@ -0,0 +1,22 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class LinebreaksTests(SimpleTestCase):
+ """
+ The contents in "linebreaks" are escaped according to the current
+ autoescape setting.
+ """
+
+ @setup({'linebreaks01': '{{ a|linebreaks }} {{ b|linebreaks }}'})
+ def test_linebreaks01(self):
+ output = render('linebreaks01', {"a": "x&\ny", "b": mark_safe("x&\ny")})
+ self.assertEqual(output, "x&
y
x&
y
")
+
+ @setup({'linebreaks02':
+ '{% autoescape off %}{{ a|linebreaks }} {{ b|linebreaks }}{% endautoescape %}'})
+ def test_linebreaks02(self):
+ output = render('linebreaks02', {"a": "x&\ny", "b": mark_safe("x&\ny")})
+ self.assertEqual(output, "x&
y
x&
y
")
diff --git a/tests/template_tests/filter_tests/test_linebreaksbr.py b/tests/template_tests/filter_tests/test_linebreaksbr.py
new file mode 100644
index 0000000000..c65b0973fb
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_linebreaksbr.py
@@ -0,0 +1,22 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class LinebreaksbrTests(SimpleTestCase):
+ """
+ The contents in "linebreaksbr" are escaped according to the current
+ autoescape setting.
+ """
+
+ @setup({'linebreaksbr01': '{{ a|linebreaksbr }} {{ b|linebreaksbr }}'})
+ def test_linebreaksbr01(self):
+ output = render('linebreaksbr01', {"a": "x&\ny", "b": mark_safe("x&\ny")})
+ self.assertEqual(output, "x&
y x&
y")
+
+ @setup({'linebreaksbr02':
+ '{% autoescape off %}{{ a|linebreaksbr }} {{ b|linebreaksbr }}{% endautoescape %}'})
+ def test_linebreaksbr02(self):
+ output = render('linebreaksbr02', {"a": "x&\ny", "b": mark_safe("x&\ny")})
+ self.assertEqual(output, "x&
y x&
y")
diff --git a/tests/template_tests/filter_tests/test_linenumbers.py b/tests/template_tests/filter_tests/test_linenumbers.py
new file mode 100644
index 0000000000..d74ce5dcfe
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_linenumbers.py
@@ -0,0 +1,30 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class LinenumbersTests(SimpleTestCase):
+ """
+ The contents of "linenumbers" is escaped according to the current
+ autoescape setting.
+ """
+
+ @setup({'linenumbers01': '{{ a|linenumbers }} {{ b|linenumbers }}'})
+ def test_linenumbers01(self):
+ output = render(
+ 'linenumbers01',
+ {'a': 'one\n\nthree', 'b': mark_safe('one\n<two>\nthree')},
+ )
+ self.assertEqual(output, '1. one\n2. <two>\n3. three '
+ '1. one\n2. <two>\n3. three')
+
+ @setup({'linenumbers02':
+ '{% autoescape off %}{{ a|linenumbers }} {{ b|linenumbers }}{% endautoescape %}'})
+ def test_linenumbers02(self):
+ output = render(
+ 'linenumbers02',
+ {'a': 'one\n\nthree', 'b': mark_safe('one\n<two>\nthree')},
+ )
+ self.assertEqual(output, '1. one\n2. \n3. three '
+ '1. one\n2. <two>\n3. three')
diff --git a/tests/template_tests/filter_tests/test_ljust.py b/tests/template_tests/filter_tests/test_ljust.py
new file mode 100644
index 0000000000..e5b0f84a8f
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_ljust.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class LjustTests(SimpleTestCase):
+
+ @setup({'ljust01': '{% autoescape off %}.{{ a|ljust:"5" }}. .{{ b|ljust:"5" }}.{% endautoescape %}'})
+ def test_ljust01(self):
+ output = render('ljust01', {"a": "a&b", "b": mark_safe("a&b")})
+ self.assertEqual(output, ".a&b . .a&b .")
+
+ @setup({'ljust02': '.{{ a|ljust:"5" }}. .{{ b|ljust:"5" }}.'})
+ def test_ljust02(self):
+ output = render('ljust02', {"a": "a&b", "b": mark_safe("a&b")})
+ self.assertEqual(output, ".a&b . .a&b .")
diff --git a/tests/template_tests/filter_tests/test_lower.py b/tests/template_tests/filter_tests/test_lower.py
new file mode 100644
index 0000000000..1443386062
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_lower.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class LowerTests(SimpleTestCase):
+
+ @setup({'lower01': '{% autoescape off %}{{ a|lower }} {{ b|lower }}{% endautoescape %}'})
+ def test_lower01(self):
+ output = render('lower01', {"a": "Apple & banana", "b": mark_safe("Apple & banana")})
+ self.assertEqual(output, "apple & banana apple & banana")
+
+ @setup({'lower02': '{{ a|lower }} {{ b|lower }}'})
+ def test_lower02(self):
+ output = render('lower02', {"a": "Apple & banana", "b": mark_safe("Apple & banana")})
+ self.assertEqual(output, "apple & banana apple & banana")
diff --git a/tests/template_tests/filter_tests/test_make_list.py b/tests/template_tests/filter_tests/test_make_list.py
new file mode 100644
index 0000000000..42cfbfaf56
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_make_list.py
@@ -0,0 +1,33 @@
+from django.test import SimpleTestCase
+from django.test.utils import str_prefix
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class MakeListTests(SimpleTestCase):
+ """
+ The make_list filter can destroy existing escaping, so the results are
+ escaped.
+ """
+
+ @setup({'make_list01': '{% autoescape off %}{{ a|make_list }}{% endautoescape %}'})
+ def test_make_list01(self):
+ output = render('make_list01', {"a": mark_safe("&")})
+ self.assertEqual(output, str_prefix("[%(_)s'&']"))
+
+ @setup({'make_list02': '{{ a|make_list }}'})
+ def test_make_list02(self):
+ output = render('make_list02', {"a": mark_safe("&")})
+ self.assertEqual(output, str_prefix("[%(_)s'&']"))
+
+ @setup({'make_list03':
+ '{% autoescape off %}{{ a|make_list|stringformat:"s"|safe }}{% endautoescape %}'})
+ def test_make_list03(self):
+ output = render('make_list03', {"a": mark_safe("&")})
+ self.assertEqual(output, str_prefix("[%(_)s'&']"))
+
+ @setup({'make_list04': '{{ a|make_list|stringformat:"s"|safe }}'})
+ def test_make_list04(self):
+ output = render('make_list04', {"a": mark_safe("&")})
+ self.assertEqual(output, str_prefix("[%(_)s'&']"))
diff --git a/tests/template_tests/filter_tests/test_phone2numeric.py b/tests/template_tests/filter_tests/test_phone2numeric.py
new file mode 100644
index 0000000000..8b899d70a3
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_phone2numeric.py
@@ -0,0 +1,35 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class Phone2numericTests(SimpleTestCase):
+
+ @setup({'phone2numeric01': '{{ a|phone2numeric }} {{ b|phone2numeric }}'})
+ def test_phone2numeric01(self):
+ output = render(
+ 'phone2numeric01',
+ {'a': '<1-800-call-me>', 'b': mark_safe('<1-800-call-me>')},
+ )
+ self.assertEqual(output, '<1-800-2255-63> <1-800-2255-63>')
+
+ @setup({'phone2numeric02':
+ '{% autoescape off %}{{ a|phone2numeric }} {{ b|phone2numeric }}{% endautoescape %}'})
+ def test_phone2numeric02(self):
+ output = render(
+ 'phone2numeric02',
+ {'a': '<1-800-call-me>', 'b': mark_safe('<1-800-call-me>')},
+ )
+ self.assertEqual(output, '<1-800-2255-63> <1-800-2255-63>')
+
+ @setup({'phone2numeric03': '{{ a|phone2numeric }}'})
+ def test_phone2numeric03(self):
+ output = render(
+ 'phone2numeric03',
+ {'a': 'How razorback-jumping frogs can level six piqued gymnasts!'},
+ )
+ self.assertEqual(
+ output,
+ '469 729672225-5867464 37647 226 53835 749 747833 49662787!'
+ )
diff --git a/tests/template_tests/filter_tests/test_random.py b/tests/template_tests/filter_tests/test_random.py
new file mode 100644
index 0000000000..2b78c546ad
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_random.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class RandomTests(SimpleTestCase):
+
+ @setup({'random01': '{{ a|random }} {{ b|random }}'})
+ def test_random01(self):
+ output = render('random01', {'a': ['a&b', 'a&b'], 'b': [mark_safe('a&b'), mark_safe('a&b')]})
+ self.assertEqual(output, 'a&b a&b')
+
+ @setup({'random02': '{% autoescape off %}{{ a|random }} {{ b|random }}{% endautoescape %}'})
+ def test_random02(self):
+ output = render('random02', {'a': ['a&b', 'a&b'], 'b': [mark_safe('a&b'), mark_safe('a&b')]})
+ self.assertEqual(output, 'a&b a&b')
diff --git a/tests/template_tests/filter_tests/test_removetags.py b/tests/template_tests/filter_tests/test_removetags.py
new file mode 100644
index 0000000000..7abe810fd8
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_removetags.py
@@ -0,0 +1,37 @@
+import warnings
+
+from django.test import SimpleTestCase
+from django.utils.deprecation import RemovedInDjango20Warning
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class RemovetagsTests(SimpleTestCase):
+
+ @setup({'removetags01': '{{ a|removetags:"a b" }} {{ b|removetags:"a b" }}'})
+ def test_removetags01(self):
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', RemovedInDjango20Warning)
+ output = render(
+ 'removetags01',
+ {
+ 'a': 'x y
',
+ 'b': mark_safe('x y
'),
+ },
+ )
+ self.assertEqual(output, 'x <p>y</p> x y
')
+
+ @setup({'removetags02':
+ '{% autoescape off %}{{ a|removetags:"a b" }} {{ b|removetags:"a b" }}{% endautoescape %}'})
+ def test_removetags02(self):
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', RemovedInDjango20Warning)
+ output = render(
+ 'removetags02',
+ {
+ 'a': 'x y
',
+ 'b': mark_safe('x y
'),
+ },
+ )
+ self.assertEqual(output, 'x y
x y
')
diff --git a/tests/template_tests/filter_tests/test_rjust.py b/tests/template_tests/filter_tests/test_rjust.py
new file mode 100644
index 0000000000..e20ce36706
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_rjust.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class RjustTests(SimpleTestCase):
+
+ @setup({'rjust01': '{% autoescape off %}.{{ a|rjust:"5" }}. .{{ b|rjust:"5" }}.{% endautoescape %}'})
+ def test_rjust01(self):
+ output = render('rjust01', {"a": "a&b", "b": mark_safe("a&b")})
+ self.assertEqual(output, ". a&b. . a&b.")
+
+ @setup({'rjust02': '.{{ a|rjust:"5" }}. .{{ b|rjust:"5" }}.'})
+ def test_rjust02(self):
+ output = render('rjust02', {"a": "a&b", "b": mark_safe("a&b")})
+ self.assertEqual(output, ". a&b. . a&b.")
diff --git a/tests/template_tests/filter_tests/test_safe.py b/tests/template_tests/filter_tests/test_safe.py
new file mode 100644
index 0000000000..cd9ab47ca1
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_safe.py
@@ -0,0 +1,16 @@
+from django.test import SimpleTestCase
+
+from ..utils import render, setup
+
+
+class SafeTests(SimpleTestCase):
+
+ @setup({'safe01': '{{ a }} -- {{ a|safe }}'})
+ def test_safe01(self):
+ output = render('safe01', {'a': 'hello'})
+ self.assertEqual(output, '<b>hello</b> -- hello')
+
+ @setup({'safe02': '{% autoescape off %}{{ a }} -- {{ a|safe }}{% endautoescape %}'})
+ def test_safe02(self):
+ output = render('safe02', {'a': 'hello'})
+ self.assertEqual(output, 'hello -- hello')
diff --git a/tests/template_tests/filter_tests/test_safeseq.py b/tests/template_tests/filter_tests/test_safeseq.py
new file mode 100644
index 0000000000..733b3f0715
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_safeseq.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+
+from ..utils import render, setup
+
+
+class SafeseqTests(SimpleTestCase):
+
+ @setup({'safeseq01': '{{ a|join:", " }} -- {{ a|safeseq|join:", " }}'})
+ def test_safeseq01(self):
+ output = render('safeseq01', {'a': ['&', '<']})
+ self.assertEqual(output, '&, < -- &, <')
+
+ @setup({'safeseq02':
+ '{% autoescape off %}{{ a|join:", " }} -- {{ a|safeseq|join:", " }}{% endautoescape %}'})
+ def test_safeseq02(self):
+ output = render('safeseq02', {'a': ['&', '<']})
+ self.assertEqual(output, '&, < -- &, <')
diff --git a/tests/template_tests/filter_tests/test_slice.py b/tests/template_tests/filter_tests/test_slice.py
new file mode 100644
index 0000000000..c66f1f2241
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_slice.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class SliceTests(SimpleTestCase):
+
+ @setup({'slice01': '{{ a|slice:"1:3" }} {{ b|slice:"1:3" }}'})
+ def test_slice01(self):
+ output = render('slice01', {'a': 'a&b', 'b': mark_safe('a&b')})
+ self.assertEqual(output, '&b &b')
+
+ @setup({'slice02': '{% autoescape off %}{{ a|slice:"1:3" }} {{ b|slice:"1:3" }}{% endautoescape %}'})
+ def test_slice02(self):
+ output = render('slice02', {'a': 'a&b', 'b': mark_safe('a&b')})
+ self.assertEqual(output, '&b &b')
diff --git a/tests/template_tests/filter_tests/test_slugify.py b/tests/template_tests/filter_tests/test_slugify.py
new file mode 100644
index 0000000000..d18502b27c
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_slugify.py
@@ -0,0 +1,21 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class SlugifyTests(SimpleTestCase):
+ """
+ Running slugify on a pre-escaped string leads to odd behavior,
+ but the result is still safe.
+ """
+
+ @setup({'slugify01': '{% autoescape off %}{{ a|slugify }} {{ b|slugify }}{% endautoescape %}'})
+ def test_slugify01(self):
+ output = render('slugify01', {'a': 'a & b', 'b': mark_safe('a & b')})
+ self.assertEqual(output, 'a-b a-amp-b')
+
+ @setup({'slugify02': '{{ a|slugify }} {{ b|slugify }}'})
+ def test_slugify02(self):
+ output = render('slugify02', {'a': 'a & b', 'b': mark_safe('a & b')})
+ self.assertEqual(output, 'a-b a-amp-b')
diff --git a/tests/template_tests/filter_tests/test_stringformat.py b/tests/template_tests/filter_tests/test_stringformat.py
new file mode 100644
index 0000000000..ef762f2e0a
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_stringformat.py
@@ -0,0 +1,22 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class StringformatTests(SimpleTestCase):
+ """
+ Notice that escaping is applied *after* any filters, so the string
+ formatting here only needs to deal with pre-escaped characters.
+ """
+
+ @setup({'stringformat01':
+ '{% autoescape off %}.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.{% endautoescape %}'})
+ def test_stringformat01(self):
+ output = render('stringformat01', {'a': 'ax
y
',
+ 'b': mark_safe('x y
'),
+ },
+ )
+ self.assertEqual(output, 'x y x y')
+
+ @setup({'striptags02': '{% autoescape off %}{{ a|striptags }} {{ b|striptags }}{% endautoescape %}'})
+ def test_striptags02(self):
+ output = render(
+ 'striptags02',
+ {
+ 'a': 'x y
',
+ 'b': mark_safe('x y
'),
+ },
+ )
+ self.assertEqual(output, 'x y x y')
diff --git a/tests/template_tests/filter_tests/test_time.py b/tests/template_tests/filter_tests/test_time.py
new file mode 100644
index 0000000000..a7fa5026cd
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_time.py
@@ -0,0 +1,42 @@
+from datetime import time
+
+from django.utils import timezone
+
+from .timezone_utils import TimezoneTestCase
+from ..utils import render, setup
+
+
+class TimeTests(TimezoneTestCase):
+ """
+ #20693: Timezone support for the time template filter
+ """
+
+ @setup({'time01': '{{ dt|time:"e:O:T:Z" }}'})
+ def test_time01(self):
+ output = render('time01', {'dt': self.now_tz_i})
+ self.assertEqual(output, '+0315:+0315:+0315:11700')
+
+ @setup({'time02': '{{ dt|time:"e:T" }}'})
+ def test_time02(self):
+ output = render('time02', {'dt': self.now})
+ self.assertEqual(output, ':' + self.now_tz.tzinfo.tzname(self.now_tz))
+
+ @setup({'time03': '{{ t|time:"P:e:O:T:Z" }}'})
+ def test_time03(self):
+ output = render('time03', {'t': time(4, 0, tzinfo=timezone.get_fixed_timezone(30))})
+ self.assertEqual(output, '4 a.m.::::')
+
+ @setup({'time04': '{{ t|time:"P:e:O:T:Z" }}'})
+ def test_time04(self):
+ output = render('time04', {'t': time(4, 0)})
+ self.assertEqual(output, '4 a.m.::::')
+
+ @setup({'time05': '{{ d|time:"P:e:O:T:Z" }}'})
+ def test_time05(self):
+ output = render('time05', {'d': self.today})
+ self.assertEqual(output, '')
+
+ @setup({'time06': '{{ obj|time:"P:e:O:T:Z" }}'})
+ def test_time06(self):
+ output = render('time06', {'obj': 'non-datetime-value'})
+ self.assertEqual(output, '')
diff --git a/tests/template_tests/filter_tests/test_timesince.py b/tests/template_tests/filter_tests/test_timesince.py
new file mode 100644
index 0000000000..999358a7f2
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_timesince.py
@@ -0,0 +1,118 @@
+from __future__ import unicode_literals
+
+from datetime import datetime, timedelta
+
+from django.test.utils import requires_tz_support
+
+from .timezone_utils import TimezoneTestCase
+from ..utils import render, setup
+
+
+class TimesinceTests(TimezoneTestCase):
+ """
+ #20246 - \xa0 in output avoids line-breaks between value and unit
+ """
+
+ # Default compare with datetime.now()
+ @setup({'timesince01': '{{ a|timesince }}'})
+ def test_timesince01(self):
+ output = render('timesince01', {'a': datetime.now() + timedelta(minutes=-1, seconds=-10)})
+ self.assertEqual(output, '1\xa0minute')
+
+ @setup({'timesince02': '{{ a|timesince }}'})
+ def test_timesince02(self):
+ output = render('timesince02', {'a': datetime.now() - timedelta(days=1, minutes=1)})
+ self.assertEqual(output, '1\xa0day')
+
+ @setup({'timesince03': '{{ a|timesince }}'})
+ def test_timesince03(self):
+ output = render('timesince03', {'a': datetime.now() - timedelta(hours=1, minutes=25, seconds=10)})
+ self.assertEqual(output, '1\xa0hour, 25\xa0minutes')
+
+ # Compare to a given parameter
+ @setup({'timesince04': '{{ a|timesince:b }}'})
+ def test_timesince04(self):
+ output = render(
+ 'timesince04',
+ {'a': self.now - timedelta(days=2), 'b': self.now - timedelta(days=1)},
+ )
+ self.assertEqual(output, '1\xa0day')
+
+ @setup({'timesince05': '{{ a|timesince:b }}'})
+ def test_timesince05(self):
+ output = render(
+ 'timesince05',
+ {'a': self.now - timedelta(days=2, minutes=1), 'b': self.now - timedelta(days=2)},
+ )
+ self.assertEqual(output, '1\xa0minute')
+
+ # Check that timezone is respected
+ @setup({'timesince06': '{{ a|timesince:b }}'})
+ def test_timesince06(self):
+ output = render('timesince06', {'a': self.now_tz - timedelta(hours=8), 'b': self.now_tz})
+ self.assertEqual(output, '8\xa0hours')
+
+ # Tests for #7443
+ @setup({'timesince07': '{{ earlier|timesince }}'})
+ def test_timesince07(self):
+ output = render('timesince07', {'earlier': self.now - timedelta(days=7)})
+ self.assertEqual(output, '1\xa0week')
+
+ @setup({'timesince08': '{{ earlier|timesince:now }}'})
+ def test_timesince08(self):
+ output = render('timesince08', {'now': self.now, 'earlier': self.now - timedelta(days=7)})
+ self.assertEqual(output, '1\xa0week')
+
+ @setup({'timesince09': '{{ later|timesince }}'})
+ def test_timesince09(self):
+ output = render('timesince09', {'later': self.now + timedelta(days=7)})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @setup({'timesince10': '{{ later|timesince:now }}'})
+ def test_timesince10(self):
+ output = render('timesince10', {'now': self.now, 'later': self.now + timedelta(days=7)})
+ self.assertEqual(output, '0\xa0minutes')
+
+ # Ensures that differing timezones are calculated correctly.
+ @setup({'timesince11': '{{ a|timesince }}'})
+ def test_timesince11(self):
+ output = render('timesince11', {'a': self.now})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @requires_tz_support
+ @setup({'timesince12': '{{ a|timesince }}'})
+ def test_timesince12(self):
+ output = render('timesince12', {'a': self.now_tz})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @requires_tz_support
+ @setup({'timesince13': '{{ a|timesince }}'})
+ def test_timesince13(self):
+ output = render('timesince13', {'a': self.now_tz_i})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @setup({'timesince14': '{{ a|timesince:b }}'})
+ def test_timesince14(self):
+ output = render('timesince14', {'a': self.now_tz, 'b': self.now_tz_i})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @setup({'timesince15': '{{ a|timesince:b }}'})
+ def test_timesince15(self):
+ output = render('timesince15', {'a': self.now, 'b': self.now_tz_i})
+ self.assertEqual(output, '')
+
+ @setup({'timesince16': '{{ a|timesince:b }}'})
+ def test_timesince16(self):
+ output = render('timesince16', {'a': self.now_tz_i, 'b': self.now})
+ self.assertEqual(output, '')
+
+ # Tests for #9065 (two date objects).
+ @setup({'timesince17': '{{ a|timesince:b }}'})
+ def test_timesince17(self):
+ output = render('timesince17', {'a': self.today, 'b': self.today})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @setup({'timesince18': '{{ a|timesince:b }}'})
+ def test_timesince18(self):
+ output = render('timesince18', {'a': self.today, 'b': self.today + timedelta(hours=24)})
+ self.assertEqual(output, '1\xa0day')
diff --git a/tests/template_tests/filter_tests/test_timeuntil.py b/tests/template_tests/filter_tests/test_timeuntil.py
new file mode 100644
index 0000000000..1662bb605c
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_timeuntil.py
@@ -0,0 +1,94 @@
+from __future__ import unicode_literals
+
+from datetime import datetime, timedelta
+
+from django.test.utils import requires_tz_support
+
+from .timezone_utils import TimezoneTestCase
+from ..utils import render, setup
+
+
+class TimeuntilTests(TimezoneTestCase):
+
+ # Default compare with datetime.now()
+ @setup({'timeuntil01': '{{ a|timeuntil }}'})
+ def test_timeuntil01(self):
+ output = render('timeuntil01', {'a': datetime.now() + timedelta(minutes=2, seconds=10)})
+ self.assertEqual(output, '2\xa0minutes')
+
+ @setup({'timeuntil02': '{{ a|timeuntil }}'})
+ def test_timeuntil02(self):
+ output = render('timeuntil02', {'a': (datetime.now() + timedelta(days=1, seconds=10))})
+ self.assertEqual(output, '1\xa0day')
+
+ @setup({'timeuntil03': '{{ a|timeuntil }}'})
+ def test_timeuntil03(self):
+ output = render('timeuntil03', {'a': (datetime.now() + timedelta(hours=8, minutes=10, seconds=10))})
+ self.assertEqual(output, '8\xa0hours, 10\xa0minutes')
+
+ # Compare to a given parameter
+ @setup({'timeuntil04': '{{ a|timeuntil:b }}'})
+ def test_timeuntil04(self):
+ output = render(
+ 'timeuntil04',
+ {'a': self.now - timedelta(days=1), 'b': self.now - timedelta(days=2)},
+ )
+ self.assertEqual(output, '1\xa0day')
+
+ @setup({'timeuntil05': '{{ a|timeuntil:b }}'})
+ def test_timeuntil05(self):
+ output = render(
+ 'timeuntil05',
+ {'a': self.now - timedelta(days=2), 'b': self.now - timedelta(days=2, minutes=1)},
+ )
+ self.assertEqual(output, '1\xa0minute')
+
+ # Regression for #7443
+ @setup({'timeuntil06': '{{ earlier|timeuntil }}'})
+ def test_timeuntil06(self):
+ output = render('timeuntil06', {'earlier': self.now - timedelta(days=7)})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @setup({'timeuntil07': '{{ earlier|timeuntil:now }}'})
+ def test_timeuntil07(self):
+ output = render('timeuntil07', {'now': self.now, 'earlier': self.now - timedelta(days=7)})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @setup({'timeuntil08': '{{ later|timeuntil }}'})
+ def test_timeuntil08(self):
+ output = render('timeuntil08', {'later': self.now + timedelta(days=7, hours=1)})
+ self.assertEqual(output, '1\xa0week')
+
+ @setup({'timeuntil09': '{{ later|timeuntil:now }}'})
+ def test_timeuntil09(self):
+ output = render('timeuntil09', {'now': self.now, 'later': self.now + timedelta(days=7)})
+ self.assertEqual(output, '1\xa0week')
+
+ # Ensures that differing timezones are calculated correctly.
+ @requires_tz_support
+ @setup({'timeuntil10': '{{ a|timeuntil }}'})
+ def test_timeuntil10(self):
+ output = render('timeuntil10', {'a': self.now_tz})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @requires_tz_support
+ @setup({'timeuntil11': '{{ a|timeuntil }}'})
+ def test_timeuntil11(self):
+ output = render('timeuntil11', {'a': self.now_tz_i})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @setup({'timeuntil12': '{{ a|timeuntil:b }}'})
+ def test_timeuntil12(self):
+ output = render('timeuntil12', {'a': self.now_tz_i, 'b': self.now_tz})
+ self.assertEqual(output, '0\xa0minutes')
+
+ # Regression for #9065 (two date objects).
+ @setup({'timeuntil13': '{{ a|timeuntil:b }}'})
+ def test_timeuntil13(self):
+ output = render('timeuntil13', {'a': self.today, 'b': self.today})
+ self.assertEqual(output, '0\xa0minutes')
+
+ @setup({'timeuntil14': '{{ a|timeuntil:b }}'})
+ def test_timeuntil14(self):
+ output = render('timeuntil14', {'a': self.today, 'b': self.today - timedelta(hours=24)})
+ self.assertEqual(output, '1\xa0day')
diff --git a/tests/template_tests/filter_tests/test_title.py b/tests/template_tests/filter_tests/test_title.py
new file mode 100644
index 0000000000..fc14330b67
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_title.py
@@ -0,0 +1,16 @@
+from django.test import SimpleTestCase
+
+from ..utils import render, setup
+
+
+class TitleTests(SimpleTestCase):
+
+ @setup({'title1': '{{ a|title }}'})
+ def test_title1(self):
+ output = render('title1', {'a': 'JOE\'S CRAB SHACK'})
+ self.assertEqual(output, 'Joe's Crab Shack')
+
+ @setup({'title2': '{{ a|title }}'})
+ def test_title2(self):
+ output = render('title2', {'a': '555 WEST 53RD STREET'})
+ self.assertEqual(output, '555 West 53rd Street')
diff --git a/tests/template_tests/filter_tests/test_truncatechars.py b/tests/template_tests/filter_tests/test_truncatechars.py
new file mode 100644
index 0000000000..f31bf90269
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_truncatechars.py
@@ -0,0 +1,16 @@
+from django.test import SimpleTestCase
+
+from ..utils import render, setup
+
+
+class TruncatecharsTests(SimpleTestCase):
+
+ @setup({'truncatechars01': '{{ a|truncatechars:5 }}'})
+ def test_truncatechars01(self):
+ output = render('truncatechars01', {'a': 'Testing, testing'})
+ self.assertEqual(output, 'Te...')
+
+ @setup({'truncatechars02': '{{ a|truncatechars:7 }}'})
+ def test_truncatechars02(self):
+ output = render('truncatechars02', {'a': 'Testing'})
+ self.assertEqual(output, 'Testing')
diff --git a/tests/template_tests/filter_tests/test_truncatewords.py b/tests/template_tests/filter_tests/test_truncatewords.py
new file mode 100644
index 0000000000..6f64d439f3
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_truncatewords.py
@@ -0,0 +1,18 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class TruncatewordsTests(SimpleTestCase):
+
+ @setup({'truncatewords01':
+ '{% autoescape off %}{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}{% endautoescape %}'})
+ def test_truncatewords01(self):
+ output = render('truncatewords01', {'a': 'alpha & bravo', 'b': mark_safe('alpha & bravo')})
+ self.assertEqual(output, 'alpha & ... alpha & ...')
+
+ @setup({'truncatewords02': '{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}'})
+ def test_truncatewords02(self):
+ output = render('truncatewords02', {'a': 'alpha & bravo', 'b': mark_safe('alpha & bravo')})
+ self.assertEqual(output, 'alpha & ... alpha & ...')
diff --git a/tests/template_tests/filter_tests/test_unordered_list.py b/tests/template_tests/filter_tests/test_unordered_list.py
new file mode 100644
index 0000000000..73eee2fb34
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_unordered_list.py
@@ -0,0 +1,75 @@
+import warnings
+
+from django.test import SimpleTestCase
+from django.utils.deprecation import RemovedInDjango20Warning
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class UnorderedListTests(SimpleTestCase):
+
+ @setup({'unordered_list01': '{{ a|unordered_list }}'})
+ def test_unordered_list01(self):
+ output = render('unordered_list01', {'a': ['x>', ['x>\n\t\n\t')
+
+ @setup({'unordered_list02': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'})
+ def test_unordered_list02(self):
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', RemovedInDjango20Warning)
+ output = render('unordered_list02', {'a': ['x>', ['x>\n\t\n\t')
+
+ @setup({'unordered_list03': '{{ a|unordered_list }}'})
+ def test_unordered_list03(self):
+ output = render('unordered_list03', {'a': ['x>', [mark_safe('x>\n\t\n\t')
+
+ @setup({'unordered_list04': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'})
+ def test_unordered_list04(self):
+ output = render('unordered_list04', {'a': ['x>', [mark_safe('x>\n\t\n\t')
+
+ @setup({'unordered_list05': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'})
+ def test_unordered_list05(self):
+ output = render('unordered_list05', {'a': ['x>', ['x>\n\t\n\t')
+
+
+class DeprecatedUnorderedListSyntaxTests(SimpleTestCase):
+
+ @setup({'unordered_list01': '{{ a|unordered_list }}'})
+ def test_unordered_list01(self):
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', RemovedInDjango20Warning)
+ output = render('unordered_list01', {'a': ['x>', [['x>\n\t\n\t')
+
+ @setup({'unordered_list02': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'})
+ def test_unordered_list02(self):
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', RemovedInDjango20Warning)
+ output = render('unordered_list02', {'a': ['x>', [['x>\n\t\n\t')
+
+ @setup({'unordered_list03': '{{ a|unordered_list }}'})
+ def test_unordered_list03(self):
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', RemovedInDjango20Warning)
+ output = render('unordered_list03', {'a': ['x>', [[mark_safe('x>\n\t\n\t')
+
+ @setup({'unordered_list04': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'})
+ def test_unordered_list04(self):
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', RemovedInDjango20Warning)
+ output = render('unordered_list04', {'a': ['x>', [[mark_safe('x>\n\t\n\t')
+
+ @setup({'unordered_list05': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'})
+ def test_unordered_list05(self):
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', RemovedInDjango20Warning)
+ output = render('unordered_list05', {'a': ['x>', [['x>\n\t\n\t')
diff --git a/tests/template_tests/filter_tests/test_upper.py b/tests/template_tests/filter_tests/test_upper.py
new file mode 100644
index 0000000000..b1fa5fcc06
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_upper.py
@@ -0,0 +1,21 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class UpperTests(SimpleTestCase):
+ """
+ The "upper" filter messes up entities (which are case-sensitive),
+ so it's not safe for non-escaping purposes.
+ """
+
+ @setup({'upper01': '{% autoescape off %}{{ a|upper }} {{ b|upper }}{% endautoescape %}'})
+ def test_upper01(self):
+ output = render('upper01', {'a': 'a & b', 'b': mark_safe('a & b')})
+ self.assertEqual(output, 'A & B A & B')
+
+ @setup({'upper02': '{{ a|upper }} {{ b|upper }}'})
+ def test_upper02(self):
+ output = render('upper02', {'a': 'a & b', 'b': mark_safe('a & b')})
+ self.assertEqual(output, 'A & B A & B')
diff --git a/tests/template_tests/filter_tests/test_urlencode.py b/tests/template_tests/filter_tests/test_urlencode.py
new file mode 100644
index 0000000000..ea68188c15
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_urlencode.py
@@ -0,0 +1,16 @@
+from django.test import SimpleTestCase
+
+from ..utils import render, setup
+
+
+class UrlencodeTests(SimpleTestCase):
+
+ @setup({'urlencode01': '{{ url|urlencode }}'})
+ def test_urlencode01(self):
+ output = render('urlencode01', {'url': '/test&"/me?/'})
+ self.assertEqual(output, '/test%26%22/me%3F/')
+
+ @setup({'urlencode02': '/test/{{ urlbit|urlencode:"" }}/'})
+ def test_urlencode02(self):
+ output = render('urlencode02', {'urlbit': 'escape/slash'})
+ self.assertEqual(output, '/test/escape%2Fslash/')
diff --git a/tests/template_tests/filter_tests/test_urlize.py b/tests/template_tests/filter_tests/test_urlize.py
new file mode 100644
index 0000000000..567f38ee4b
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_urlize.py
@@ -0,0 +1,70 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class UrlizeTests(SimpleTestCase):
+
+ @setup({'urlize01': '{% autoescape off %}{{ a|urlize }} {{ b|urlize }}{% endautoescape %}'})
+ def test_urlize01(self):
+ output = render(
+ 'urlize01',
+ {'a': 'http://example.com/?x=&y=', 'b': mark_safe('http://example.com?x=&y=<2>')},
+ )
+ self.assertEqual(
+ output,
+ 'http://example.com/?x=&y= '
+ 'http://example.com?x=&y=<2>'
+ )
+
+ @setup({'urlize02': '{{ a|urlize }} {{ b|urlize }}'})
+ def test_urlize02(self):
+ output = render(
+ 'urlize02',
+ {'a': "http://example.com/?x=&y=", 'b': mark_safe("http://example.com?x=&y=")},
+ )
+ self.assertEqual(
+ output,
+ 'http://example.com/?x=&y= '
+ 'http://example.com?x=&y='
+ )
+
+ @setup({'urlize03': '{% autoescape off %}{{ a|urlize }}{% endautoescape %}'})
+ def test_urlize03(self):
+ output = render('urlize03', {'a': mark_safe("a & b")})
+ self.assertEqual(output, 'a & b')
+
+ @setup({'urlize04': '{{ a|urlize }}'})
+ def test_urlize04(self):
+ output = render('urlize04', {'a': mark_safe("a & b")})
+ self.assertEqual(output, 'a & b')
+
+ # This will lead to a nonsense result, but at least it won't be
+ # exploitable for XSS purposes when auto-escaping is on.
+ @setup({'urlize05': '{% autoescape off %}{{ a|urlize }}{% endautoescape %}'})
+ def test_urlize05(self):
+ output = render('urlize05', {'a': ""})
+ self.assertEqual(output, "")
+
+ @setup({'urlize06': '{{ a|urlize }}'})
+ def test_urlize06(self):
+ output = render('urlize06', {'a': ""})
+ self.assertEqual(output, '<script>alert('foo')</script>')
+
+ # mailto: testing for urlize
+ @setup({'urlize07': '{{ a|urlize }}'})
+ def test_urlize07(self):
+ output = render('urlize07', {'a': "Email me at me@example.com"})
+ self.assertEqual(
+ output,
+ 'Email me at me@example.com',
+ )
+
+ @setup({'urlize08': '{{ a|urlize }}'})
+ def test_urlize08(self):
+ output = render('urlize08', {'a': "Email me at "})
+ self.assertEqual(
+ output,
+ 'Email me at <me@example.com>',
+ )
diff --git a/tests/template_tests/filter_tests/test_urlizetrunc.py b/tests/template_tests/filter_tests/test_urlizetrunc.py
new file mode 100644
index 0000000000..19f5f1dcae
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_urlizetrunc.py
@@ -0,0 +1,38 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class UrlizetruncTests(SimpleTestCase):
+
+ @setup({'urlizetrunc01':
+ '{% autoescape off %}{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}{% endautoescape %}'})
+ def test_urlizetrunc01(self):
+ output = render(
+ 'urlizetrunc01',
+ {
+ 'a': '"Unsafe" http://example.com/x=&y=',
+ 'b': mark_safe('"Safe" http://example.com?x=&y='),
+ },
+ )
+ self.assertEqual(
+ output,
+ '"Unsafe" http:... '
+ '"Safe" http:...'
+ )
+
+ @setup({'urlizetrunc02': '{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}'})
+ def test_urlizetrunc02(self):
+ output = render(
+ 'urlizetrunc02',
+ {
+ 'a': '"Unsafe" http://example.com/x=&y=',
+ 'b': mark_safe('"Safe" http://example.com?x=&y='),
+ },
+ )
+ self.assertEqual(
+ output,
+ '"Unsafe" http:... '
+ '"Safe" http:...'
+ )
diff --git a/tests/template_tests/filter_tests/test_wordcount.py b/tests/template_tests/filter_tests/test_wordcount.py
new file mode 100644
index 0000000000..8c1e43f539
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_wordcount.py
@@ -0,0 +1,17 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class WordcountTests(SimpleTestCase):
+
+ @setup({'wordcount01': '{% autoescape off %}{{ a|wordcount }} {{ b|wordcount }}{% endautoescape %}'})
+ def test_wordcount01(self):
+ output = render('wordcount01', {'a': 'a & b', 'b': mark_safe('a & b')})
+ self.assertEqual(output, '3 3')
+
+ @setup({'wordcount02': '{{ a|wordcount }} {{ b|wordcount }}'})
+ def test_wordcount02(self):
+ output = render('wordcount02', {'a': 'a & b', 'b': mark_safe('a & b')})
+ self.assertEqual(output, '3 3')
diff --git a/tests/template_tests/filter_tests/test_wordwrap.py b/tests/template_tests/filter_tests/test_wordwrap.py
new file mode 100644
index 0000000000..7b6489ca0f
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_wordwrap.py
@@ -0,0 +1,18 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import render, setup
+
+
+class WordwrapTests(SimpleTestCase):
+
+ @setup({'wordwrap01':
+ '{% autoescape off %}{{ a|wordwrap:"3" }} {{ b|wordwrap:"3" }}{% endautoescape %}'})
+ def test_wordwrap01(self):
+ output = render('wordwrap01', {'a': 'a & b', 'b': mark_safe('a & b')})
+ self.assertEqual(output, 'a &\nb a &\nb')
+
+ @setup({'wordwrap02': '{{ a|wordwrap:"3" }} {{ b|wordwrap:"3" }}'})
+ def test_wordwrap02(self):
+ output = render('wordwrap02', {'a': 'a & b', 'b': mark_safe('a & b')})
+ self.assertEqual(output, 'a &\nb a &\nb')
diff --git a/tests/template_tests/filter_tests/timezone_utils.py b/tests/template_tests/filter_tests/timezone_utils.py
new file mode 100644
index 0000000000..874c634cfd
--- /dev/null
+++ b/tests/template_tests/filter_tests/timezone_utils.py
@@ -0,0 +1,17 @@
+from datetime import date, datetime
+
+from django.test import SimpleTestCase
+from django.utils import timezone
+
+
+class TimezoneTestCase(SimpleTestCase):
+
+ def setUp(self):
+ self.now = datetime.now()
+ self.now_tz = timezone.make_aware(
+ self.now, timezone.get_default_timezone(),
+ )
+ self.now_tz_i = timezone.localtime(
+ self.now_tz, timezone.get_fixed_timezone(195),
+ )
+ self.today = date.today()
diff --git a/tests/template_tests/filters.py b/tests/template_tests/filters.py
deleted file mode 100644
index 97396fbc69..0000000000
--- a/tests/template_tests/filters.py
+++ /dev/null
@@ -1,387 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Tests for template filters (as opposed to template tags).
-
-The tests are hidden inside a function so that things like timestamps and
-timezones are only evaluated at the moment of execution and will therefore be
-consistent.
-"""
-from __future__ import unicode_literals
-
-from datetime import date, datetime, time, timedelta
-
-from django.test.utils import str_prefix, TZ_SUPPORT
-from django.utils.safestring import mark_safe
-from django.utils import timezone
-
-from .syntax_tests.utils import SafeClass, UnsafeClass
-
-
-# RESULT SYNTAX --
-# 'template_name': ('template contents', 'context dict',
-# 'expected string output' or Exception class)
-
-
-def get_filter_tests():
- now = datetime.now()
- now_tz = timezone.make_aware(now, timezone.get_default_timezone())
- now_tz_i = timezone.localtime(now_tz, timezone.get_fixed_timezone(195))
- today = date.today()
-
- # NOTE: \xa0 avoids wrapping between value and unit
- return {
- # Default compare with datetime.now()
- 'filter-timesince01': ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds=-10)}, '1\xa0minute'),
- 'filter-timesince02': ('{{ a|timesince }}', {'a': datetime.now() - timedelta(days=1, minutes=1)}, '1\xa0day'),
- 'filter-timesince03': ('{{ a|timesince }}', {'a': datetime.now() - timedelta(hours=1, minutes=25, seconds=10)}, '1\xa0hour, 25\xa0minutes'),
-
- # Compare to a given parameter
- 'filter-timesince04': ('{{ a|timesince:b }}', {'a': now - timedelta(days=2), 'b': now - timedelta(days=1)}, '1\xa0day'),
- 'filter-timesince05': ('{{ a|timesince:b }}', {'a': now - timedelta(days=2, minutes=1), 'b': now - timedelta(days=2)}, '1\xa0minute'),
-
- # Check that timezone is respected
- 'filter-timesince06': ('{{ a|timesince:b }}', {'a': now_tz - timedelta(hours=8), 'b': now_tz}, '8\xa0hours'),
-
- # Regression for #7443
- 'filter-timesince07': ('{{ earlier|timesince }}', {'earlier': now - timedelta(days=7)}, '1\xa0week'),
- 'filter-timesince08': ('{{ earlier|timesince:now }}', {'now': now, 'earlier': now - timedelta(days=7)}, '1\xa0week'),
- 'filter-timesince09': ('{{ later|timesince }}', {'later': now + timedelta(days=7)}, '0\xa0minutes'),
- 'filter-timesince10': ('{{ later|timesince:now }}', {'now': now, 'later': now + timedelta(days=7)}, '0\xa0minutes'),
-
- # Ensures that differing timezones are calculated correctly
- # Tests trying to compare a timezone-aware 'now' to now aren't supported on no-tz-support systems (e.g Windows).
- 'filter-timesince11': ('{{ a|timesince }}', {'a': now}, '0\xa0minutes'),
- 'filter-timesince12': ('{{ a|timesince }}', {'a': now_tz}, '0\xa0minutes') if TZ_SUPPORT else ('', {}, ''),
- 'filter-timesince13': ('{{ a|timesince }}', {'a': now_tz_i}, '0\xa0minutes') if TZ_SUPPORT else ('', {}, ''),
- 'filter-timesince14': ('{{ a|timesince:b }}', {'a': now_tz, 'b': now_tz_i}, '0\xa0minutes'),
- 'filter-timesince15': ('{{ a|timesince:b }}', {'a': now, 'b': now_tz_i}, ''),
- 'filter-timesince16': ('{{ a|timesince:b }}', {'a': now_tz_i, 'b': now}, ''),
-
- # Regression for #9065 (two date objects).
- 'filter-timesince17': ('{{ a|timesince:b }}', {'a': today, 'b': today}, '0\xa0minutes'),
- 'filter-timesince18': ('{{ a|timesince:b }}', {'a': today, 'b': today + timedelta(hours=24)}, '1\xa0day'),
-
- # Default compare with datetime.now()
- 'filter-timeuntil01': ('{{ a|timeuntil }}', {'a': datetime.now() + timedelta(minutes=2, seconds=10)}, '2\xa0minutes'),
- 'filter-timeuntil02': ('{{ a|timeuntil }}', {'a': (datetime.now() + timedelta(days=1, seconds=10))}, '1\xa0day'),
- 'filter-timeuntil03': ('{{ a|timeuntil }}', {'a': (datetime.now() + timedelta(hours=8, minutes=10, seconds=10))}, '8\xa0hours, 10\xa0minutes'),
-
- # Compare to a given parameter
- 'filter-timeuntil04': ('{{ a|timeuntil:b }}', {'a': now - timedelta(days=1), 'b': now - timedelta(days=2)}, '1\xa0day'),
- 'filter-timeuntil05': ('{{ a|timeuntil:b }}', {'a': now - timedelta(days=2), 'b': now - timedelta(days=2, minutes=1)}, '1\xa0minute'),
-
- # Regression for #7443
- 'filter-timeuntil06': ('{{ earlier|timeuntil }}', {'earlier': now - timedelta(days=7)}, '0\xa0minutes'),
- 'filter-timeuntil07': ('{{ earlier|timeuntil:now }}', {'now': now, 'earlier': now - timedelta(days=7)}, '0\xa0minutes'),
- 'filter-timeuntil08': ('{{ later|timeuntil }}', {'later': now + timedelta(days=7, hours=1)}, '1\xa0week'),
- 'filter-timeuntil09': ('{{ later|timeuntil:now }}', {'now': now, 'later': now + timedelta(days=7)}, '1\xa0week'),
-
- # Ensures that differing timezones are calculated correctly
- # Tests trying to compare a timezone-aware 'now' to now aren't supported on no-tz-support systems (e.g Windows).
- 'filter-timeuntil10': ('{{ a|timeuntil }}', {'a': now_tz}, '0\xa0minutes') if TZ_SUPPORT else ('', {}, ''),
- 'filter-timeuntil11': ('{{ a|timeuntil }}', {'a': now_tz_i}, '0\xa0minutes') if TZ_SUPPORT else ('', {}, ''),
- 'filter-timeuntil12': ('{{ a|timeuntil:b }}', {'a': now_tz_i, 'b': now_tz}, '0\xa0minutes'),
-
- # Regression for #9065 (two date objects).
- 'filter-timeuntil13': ('{{ a|timeuntil:b }}', {'a': today, 'b': today}, '0\xa0minutes'),
- 'filter-timeuntil14': ('{{ a|timeuntil:b }}', {'a': today, 'b': today - timedelta(hours=24)}, '1\xa0day'),
-
- 'filter-addslash01': ("{% autoescape off %}{{ a|addslashes }} {{ b|addslashes }}{% endautoescape %}", {"a": "'", "b": mark_safe("'")}, r"\' \'"),
- 'filter-addslash02': ("{{ a|addslashes }} {{ b|addslashes }}", {"a": "'", "b": mark_safe("'")}, r"<a>\' \'"),
-
- 'filter-capfirst01': ("{% autoescape off %}{{ a|capfirst }} {{ b|capfirst }}{% endautoescape %}", {"a": "fred>", "b": mark_safe("fred>")}, "Fred> Fred>"),
- 'filter-capfirst02': ("{{ a|capfirst }} {{ b|capfirst }}", {"a": "fred>", "b": mark_safe("fred>")}, "Fred> Fred>"),
-
- 'filter-floatformat01': ("{% autoescape off %}{{ a|floatformat }} {{ b|floatformat }}{% endautoescape %}", {"a": "1.42", "b": mark_safe("1.42")}, "1.4 1.4"),
- 'filter-floatformat02': ("{{ a|floatformat }} {{ b|floatformat }}", {"a": "1.42", "b": mark_safe("1.42")}, "1.4 1.4"),
-
- # The contents of "linenumbers" is escaped according to the current
- # autoescape setting.
- 'filter-linenumbers01': ("{{ a|linenumbers }} {{ b|linenumbers }}", {"a": "one\n\nthree", "b": mark_safe("one\n<two>\nthree")}, "1. one\n2. <two>\n3. three 1. one\n2. <two>\n3. three"),
- 'filter-linenumbers02': ("{% autoescape off %}{{ a|linenumbers }} {{ b|linenumbers }}{% endautoescape %}", {"a": "one\n\nthree", "b": mark_safe("one\n<two>\nthree")}, "1. one\n2. \n3. three 1. one\n2. <two>\n3. three"),
-
- 'filter-lower01': ("{% autoescape off %}{{ a|lower }} {{ b|lower }}{% endautoescape %}", {"a": "Apple & banana", "b": mark_safe("Apple & banana")}, "apple & banana apple & banana"),
- 'filter-lower02': ("{{ a|lower }} {{ b|lower }}", {"a": "Apple & banana", "b": mark_safe("Apple & banana")}, "apple & banana apple & banana"),
-
- # The make_list filter can destroy existing escaping, so the results are
- # escaped.
- 'filter-make_list01': ("{% autoescape off %}{{ a|make_list }}{% endautoescape %}", {"a": mark_safe("&")}, str_prefix("[%(_)s'&']")),
- 'filter-make_list02': ("{{ a|make_list }}", {"a": mark_safe("&")}, str_prefix("[%(_)s'&']")),
- 'filter-make_list03': ('{% autoescape off %}{{ a|make_list|stringformat:"s"|safe }}{% endautoescape %}', {"a": mark_safe("&")}, str_prefix("[%(_)s'&']")),
- 'filter-make_list04': ('{{ a|make_list|stringformat:"s"|safe }}', {"a": mark_safe("&")}, str_prefix("[%(_)s'&']")),
-
- # Running slugify on a pre-escaped string leads to odd behavior,
- # but the result is still safe.
- 'filter-slugify01': ("{% autoescape off %}{{ a|slugify }} {{ b|slugify }}{% endautoescape %}", {"a": "a & b", "b": mark_safe("a & b")}, "a-b a-amp-b"),
- 'filter-slugify02': ("{{ a|slugify }} {{ b|slugify }}", {"a": "a & b", "b": mark_safe("a & b")}, "a-b a-amp-b"),
-
- # Notice that escaping is applied *after* any filters, so the string
- # formatting here only needs to deal with pre-escaped characters.
- 'filter-stringformat01': ('{% autoescape off %}.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.{% endautoescape %}',
- {"a": "ahttp://example.com/?x=&y= '
- 'http://example.com?x=&y=<2>'),
- 'filter-urlize02': (
- '{{ a|urlize }} {{ b|urlize }}',
- {"a": "http://example.com/?x=&y=", "b": mark_safe("http://example.com?x=&y=")},
- 'http://example.com/?x=&y= '
- 'http://example.com?x=&y='),
- 'filter-urlize03': ('{% autoescape off %}{{ a|urlize }}{% endautoescape %}', {"a": mark_safe("a & b")}, 'a & b'),
- 'filter-urlize04': ('{{ a|urlize }}', {"a": mark_safe("a & b")}, 'a & b'),
-
- # This will lead to a nonsense result, but at least it won't be
- # exploitable for XSS purposes when auto-escaping is on.
- 'filter-urlize05': ('{% autoescape off %}{{ a|urlize }}{% endautoescape %}', {"a": ""}, ""),
- 'filter-urlize06': ('{{ a|urlize }}', {"a": ""}, '<script>alert('foo')</script>'),
-
- # mailto: testing for urlize
- 'filter-urlize07': ('{{ a|urlize }}', {"a": "Email me at me@example.com"}, 'Email me at me@example.com'),
- 'filter-urlize08': ('{{ a|urlize }}', {"a": "Email me at "}, 'Email me at <me@example.com>'),
-
- 'filter-urlizetrunc01': (
- '{% autoescape off %}{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}{% endautoescape %}',
- {"a": '"Unsafe" http://example.com/x=&y=', "b": mark_safe('"Safe" http://example.com?x=&y=')},
- '"Unsafe" http:... '
- '"Safe" http:...'),
- 'filter-urlizetrunc02': (
- '{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}',
- {"a": '"Unsafe" http://example.com/x=&y=', "b": mark_safe('"Safe" http://example.com?x=&y=')},
- '"Unsafe" http:... '
- '"Safe" http:...'),
-
- 'filter-wordcount01': ('{% autoescape off %}{{ a|wordcount }} {{ b|wordcount }}{% endautoescape %}', {"a": "a & b", "b": mark_safe("a & b")}, "3 3"),
- 'filter-wordcount02': ('{{ a|wordcount }} {{ b|wordcount }}', {"a": "a & b", "b": mark_safe("a & b")}, "3 3"),
-
- 'filter-wordwrap01': ('{% autoescape off %}{{ a|wordwrap:"3" }} {{ b|wordwrap:"3" }}{% endautoescape %}', {"a": "a & b", "b": mark_safe("a & b")}, "a &\nb a &\nb"),
- 'filter-wordwrap02': ('{{ a|wordwrap:"3" }} {{ b|wordwrap:"3" }}', {"a": "a & b", "b": mark_safe("a & b")}, "a &\nb a &\nb"),
-
- 'filter-ljust01': ('{% autoescape off %}.{{ a|ljust:"5" }}. .{{ b|ljust:"5" }}.{% endautoescape %}', {"a": "a&b", "b": mark_safe("a&b")}, ".a&b . .a&b ."),
- 'filter-ljust02': ('.{{ a|ljust:"5" }}. .{{ b|ljust:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ".a&b . .a&b ."),
-
- 'filter-rjust01': ('{% autoescape off %}.{{ a|rjust:"5" }}. .{{ b|rjust:"5" }}.{% endautoescape %}', {"a": "a&b", "b": mark_safe("a&b")}, ". a&b. . a&b."),
- 'filter-rjust02': ('.{{ a|rjust:"5" }}. .{{ b|rjust:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ". a&b. . a&b."),
-
- 'filter-center01': ('{% autoescape off %}.{{ a|center:"5" }}. .{{ b|center:"5" }}.{% endautoescape %}', {"a": "a&b", "b": mark_safe("a&b")}, ". a&b . . a&b ."),
- 'filter-center02': ('.{{ a|center:"5" }}. .{{ b|center:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ". a&b . . a&b ."),
-
- 'filter-cut01': ('{% autoescape off %}{{ a|cut:"x" }} {{ b|cut:"x" }}{% endautoescape %}', {"a": "x&y", "b": mark_safe("x&y")}, "&y &y"),
- 'filter-cut02': ('{{ a|cut:"x" }} {{ b|cut:"x" }}', {"a": "x&y", "b": mark_safe("x&y")}, "&y &y"),
- 'filter-cut03': ('{% autoescape off %}{{ a|cut:"&" }} {{ b|cut:"&" }}{% endautoescape %}', {"a": "x&y", "b": mark_safe("x&y")}, "xy xamp;y"),
- 'filter-cut04': ('{{ a|cut:"&" }} {{ b|cut:"&" }}', {"a": "x&y", "b": mark_safe("x&y")}, "xy xamp;y"),
- # Passing ';' to cut can break existing HTML entities, so those strings
- # are auto-escaped.
- 'filter-cut05': ('{% autoescape off %}{{ a|cut:";" }} {{ b|cut:";" }}{% endautoescape %}', {"a": "x&y", "b": mark_safe("x&y")}, "x&y x&y"),
- 'filter-cut06': ('{{ a|cut:";" }} {{ b|cut:";" }}', {"a": "x&y", "b": mark_safe("x&y")}, "x&y x&y"),
-
- # The "escape" filter works the same whether autoescape is on or off,
- # but it has no effect on strings already marked as safe.
- 'filter-escape01': ('{{ a|escape }} {{ b|escape }}', {"a": "x&y", "b": mark_safe("x&y")}, "x&y x&y"),
- 'filter-escape02': ('{% autoescape off %}{{ a|escape }} {{ b|escape }}{% endautoescape %}', {"a": "x&y", "b": mark_safe("x&y")}, "x&y x&y"),
-
- # It is only applied once, regardless of the number of times it
- # appears in a chain.
- 'filter-escape03': ('{% autoescape off %}{{ a|escape|escape }}{% endautoescape %}', {"a": "x&y"}, "x&y"),
- 'filter-escape04': ('{{ a|escape|escape }}', {"a": "x&y"}, "x&y"),
-
- # Force_escape is applied immediately. It can be used to provide
- # double-escaping, for example.
- 'filter-force-escape01': ('{% autoescape off %}{{ a|force_escape }}{% endautoescape %}', {"a": "x&y"}, "x&y"),
- 'filter-force-escape02': ('{{ a|force_escape }}', {"a": "x&y"}, "x&y"),
- 'filter-force-escape03': ('{% autoescape off %}{{ a|force_escape|force_escape }}{% endautoescape %}', {"a": "x&y"}, "x&y"),
- 'filter-force-escape04': ('{{ a|force_escape|force_escape }}', {"a": "x&y"}, "x&y"),
-
- # Because the result of force_escape is "safe", an additional
- # escape filter has no effect.
- 'filter-force-escape05': ('{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}', {"a": "x&y"}, "x&y"),
- 'filter-force-escape06': ('{{ a|force_escape|escape }}', {"a": "x&y"}, "x&y"),
- 'filter-force-escape07': ('{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}', {"a": "x&y"}, "x&y"),
- 'filter-force-escape08': ('{{ a|escape|force_escape }}', {"a": "x&y"}, "x&y"),
-
- # The contents in "linebreaks" and "linebreaksbr" are escaped
- # according to the current autoescape setting.
- 'filter-linebreaks01': ('{{ a|linebreaks }} {{ b|linebreaks }}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "x&
y
x&
y
"),
- 'filter-linebreaks02': ('{% autoescape off %}{{ a|linebreaks }} {{ b|linebreaks }}{% endautoescape %}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "x&
y
x&
y
"),
-
- 'filter-linebreaksbr01': ('{{ a|linebreaksbr }} {{ b|linebreaksbr }}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "x&
y x&
y"),
- 'filter-linebreaksbr02': ('{% autoescape off %}{{ a|linebreaksbr }} {{ b|linebreaksbr }}{% endautoescape %}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "x&
y x&
y"),
-
- 'filter-safe01': ("{{ a }} -- {{ a|safe }}", {"a": "hello"}, "<b>hello</b> -- hello"),
- 'filter-safe02': ("{% autoescape off %}{{ a }} -- {{ a|safe }}{% endautoescape %}", {"a": "hello"}, "hello -- hello"),
-
- 'filter-safeseq01': ('{{ a|join:", " }} -- {{ a|safeseq|join:", " }}', {"a": ["&", "<"]}, "&, < -- &, <"),
- 'filter-safeseq02': ('{% autoescape off %}{{ a|join:", " }} -- {{ a|safeseq|join:", " }}{% endautoescape %}', {"a": ["&", "<"]}, "&, < -- &, <"),
-
- 'filter-removetags01': ('{{ a|removetags:"a b" }} {{ b|removetags:"a b" }}', {"a": "x y
", "b": mark_safe("x y
")}, "x <p>y</p> x y
"),
- 'filter-removetags02': ('{% autoescape off %}{{ a|removetags:"a b" }} {{ b|removetags:"a b" }}{% endautoescape %}', {"a": "x y
", "b": mark_safe("x y
")}, "x y
x y
"),
-
- 'filter-striptags01': ('{{ a|striptags }} {{ b|striptags }}', {"a": "x y
", "b": mark_safe("x y
")}, "x y x y"),
- 'filter-striptags02': ('{% autoescape off %}{{ a|striptags }} {{ b|striptags }}{% endautoescape %}', {"a": "x y
", "b": mark_safe("x y
")}, "x y x y"),
-
- 'filter-first01': ('{{ a|first }} {{ b|first }}', {"a": ["a&b", "x"], "b": [mark_safe("a&b"), "x"]}, "a&b a&b"),
- 'filter-first02': ('{% autoescape off %}{{ a|first }} {{ b|first }}{% endautoescape %}', {"a": ["a&b", "x"], "b": [mark_safe("a&b"), "x"]}, "a&b a&b"),
-
- 'filter-last01': ('{{ a|last }} {{ b|last }}', {"a": ["x", "a&b"], "b": ["x", mark_safe("a&b")]}, "a&b a&b"),
- 'filter-last02': ('{% autoescape off %}{{ a|last }} {{ b|last }}{% endautoescape %}', {"a": ["x", "a&b"], "b": ["x", mark_safe("a&b")]}, "a&b a&b"),
-
- 'filter-random01': ('{{ a|random }} {{ b|random }}', {"a": ["a&b", "a&b"], "b": [mark_safe("a&b"), mark_safe("a&b")]}, "a&b a&b"),
- 'filter-random02': ('{% autoescape off %}{{ a|random }} {{ b|random }}{% endautoescape %}', {"a": ["a&b", "a&b"], "b": [mark_safe("a&b"), mark_safe("a&b")]}, "a&b a&b"),
-
- 'filter-slice01': ('{{ a|slice:"1:3" }} {{ b|slice:"1:3" }}', {"a": "a&b", "b": mark_safe("a&b")}, "&b &b"),
- 'filter-slice02': ('{% autoescape off %}{{ a|slice:"1:3" }} {{ b|slice:"1:3" }}{% endautoescape %}', {"a": "a&b", "b": mark_safe("a&b")}, "&b &b"),
-
- 'filter-unordered_list01': ('{{ a|unordered_list }}', {"a": ["x>", [["x>\n\t\n\t"),
- 'filter-unordered_list02': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [["x>\n\t\n\t"),
- 'filter-unordered_list03': ('{{ a|unordered_list }}', {"a": ["x>", [[mark_safe("x>\n\t\n\t"),
- 'filter-unordered_list04': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [[mark_safe("x>\n\t\n\t"),
- 'filter-unordered_list05': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [["x>\n\t\n\t"),
-
- # Literal string arguments to the default filter are always treated as
- # safe strings, regardless of the auto-escaping state.
- #
- # Note: we have to use {"a": ""} here, otherwise the invalid template
- # variable string interferes with the test result.
- 'filter-default01': ('{{ a|default:"x<" }}', {"a": ""}, "x<"),
- 'filter-default02': ('{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}', {"a": ""}, "x<"),
- 'filter-default03': ('{{ a|default:"x<" }}', {"a": mark_safe("x>")}, "x>"),
- 'filter-default04': ('{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}', {"a": mark_safe("x>")}, "x>"),
-
- 'filter-default_if_none01': ('{{ a|default:"x<" }}', {"a": None}, "x<"),
- 'filter-default_if_none02': ('{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}', {"a": None}, "x<"),
-
- 'filter-phone2numeric01': ('{{ a|phone2numeric }} {{ b|phone2numeric }}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>")}, "<1-800-2255-63> <1-800-2255-63>"),
- 'filter-phone2numeric02': ('{% autoescape off %}{{ a|phone2numeric }} {{ b|phone2numeric }}{% endautoescape %}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>")}, "<1-800-2255-63> <1-800-2255-63>"),
- 'filter-phone2numeric03': ('{{ a|phone2numeric }}', {"a": "How razorback-jumping frogs can level six piqued gymnasts!"}, "469 729672225-5867464 37647 226 53835 749 747833 49662787!"),
-
- # Ensure iriencode keeps safe strings:
- 'filter-iriencode01': ('{{ url|iriencode }}', {'url': '?test=1&me=2'}, '?test=1&me=2'),
- 'filter-iriencode02': ('{% autoescape off %}{{ url|iriencode }}{% endautoescape %}', {'url': '?test=1&me=2'}, '?test=1&me=2'),
- 'filter-iriencode03': ('{{ url|iriencode }}', {'url': mark_safe('?test=1&me=2')}, '?test=1&me=2'),
- 'filter-iriencode04': ('{% autoescape off %}{{ url|iriencode }}{% endautoescape %}', {'url': mark_safe('?test=1&me=2')}, '?test=1&me=2'),
-
- # urlencode
- 'filter-urlencode01': ('{{ url|urlencode }}', {'url': '/test&"/me?/'}, '/test%26%22/me%3F/'),
- 'filter-urlencode02': ('/test/{{ urlbit|urlencode:"" }}/', {'urlbit': 'escape/slash'}, '/test/escape%2Fslash/'),
-
- # Chaining a bunch of safeness-preserving filters should not alter
- # the safe status either way.
- 'chaining01': ('{{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }}', {"a": "a < b", "b": mark_safe("a < b")}, " A < b . A < b "),
- 'chaining02': ('{% autoescape off %}{{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }}{% endautoescape %}', {"a": "a < b", "b": mark_safe("a < b")}, " A < b . A < b "),
-
- # Using a filter that forces a string back to unsafe:
- 'chaining03': ('{{ a|cut:"b"|capfirst }}.{{ b|cut:"b"|capfirst }}', {"a": "a < b", "b": mark_safe("a < b")}, "A < .A < "),
- 'chaining04': ('{% autoescape off %}{{ a|cut:"b"|capfirst }}.{{ b|cut:"b"|capfirst }}{% endautoescape %}', {"a": "a < b", "b": mark_safe("a < b")}, "A < .A < "),
-
- # Using a filter that forces safeness does not lead to double-escaping
- 'chaining05': ('{{ a|escape|capfirst }}', {"a": "a < b"}, "A < b"),
- 'chaining06': ('{% autoescape off %}{{ a|escape|capfirst }}{% endautoescape %}', {"a": "a < b"}, "A < b"),
-
- # Force to safe, then back (also showing why using force_escape too
- # early in a chain can lead to unexpected results).
- 'chaining07': ('{{ a|force_escape|cut:";" }}', {"a": "a < b"}, "a < b"),
- 'chaining08': ('{% autoescape off %}{{ a|force_escape|cut:";" }}{% endautoescape %}', {"a": "a < b"}, "a < b"),
- 'chaining09': ('{{ a|cut:";"|force_escape }}', {"a": "a < b"}, "a < b"),
- 'chaining10': ('{% autoescape off %}{{ a|cut:";"|force_escape }}{% endautoescape %}', {"a": "a < b"}, "a < b"),
- 'chaining11': ('{{ a|cut:"b"|safe }}', {"a": "a < b"}, "a < "),
- 'chaining12': ('{% autoescape off %}{{ a|cut:"b"|safe }}{% endautoescape %}', {"a": "a < b"}, "a < "),
- 'chaining13': ('{{ a|safe|force_escape }}', {"a": "a < b"}, "a < b"),
- 'chaining14': ('{% autoescape off %}{{ a|safe|force_escape }}{% endautoescape %}', {"a": "a < b"}, "a < b"),
-
- # Filters decorated with stringfilter still respect is_safe.
- 'autoescape-stringfilter01': (r'{{ unsafe|capfirst }}', {'unsafe': UnsafeClass()}, 'You & me'),
- 'autoescape-stringfilter02': (r'{% autoescape off %}{{ unsafe|capfirst }}{% endautoescape %}', {'unsafe': UnsafeClass()}, 'You & me'),
- 'autoescape-stringfilter03': (r'{{ safe|capfirst }}', {'safe': SafeClass()}, 'You > me'),
- 'autoescape-stringfilter04': (r'{% autoescape off %}{{ safe|capfirst }}{% endautoescape %}', {'safe': SafeClass()}, 'You > me'),
-
- 'escapejs01': (r'{{ a|escapejs }}', {'a': 'testing\r\njavascript \'string" escaping'}, 'testing\\u000D\\u000Ajavascript \\u0027string\\u0022 \\u003Cb\\u003Eescaping\\u003C/b\\u003E'),
- 'escapejs02': (r'{% autoescape off %}{{ a|escapejs }}{% endautoescape %}', {'a': 'testing\r\njavascript \'string" escaping'}, 'testing\\u000D\\u000Ajavascript \\u0027string\\u0022 \\u003Cb\\u003Eescaping\\u003C/b\\u003E'),
-
-
- # length filter.
- 'length01': ('{{ list|length }}', {'list': ['4', None, True, {}]}, '4'),
- 'length02': ('{{ list|length }}', {'list': []}, '0'),
- 'length03': ('{{ string|length }}', {'string': ''}, '0'),
- 'length04': ('{{ string|length }}', {'string': 'django'}, '6'),
- 'length05': ('{% if string|length == 6 %}Pass{% endif %}', {'string': mark_safe('django')}, 'Pass'),
- # Invalid uses that should fail silently.
- 'length06': ('{{ int|length }}', {'int': 7}, '0'),
- 'length07': ('{{ None|length }}', {'None': None}, '0'),
-
- # length_is filter.
- 'length_is01': ('{% if some_list|length_is:"4" %}Four{% endif %}', {'some_list': ['4', None, True, {}]}, 'Four'),
- 'length_is02': ('{% if some_list|length_is:"4" %}Four{% else %}Not Four{% endif %}', {'some_list': ['4', None, True, {}, 17]}, 'Not Four'),
- 'length_is03': ('{% if mystring|length_is:"4" %}Four{% endif %}', {'mystring': 'word'}, 'Four'),
- 'length_is04': ('{% if mystring|length_is:"4" %}Four{% else %}Not Four{% endif %}', {'mystring': 'Python'}, 'Not Four'),
- 'length_is05': ('{% if mystring|length_is:"4" %}Four{% else %}Not Four{% endif %}', {'mystring': ''}, 'Not Four'),
- 'length_is06': ('{% with var|length as my_length %}{{ my_length }}{% endwith %}', {'var': 'django'}, '6'),
- # Boolean return value from length_is should not be coerced to a string
- 'length_is07': (r'{% if "X"|length_is:0 %}Length is 0{% else %}Length not 0{% endif %}', {}, 'Length not 0'),
- 'length_is08': (r'{% if "X"|length_is:1 %}Length is 1{% else %}Length not 1{% endif %}', {}, 'Length is 1'),
- # Invalid uses that should fail silently.
- 'length_is09': ('{{ var|length_is:"fish" }}', {'var': 'django'}, ''),
- 'length_is10': ('{{ int|length_is:"1" }}', {'int': 7}, ''),
- 'length_is11': ('{{ none|length_is:"1" }}', {'none': None}, ''),
-
- 'join01': (r'{{ a|join:", " }}', {'a': ['alpha', 'beta & me']}, 'alpha, beta & me'),
- 'join02': (r'{% autoescape off %}{{ a|join:", " }}{% endautoescape %}', {'a': ['alpha', 'beta & me']}, 'alpha, beta & me'),
- 'join03': (r'{{ a|join:" & " }}', {'a': ['alpha', 'beta & me']}, 'alpha & beta & me'),
- 'join04': (r'{% autoescape off %}{{ a|join:" & " }}{% endautoescape %}', {'a': ['alpha', 'beta & me']}, 'alpha & beta & me'),
-
- # Test that joining with unsafe joiners don't result in unsafe strings (#11377)
- 'join05': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': ' & '}, 'alpha & beta & me'),
- 'join06': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'),
- 'join07': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': ' & '}, 'alpha & beta & me'),
- 'join08': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'),
-
- 'date01': (r'{{ d|date:"m" }}', {'d': datetime(2008, 1, 1)}, '01'),
- 'date02': (r'{{ d|date }}', {'d': datetime(2008, 1, 1)}, 'Jan. 1, 2008'),
- # Ticket 9520: Make sure |date doesn't blow up on non-dates
- 'date03': (r'{{ d|date:"m" }}', {'d': 'fail_string'}, ''),
- # ISO date formats
- 'date04': (r'{{ d|date:"o" }}', {'d': datetime(2008, 12, 29)}, '2009'),
- 'date05': (r'{{ d|date:"o" }}', {'d': datetime(2010, 1, 3)}, '2009'),
- # Timezone name
- 'date06': (r'{{ d|date:"e" }}', {'d': datetime(2009, 3, 12, tzinfo=timezone.get_fixed_timezone(30))}, '+0030'),
- 'date07': (r'{{ d|date:"e" }}', {'d': datetime(2009, 3, 12)}, ''),
- # Ticket 19370: Make sure |date doesn't blow up on a midnight time object
- 'date08': (r'{{ t|date:"H:i" }}', {'t': time(0, 1)}, '00:01'),
- 'date09': (r'{{ t|date:"H:i" }}', {'t': time(0, 0)}, '00:00'),
- # Ticket 20693: Add timezone support to built-in time template filter
- 'time01': (r'{{ dt|time:"e:O:T:Z" }}', {'dt': now_tz_i}, '+0315:+0315:+0315:11700'),
- 'time02': (r'{{ dt|time:"e:T" }}', {'dt': now}, ':' + now_tz.tzinfo.tzname(now_tz)),
- 'time03': (r'{{ t|time:"P:e:O:T:Z" }}', {'t': time(4, 0, tzinfo=timezone.get_fixed_timezone(30))}, '4 a.m.::::'),
- 'time04': (r'{{ t|time:"P:e:O:T:Z" }}', {'t': time(4, 0)}, '4 a.m.::::'),
- 'time05': (r'{{ d|time:"P:e:O:T:Z" }}', {'d': today}, ''),
- 'time06': (r'{{ obj|time:"P:e:O:T:Z" }}', {'obj': 'non-datetime-value'}, ''),
-
- # Tests for #11687 and #16676
- 'add01': (r'{{ i|add:"5" }}', {'i': 2000}, '2005'),
- 'add02': (r'{{ i|add:"napis" }}', {'i': 2000}, ''),
- 'add03': (r'{{ i|add:16 }}', {'i': 'not_an_int'}, ''),
- 'add04': (r'{{ i|add:"16" }}', {'i': 'not_an_int'}, 'not_an_int16'),
- 'add05': (r'{{ l1|add:l2 }}', {'l1': [1, 2], 'l2': [3, 4]}, '[1, 2, 3, 4]'),
- 'add06': (r'{{ t1|add:t2 }}', {'t1': (3, 4), 't2': (1, 2)}, '(3, 4, 1, 2)'),
- 'add07': (r'{{ d|add:t }}', {'d': date(2000, 1, 1), 't': timedelta(10)}, 'Jan. 11, 2000'),
- }
diff --git a/tests/template_tests/syntax_tests/test_autoescape.py b/tests/template_tests/syntax_tests/test_autoescape.py
index a908daca20..589265082c 100644
--- a/tests/template_tests/syntax_tests/test_autoescape.py
+++ b/tests/template_tests/syntax_tests/test_autoescape.py
@@ -2,7 +2,7 @@ from django.template.base import TemplateSyntaxError
from django.test import SimpleTestCase
from django.utils.safestring import mark_safe
-from .utils import render, setup, SafeClass, UnsafeClass
+from ..utils import render, setup, SafeClass, UnsafeClass
class AutoescapeTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_basic.py b/tests/template_tests/syntax_tests/test_basic.py
index 5678b935ba..72b0b7486d 100644
--- a/tests/template_tests/syntax_tests/test_basic.py
+++ b/tests/template_tests/syntax_tests/test_basic.py
@@ -3,7 +3,7 @@ from django.template.base import Context, TemplateSyntaxError
from django.template.loader import get_template
from django.test import SimpleTestCase
-from .utils import render, setup, SilentGetItemClass, SilentAttrClass, SomeClass
+from ..utils import render, setup, SilentGetItemClass, SilentAttrClass, SomeClass
basic_templates = {
diff --git a/tests/template_tests/syntax_tests/test_builtins.py b/tests/template_tests/syntax_tests/test_builtins.py
index ecd428154c..a45b1567f1 100644
--- a/tests/template_tests/syntax_tests/test_builtins.py
+++ b/tests/template_tests/syntax_tests/test_builtins.py
@@ -1,6 +1,6 @@
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class BuiltinsTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_cache.py b/tests/template_tests/syntax_tests/test_cache.py
index 3e304eb1a4..a702fc05e4 100644
--- a/tests/template_tests/syntax_tests/test_cache.py
+++ b/tests/template_tests/syntax_tests/test_cache.py
@@ -3,7 +3,7 @@ from django.template.base import TemplateSyntaxError
from django.template.loader import get_template
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class CacheTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_comment.py b/tests/template_tests/syntax_tests/test_comment.py
index 35c720efd7..73099e963b 100644
--- a/tests/template_tests/syntax_tests/test_comment.py
+++ b/tests/template_tests/syntax_tests/test_comment.py
@@ -1,6 +1,6 @@
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class CommentSyntaxTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_cycle.py b/tests/template_tests/syntax_tests/test_cycle.py
index bab3418e84..0c18ccba90 100644
--- a/tests/template_tests/syntax_tests/test_cycle.py
+++ b/tests/template_tests/syntax_tests/test_cycle.py
@@ -5,7 +5,7 @@ from django.template.loader import get_template
from django.test import SimpleTestCase
from django.utils.deprecation import RemovedInDjango20Warning
-from .utils import render, setup
+from ..utils import render, setup
class CycleTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_exceptions.py b/tests/template_tests/syntax_tests/test_exceptions.py
index 78c9df35e9..be313d8e76 100644
--- a/tests/template_tests/syntax_tests/test_exceptions.py
+++ b/tests/template_tests/syntax_tests/test_exceptions.py
@@ -4,7 +4,7 @@ from django.template.loader import get_template
from django.test import SimpleTestCase
from .test_extends import inheritance_templates
-from .utils import render, setup
+from ..utils import render, setup
class ExceptionsTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_extends.py b/tests/template_tests/syntax_tests/test_extends.py
index f4b2465616..8d546633ab 100644
--- a/tests/template_tests/syntax_tests/test_extends.py
+++ b/tests/template_tests/syntax_tests/test_extends.py
@@ -1,7 +1,7 @@
from django.template.base import Template
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
inheritance_templates = {
diff --git a/tests/template_tests/syntax_tests/test_filter_syntax.py b/tests/template_tests/syntax_tests/test_filter_syntax.py
index bdf800b9c1..dda222ddc7 100644
--- a/tests/template_tests/syntax_tests/test_filter_syntax.py
+++ b/tests/template_tests/syntax_tests/test_filter_syntax.py
@@ -8,7 +8,7 @@ from django.template.loader import get_template
from django.test import SimpleTestCase
from django.utils.deprecation import RemovedInDjango20Warning
-from .utils import render, setup, SomeClass, SomeOtherException, UTF8Class
+from ..utils import render, setup, SomeClass, SomeOtherException, UTF8Class
class FilterSyntaxTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_filter_tag.py b/tests/template_tests/syntax_tests/test_filter_tag.py
index 4a54efd924..8d2e67c692 100644
--- a/tests/template_tests/syntax_tests/test_filter_tag.py
+++ b/tests/template_tests/syntax_tests/test_filter_tag.py
@@ -2,7 +2,7 @@ from django.template.base import TemplateSyntaxError
from django.template.loader import get_template
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class FilterTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_firstof.py b/tests/template_tests/syntax_tests/test_firstof.py
index 7f44818171..3c8291849b 100644
--- a/tests/template_tests/syntax_tests/test_firstof.py
+++ b/tests/template_tests/syntax_tests/test_firstof.py
@@ -5,7 +5,7 @@ from django.template.loader import get_template
from django.test import SimpleTestCase
from django.utils.deprecation import RemovedInDjango20Warning
-from .utils import render, setup
+from ..utils import render, setup
class FirstOfTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_for.py b/tests/template_tests/syntax_tests/test_for.py
index 0c55c43585..674ca6015b 100644
--- a/tests/template_tests/syntax_tests/test_for.py
+++ b/tests/template_tests/syntax_tests/test_for.py
@@ -5,7 +5,7 @@ from django.template.base import TemplateSyntaxError
from django.test import SimpleTestCase
from django.utils.deprecation import RemovedInDjango20Warning
-from .utils import render, setup
+from ..utils import render, setup
class ForTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_i18n.py b/tests/template_tests/syntax_tests/test_i18n.py
index d2b7d5992d..da54b68d84 100644
--- a/tests/template_tests/syntax_tests/test_i18n.py
+++ b/tests/template_tests/syntax_tests/test_i18n.py
@@ -5,7 +5,7 @@ from django.conf import settings
from django.test import SimpleTestCase
from django.utils.safestring import mark_safe
-from .utils import render, setup
+from ..utils import render, setup
class I18nTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_if.py b/tests/template_tests/syntax_tests/test_if.py
index f54bfec033..76135c7308 100644
--- a/tests/template_tests/syntax_tests/test_if.py
+++ b/tests/template_tests/syntax_tests/test_if.py
@@ -2,7 +2,7 @@ from django.template.base import TemplateSyntaxError
from django.template.loader import get_template
from django.test import SimpleTestCase
-from .utils import render, setup, TestObj
+from ..utils import render, setup, TestObj
class IfTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_if_changed.py b/tests/template_tests/syntax_tests/test_if_changed.py
index baad615e30..705530d39f 100644
--- a/tests/template_tests/syntax_tests/test_if_changed.py
+++ b/tests/template_tests/syntax_tests/test_if_changed.py
@@ -1,6 +1,6 @@
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class IfChangedTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_if_equal.py b/tests/template_tests/syntax_tests/test_if_equal.py
index f20a5db350..6aca815e8f 100644
--- a/tests/template_tests/syntax_tests/test_if_equal.py
+++ b/tests/template_tests/syntax_tests/test_if_equal.py
@@ -1,6 +1,6 @@
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class IfEqualTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_include.py b/tests/template_tests/syntax_tests/test_include.py
index 2e881ade1d..5b4c3ee8f8 100644
--- a/tests/template_tests/syntax_tests/test_include.py
+++ b/tests/template_tests/syntax_tests/test_include.py
@@ -4,7 +4,7 @@ from django.template.loader import get_template
from django.test import SimpleTestCase
from .test_basic import basic_templates
-from .utils import render, setup
+from ..utils import render, setup
include_fail_templates = {
diff --git a/tests/template_tests/syntax_tests/test_invalid_string.py b/tests/template_tests/syntax_tests/test_invalid_string.py
index a2893dc467..f56b091389 100644
--- a/tests/template_tests/syntax_tests/test_invalid_string.py
+++ b/tests/template_tests/syntax_tests/test_invalid_string.py
@@ -1,7 +1,7 @@
from django.conf import settings
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class InvalidStringTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_list_index.py b/tests/template_tests/syntax_tests/test_list_index.py
index 0b0bb3837d..5d7319651b 100644
--- a/tests/template_tests/syntax_tests/test_list_index.py
+++ b/tests/template_tests/syntax_tests/test_list_index.py
@@ -1,7 +1,7 @@
from django.conf import settings
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class ListIndexTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_load.py b/tests/template_tests/syntax_tests/test_load.py
index 9342abb09d..96c657582b 100644
--- a/tests/template_tests/syntax_tests/test_load.py
+++ b/tests/template_tests/syntax_tests/test_load.py
@@ -2,7 +2,7 @@ from django.template.base import TemplateSyntaxError
from django.template.loader import get_template
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class LoadTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_lorem.py b/tests/template_tests/syntax_tests/test_lorem.py
index 8692201dce..339163ea8d 100644
--- a/tests/template_tests/syntax_tests/test_lorem.py
+++ b/tests/template_tests/syntax_tests/test_lorem.py
@@ -1,6 +1,6 @@
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class LoremTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_multiline.py b/tests/template_tests/syntax_tests/test_multiline.py
index 6ae3ee0f83..23a0a2f253 100644
--- a/tests/template_tests/syntax_tests/test_multiline.py
+++ b/tests/template_tests/syntax_tests/test_multiline.py
@@ -1,6 +1,6 @@
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
multiline_string = """
diff --git a/tests/template_tests/syntax_tests/test_named_endblock.py b/tests/template_tests/syntax_tests/test_named_endblock.py
index 7a06498d6b..a83ea390f7 100644
--- a/tests/template_tests/syntax_tests/test_named_endblock.py
+++ b/tests/template_tests/syntax_tests/test_named_endblock.py
@@ -2,7 +2,7 @@ from django.template.base import TemplateSyntaxError
from django.template.loader import get_template
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class NamedEndblockTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_now.py b/tests/template_tests/syntax_tests/test_now.py
index c8dde4bf0c..82c4bc1c4d 100644
--- a/tests/template_tests/syntax_tests/test_now.py
+++ b/tests/template_tests/syntax_tests/test_now.py
@@ -3,7 +3,7 @@ from datetime import datetime
from django.test import SimpleTestCase
from django.utils.formats import date_format
-from .utils import render, setup
+from ..utils import render, setup
class NowTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_numpy.py b/tests/template_tests/syntax_tests/test_numpy.py
index aa40bdc7c1..fe60939586 100644
--- a/tests/template_tests/syntax_tests/test_numpy.py
+++ b/tests/template_tests/syntax_tests/test_numpy.py
@@ -3,7 +3,7 @@ from unittest import skipIf
from django.conf import settings
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
try:
import numpy
diff --git a/tests/template_tests/syntax_tests/test_regroup.py b/tests/template_tests/syntax_tests/test_regroup.py
index 09313dcefc..cd06420c50 100644
--- a/tests/template_tests/syntax_tests/test_regroup.py
+++ b/tests/template_tests/syntax_tests/test_regroup.py
@@ -4,7 +4,7 @@ from django.template.base import TemplateSyntaxError
from django.template.loader import get_template
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class RegroupTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_setup.py b/tests/template_tests/syntax_tests/test_setup.py
index ea37698ee9..fd7355a738 100644
--- a/tests/template_tests/syntax_tests/test_setup.py
+++ b/tests/template_tests/syntax_tests/test_setup.py
@@ -1,7 +1,7 @@
from django.conf import settings
from django.test import SimpleTestCase
-from .utils import setup
+from ..utils import setup
class SetupTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_simple_tag.py b/tests/template_tests/syntax_tests/test_simple_tag.py
index 7529da014c..79c43f88c9 100644
--- a/tests/template_tests/syntax_tests/test_simple_tag.py
+++ b/tests/template_tests/syntax_tests/test_simple_tag.py
@@ -2,7 +2,7 @@ from django.template.base import TemplateSyntaxError
from django.template.loader import get_template
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class SimpleTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_spaceless.py b/tests/template_tests/syntax_tests/test_spaceless.py
index 1f5cb54e29..18bda09c83 100644
--- a/tests/template_tests/syntax_tests/test_spaceless.py
+++ b/tests/template_tests/syntax_tests/test_spaceless.py
@@ -1,6 +1,6 @@
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class SpacelessTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_ssi.py b/tests/template_tests/syntax_tests/test_ssi.py
index d6945bdf60..caa1901943 100644
--- a/tests/template_tests/syntax_tests/test_ssi.py
+++ b/tests/template_tests/syntax_tests/test_ssi.py
@@ -5,7 +5,7 @@ from django.test import override_settings, SimpleTestCase
from django.utils._os import upath
from django.utils.deprecation import RemovedInDjango19Warning
-from .utils import render, setup
+from ..utils import render, setup
cwd = os.path.dirname(os.path.abspath(upath(__file__)))
diff --git a/tests/template_tests/syntax_tests/test_static.py b/tests/template_tests/syntax_tests/test_static.py
index 460dbd7077..95ddb196af 100644
--- a/tests/template_tests/syntax_tests/test_static.py
+++ b/tests/template_tests/syntax_tests/test_static.py
@@ -2,7 +2,7 @@ from django.conf import settings
from django.test import override_settings, SimpleTestCase
from django.utils.six.moves.urllib.parse import urljoin
-from .utils import render, setup
+from ..utils import render, setup
@override_settings(MEDIA_URL="/media/", STATIC_URL="/static/")
diff --git a/tests/template_tests/syntax_tests/test_template_tag.py b/tests/template_tests/syntax_tests/test_template_tag.py
index 212c2c1d50..18faafd252 100644
--- a/tests/template_tests/syntax_tests/test_template_tag.py
+++ b/tests/template_tests/syntax_tests/test_template_tag.py
@@ -2,7 +2,7 @@ from django.template.base import TemplateSyntaxError
from django.template.loader import get_template
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class TemplateTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_url.py b/tests/template_tests/syntax_tests/test_url.py
index cd684c9c13..56af43d131 100644
--- a/tests/template_tests/syntax_tests/test_url.py
+++ b/tests/template_tests/syntax_tests/test_url.py
@@ -7,7 +7,7 @@ from django.template.loader import get_template
from django.test import override_settings, SimpleTestCase
from django.utils.deprecation import RemovedInDjango20Warning
-from .utils import render, setup
+from ..utils import render, setup
@override_settings(ROOT_URLCONF='template_tests.urls')
diff --git a/tests/template_tests/syntax_tests/test_verbatim.py b/tests/template_tests/syntax_tests/test_verbatim.py
index ef10c12fad..43a6e8546c 100644
--- a/tests/template_tests/syntax_tests/test_verbatim.py
+++ b/tests/template_tests/syntax_tests/test_verbatim.py
@@ -2,7 +2,7 @@ from django.template.base import TemplateSyntaxError
from django.template.loader import get_template
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class VerbatimTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_width_ratio.py b/tests/template_tests/syntax_tests/test_width_ratio.py
index fc098b79cf..225cd602fd 100644
--- a/tests/template_tests/syntax_tests/test_width_ratio.py
+++ b/tests/template_tests/syntax_tests/test_width_ratio.py
@@ -3,7 +3,7 @@ from django.template.loader import get_template
from django.test import SimpleTestCase
from django.utils import six
-from .utils import render, setup
+from ..utils import render, setup
class WidthRatioTagTests(SimpleTestCase):
diff --git a/tests/template_tests/syntax_tests/test_with.py b/tests/template_tests/syntax_tests/test_with.py
index 11e8ef217f..571ea7f1c1 100644
--- a/tests/template_tests/syntax_tests/test_with.py
+++ b/tests/template_tests/syntax_tests/test_with.py
@@ -2,7 +2,7 @@ from django.conf import settings
from django.template.base import TemplateSyntaxError
from django.test import SimpleTestCase
-from .utils import render, setup
+from ..utils import render, setup
class WithTagTests(SimpleTestCase):
diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py
index cec5915b40..2ba1cdb18a 100644
--- a/tests/template_tests/tests.py
+++ b/tests/template_tests/tests.py
@@ -3,9 +3,7 @@ from __future__ import unicode_literals
import os
import sys
-import traceback
import unittest
-import warnings
from django import template
from django.contrib.auth.models import Group
@@ -15,17 +13,7 @@ from django.template.engine import Engine
from django.template.loaders import app_directories, filesystem
from django.test import RequestFactory, SimpleTestCase
from django.test.utils import override_settings, extend_sys_path
-from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning
from django.utils._os import upath
-from django.utils import six
-from django.utils import translation
-
-from . import filters
-from .syntax_tests.utils import register_test_tags, ShouldNotExecuteException
-
-
-class ContextStackException(Exception):
- pass
class TemplateLoaderTests(SimpleTestCase):
@@ -393,114 +381,6 @@ class TemplateRegressionTests(SimpleTestCase):
self.assertIn("清風", t1.render(c1))
-# Set ALLOWED_INCLUDE_ROOTS so that ssi works.
-@override_settings(TEMPLATE_DEBUG=False, ROOT_URLCONF='template_tests.urls')
-class TemplateTests(SimpleTestCase):
-
- @register_test_tags
- def test_templates(self):
- template_tests = filters.get_filter_tests()
-
- templates = dict((name, t[0]) for name, t in six.iteritems(template_tests))
- with override_settings(TEMPLATE_LOADERS=[
- ('django.template.loaders.cached.Loader', [
- ('django.template.loaders.locmem.Loader', templates),
- ]),
- ]):
- failures = []
- tests = sorted(template_tests.items())
-
- # Warm the URL reversing cache. This ensures we don't pay the cost
- # warming the cache during one of the tests.
- urlresolvers.reverse('named.client', args=(0,))
-
- for name, vals in tests:
-
- # Set TEMPLATE_STRING_IF_INVALID to a known string.
- expected_invalid_str = 'INVALID'
-
- if isinstance(vals[2], tuple):
- normal_string_result = vals[2][0]
- invalid_string_result = vals[2][1]
-
- if isinstance(invalid_string_result, tuple):
- expected_invalid_str = 'INVALID %s'
- invalid_string_result = invalid_string_result[0] % invalid_string_result[1]
-
- try:
- template_debug_result = vals[2][2]
- except IndexError:
- template_debug_result = normal_string_result
-
- else:
- normal_string_result = vals[2]
- invalid_string_result = vals[2]
- template_debug_result = vals[2]
-
- with translation.override(vals[1].get('LANGUAGE_CODE', 'en-us')):
- for invalid_str, template_debug, result in [
- ('', False, normal_string_result),
- (expected_invalid_str, False, invalid_string_result),
- ('', True, template_debug_result)
- ]:
- with override_settings(TEMPLATE_STRING_IF_INVALID=invalid_str,
- TEMPLATE_DEBUG=template_debug):
- for is_cached in (False, True):
- try:
- try:
- with warnings.catch_warnings():
- # Ignore pending deprecations of loading 'ssi' and 'url' tags from future.
- warnings.filterwarnings("ignore", category=RemovedInDjango19Warning, module='django.templatetags.future')
- # Ignore deprecations of loading 'cycle' and 'firstof' tags from future.
- warnings.filterwarnings("ignore", category=RemovedInDjango20Warning, module="django.templatetags.future")
- test_template = loader.get_template(name)
- except ShouldNotExecuteException:
- failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Template loading invoked method that shouldn't have been invoked." % (is_cached, invalid_str, template_debug, name))
-
- try:
- with warnings.catch_warnings():
- # Ignore deprecations of using the wrong number of variables with the 'for' tag.
- # and warnings for {% url %} reversing by dotted path
- warnings.filterwarnings("ignore", category=RemovedInDjango20Warning, module="django.template.defaulttags")
- # Ignore deprecations of old style unordered_list
- # and removetags.
- warnings.filterwarnings("ignore", category=RemovedInDjango20Warning, module="django.template.defaultfilters")
- # Ignore numpy deprecation warnings (#23890)
- warnings.filterwarnings(
- "ignore",
- "Using a non-integer number instead of an "
- "integer will result in an error in the future",
- DeprecationWarning
- )
- output = self.render(test_template, vals)
- except ShouldNotExecuteException:
- failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Template rendering invoked method that shouldn't have been invoked." % (is_cached, invalid_str, template_debug, name))
- except ContextStackException:
- failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Context stack was left imbalanced" % (is_cached, invalid_str, template_debug, name))
- continue
- except Exception:
- exc_type, exc_value, exc_tb = sys.exc_info()
- if exc_type != result:
- tb = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_tb))
- failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Got %s, exception: %s\n%s" % (is_cached, invalid_str, template_debug, name, exc_type, exc_value, tb))
- continue
- if output != result:
- failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Expected %r, got %r" % (is_cached, invalid_str, template_debug, name, result, output))
-
- Engine.get_default().template_loaders[0].reset()
-
- self.assertEqual(failures, [], "Tests failed:\n%s\n%s" %
- ('-' * 70, ("\n%s\n" % ('-' * 70)).join(failures)))
-
- def render(self, test_template, vals):
- context = template.Context(vals[1])
- before_stack_size = len(context.dicts)
- output = test_template.render(context)
- if len(context.dicts) != before_stack_size:
- raise ContextStackException
- return output
-
-
class TemplateTagLoading(SimpleTestCase):
def setUp(self):
diff --git a/tests/template_tests/syntax_tests/utils.py b/tests/template_tests/utils.py
similarity index 100%
rename from tests/template_tests/syntax_tests/utils.py
rename to tests/template_tests/utils.py