From 5c68870169deec9eff392c53a8e74e3482f02ade Mon Sep 17 00:00:00 2001 From: Preston Timmons Date: Wed, 3 Dec 2014 20:21:11 -0600 Subject: [PATCH] Fixed #23958 -- Rewrote filter tests as unit tests. --- tests/template_tests/filter_tests/__init__.py | 0 tests/template_tests/filter_tests/test_add.py | 46 +++ .../filter_tests/test_addslashes.py | 17 + .../filter_tests/test_autoescape.py | 29 ++ .../filter_tests/test_capfirst.py | 17 + .../filter_tests/test_center.py | 18 + .../filter_tests/test_chaining.py | 86 ++++ tests/template_tests/filter_tests/test_cut.py | 39 ++ .../template_tests/filter_tests/test_date.py | 60 +++ .../filter_tests/test_default.py | 47 +++ .../filter_tests/test_escape.py | 33 ++ .../filter_tests/test_escapejs.py | 20 + .../template_tests/filter_tests/test_first.py | 17 + .../filter_tests/test_floatformat.py | 18 + .../filter_tests/test_force_escape.py | 52 +++ .../filter_tests/test_iriencode.py | 30 ++ .../template_tests/filter_tests/test_join.py | 49 +++ .../template_tests/filter_tests/test_last.py | 17 + .../filter_tests/test_length.py | 43 ++ .../filter_tests/test_length_is.py | 63 +++ .../filter_tests/test_linebreaks.py | 22 + .../filter_tests/test_linebreaksbr.py | 22 + .../filter_tests/test_linenumbers.py | 30 ++ .../template_tests/filter_tests/test_ljust.py | 17 + .../template_tests/filter_tests/test_lower.py | 17 + .../filter_tests/test_make_list.py | 33 ++ .../filter_tests/test_phone2numeric.py | 35 ++ .../filter_tests/test_random.py | 17 + .../filter_tests/test_removetags.py | 37 ++ .../template_tests/filter_tests/test_rjust.py | 17 + .../template_tests/filter_tests/test_safe.py | 16 + .../filter_tests/test_safeseq.py | 17 + .../template_tests/filter_tests/test_slice.py | 17 + .../filter_tests/test_slugify.py | 21 + .../filter_tests/test_stringformat.py | 22 + .../filter_tests/test_striptags.py | 29 ++ .../template_tests/filter_tests/test_time.py | 42 ++ .../filter_tests/test_timesince.py | 118 ++++++ .../filter_tests/test_timeuntil.py | 94 +++++ .../template_tests/filter_tests/test_title.py | 16 + .../filter_tests/test_truncatechars.py | 16 + .../filter_tests/test_truncatewords.py | 18 + .../filter_tests/test_unordered_list.py | 75 ++++ .../template_tests/filter_tests/test_upper.py | 21 + .../filter_tests/test_urlencode.py | 16 + .../filter_tests/test_urlize.py | 70 ++++ .../filter_tests/test_urlizetrunc.py | 38 ++ .../filter_tests/test_wordcount.py | 17 + .../filter_tests/test_wordwrap.py | 18 + .../filter_tests/timezone_utils.py | 17 + tests/template_tests/filters.py | 387 ------------------ .../syntax_tests/test_autoescape.py | 2 +- .../template_tests/syntax_tests/test_basic.py | 2 +- .../syntax_tests/test_builtins.py | 2 +- .../template_tests/syntax_tests/test_cache.py | 2 +- .../syntax_tests/test_comment.py | 2 +- .../template_tests/syntax_tests/test_cycle.py | 2 +- .../syntax_tests/test_exceptions.py | 2 +- .../syntax_tests/test_extends.py | 2 +- .../syntax_tests/test_filter_syntax.py | 2 +- .../syntax_tests/test_filter_tag.py | 2 +- .../syntax_tests/test_firstof.py | 2 +- tests/template_tests/syntax_tests/test_for.py | 2 +- .../template_tests/syntax_tests/test_i18n.py | 2 +- tests/template_tests/syntax_tests/test_if.py | 2 +- .../syntax_tests/test_if_changed.py | 2 +- .../syntax_tests/test_if_equal.py | 2 +- .../syntax_tests/test_include.py | 2 +- .../syntax_tests/test_invalid_string.py | 2 +- .../syntax_tests/test_list_index.py | 2 +- .../template_tests/syntax_tests/test_load.py | 2 +- .../template_tests/syntax_tests/test_lorem.py | 2 +- .../syntax_tests/test_multiline.py | 2 +- .../syntax_tests/test_named_endblock.py | 2 +- tests/template_tests/syntax_tests/test_now.py | 2 +- .../template_tests/syntax_tests/test_numpy.py | 2 +- .../syntax_tests/test_regroup.py | 2 +- .../template_tests/syntax_tests/test_setup.py | 2 +- .../syntax_tests/test_simple_tag.py | 2 +- .../syntax_tests/test_spaceless.py | 2 +- tests/template_tests/syntax_tests/test_ssi.py | 2 +- .../syntax_tests/test_static.py | 2 +- .../syntax_tests/test_template_tag.py | 2 +- tests/template_tests/syntax_tests/test_url.py | 2 +- .../syntax_tests/test_verbatim.py | 2 +- .../syntax_tests/test_width_ratio.py | 2 +- .../template_tests/syntax_tests/test_with.py | 2 +- tests/template_tests/tests.py | 120 ------ .../{syntax_tests => }/utils.py | 0 89 files changed, 1682 insertions(+), 543 deletions(-) create mode 100644 tests/template_tests/filter_tests/__init__.py create mode 100644 tests/template_tests/filter_tests/test_add.py create mode 100644 tests/template_tests/filter_tests/test_addslashes.py create mode 100644 tests/template_tests/filter_tests/test_autoescape.py create mode 100644 tests/template_tests/filter_tests/test_capfirst.py create mode 100644 tests/template_tests/filter_tests/test_center.py create mode 100644 tests/template_tests/filter_tests/test_chaining.py create mode 100644 tests/template_tests/filter_tests/test_cut.py create mode 100644 tests/template_tests/filter_tests/test_date.py create mode 100644 tests/template_tests/filter_tests/test_default.py create mode 100644 tests/template_tests/filter_tests/test_escape.py create mode 100644 tests/template_tests/filter_tests/test_escapejs.py create mode 100644 tests/template_tests/filter_tests/test_first.py create mode 100644 tests/template_tests/filter_tests/test_floatformat.py create mode 100644 tests/template_tests/filter_tests/test_force_escape.py create mode 100644 tests/template_tests/filter_tests/test_iriencode.py create mode 100644 tests/template_tests/filter_tests/test_join.py create mode 100644 tests/template_tests/filter_tests/test_last.py create mode 100644 tests/template_tests/filter_tests/test_length.py create mode 100644 tests/template_tests/filter_tests/test_length_is.py create mode 100644 tests/template_tests/filter_tests/test_linebreaks.py create mode 100644 tests/template_tests/filter_tests/test_linebreaksbr.py create mode 100644 tests/template_tests/filter_tests/test_linenumbers.py create mode 100644 tests/template_tests/filter_tests/test_ljust.py create mode 100644 tests/template_tests/filter_tests/test_lower.py create mode 100644 tests/template_tests/filter_tests/test_make_list.py create mode 100644 tests/template_tests/filter_tests/test_phone2numeric.py create mode 100644 tests/template_tests/filter_tests/test_random.py create mode 100644 tests/template_tests/filter_tests/test_removetags.py create mode 100644 tests/template_tests/filter_tests/test_rjust.py create mode 100644 tests/template_tests/filter_tests/test_safe.py create mode 100644 tests/template_tests/filter_tests/test_safeseq.py create mode 100644 tests/template_tests/filter_tests/test_slice.py create mode 100644 tests/template_tests/filter_tests/test_slugify.py create mode 100644 tests/template_tests/filter_tests/test_stringformat.py create mode 100644 tests/template_tests/filter_tests/test_striptags.py create mode 100644 tests/template_tests/filter_tests/test_time.py create mode 100644 tests/template_tests/filter_tests/test_timesince.py create mode 100644 tests/template_tests/filter_tests/test_timeuntil.py create mode 100644 tests/template_tests/filter_tests/test_title.py create mode 100644 tests/template_tests/filter_tests/test_truncatechars.py create mode 100644 tests/template_tests/filter_tests/test_truncatewords.py create mode 100644 tests/template_tests/filter_tests/test_unordered_list.py create mode 100644 tests/template_tests/filter_tests/test_upper.py create mode 100644 tests/template_tests/filter_tests/test_urlencode.py create mode 100644 tests/template_tests/filter_tests/test_urlize.py create mode 100644 tests/template_tests/filter_tests/test_urlizetrunc.py create mode 100644 tests/template_tests/filter_tests/test_wordcount.py create mode 100644 tests/template_tests/filter_tests/test_wordwrap.py create mode 100644 tests/template_tests/filter_tests/timezone_utils.py delete mode 100644 tests/template_tests/filters.py rename tests/template_tests/{syntax_tests => }/utils.py (100%) 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 &lt 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&ampy") 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&amp;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&amp;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\t
  • <y
  • \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\t
  • \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\t
  • \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\t
  • \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\t
  • \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\t
  • <y
  • \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\t
  • \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\t
  • \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\t
  • \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\t
  • \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 &AMP; 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&ampy"), - - # 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&amp;y"), - 'filter-force-escape04': ('{{ a|force_escape|force_escape }}', {"a": "x&y"}, "x&amp;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\t
  • <y
  • \n\t
\n\t"), - 'filter-unordered_list02': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [["x>\n\t
    \n\t\t
  • \n\t
\n\t"), - 'filter-unordered_list03': ('{{ a|unordered_list }}', {"a": ["x>", [[mark_safe("x>\n\t
    \n\t\t
  • \n\t
\n\t"), - 'filter-unordered_list04': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [[mark_safe("x>\n\t
    \n\t\t
  • \n\t
\n\t"), - 'filter-unordered_list05': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [["x>\n\t
    \n\t\t
  • \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 &lt 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