diff options
| author | Jake Howard <RealOrangeOne@users.noreply.github.com> | 2024-11-19 17:35:02 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-19 14:35:02 -0300 |
| commit | 4c452cc377f6f43acd90c6e54826ebd2e6219b0d (patch) | |
| tree | ad216420ba1d3564e736add4e5716fe259168137 /tests | |
| parent | 9543c605c38b80b3ace3836b110e9e1938a44a97 (diff) | |
Fixed #35535 -- Added template tag decorator simple_block_tag().
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/template_tests/templatetags/custom.py | 174 | ||||
| -rw-r--r-- | tests/template_tests/test_custom.py | 337 | ||||
| -rw-r--r-- | tests/template_tests/test_library.py | 41 |
3 files changed, 552 insertions, 0 deletions
diff --git a/tests/template_tests/templatetags/custom.py b/tests/template_tests/templatetags/custom.py index 8d1130ae78..2c0a1b7f3f 100644 --- a/tests/template_tests/templatetags/custom.py +++ b/tests/template_tests/templatetags/custom.py @@ -20,6 +20,16 @@ def make_data_div(value): return '<div data-name="%s"></div>' % value +@register.simple_block_tag +def div(content, id="test"): + return format_html("<div id='{}'>{}</div>", id, content) + + +@register.simple_block_tag(end_name="divend") +def div_custom_end(content): + return format_html("<div>{}</div>", content) + + @register.filter def noop(value, param=None): """A noop filter that always return its first argument and does nothing with @@ -51,6 +61,12 @@ def one_param(arg): one_param.anything = "Expected one_param __dict__" +@register.simple_block_tag +def one_param_block(content, arg): + """Expected one_param_block __doc__""" + return f"one_param_block - Expected result: {arg} with content {content}" + + @register.simple_tag(takes_context=False) def explicit_no_context(arg): """Expected explicit_no_context __doc__""" @@ -60,6 +76,12 @@ def explicit_no_context(arg): explicit_no_context.anything = "Expected explicit_no_context __dict__" +@register.simple_block_tag(takes_context=False) +def explicit_no_context_block(content, arg): + """Expected explicit_no_context_block __doc__""" + return f"explicit_no_context_block - Expected result: {arg} with content {content}" + + @register.simple_tag(takes_context=True) def no_params_with_context(context): """Expected no_params_with_context __doc__""" @@ -72,6 +94,15 @@ def no_params_with_context(context): no_params_with_context.anything = "Expected no_params_with_context __dict__" +@register.simple_block_tag(takes_context=True) +def no_params_with_context_block(context, content): + """Expected no_params_with_context_block __doc__""" + return ( + "no_params_with_context_block - Expected result (context value: %s) " + "(content value: %s)" % (context["value"], content) + ) + + @register.simple_tag(takes_context=True) def params_and_context(context, arg): """Expected params_and_context __doc__""" @@ -84,6 +115,20 @@ def params_and_context(context, arg): params_and_context.anything = "Expected params_and_context __dict__" +@register.simple_block_tag(takes_context=True) +def params_and_context_block(context, content, arg): + """Expected params_and_context_block __doc__""" + return ( + "params_and_context_block - Expected result (context value: %s) " + "(content value: %s): %s" + % ( + context["value"], + content, + arg, + ) + ) + + @register.simple_tag def simple_two_params(one, two): """Expected simple_two_params __doc__""" @@ -93,16 +138,48 @@ def simple_two_params(one, two): simple_two_params.anything = "Expected simple_two_params __dict__" +@register.simple_block_tag +def simple_two_params_block(content, one, two): + """Expected simple_two_params_block __doc__""" + return "simple_two_params_block - Expected result (content value: %s): %s, %s" % ( + content, + one, + two, + ) + + @register.simple_tag def simple_keyword_only_param(*, kwarg): return "simple_keyword_only_param - Expected result: %s" % kwarg +@register.simple_block_tag +def simple_keyword_only_param_block(content, *, kwarg): + return ( + "simple_keyword_only_param_block - Expected result (content value: %s): %s" + % ( + content, + kwarg, + ) + ) + + @register.simple_tag def simple_keyword_only_default(*, kwarg=42): return "simple_keyword_only_default - Expected result: %s" % kwarg +@register.simple_block_tag +def simple_keyword_only_default_block(content, *, kwarg=42): + return ( + "simple_keyword_only_default_block - Expected result (content value: %s): %s" + % ( + content, + kwarg, + ) + ) + + @register.simple_tag def simple_one_default(one, two="hi"): """Expected simple_one_default __doc__""" @@ -112,6 +189,16 @@ def simple_one_default(one, two="hi"): simple_one_default.anything = "Expected simple_one_default __dict__" +@register.simple_block_tag +def simple_one_default_block(content, one, two="hi"): + """Expected simple_one_default_block __doc__""" + return "simple_one_default_block - Expected result (content value: %s): %s, %s" % ( + content, + one, + two, + ) + + @register.simple_tag def simple_unlimited_args(one, two="hi", *args): """Expected simple_unlimited_args __doc__""" @@ -123,6 +210,15 @@ def simple_unlimited_args(one, two="hi", *args): simple_unlimited_args.anything = "Expected simple_unlimited_args __dict__" +@register.simple_block_tag +def simple_unlimited_args_block(content, one, two="hi", *args): + """Expected simple_unlimited_args_block __doc__""" + return "simple_unlimited_args_block - Expected result (content value: %s): %s" % ( + content, + ", ".join(str(arg) for arg in [one, two, *args]), + ) + + @register.simple_tag def simple_only_unlimited_args(*args): """Expected simple_only_unlimited_args __doc__""" @@ -134,6 +230,18 @@ def simple_only_unlimited_args(*args): simple_only_unlimited_args.anything = "Expected simple_only_unlimited_args __dict__" +@register.simple_block_tag +def simple_only_unlimited_args_block(content, *args): + """Expected simple_only_unlimited_args_block __doc__""" + return ( + "simple_only_unlimited_args_block - Expected result (content value: %s): %s" + % ( + content, + ", ".join(str(arg) for arg in args), + ) + ) + + @register.simple_tag def simple_unlimited_args_kwargs(one, two="hi", *args, **kwargs): """Expected simple_unlimited_args_kwargs __doc__""" @@ -146,6 +254,38 @@ def simple_unlimited_args_kwargs(one, two="hi", *args, **kwargs): simple_unlimited_args_kwargs.anything = "Expected simple_unlimited_args_kwargs __dict__" +@register.simple_block_tag +def simple_unlimited_args_kwargs_block(content, one, two="hi", *args, **kwargs): + """Expected simple_unlimited_args_kwargs_block __doc__""" + return ( + "simple_unlimited_args_kwargs_block - Expected result (content value: %s): " + "%s / %s" + % ( + content, + ", ".join(str(arg) for arg in [one, two, *args]), + ", ".join("%s=%s" % (k, v) for (k, v) in kwargs.items()), + ) + ) + + +@register.simple_block_tag(takes_context=True) +def simple_block_tag_without_context_parameter(arg): + """Expected simple_block_tag_without_context_parameter __doc__""" + return "Expected result" + + +@register.simple_block_tag +def simple_tag_without_content_parameter(arg): + """Expected simple_tag_without_content_parameter __doc__""" + return "Expected result" + + +@register.simple_block_tag(takes_context=True) +def simple_tag_with_context_without_content_parameter(context, arg): + """Expected simple_tag_with_context_without_content_parameter __doc__""" + return "Expected result" + + @register.simple_tag(takes_context=True) def simple_tag_without_context_parameter(arg): """Expected simple_tag_without_context_parameter __doc__""" @@ -157,6 +297,12 @@ simple_tag_without_context_parameter.anything = ( ) +@register.simple_block_tag(takes_context=True) +def simple_tag_takes_context_without_params_block(): + """Expected simple_tag_takes_context_without_params_block __doc__""" + return "Expected result" + + @register.simple_tag(takes_context=True) def simple_tag_takes_context_without_params(): """Expected simple_tag_takes_context_without_params __doc__""" @@ -168,24 +314,52 @@ simple_tag_takes_context_without_params.anything = ( ) +@register.simple_block_tag +def simple_block_tag_without_content(): + return "Expected result" + + +@register.simple_block_tag(takes_context=True) +def simple_block_tag_with_context_without_content(): + return "Expected result" + + @register.simple_tag(takes_context=True) def escape_naive(context): """A tag that doesn't even think about escaping issues""" return "Hello {}!".format(context["name"]) +@register.simple_block_tag(takes_context=True) +def escape_naive_block(context, content): + """A block tag that doesn't even think about escaping issues""" + return "Hello {}: {}!".format(context["name"], content) + + @register.simple_tag(takes_context=True) def escape_explicit(context): """A tag that uses escape explicitly""" return escape("Hello {}!".format(context["name"])) +@register.simple_block_tag(takes_context=True) +def escape_explicit_block(context, content): + """A block tag that uses escape explicitly""" + return escape("Hello {}: {}!".format(context["name"], content)) + + @register.simple_tag(takes_context=True) def escape_format_html(context): """A tag that uses format_html""" return format_html("Hello {0}!", context["name"]) +@register.simple_block_tag(takes_context=True) +def escape_format_html_block(context, content): + """A block tag that uses format_html""" + return format_html("Hello {0}: {1}!", context["name"], content) + + @register.simple_tag(takes_context=True) def current_app(context): return str(context.current_app) diff --git a/tests/template_tests/test_custom.py b/tests/template_tests/test_custom.py index 1697d16ef5..9ec27b481f 100644 --- a/tests/template_tests/test_custom.py +++ b/tests/template_tests/test_custom.py @@ -243,6 +243,343 @@ class SimpleTagTests(TagTestCase): ) +class SimpleBlockTagTests(TagTestCase): + def test_simple_block_tags(self): + c = Context({"value": 42}) + + templates = [ + ( + "{% load custom %}{% div %}content{% enddiv %}", + "<div id='test'>content</div>", + ), + ( + "{% load custom %}{% one_param_block 37 %}inner" + "{% endone_param_block %}", + "one_param_block - Expected result: 37 with content inner", + ), + ( + "{% load custom %}{% explicit_no_context_block 37 %}inner" + "{% endexplicit_no_context_block %}", + "explicit_no_context_block - Expected result: 37 with content inner", + ), + ( + "{% load custom %}{% no_params_with_context_block %}inner" + "{% endno_params_with_context_block %}", + "no_params_with_context_block - Expected result (context value: 42) " + "(content value: inner)", + ), + ( + "{% load custom %}{% params_and_context_block 37 %}inner" + "{% endparams_and_context_block %}", + "params_and_context_block - Expected result (context value: 42) " + "(content value: inner): 37", + ), + ( + "{% load custom %}{% simple_two_params_block 37 42 %}inner" + "{% endsimple_two_params_block %}", + "simple_two_params_block - Expected result (content value: inner): " + "37, 42", + ), + ( + "{% load custom %}{% simple_keyword_only_param_block kwarg=37 %}thirty " + "seven{% endsimple_keyword_only_param_block %}", + "simple_keyword_only_param_block - Expected result (content value: " + "thirty seven): 37", + ), + ( + "{% load custom %}{% simple_keyword_only_default_block %}forty two" + "{% endsimple_keyword_only_default_block %}", + "simple_keyword_only_default_block - Expected result (content value: " + "forty two): 42", + ), + ( + "{% load custom %}{% simple_keyword_only_default_block kwarg=37 %}" + "thirty seven{% endsimple_keyword_only_default_block %}", + "simple_keyword_only_default_block - Expected result (content value: " + "thirty seven): 37", + ), + ( + "{% load custom %}{% simple_one_default_block 37 %}inner" + "{% endsimple_one_default_block %}", + "simple_one_default_block - Expected result (content value: inner): " + "37, hi", + ), + ( + '{% load custom %}{% simple_one_default_block 37 two="hello" %}inner' + "{% endsimple_one_default_block %}", + "simple_one_default_block - Expected result (content value: inner): " + "37, hello", + ), + ( + '{% load custom %}{% simple_one_default_block one=99 two="hello" %}' + "inner{% endsimple_one_default_block %}", + "simple_one_default_block - Expected result (content value: inner): " + "99, hello", + ), + ( + "{% load custom %}{% simple_one_default_block 37 42 %}inner" + "{% endsimple_one_default_block %}", + "simple_one_default_block - Expected result (content value: inner): " + "37, 42", + ), + ( + "{% load custom %}{% simple_unlimited_args_block 37 %}thirty seven" + "{% endsimple_unlimited_args_block %}", + "simple_unlimited_args_block - Expected result (content value: thirty " + "seven): 37, hi", + ), + ( + "{% load custom %}{% simple_unlimited_args_block 37 42 56 89 %}numbers" + "{% endsimple_unlimited_args_block %}", + "simple_unlimited_args_block - Expected result " + "(content value: numbers): 37, 42, 56, 89", + ), + ( + "{% load custom %}{% simple_only_unlimited_args_block %}inner" + "{% endsimple_only_unlimited_args_block %}", + "simple_only_unlimited_args_block - Expected result (content value: " + "inner): ", + ), + ( + "{% load custom %}{% simple_only_unlimited_args_block 37 42 56 89 %}" + "numbers{% endsimple_only_unlimited_args_block %}", + "simple_only_unlimited_args_block - Expected result " + "(content value: numbers): 37, 42, 56, 89", + ), + ( + "{% load custom %}" + '{% simple_unlimited_args_kwargs_block 37 40|add:2 56 eggs="scrambled" ' + "four=1|add:3 %}inner content" + "{% endsimple_unlimited_args_kwargs_block %}", + "simple_unlimited_args_kwargs_block - Expected result (content value: " + "inner content): 37, 42, 56 / eggs=scrambled, four=4", + ), + ] + + for entry in templates: + with self.subTest(entry[0]): + t = self.engine.from_string(entry[0]) + self.assertEqual(t.render(c), entry[1]) + + def test_simple_block_tag_errors(self): + errors = [ + ( + "'simple_one_default_block' received unexpected keyword argument " + "'three'", + "{% load custom %}" + '{% simple_one_default_block 99 two="hello" three="foo" %}' + "{% endsimple_one_default_block %}", + ), + ( + "'simple_two_params_block' received too many positional arguments", + "{% load custom %}{% simple_two_params_block 37 42 56 %}" + "{% endsimple_two_params_block %}", + ), + ( + "'simple_one_default_block' received too many positional arguments", + "{% load custom %}{% simple_one_default_block 37 42 56 %}" + "{% endsimple_one_default_block %}", + ), + ( + "'simple_keyword_only_param_block' did not receive value(s) for the " + "argument(s): 'kwarg'", + "{% load custom %}{% simple_keyword_only_param_block %}" + "{% endsimple_keyword_only_param_block %}", + ), + ( + "'simple_keyword_only_param_block' received multiple values for " + "keyword argument 'kwarg'", + "{% load custom %}" + "{% simple_keyword_only_param_block kwarg=42 kwarg=37 %}" + "{% endsimple_keyword_only_param_block %}", + ), + ( + "'simple_keyword_only_default_block' received multiple values for " + "keyword argument 'kwarg'", + "{% load custom %}{% simple_keyword_only_default_block kwarg=42 " + "kwarg=37 %}{% endsimple_keyword_only_default_block %}", + ), + ( + "'simple_unlimited_args_kwargs_block' received some positional " + "argument(s) after some keyword argument(s)", + "{% load custom %}" + '{% simple_unlimited_args_kwargs_block 37 40|add:2 eggs="scrambled" 56 ' + "four=1|add:3 %}{% endsimple_unlimited_args_kwargs_block %}", + ), + ( + "'simple_unlimited_args_kwargs_block' received multiple values for " + "keyword argument 'eggs'", + "{% load custom %}" + "{% simple_unlimited_args_kwargs_block 37 " + 'eggs="scrambled" eggs="scrambled" %}' + "{% endsimple_unlimited_args_kwargs_block %}", + ), + ( + "Unclosed tag on line 1: 'div'. Looking for one of: enddiv.", + "{% load custom %}{% div %}Some content", + ), + ( + "Unclosed tag on line 1: 'simple_one_default_block'. Looking for one " + "of: endsimple_one_default_block.", + "{% load custom %}{% simple_one_default_block %}Some content", + ), + ( + "'simple_tag_without_content_parameter' must have a first argument " + "of 'content'", + "{% load custom %}{% simple_tag_without_content_parameter %}", + ), + ( + "'simple_tag_with_context_without_content_parameter' is decorated with " + "takes_context=True so it must have a first argument of 'context' and " + "a second argument of 'content'", + "{% load custom %}" + "{% simple_tag_with_context_without_content_parameter %}", + ), + ] + + for entry in errors: + with self.subTest(entry[1]): + with self.assertRaisesMessage(TemplateSyntaxError, entry[0]): + self.engine.from_string(entry[1]) + + def test_simple_block_tag_escaping_autoescape_off(self): + c = Context({"name": "Jack & Jill"}, autoescape=False) + t = self.engine.from_string( + "{% load custom %}{% escape_naive_block %}{{ name }} again" + "{% endescape_naive_block %}" + ) + self.assertEqual(t.render(c), "Hello Jack & Jill: Jack & Jill again!") + + def test_simple_block_tag_naive_escaping(self): + c = Context({"name": "Jack & Jill"}) + t = self.engine.from_string( + "{% load custom %}{% escape_naive_block %}{{ name }} again" + "{% endescape_naive_block %}" + ) + self.assertEqual( + t.render(c), "Hello Jack & Jill: Jack &amp; Jill again!" + ) + + def test_simple_block_tag_explicit_escaping(self): + # Check we don't double escape + c = Context({"name": "Jack & Jill"}) + t = self.engine.from_string( + "{% load custom %}{% escape_explicit_block %}again" + "{% endescape_explicit_block %}" + ) + self.assertEqual(t.render(c), "Hello Jack & Jill: again!") + + def test_simple_block_tag_format_html_escaping(self): + # Check we don't double escape + c = Context({"name": "Jack & Jill"}) + t = self.engine.from_string( + "{% load custom %}{% escape_format_html_block %}again" + "{% endescape_format_html_block %}" + ) + self.assertEqual(t.render(c), "Hello Jack & Jill: again!") + + def test_simple_block_tag_missing_context(self): + # The 'context' parameter must be present when takes_context is True + msg = ( + "'simple_block_tag_without_context_parameter' is decorated with " + "takes_context=True so it must have a first argument of 'context'" + ) + with self.assertRaisesMessage(TemplateSyntaxError, msg): + self.engine.from_string( + "{% load custom %}{% simple_block_tag_without_context_parameter 123 %}" + "{% endsimple_block_tag_without_context_parameter %}" + ) + + def test_simple_block_tag_missing_context_no_params(self): + msg = ( + "'simple_tag_takes_context_without_params_block' is decorated with " + "takes_context=True so it must have a first argument of 'context'" + ) + with self.assertRaisesMessage(TemplateSyntaxError, msg): + self.engine.from_string( + "{% load custom %}{% simple_tag_takes_context_without_params_block %}" + "{% endsimple_tag_takes_context_without_params_block %}" + ) + + def test_simple_block_tag_missing_content(self): + # The 'content' parameter must be present when takes_context is True + msg = ( + "'simple_block_tag_without_content' must have a first argument of 'content'" + ) + with self.assertRaisesMessage(TemplateSyntaxError, msg): + self.engine.from_string( + "{% load custom %}{% simple_block_tag_without_content %}" + "{% endsimple_block_tag_without_content %}" + ) + + def test_simple_block_tag_with_context_missing_content(self): + # The 'content' parameter must be present when takes_context is True + msg = "'simple_block_tag_with_context_without_content' is decorated with " + "takes_context=True so it must have a first argument of 'context' and a " + "second argument of 'content'" + with self.assertRaisesMessage(TemplateSyntaxError, msg): + self.engine.from_string( + "{% load custom %}{% simple_block_tag_with_context_without_content %}" + "{% endsimple_block_tag_with_context_without_content %}" + ) + + def test_simple_block_gets_context(self): + c = Context({"name": "Jack & Jill"}) + t = self.engine.from_string("{% load custom %}{% div %}{{ name }}{% enddiv %}") + self.assertEqual(t.render(c), "<div id='test'>Jack & Jill</div>") + + def test_simple_block_capture_as(self): + c = Context({"name": "Jack & Jill"}) + t = self.engine.from_string( + "{% load custom %}{% div as div_content %}{{ name }}{% enddiv %}" + "My div is: {{ div_content }}" + ) + self.assertEqual(t.render(c), "My div is: <div id='test'>Jack & Jill</div>") + + def test_simple_block_nested(self): + c = Context({"name": "Jack & Jill"}) + t = self.engine.from_string( + "{% load custom %}Start{% div id='outer' %}Before{% div id='inner' %}" + "{{ name }}{% enddiv %}After{% enddiv %}End" + ) + self.assertEqual( + t.render(c), + "Start<div id='outer'>Before<div id='inner'>Jack & Jill</div>After" + "</div>End", + ) + + def test_different_simple_block_nested(self): + c = Context({"name": "Jack & Jill"}) + t = self.engine.from_string( + "{% load custom %}Start{% div id='outer' %}Before" + "{% simple_keyword_only_default_block %}Inner" + "{% endsimple_keyword_only_default_block %}" + "After{% enddiv %}End" + ) + self.assertEqual( + t.render(c), + "Start<div id='outer'>Before" + "simple_keyword_only_default_block - Expected result (content value: " + "Inner): 42After</div>End", + ) + + def test_custom_end_tag(self): + c = Context({"name": "Jack & Jill"}) + t = self.engine.from_string( + "{% load custom %}{% div_custom_end %}{{ name }}{% divend %}" + ) + self.assertEqual(t.render(c), "<div>Jack & Jill</div>") + + with self.assertRaisesMessage( + TemplateSyntaxError, + "'enddiv_custom_end', expected 'divend'. Did you forget to register or " + "load this tag?", + ): + self.engine.from_string( + "{% load custom %}{% div_custom_end %}{{ name }}{% enddiv_custom_end %}" + ) + + class InclusionTagTests(TagTestCase): def test_inclusion_tags(self): c = Context({"value": 42}) diff --git a/tests/template_tests/test_library.py b/tests/template_tests/test_library.py index 7376832879..98e9d228aa 100644 --- a/tests/template_tests/test_library.py +++ b/tests/template_tests/test_library.py @@ -120,6 +120,47 @@ class SimpleTagRegistrationTests(SimpleTestCase): self.assertTrue(hasattr(func_wrapped, "cache_info")) +class SimpleBlockTagRegistrationTests(SimpleTestCase): + def setUp(self): + self.library = Library() + + def test_simple_block_tag(self): + @self.library.simple_block_tag + def func(content): + return content + + self.assertIn("func", self.library.tags) + + def test_simple_block_tag_parens(self): + @self.library.simple_tag() + def func(content): + return content + + self.assertIn("func", self.library.tags) + + def test_simple_block_tag_name_kwarg(self): + @self.library.simple_block_tag(name="name") + def func(content): + return content + + self.assertIn("name", self.library.tags) + + def test_simple_block_tag_invalid(self): + msg = "Invalid arguments provided to simple_block_tag" + with self.assertRaisesMessage(ValueError, msg): + self.library.simple_block_tag("invalid") + + def test_simple_tag_wrapped(self): + @self.library.simple_block_tag + @functools.lru_cache(maxsize=32) + def func(content): + return content + + func_wrapped = self.library.tags["func"].__wrapped__ + self.assertIs(func_wrapped, func) + self.assertTrue(hasattr(func_wrapped, "cache_info")) + + class TagRegistrationTests(SimpleTestCase): def setUp(self): self.library = Library() |
